attachment

package
v0.1.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2026 License: GPL-3.0 Imports: 5 Imported by: 0

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

View Source
const (
	MaxFileSize     = 10 * 1024 * 1024 // 10MB per file
	MaxTotalPerItem = 50 * 1024 * 1024 // 50MB total per finding/retest
)

Validation constants

Variables

View Source
var (
	ErrNotFound    = fmt.Errorf("attachment not found")
	ErrTooLarge    = fmt.Errorf("file too large")
	ErrUnsupported = fmt.Errorf("unsupported file type")
)

Errors

View Source
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) ID

func (a *Attachment) ID() shared.ID

Getters

func (a *Attachment) MarkdownLink() string

MarkdownImageLink returns the markdown syntax to embed this attachment. For images it uses ![alt](url), 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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL