feat: tenant-scoped routing and portal navigation
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgconn"
|
||||
"go.ipao.vip/gen/types"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// @provider
|
||||
@@ -49,7 +50,7 @@ func (s *common) Options(ctx context.Context) (*common_dto.OptionsResponse, erro
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *common) CheckHash(ctx context.Context, userID int64, hash string) (*common_dto.UploadResult, error) {
|
||||
func (s *common) CheckHash(ctx context.Context, tenantID, userID int64, hash string) (*common_dto.UploadResult, error) {
|
||||
existing, err := models.MediaAssetQuery.WithContext(ctx).Where(models.MediaAssetQuery.Hash.Eq(hash)).First()
|
||||
if err != nil {
|
||||
return nil, nil // Not found, proceed to upload
|
||||
@@ -58,18 +59,25 @@ func (s *common) CheckHash(ctx context.Context, userID int64, hash string) (*com
|
||||
// Found existing file (Global deduplication hit)
|
||||
|
||||
// Check if user already has it (Logic deduplication hit)
|
||||
myExisting, err := models.MediaAssetQuery.WithContext(ctx).
|
||||
Where(models.MediaAssetQuery.Hash.Eq(hash), models.MediaAssetQuery.UserID.Eq(userID)).
|
||||
First()
|
||||
myQuery := models.MediaAssetQuery.WithContext(ctx).
|
||||
Where(models.MediaAssetQuery.Hash.Eq(hash), models.MediaAssetQuery.UserID.Eq(userID))
|
||||
if tenantID > 0 {
|
||||
myQuery = myQuery.Where(models.MediaAssetQuery.TenantID.Eq(tenantID))
|
||||
}
|
||||
myExisting, err := myQuery.First()
|
||||
if err == nil {
|
||||
return s.composeUploadResult(myExisting), nil
|
||||
}
|
||||
|
||||
// Create new record for this user reusing existing ObjectKey
|
||||
t, err := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.UserID.Eq(userID)).First()
|
||||
var tid int64 = 0
|
||||
if err == nil {
|
||||
tid = t.ID
|
||||
// 优先使用路径租户,避免跨租户写入。
|
||||
tenant, err := s.resolveTenant(ctx, tenantID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tid int64
|
||||
if tenant != nil {
|
||||
tid = tenant.ID
|
||||
}
|
||||
|
||||
asset := &models.MediaAsset{
|
||||
@@ -107,13 +115,47 @@ func (s *common) buildObjectKey(tenant *models.Tenant, hash, filename string) st
|
||||
return path.Join("quyun", tenantUUID, hash+ext)
|
||||
}
|
||||
|
||||
func (s *common) InitUpload(ctx context.Context, userID int64, form *common_dto.UploadInitForm) (*common_dto.UploadInitResponse, error) {
|
||||
func (s *common) resolveTenant(ctx context.Context, tenantID, userID int64) (*models.Tenant, error) {
|
||||
if tenantID > 0 {
|
||||
tbl, q := models.TenantQuery.QueryContext(ctx)
|
||||
tenant, err := q.Where(tbl.ID.Eq(tenantID)).First()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errorx.ErrRecordNotFound.WithMsg("租户不存在")
|
||||
}
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return tenant, nil
|
||||
}
|
||||
if userID == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
tbl, q := models.TenantQuery.QueryContext(ctx)
|
||||
tenant, err := q.Where(tbl.UserID.Eq(userID)).First()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return tenant, nil
|
||||
}
|
||||
|
||||
func (s *common) uploadTempDir(localPath string, tenantID int64, uploadID string) string {
|
||||
tenantKey := "public"
|
||||
if tenantID > 0 {
|
||||
tenantKey = strconv.FormatInt(tenantID, 10)
|
||||
}
|
||||
return filepath.Join(localPath, "temp", tenantKey, uploadID)
|
||||
}
|
||||
|
||||
func (s *common) InitUpload(ctx context.Context, tenantID, userID int64, form *common_dto.UploadInitForm) (*common_dto.UploadInitResponse, error) {
|
||||
uploadID := uuid.NewString()
|
||||
localPath := s.storage.Config.LocalPath
|
||||
if localPath == "" {
|
||||
localPath = "./storage"
|
||||
}
|
||||
tempDir := filepath.Join(localPath, "temp", uploadID)
|
||||
tempDir := s.uploadTempDir(localPath, tenantID, uploadID)
|
||||
if err := os.MkdirAll(tempDir, 0o755); err != nil {
|
||||
return nil, errorx.ErrInternalError.WithCause(err)
|
||||
}
|
||||
@@ -134,12 +176,12 @@ func (s *common) InitUpload(ctx context.Context, userID int64, form *common_dto.
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *common) UploadPart(ctx context.Context, userID int64, file *multipart.FileHeader, form *common_dto.UploadPartForm) error {
|
||||
func (s *common) UploadPart(ctx context.Context, tenantID, userID int64, file *multipart.FileHeader, form *common_dto.UploadPartForm) error {
|
||||
localPath := s.storage.Config.LocalPath
|
||||
if localPath == "" {
|
||||
localPath = "./storage"
|
||||
}
|
||||
partPath := filepath.Join(localPath, "temp", form.UploadID, strconv.Itoa(form.PartNumber))
|
||||
partPath := filepath.Join(s.uploadTempDir(localPath, tenantID, form.UploadID), strconv.Itoa(form.PartNumber))
|
||||
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
@@ -159,12 +201,12 @@ func (s *common) UploadPart(ctx context.Context, userID int64, file *multipart.F
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *common) CompleteUpload(ctx context.Context, userID int64, form *common_dto.UploadCompleteForm) (*common_dto.UploadResult, error) {
|
||||
func (s *common) CompleteUpload(ctx context.Context, tenantID, userID int64, form *common_dto.UploadCompleteForm) (*common_dto.UploadResult, error) {
|
||||
localPath := s.storage.Config.LocalPath
|
||||
if localPath == "" {
|
||||
localPath = "./storage"
|
||||
}
|
||||
tempDir := filepath.Join(localPath, "temp", form.UploadID)
|
||||
tempDir := s.uploadTempDir(localPath, tenantID, form.UploadID)
|
||||
|
||||
// Read Meta
|
||||
var meta UploadMeta
|
||||
@@ -220,21 +262,27 @@ func (s *common) CompleteUpload(ctx context.Context, userID int64, form *common_
|
||||
dst.Close() // Ensure flush before potential removal
|
||||
|
||||
// Deduplication Logic (Similar to Upload)
|
||||
t, err := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.UserID.Eq(userID)).First()
|
||||
var tid int64 = 0
|
||||
if err == nil {
|
||||
tid = t.ID
|
||||
tenant, err := s.resolveTenant(ctx, tenantID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tid int64
|
||||
if tenant != nil {
|
||||
tid = tenant.ID
|
||||
}
|
||||
|
||||
objectKey := s.buildObjectKey(t, hash, meta.Filename)
|
||||
objectKey := s.buildObjectKey(tenant, hash, meta.Filename)
|
||||
existing, err := models.MediaAssetQuery.WithContext(ctx).Where(models.MediaAssetQuery.Hash.Eq(hash)).First()
|
||||
var asset *models.MediaAsset
|
||||
|
||||
if err == nil {
|
||||
os.Remove(mergedPath) // Delete duplicate
|
||||
myExisting, err := models.MediaAssetQuery.WithContext(ctx).
|
||||
Where(models.MediaAssetQuery.Hash.Eq(hash), models.MediaAssetQuery.UserID.Eq(userID)).
|
||||
First()
|
||||
myQuery := models.MediaAssetQuery.WithContext(ctx).
|
||||
Where(models.MediaAssetQuery.Hash.Eq(hash), models.MediaAssetQuery.UserID.Eq(userID))
|
||||
if tenantID > 0 {
|
||||
myQuery = myQuery.Where(models.MediaAssetQuery.TenantID.Eq(tenantID))
|
||||
}
|
||||
myExisting, err := myQuery.First()
|
||||
if err == nil {
|
||||
os.RemoveAll(tempDir)
|
||||
return s.composeUploadResult(myExisting), nil
|
||||
@@ -282,10 +330,13 @@ func (s *common) CompleteUpload(ctx context.Context, userID int64, form *common_
|
||||
return s.composeUploadResult(asset), nil
|
||||
}
|
||||
|
||||
func (s *common) DeleteMediaAsset(ctx context.Context, userID, id int64) error {
|
||||
asset, err := models.MediaAssetQuery.WithContext(ctx).
|
||||
Where(models.MediaAssetQuery.ID.Eq(id), models.MediaAssetQuery.UserID.Eq(userID)).
|
||||
First()
|
||||
func (s *common) DeleteMediaAsset(ctx context.Context, tenantID, userID, id int64) error {
|
||||
query := models.MediaAssetQuery.WithContext(ctx).
|
||||
Where(models.MediaAssetQuery.ID.Eq(id), models.MediaAssetQuery.UserID.Eq(userID))
|
||||
if tenantID > 0 {
|
||||
query = query.Where(models.MediaAssetQuery.TenantID.Eq(tenantID))
|
||||
}
|
||||
asset, err := query.First()
|
||||
if err != nil {
|
||||
return errorx.ErrRecordNotFound
|
||||
}
|
||||
@@ -308,17 +359,18 @@ func (s *common) DeleteMediaAsset(ctx context.Context, userID, id int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *common) AbortUpload(ctx context.Context, userID int64, uploadId string) error {
|
||||
func (s *common) AbortUpload(ctx context.Context, tenantID, userID int64, uploadId string) error {
|
||||
localPath := s.storage.Config.LocalPath
|
||||
if localPath == "" {
|
||||
localPath = "./storage"
|
||||
}
|
||||
tempDir := filepath.Join(localPath, "temp", uploadId)
|
||||
tempDir := s.uploadTempDir(localPath, tenantID, uploadId)
|
||||
return os.RemoveAll(tempDir)
|
||||
}
|
||||
|
||||
func (s *common) Upload(
|
||||
ctx context.Context,
|
||||
tenantID int64,
|
||||
userID int64,
|
||||
file *multipart.FileHeader,
|
||||
typeArg string,
|
||||
@@ -357,13 +409,16 @@ func (s *common) Upload(
|
||||
|
||||
hash := hex.EncodeToString(hasher.Sum(nil))
|
||||
|
||||
t, err := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.UserID.Eq(userID)).First()
|
||||
var tid int64 = 0
|
||||
if err == nil {
|
||||
tid = t.ID
|
||||
tenant, err := s.resolveTenant(ctx, tenantID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tid int64
|
||||
if tenant != nil {
|
||||
tid = tenant.ID
|
||||
}
|
||||
|
||||
objectKey := s.buildObjectKey(t, hash, file.Filename)
|
||||
objectKey := s.buildObjectKey(tenant, hash, file.Filename)
|
||||
var asset *models.MediaAsset
|
||||
|
||||
// Deduplication Check
|
||||
@@ -374,9 +429,12 @@ func (s *common) Upload(
|
||||
os.RemoveAll(tmpDir)
|
||||
|
||||
// Check if user already has it (Logic Deduplication)
|
||||
myExisting, err := models.MediaAssetQuery.WithContext(ctx).
|
||||
Where(models.MediaAssetQuery.Hash.Eq(hash), models.MediaAssetQuery.UserID.Eq(userID)).
|
||||
First()
|
||||
myQuery := models.MediaAssetQuery.WithContext(ctx).
|
||||
Where(models.MediaAssetQuery.Hash.Eq(hash), models.MediaAssetQuery.UserID.Eq(userID))
|
||||
if tenantID > 0 {
|
||||
myQuery = myQuery.Where(models.MediaAssetQuery.TenantID.Eq(tenantID))
|
||||
}
|
||||
myExisting, err := myQuery.First()
|
||||
if err == nil {
|
||||
return s.composeUploadResult(myExisting), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user