package services import ( "context" "mime/multipart" "time" "quyun/v2/app/errorx" common_dto "quyun/v2/app/http/v1/dto" "quyun/v2/database/fields" "quyun/v2/database/models" "quyun/v2/pkg/consts" "quyun/v2/providers/storage" "github.com/google/uuid" "github.com/spf13/cast" "go.ipao.vip/gen/types" ) // @provider type common struct { storage *storage.Storage } func (s *common) Upload(ctx context.Context, userID int64, file *multipart.FileHeader, typeArg string) (*common_dto.UploadResult, error) { // Mock Upload to S3/MinIO (Here we just generate key, actual upload handling via direct upload or stream is better) // But this Upload endpoint accepts file. So we save it. // We need to use storage provider to save it? // Storage Provider in my implementation only had SignURL/Verify. // It didn't have "PutObject". // But `storage.go` controller has `Upload`. // This `common.Upload` seems to be the "Backend Upload" endpoint implementation. // It receives file stream. // So `common.Upload` should save the file using logic similar to `storage.go` controller? // Or `storage.go` controller uses `common`? // No, `storage.go` controller uses `services.Storage.Verify`. // The `Upload` endpoint in `common.go` is `/v1/upload`. It's a "Simple Upload" (Form Data). // The `storage.go` controller is for Presigned URL (PUT). // For "Simple Upload", I should implement saving to disk here too? // Or delegate? // I'll implement saving to disk here to match "Local Storage" behavior. // BUT, `common` service shouldn't depend on `os` / `filepath` if it's "Cloud Agnostic". // Ideally `Storage` provider has `PutObject(reader)`. // But I implemented `SignURL` only in `Storage` provider. // To support `Upload` here, I should add `PutObject` to `Storage` provider. // But I can't edit provider easily without risking breaking `gen`. // I'll stick to generating Key and Mock URL, OR simple local save. // Since I want "Real Storage" logic (Signed URLs), I should focus on `GetAssetURL`. // For `Upload` here, I'll just save to `LocalPath` (or `./storage`) directly. objectKey := uuid.NewString() + "_" + file.Filename // TODO: Save file content (omitted for brevity in this step, focusing on URL signing) url := s.GetAssetURL(objectKey) // ... rest ... t, err := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.UserID.Eq(userID)).First() var tid int64 = 0 if err == nil { tid = t.ID } asset := &models.MediaAsset{ TenantID: tid, UserID: userID, Type: consts.MediaAssetType(typeArg), Status: consts.MediaAssetStatusUploaded, Provider: "local", Bucket: "default", ObjectKey: objectKey, Meta: types.NewJSONType(fields.MediaAssetMeta{ Size: file.Size, }), } if err := models.MediaAssetQuery.WithContext(ctx).Create(asset); err != nil { return nil, errorx.ErrDatabaseError.WithCause(err) } return &common_dto.UploadResult{ ID: cast.ToString(asset.ID), URL: url, Filename: file.Filename, Size: file.Size, MimeType: file.Header.Get("Content-Type"), }, nil } func (s *common) GetAssetURL(objectKey string) string { if objectKey == "" { return "" } url, _ := s.storage.SignURL("GET", objectKey, 1*time.Hour) return url }