Documentation
¶
Overview ¶
Package attachment provides the domain model for file attachments.
Attachments are tenant-scoped files uploaded by users and referenced from markdown fields (finding descriptions, retest notes, PoC code, etc.). The actual file bytes are stored via a pluggable FileStorage interface so each tenant can use a different backend (local disk, S3, MinIO, GCS, ...).
Index ¶
- Constants
- Variables
- type Attachment
- func (a *Attachment) ContentHash() string
- func (a *Attachment) ContentType() string
- func (a *Attachment) ContextID() string
- func (a *Attachment) ContextType() string
- func (a *Attachment) CreatedAt() time.Time
- func (a *Attachment) Filename() string
- func (a *Attachment) ID() shared.ID
- func (a *Attachment) MarkdownLink() string
- func (a *Attachment) SetContentHash(hash string)
- func (a *Attachment) SetStorageProvider(provider string)
- func (a *Attachment) Size() int64
- func (a *Attachment) StorageKey() string
- func (a *Attachment) StorageProvider() string
- func (a *Attachment) TenantID() shared.ID
- func (a *Attachment) URL() string
- func (a *Attachment) UploadedBy() shared.ID
- type FileStorage
- type Repository
- type StorageConfig
Constants ¶
const ( MaxFileSize = 10 * 1024 * 1024 // 10MB per file MaxTotalPerItem = 50 * 1024 * 1024 // 50MB total per finding/retest )
Validation constants
Variables ¶
var ( ErrNotFound = fmt.Errorf("attachment not found") ErrTooLarge = fmt.Errorf("file too large") ErrUnsupported = fmt.Errorf("unsupported file type") )
Errors
var AllowedContentTypes = map[string]bool{ "image/png": true, "image/jpeg": true, "image/gif": true, "image/webp": true, "application/pdf": true, "text/plain": true, "text/markdown": true, "text/csv": true, "application/zip": true, "application/x-gzip": true, "application/har+json": true, "application/json": true, "video/mp4": true, "video/webm": true, }
AllowedContentTypes is the whitelist of MIME types accepted for upload.
Functions ¶
This section is empty.
Types ¶
type Attachment ¶
type Attachment struct {
// contains filtered or unexported fields
}
Attachment tracks metadata about an uploaded file. The binary content lives in the storage backend identified by StorageKey; this entity is the DB row.
func NewAttachment ¶
func NewAttachment( tenantID shared.ID, filename, contentType string, size int64, storageKey string, uploadedBy shared.ID, contextType, contextID string, ) *Attachment
NewAttachment creates a new Attachment metadata record.
func ReconstituteAttachment ¶
func ReconstituteAttachment( id, tenantID shared.ID, filename, contentType string, size int64, storageKey string, uploadedBy shared.ID, contextType, contextID, contentHash, storageProvider string, createdAt time.Time, ) *Attachment
ReconstituteAttachment recreates an Attachment from persistence.
func (*Attachment) ContentHash ¶
func (a *Attachment) ContentHash() string
func (*Attachment) ContentType ¶
func (a *Attachment) ContentType() string
func (*Attachment) ContextID ¶
func (a *Attachment) ContextID() string
func (*Attachment) ContextType ¶
func (a *Attachment) ContextType() string
func (*Attachment) CreatedAt ¶
func (a *Attachment) CreatedAt() time.Time
func (*Attachment) Filename ¶
func (a *Attachment) Filename() string
func (*Attachment) MarkdownLink ¶
func (a *Attachment) MarkdownLink() string
MarkdownImageLink returns the markdown syntax to embed this attachment. For images it uses , for other files [filename](url).
func (*Attachment) SetContentHash ¶
func (a *Attachment) SetContentHash(hash string)
SetContentHash sets the SHA-256 hash for dedup.
func (*Attachment) SetStorageProvider ¶
func (a *Attachment) SetStorageProvider(provider string)
SetStorageProvider records which backend stores this file.
func (*Attachment) Size ¶
func (a *Attachment) Size() int64
func (*Attachment) StorageKey ¶
func (a *Attachment) StorageKey() string
func (*Attachment) StorageProvider ¶
func (a *Attachment) StorageProvider() string
func (*Attachment) TenantID ¶
func (a *Attachment) TenantID() shared.ID
func (*Attachment) URL ¶
func (a *Attachment) URL() string
URL returns the API-served download URL for this attachment.
func (*Attachment) UploadedBy ¶
func (a *Attachment) UploadedBy() shared.ID
type FileStorage ¶
type FileStorage interface {
// Upload stores file content and returns an opaque storage key.
// The key is used for Download/Delete and stored in the attachments table.
Upload(ctx context.Context, tenantID, filename, contentType string, reader io.Reader) (storageKey string, err error)
// Download returns a reader for the file content.
// Caller is responsible for closing the reader.
Download(ctx context.Context, tenantID, storageKey string) (io.ReadCloser, string, error)
// Delete removes a file from storage. Idempotent — no error if already gone.
Delete(ctx context.Context, tenantID, storageKey string) error
}
FileStorage is the pluggable interface for file persistence. Each tenant can use a different implementation (local, S3, MinIO, GCS, ...) configured via tenant_settings.storage JSONB.
Implementations MUST:
- Namespace files by tenantID to prevent cross-tenant access
- Return ErrNotFound for missing keys (not generic errors)
- Be safe for concurrent use
type Repository ¶
type Repository interface {
Create(ctx context.Context, att *Attachment) error
GetByID(ctx context.Context, tenantID, id shared.ID) (*Attachment, error)
Delete(ctx context.Context, tenantID, id shared.ID) error
ListByContext(ctx context.Context, tenantID shared.ID, contextType, contextID string) ([]*Attachment, error)
// LinkToContext bulk-updates context for attachments uploaded before the parent entity existed.
LinkToContext(ctx context.Context, tenantID shared.ID, ids []shared.ID, uploaderID shared.ID, contextType, contextID string) (int64, error)
// FindByHash returns an existing attachment with the same content hash in the same context.
// Returns nil, nil if no duplicate found.
FindByHash(ctx context.Context, tenantID shared.ID, contextType, contextID, contentHash string) (*Attachment, error)
}
Repository persists attachment metadata (not file content — that's FileStorage).
type StorageConfig ¶
type StorageConfig struct {
Provider string `json:"provider"` // "local", "s3", "minio", "gcs"
Bucket string `json:"bucket"` // S3/MinIO bucket name
Region string `json:"region"` // AWS region
Endpoint string `json:"endpoint"` // Custom endpoint (MinIO)
BasePath string `json:"base_path"` // Local filesystem base path
AccessKey string `json:"access_key"` // Encrypted via tenant credentials
SecretKey string `json:"secret_key"` // Encrypted via tenant credentials
}
StorageConfig holds provider-specific configuration. Stored in tenant_settings.storage JSONB so each tenant can use a different backend.
func DefaultStorageConfig ¶
func DefaultStorageConfig() StorageConfig
DefaultStorageConfig returns a local filesystem config for development.