4009214cbc
CI / docker (push) Successful in 7m4s
引入用户端对话/生图与管理端 Provider 配置,统一 invoke 编排、Gateway、请求日志与模型管理;前端按设计规范拆分布局,Go module 更名为 github.com/rose_cat707/Atlas;生图长任务 SSE 推送;管理端登录防爆破;协议与服务类型联动;Replicate 模型同步修复;用户端创作台 UI 重构;管理端创作历史替代请求日志。 Co-authored-by: Cursor <cursoragent@cursor.com>
86 lines
1.9 KiB
Go
86 lines
1.9 KiB
Go
package asset
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"mime"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type Signer struct {
|
|
secret string
|
|
}
|
|
|
|
func NewSigner(secret string) *Signer {
|
|
return &Signer{secret: secret}
|
|
}
|
|
|
|
func (s *Signer) Sign(fileID int64, exp int64) string {
|
|
mac := hmac.New(sha256.New, []byte(s.secret))
|
|
mac.Write([]byte(fmt.Sprintf("%d:%d", fileID, exp)))
|
|
return hex.EncodeToString(mac.Sum(nil))
|
|
}
|
|
|
|
func (s *Signer) Verify(fileID int64, exp int64, sig string) bool {
|
|
if time.Now().Unix() > exp {
|
|
return false
|
|
}
|
|
expected := s.Sign(fileID, exp)
|
|
return hmac.Equal([]byte(expected), []byte(sig))
|
|
}
|
|
|
|
func (s *Signer) SignedURL(basePath string, fileID int64, ttl time.Duration) string {
|
|
exp := time.Now().Add(ttl).Unix()
|
|
sig := s.Sign(fileID, exp)
|
|
return fmt.Sprintf("%s/user/files/%d?exp=%d&sig=%s", basePath, fileID, exp, sig)
|
|
}
|
|
|
|
func SaveUpload(dataDir, sessionID string, filename string, r io.Reader, maxBytes int64) (string, int64, string, error) {
|
|
ext := filepath.Ext(filename)
|
|
if ext == "" {
|
|
ext = ".bin"
|
|
}
|
|
id := uuid.New().String()
|
|
dir := filepath.Join(dataDir, "files", sessionID, "uploads")
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return "", 0, "", err
|
|
}
|
|
path := filepath.Join(dir, id+ext)
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return "", 0, "", err
|
|
}
|
|
defer f.Close()
|
|
n, err := io.Copy(f, io.LimitReader(r, maxBytes+1))
|
|
if err != nil {
|
|
os.Remove(path)
|
|
return "", 0, "", err
|
|
}
|
|
if n > maxBytes {
|
|
os.Remove(path)
|
|
return "", 0, "", fmt.Errorf("file too large")
|
|
}
|
|
mimeType := mime.TypeByExtension(ext)
|
|
if mimeType == "" {
|
|
mimeType = "application/octet-stream"
|
|
}
|
|
return path, n, mimeType, nil
|
|
}
|
|
|
|
func ParseSignedQuery(expStr, sig string) (int64, string, error) {
|
|
exp, err := strconv.ParseInt(expStr, 10, 64)
|
|
if err != nil {
|
|
return 0, "", err
|
|
}
|
|
return exp, strings.TrimSpace(sig), nil
|
|
}
|