mirror of
https://github.com/wgh136/nysoure.git
synced 2025-09-27 12:17:24 +00:00
Add temporary token generation for secure file downloads
This commit is contained in:
@@ -1,9 +1,12 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"nysoure/server/model"
|
"nysoure/server/model"
|
||||||
"nysoure/server/service"
|
"nysoure/server/service"
|
||||||
|
"nysoure/server/utils"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -21,6 +24,7 @@ func AddFileRoutes(router fiber.Router) {
|
|||||||
fileGroup.Get("/:id", getFile)
|
fileGroup.Get("/:id", getFile)
|
||||||
fileGroup.Put("/:id", updateFile)
|
fileGroup.Put("/:id", updateFile)
|
||||||
fileGroup.Delete("/:id", deleteFile)
|
fileGroup.Delete("/:id", deleteFile)
|
||||||
|
fileGroup.Get("/download/local", downloadLocalFile)
|
||||||
fileGroup.Get("/download/:id", downloadFile)
|
fileGroup.Get("/download/:id", downloadFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,6 +208,40 @@ func downloadFile(c fiber.Ctx) error {
|
|||||||
if strings.HasPrefix(s, "http") {
|
if strings.HasPrefix(s, "http") {
|
||||||
return c.Redirect().Status(fiber.StatusFound).To(s)
|
return c.Redirect().Status(fiber.StatusFound).To(s)
|
||||||
}
|
}
|
||||||
c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
|
data := map[string]string{
|
||||||
return c.SendFile(s)
|
"path": s,
|
||||||
|
"filename": filename,
|
||||||
|
}
|
||||||
|
j, _ := json.Marshal(data)
|
||||||
|
token, err := utils.GenerateTemporaryToken(string(j))
|
||||||
|
if err != nil {
|
||||||
|
return model.NewInternalServerError("Failed to generate download token")
|
||||||
|
}
|
||||||
|
return c.Redirect().Status(fiber.StatusFound).To(fmt.Sprintf("%s/api/files/download/local?token=%s", c.BaseURL(), token))
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadLocalFile(c fiber.Ctx) error {
|
||||||
|
token := c.Query("token")
|
||||||
|
if token == "" {
|
||||||
|
return model.NewRequestError("Download token is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := utils.ParseTemporaryToken(token)
|
||||||
|
if err != nil {
|
||||||
|
return model.NewRequestError("Invalid or expired download token")
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileData map[string]string
|
||||||
|
if err := json.Unmarshal([]byte(data), &fileData); err != nil {
|
||||||
|
return model.NewInternalServerError("Failed to parse download data")
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fileData["path"]
|
||||||
|
filename := fileData["filename"]
|
||||||
|
|
||||||
|
c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", url.PathEscape(filename)))
|
||||||
|
|
||||||
|
return c.SendFile(path, fiber.SendFile{
|
||||||
|
ByteRange: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@@ -403,6 +403,7 @@ func GetFile(fid string) (*model.FileView, error) {
|
|||||||
return file.ToView(), nil
|
return file.ToView(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DownloadFile handles the file download request. Return a presigned URL or a direct file path.
|
||||||
func DownloadFile(ip, fid, cfToken string) (string, string, error) {
|
func DownloadFile(ip, fid, cfToken string) (string, string, error) {
|
||||||
passed, err := verifyCfToken(cfToken)
|
passed, err := verifyCfToken(cfToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -60,3 +60,37 @@ func ParseToken(token string) (uint, error) {
|
|||||||
}
|
}
|
||||||
return 0, errors.New("invalid token")
|
return 0, errors.New("invalid token")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateTemporaryToken creates a JWT token that expires in 15 minutes
|
||||||
|
func GenerateTemporaryToken(data string) (string, error) {
|
||||||
|
t := jwt.NewWithClaims(jwt.SigningMethodHS256,
|
||||||
|
jwt.MapClaims{
|
||||||
|
"data": data,
|
||||||
|
"exp": time.Now().Add(15 * time.Minute).Unix(),
|
||||||
|
})
|
||||||
|
s, err := t.SignedString(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseTemporaryToken parses a JWT token and returns the data if valid
|
||||||
|
func ParseTemporaryToken(token string) (string, error) {
|
||||||
|
t, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
|
||||||
|
return key, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if claims, ok := t.Claims.(jwt.MapClaims); ok && t.Valid {
|
||||||
|
data := claims["data"].(string)
|
||||||
|
expF := claims["exp"].(float64)
|
||||||
|
exp := time.Unix(int64(expF), 0)
|
||||||
|
if time.Now().After(exp) {
|
||||||
|
return "", errors.New("token expired")
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
return "", errors.New("invalid token")
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user