fix: scope creator contents and bind uploads

This commit is contained in:
2026-01-12 15:33:39 +08:00
parent 16d13a2ab1
commit 4022a776a6
4 changed files with 69 additions and 15 deletions

View File

@@ -104,9 +104,16 @@ func (s *common) CheckHash(ctx context.Context, tenantID, userID int64, hash str
}
type UploadMeta struct {
Filename string
Type string
MimeType string
// Filename 原始文件名,用于生成对象路径。
Filename string `json:"filename"`
// Type 业务媒体类型video/audio/image 等)。
Type string `json:"type"`
// MimeType 上传文件的 MIME 类型。
MimeType string `json:"mime_type"`
// TenantID 上传所属租户ID用于归属校验。
TenantID int64 `json:"tenant_id"`
// UserID 上传发起用户ID用于归属校验。
UserID int64 `json:"user_id"`
}
func (s *common) buildObjectKey(tenant *models.Tenant, hash, filename string) string {
@@ -119,6 +126,31 @@ func (s *common) buildObjectKey(tenant *models.Tenant, hash, filename string) st
return path.Join("quyun", tenantUUID, hash+ext)
}
func (s *common) loadUploadMeta(tempDir string) (*UploadMeta, error) {
metaFile, err := os.Open(filepath.Join(tempDir, "meta.json"))
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, errorx.ErrRecordNotFound.WithCause(err).WithMsg("上传会话不存在")
}
return nil, errorx.ErrInternalError.WithCause(err)
}
defer metaFile.Close()
var meta UploadMeta
if err := json.NewDecoder(metaFile).Decode(&meta); err != nil {
return nil, errorx.ErrDataCorrupted.WithCause(err).WithMsg("上传会话元信息损坏")
}
return &meta, nil
}
func (s *common) verifyUploadOwner(meta *UploadMeta, tenantID, userID int64) error {
// 校验上传会话归属,避免同租户猜测 upload_id 进行越权操作。
if meta.TenantID != tenantID || meta.UserID != userID {
return errorx.ErrForbidden.WithMsg("无权访问该上传会话")
}
return nil
}
func (s *common) resolveTenant(ctx context.Context, tenantID, userID int64) (*models.Tenant, error) {
if tenantID > 0 {
tbl, q := models.TenantQuery.QueryContext(ctx)
@@ -169,6 +201,8 @@ func (s *common) InitUpload(ctx context.Context, tenantID, userID int64, form *c
Filename: form.Filename,
Type: form.Type, // Ensure form has Type
MimeType: form.MimeType,
TenantID: tenantID,
UserID: userID,
}
metaFile, _ := os.Create(filepath.Join(tempDir, "meta.json"))
json.NewEncoder(metaFile).Encode(meta)
@@ -185,7 +219,15 @@ func (s *common) UploadPart(ctx context.Context, tenantID, userID int64, file *m
if localPath == "" {
localPath = "./storage"
}
partPath := filepath.Join(s.uploadTempDir(localPath, tenantID, form.UploadID), strconv.Itoa(form.PartNumber))
tempDir := s.uploadTempDir(localPath, tenantID, form.UploadID)
meta, err := s.loadUploadMeta(tempDir)
if err != nil {
return err
}
if err := s.verifyUploadOwner(meta, tenantID, userID); err != nil {
return err
}
partPath := filepath.Join(tempDir, strconv.Itoa(form.PartNumber))
src, err := file.Open()
if err != nil {
@@ -212,14 +254,14 @@ func (s *common) CompleteUpload(ctx context.Context, tenantID, userID int64, for
}
tempDir := s.uploadTempDir(localPath, tenantID, form.UploadID)
// Read Meta
var meta UploadMeta
metaFile, err := os.Open(filepath.Join(tempDir, "meta.json"))
// 校验上传会话归属,避免同租户猜测 upload_id 进行越权操作。
meta, err := s.loadUploadMeta(tempDir)
if err != nil {
return nil, errorx.ErrRecordNotFound.WithMsg("Upload session expired or invalid")
return nil, err
}
if err := s.verifyUploadOwner(meta, tenantID, userID); err != nil {
return nil, err
}
json.NewDecoder(metaFile).Decode(&meta)
metaFile.Close()
// List parts
entries, err := os.ReadDir(tempDir)
@@ -373,6 +415,13 @@ func (s *common) AbortUpload(ctx context.Context, tenantID, userID int64, upload
localPath = "./storage"
}
tempDir := s.uploadTempDir(localPath, tenantID, uploadId)
meta, err := s.loadUploadMeta(tempDir)
if err != nil {
return err
}
if err := s.verifyUploadOwner(meta, tenantID, userID); err != nil {
return err
}
return os.RemoveAll(tempDir)
}

View File

@@ -31,6 +31,9 @@ func (s *content) List(ctx context.Context, tenantID int64, filter *content_dto.
if tenantID > 0 {
q = q.Where(tbl.TenantID.Eq(tenantID))
}
if filter.AuthorID != nil && *filter.AuthorID > 0 {
q = q.Where(tbl.UserID.Eq(*filter.AuthorID))
}
if filter.Genre != nil && *filter.Genre != "" {
q = q.Where(tbl.Genre.Eq(*filter.Genre))
}