package storage import ( "context" "errors" "io" ) // ── Errors ───────────────────────────────── var ( // ErrNotFound is returned when a requested object does not exist. ErrNotFound = errors.New("storage: object not found") // ErrNotConfigured is returned when storage operations are attempted // but no backend has been configured or the backend is unhealthy. ErrNotConfigured = errors.New("storage: backend not configured") ) // ── ObjectStore Interface ────────────────── // ObjectStore is the abstraction for blob storage. // Implementations: PVC (filesystem), S3 (minio-go v7). // // Keys are slash-delimited paths relative to the storage root: // // attachments/{channel_id}/{attachment_id}_{filename} // // Implementations must create intermediate directories as needed. type ObjectStore interface { // Put writes data to the given key. // Creates parent directories as needed. Overwrites if exists. Put(ctx context.Context, key string, r io.Reader, size int64, contentType string) error // Get returns a reader for the given key. // Returns the reader, size in bytes, content type, and error. // Caller must close the returned reader. // Returns ErrNotFound if key does not exist. Get(ctx context.Context, key string) (io.ReadCloser, int64, string, error) // Delete removes the object at key. // No error if the key does not exist (idempotent). Delete(ctx context.Context, key string) error // DeletePrefix removes all objects under the given prefix. // Used for channel deletion (bulk cleanup). // Example: DeletePrefix(ctx, "attachments/{channel_id}/") DeletePrefix(ctx context.Context, prefix string) error // Exists checks if an object exists at key without reading it. Exists(ctx context.Context, key string) (bool, error) // Healthy returns nil if the backend is operational (writable). Healthy(ctx context.Context) error // Stats returns aggregate storage statistics. Stats(ctx context.Context) (*StorageStats, error) // Backend returns the backend type identifier ("pvc", "s3"). Backend() string } // StorageStats holds aggregate storage metrics. type StorageStats struct { Backend string `json:"backend"` Path string `json:"path,omitempty"` // PVC only Endpoint string `json:"endpoint,omitempty"` // S3 only Bucket string `json:"bucket,omitempty"` // S3 only Healthy bool `json:"healthy"` Configured bool `json:"configured"` TotalFiles int64 `json:"total_files"` TotalBytes int64 `json:"total_bytes"` }