Documentation
¶
Overview ¶
Package media provides media file management functionality.
Index ¶
- Variables
- func AllowedExtensionsForMIME(mimeType string) ([]string, bool)
- func ExtensionMatchesMIME(ext, mimeType string) bool
- func ImageTypeAndExtensionAllowed(ext, mimeType string) bool
- type BlobStore
- type Config
- type CreateMediaInput
- type ImageVariant
- type ListMediaFilter
- type Media
- type PagedResult
- type PostgresRepository
- func (r *PostgresRepository) Create(ctx context.Context, m *Media) error
- func (r *PostgresRepository) Delete(ctx context.Context, id uuid.UUID) error
- func (r *PostgresRepository) GetByID(ctx context.Context, id uuid.UUID) (*Media, error)
- func (r *PostgresRepository) List(ctx context.Context, filter *ListMediaFilter, limit, offset int) (*PagedResult, error)
- func (r *PostgresRepository) Update(ctx context.Context, m *Media) error
- func (r *PostgresRepository) Usage(ctx context.Context, stem string) ([]Usage, error)
- type Repository
- type Service
- func (s *Service) Config() Config
- func (s *Service) Delete(ctx context.Context, id uuid.UUID) error
- func (s *Service) Get(ctx context.Context, id uuid.UUID) (*Media, error)
- func (s *Service) List(ctx context.Context, filter *ListMediaFilter, limit, offset int) (*PagedResult, error)
- func (s *Service) OpenFile(rel string) (*os.File, error)
- func (s *Service) SetChangeObserver(f func(ctx context.Context))
- func (s *Service) Update(ctx context.Context, id uuid.UUID, input *UpdateMediaInput) (*Media, error)
- func (s *Service) Upload(ctx context.Context, userID *uuid.UUID, file io.Reader, filename, _ string, ...) (*Media, error)
- func (s *Service) Usage(ctx context.Context, m *Media) ([]Usage, error)
- type UpdateMediaInput
- type Usage
Constants ¶
This section is empty.
Variables ¶
var ( ErrMediaNotFound = errors.New("media not found") ErrInvalidMimeType = errors.New("invalid mime type") ErrFileTooLarge = errors.New("file too large") ErrInvalidFile = errors.New("invalid file") // ErrQuotaExceeded is returned when Config.MaxTotalBytes would be exceeded // (the public demo caps its total media size). ErrQuotaExceeded = errors.New("media storage quota exceeded") ErrStorageFailed = errors.New("storage operation failed") // ErrImageTooLarge is returned when an image's pixel count exceeds the // configured cap (decompression-bomb guard). ErrImageTooLarge = errors.New("image dimensions exceed the maximum allowed") )
Error definitions for the media package.
var VariantSizes = map[string]struct { MaxWidth int MaxHeight int Crop bool }{ "thumbnail": {MaxWidth: 150, MaxHeight: 150, Crop: true}, "medium": {MaxWidth: 300, MaxHeight: 300, Crop: false}, "large": {MaxWidth: 1024, MaxHeight: 1024, Crop: false}, }
VariantSizes defines the standard image variant sizes.
Functions ¶
func AllowedExtensionsForMIME ¶
AllowedExtensionsForMIME returns the extensions permitted for a sniffed MIME type and whether the type is allowed at all.
func ExtensionMatchesMIME ¶
ExtensionMatchesMIME reports whether a filename extension is consistent with the sniffed MIME type. It cross-validates content against the declared name so a .png full of JPEG bytes, or a .php renamed .jpg, is rejected. ext is matched case-insensitively and must include the leading dot.
func ImageTypeAndExtensionAllowed ¶
ImageTypeAndExtensionAllowed reports whether the sniffed type and the extension are each an allowed image type, even when they do not pair. Resized copies of WebP uploads written by older versions are JPEG bytes under a .webp name; they must still restore. Only images qualify: html, svg, pdf and video never pass through here.
Types ¶
type BlobStore ¶
type BlobStore interface {
// Put streams r into key and returns the bytes written. A failed Put
// leaves no partial blob behind.
Put(ctx context.Context, key string, r io.Reader) (int64, error)
// Get opens key for streaming; close the returned reader.
Get(ctx context.Context, key string) (io.ReadCloser, error)
// Delete removes key. A missing key returns an error satisfying
// errors.Is(err, fs.ErrNotExist).
Delete(ctx context.Context, key string) error
// URL is the public URL of key.
URL(key string) string
}
BlobStore holds the bytes of stored media. Keys are the persisted StoragePath strings, relative to the store and never absolute.
func NewLocalStore ¶
NewLocalStore returns the local-disk store rooted at root (created if missing, symlinks resolved once) whose URLs start with baseURL.
type Config ¶
type Config struct {
UploadDir string // Directory for storing uploaded files
BaseURL string // Base URL for serving files (e.g., "/media")
MaxSizeMB int // Maximum file size in MB
MaxPixels int // Maximum decoded pixel count (width*height); 0 = default
// MaxTotalBytes caps everything under UploadDir (originals and variants);
// 0 = no cap. Set by the public demo so visitors cannot fill the disk.
MaxTotalBytes int64
}
Config holds media service configuration.
func DefaultConfig ¶
func DefaultConfig() Config
type CreateMediaInput ¶
type CreateMediaInput struct {
Filename string
OriginalFilename string
MimeType string
FileSize int64
StoragePath string
Width *int
Height *int
Folder string
}
CreateMediaInput contains the input for creating media.
type ImageVariant ¶
type ImageVariant struct {
Size string `json:"size"`
Path string `json:"path"`
URL string `json:"url"`
Width int `json:"width"`
Height int `json:"height"`
}
ImageVariant represents a resized version of an image.
type ListMediaFilter ¶
ListMediaFilter contains filter options for listing media.
type Media ¶
type Media struct {
ID uuid.UUID `json:"id"`
Filename string `json:"filename"`
OriginalFilename string `json:"original_filename"`
MimeType string `json:"mime_type"`
FileSize int64 `json:"file_size"`
StorageProvider string `json:"storage_provider"`
StoragePath string `json:"storage_path"`
PublicURL *string `json:"public_url,omitempty"`
Width *int `json:"width,omitempty"`
Height *int `json:"height,omitempty"`
Variants []ImageVariant `json:"variants,omitempty"`
AltText *string `json:"alt_text,omitempty"`
Caption *string `json:"caption,omitempty"`
Folder string `json:"folder"`
UploadedBy *uuid.UUID `json:"uploaded_by,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Media represents a media file stored in the system.
func (*Media) MarshalVariants ¶
MarshalVariants converts variants slice to JSON for database storage.
func (*Media) SupportsVariants ¶
SupportsVariants returns true if the image format supports creating variants.
func (*Media) UnmarshalVariants ¶
UnmarshalVariants converts JSON from database to variants slice.
type PagedResult ¶
PagedResult represents a paginated result set.
type PostgresRepository ¶
type PostgresRepository struct {
// contains filtered or unexported fields
}
PostgresRepository implements Repository using PostgreSQL.
func NewPostgresRepository ¶
func NewPostgresRepository(db *database.DB) *PostgresRepository
NewPostgresRepository creates a new PostgreSQL media repository.
func (*PostgresRepository) Create ¶
func (r *PostgresRepository) Create(ctx context.Context, m *Media) error
Create inserts a new media record.
func (*PostgresRepository) List ¶
func (r *PostgresRepository) List(ctx context.Context, filter *ListMediaFilter, limit, offset int) (*PagedResult, error)
List retrieves media records with filtering and pagination.
type Repository ¶
type Repository interface {
Create(ctx context.Context, m *Media) error
GetByID(ctx context.Context, id uuid.UUID) (*Media, error)
List(ctx context.Context, filter *ListMediaFilter, limit, offset int) (*PagedResult, error)
Update(ctx context.Context, m *Media) error
Delete(ctx context.Context, id uuid.UUID) error
}
Repository defines the interface for media data operations.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service handles media operations.
func NewService ¶
func NewService(repo Repository, config Config, logger *slog.Logger) *Service
NewService creates a new media service.
func (*Service) List ¶
func (s *Service) List(ctx context.Context, filter *ListMediaFilter, limit, offset int) (*PagedResult, error)
List retrieves media records with filtering and pagination.
func (*Service) OpenFile ¶
OpenFile opens a stored file by its path relative to the upload directory. The path is resolved inside an os.Root: "..", absolute paths and symlinks that lead outside the directory are refused. Close the returned file.
func (*Service) SetChangeObserver ¶
SetChangeObserver installs the callback run after Update or Delete; nil disables it.
func (*Service) Update ¶
func (s *Service) Update(ctx context.Context, id uuid.UUID, input *UpdateMediaInput) (*Media, error)
Update modifies media metadata.
type UpdateMediaInput ¶
type UpdateMediaInput struct {
AltText *string `json:"alt_text,omitempty"`
Caption *string `json:"caption,omitempty"`
Folder *string `json:"folder,omitempty"`
}
UpdateMediaInput contains the input for updating media metadata.
type Usage ¶
type Usage struct {
Kind string // entity | site | menu | plugin
ID uuid.UUID
Name string
SchemaSlug string // entities only
}
Usage is one place a media file is referenced from: an entity (its attributes mention the file's URL, which covers a person's photo), the site settings (logo, theme), a menu or a plugin's settings. The legacy authors table is a rollback mirror of the people and is not a usage of its own.