Files
015/backend/internal/controllers/share.go

245 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package controllers
import (
"backend/internal/utils"
"encoding/json"
"pkg/models"
u "pkg/utils"
"strings"
"time"
"github.com/hibiken/asynq"
"github.com/labstack/echo/v5"
gonanoid "github.com/matoous/go-nanoid/v2"
"github.com/spf13/cast"
)
type CreateShareProps struct {
Type models.ShareType `json:"type"`
// ShareId string `json:"id"`
Config ShareConfig `json:"config"`
Data string `json:"data"`
FileName string `json:"file_name"`
}
type ShareConfig struct {
ExpireAt int `json:"expire_time"` // 分钟
ViewNum int64 `json:"download_nums"`
HasPassword bool `json:"has_password"`
Password string `json:"password"`
HasNotify bool `json:"has_notify"`
NotifyTypes []string `json:"notify_types"`
NotifyEmails []string `json:"notify_emails"`
NotifyWebhooks []models.NotifyWebhook `json:"notify_webhooks"`
Locale string `json:"locale"`
HasPickupCode bool `json:"has_pickup_code"`
}
func CreateShareInfo(c *echo.Context) error {
owner, _ := echo.ContextGet[string](c, "auth")
r := new(CreateShareProps)
if err := c.Bind(r); err != nil {
return utils.HTTPErrorHandler(c, err)
}
if r.Config.ExpireAt < 1 {
return utils.HTTPErrorHandler(c, ErrInvalidRequest)
}
ExpireTime := time.Now().Add(time.Duration(r.Config.ExpireAt) * time.Minute)
if r.Data == "" || (r.Type != models.ShareTypeFile && r.Type != models.ShareTypeText) || ExpireTime.Before(time.Now()) || r.Config.ViewNum < 1 {
return utils.HTTPErrorHandler(c, ErrInvalidRequest)
}
id, err := gonanoid.New()
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
if r.Type == models.ShareTypeFile {
fileInfo, err := models.GetRedisFileInfo(r.Data)
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
if fileInfo == nil {
return utils.HTTPErrorHandler(c, ErrShareFileNotFound)
}
if fileInfo.FileType != models.FileTypeUpload {
return utils.HTTPErrorHandler(c, ErrInvalidShareFileState)
}
}
password := ""
if r.Config.Password != "" {
hash, err := utils.GeneratePasswordHash(r.Config.Password)
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
password = hash
}
var notifyEmails []string
var notifyWebhooks []models.NotifyWebhook
if r.Config.HasNotify {
hasEmail, hasWebhook := false, false
for _, nt := range r.Config.NotifyTypes {
switch nt {
case "email":
hasEmail = true
case "webhook":
hasWebhook = true
default:
return utils.HTTPErrorHandler(c, ErrInvalidRequest)
}
}
if hasEmail {
notifyEmails = r.Config.NotifyEmails
}
if hasWebhook {
notifyWebhooks = r.Config.NotifyWebhooks
}
}
_, err = models.SetRedisShareInfo(id, func(shareInfo *models.RedisShareInfo) *models.RedisShareInfo {
shareInfo.Data = r.Data
shareInfo.Type = r.Type
shareInfo.CreatedAt = time.Now().Unix()
shareInfo.Owner = owner
shareInfo.ViewNum = r.Config.ViewNum
shareInfo.Password = password
shareInfo.FileName = r.FileName
shareInfo.NotifyEmails = notifyEmails
shareInfo.NotifyWebhooks = notifyWebhooks
shareInfo.Locale = r.Config.Locale
shareInfo.ExpireAt = ExpireTime.Unix()
return shareInfo
})
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
var pickupCode string
if r.Config.HasPickupCode {
for {
pickupCode = utils.GeneratePickupCode()
ok, err := models.SetRedisPickupData(pickupCode, id)
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
if !ok {
continue
}
break
}
}
if r.Type == models.ShareTypeFile {
shareIDs, err := models.GetRedisFileShareRelational(r.Data)
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
shareIDs = append(shareIDs, id)
err = models.SetRedisFileShareRelational(r.Data, shareIDs)
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
client := u.GetQueueClient()
json, err := json.Marshal(map[string]any{"share_id": id, "file_id": r.Data})
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
// 这里延时分享过期时间基础上加下载窗口期后1小时删除防止用户过期前几分钟才开始下载下载一半文件不见了
downloadWindow := u.GetEnvWithDefault("share.download_window", "12")
deleteTime := time.Duration(r.Config.ExpireAt)*time.Minute + cast.ToDuration(downloadWindow+"h") + 1*time.Hour
_, err = client.Enqueue(asynq.NewTask("share:remove", json), asynq.ProcessIn(deleteTime))
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
}
// 统计分享数
currentDate := time.Now().Format("2006-01-02")
_, err = models.SetRedisStat(currentDate, func(stat *models.StatData) *models.StatData {
stat.ShareNum += 1
return stat
})
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
return utils.HTTPSuccessHandler(c, map[string]any{
"id": id,
"file_name": r.FileName,
"download_nums": r.Config.ViewNum,
"expire_at": ExpireTime.Unix(),
"pickup_code": pickupCode,
})
}
type GetShareProps struct {
ShareId string `param:"id"`
}
func GetShareInfo(c *echo.Context) error {
shareId := c.Param("id")
if shareId == "" {
return utils.HTTPErrorHandler(c, ErrInvalidRequest)
}
shareInfo, err := models.GetRedisShareInfo(shareId)
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
if shareInfo == nil || shareInfo.ViewNum < 1 {
return utils.HTTPErrorHandler(c, ErrShareNotFound)
}
if shareInfo.Type == models.ShareTypeFile {
fileInfo, err := models.GetRedisFileInfo(shareInfo.Data)
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
if fileInfo == nil {
return utils.HTTPErrorHandler(c, ErrShareFileNotFound)
}
if fileInfo.FileType != models.FileTypeUpload {
return utils.HTTPErrorHandler(c, ErrInvalidShareFileState)
}
return utils.HTTPSuccessHandler(c, map[string]any{
"id": shareId,
"type": shareInfo.Type,
"name": shareInfo.FileName,
"download_nums": shareInfo.ViewNum,
"has_password": shareInfo.Password != "",
"expire_at": shareInfo.ExpireAt,
"owner": shareInfo.Owner,
"size": fileInfo.FileSize,
"mime_type": fileInfo.MimeType,
})
}
return utils.HTTPSuccessHandler(c, map[string]any{
"id": shareId,
"type": shareInfo.Type,
"name": shareInfo.FileName,
"download_nums": shareInfo.ViewNum,
"has_password": shareInfo.Password != "",
"expire_at": shareInfo.ExpireAt,
"owner": shareInfo.Owner,
})
}
func GetShareByPickupCode(c *echo.Context) error {
pickupCode := c.Param("code")
if pickupCode == "" {
return utils.HTTPErrorHandler(c, ErrInvalidRequest)
}
shareId, err := models.GetRedisPickupData(strings.ToUpper(pickupCode))
if err != nil {
return utils.HTTPErrorHandler(c, err)
}
if shareId == "" {
return utils.HTTPErrorHandler(c, ErrShareNotFound)
}
return utils.HTTPSuccessHandler(c, map[string]any{
"share_id": shareId,
})
}