package objectstorage import ( "context" "fmt" "net/url" "path/filepath" "strings" "time" "img_infinite_canvas/internal/domain/design" "github.com/google/uuid" ) type MemoryStorage struct { bucket string publicBase string expiresIn int64 } func NewMemoryStorage(bucket string, publicBase string, expiresIn int64) *MemoryStorage { if bucket == "" { bucket = "memory-assets" } if expiresIn <= 0 { expiresIn = 900 } return &MemoryStorage{ bucket: bucket, publicBase: strings.TrimRight(publicBase, "/"), expiresIn: expiresIn, } } func (s *MemoryStorage) CreateUpload(ctx context.Context, req design.AssetUploadRequest) (design.AssetUpload, error) { if err := ctx.Err(); err != nil { return design.AssetUpload{}, err } objectKey := objectKey(req.FileName) publicURL := fmt.Sprintf("/memory-assets/%s", escapeObjectPath(objectKey)) if s.publicBase != "" { publicURL = publicObjectURL(s.publicBase, objectKey) } return design.AssetUpload{ Driver: "memory", Bucket: s.bucket, ObjectKey: objectKey, UploadURL: publicURL + "?upload=1", PublicURL: publicURL, Headers: map[string]string{ "Content-Type": req.ContentType, }, ExpiresIn: s.expiresIn, }, nil } func (s *MemoryStorage) PutObject(ctx context.Context, req design.AssetObjectRequest) (design.AssetObject, error) { if err := ctx.Err(); err != nil { return design.AssetObject{}, err } objectKey := objectKey(req.FileName) publicURL := fmt.Sprintf("/memory-assets/%s", escapeObjectPath(objectKey)) if s.publicBase != "" { publicURL = publicObjectURL(s.publicBase, objectKey) } return design.AssetObject{ Driver: "memory", Bucket: s.bucket, ObjectKey: objectKey, PublicURL: publicURL, }, nil } func (s *MemoryStorage) DeleteObject(ctx context.Context, publicURL string) error { return ctx.Err() } func objectKey(fileName string) string { ext := filepath.Ext(fileName) return time.Now().UTC().Format("2006/01/02") + "/" + uuid.NewString() + ext } func publicObjectURL(publicBase string, objectKey string) string { publicBase = strings.TrimRight(publicBase, "/") if publicBase == "" { return "" } return publicBase + "/" + escapeObjectPath(objectKey) } func escapeObjectPath(objectKey string) string { parts := strings.Split(objectKey, "/") for index, part := range parts { parts[index] = url.PathEscape(part) } return strings.Join(parts, "/") }