Help us shape the future of Go! Take the 2026 Go Developer Survey

media

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: AGPL-3.0 Imports: 43 Imported by: 0

Documentation

Overview

Package media manages the media library, storage drivers and image variants.

Uploads are sniffed (content must match the extension) and checked against the media.allowed_mimes allowlist and media.max_upload_mb.

SVG security: uploaded SVG is sanitized and is also served with a sandboxing Content-Security-Policy. The sanitizer keeps allowlisted elements in the SVG namespace only and drops event handlers; hrefs are checked per element (local fragments, embedded raster data:, http(s)/ mailto links on <a>). An attribute value is dropped, and a whole <style> element is dropped, when it contains (after XML entity decoding, case- and whitespace-insensitively) a script URL, @import, expression(), a url() other than url(#local), one of the resource-loading functions image-set()/-webkit-image-set(), src(), image(), cross-fade(), or any backslash: CSS escapes ("\75rl(", "@\69mport") could otherwise spell those, and plain SVG never needs them. This blocks the known script and external-fetch vectors; it is defense in depth, not a guarantee: themes or editors that INLINE SVG markup into HTML must still treat it as untrusted (prefer <img src> for media SVG).

contract.go holds the frozen wave-0 contract (types and interfaces only).

Index

Constants

View Source
const (
	SettingMaxUploadMB  = "media.max_upload_mb"
	SettingAllowedMimes = "media.allowed_mimes"
)

Settings keys (defaults below apply when unset).

View Source
const (
	HookUploaded = "media.uploaded"
	HookDeleted  = "media.deleted"
)

Hook (event) names emitted after commit; payload is the Media record.

View Source
const DestinationPrefix = "storage.destination."
View Source
const FetchJobKind = "media.fetch_url"

FetchJobKind downloads a URL into the library (payload FetchJob). The service's FromURL is synchronous; this job lets callers do it in the background.

View Source
const RevisionMarker = "#gorun-rev-"

RevisionMarker separates a source URL from a revision suffix. When a re-imported file's bytes changed, the new media row keeps the source URL plus RevisionMarker and a content hash (source_url is unique), so FindBySourceURL and FromURL of the plain URL return the newest revision.

View Source
const SettingStripMetadata = "media.strip_metadata"

SettingStripMetadata turns stripping off when set to false (default on).

View Source
const URLPrefix = "/media/"

URLPrefix is where stored files are served (GET /media/{key...}).

Variables

View Source
var (
	ErrNotFound   = errors.New("media: not found")
	ErrTooLarge   = errors.New("media: file too large")
	ErrEmpty      = errors.New("media: empty file")
	ErrNotAllowed = errors.New("media: file type not allowed")
)

Upload errors.

View Source
var DefaultAllowedMimes = []string{"image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml",
	"application/pdf", "application/zip", "video/mp4", "audio/mpeg"}

DefaultAllowedMimes is the allowlist used when media.allowed_mimes is unset.

View Source
var ErrInvalidKey = errors.New("media: invalid storage key")

ErrInvalidKey is returned for keys outside keyPattern.

View Source
var ErrInvalidSVG = errors.New("media: invalid SVG document")

ErrInvalidSVG is returned for documents that are not well-formed SVG.

Functions

func PublicPath

func PublicPath(key string) string

PublicPath uses the native media handler, including its SVG sandbox and managed-storage routing.

func SafeStorageClient

func SafeStorageClient() *http.Client

func SaveDestination

func SaveDestination(ctx context.Context, database *sql.DB, store *settings.Store, d Destination) error

func SaveDestinationTx

func SaveDestinationTx(ctx context.Context, tx *sql.Tx, store *settings.Store, d Destination) error

func StorageSiteID

func StorageSiteID(ctx context.Context, database *sql.DB) (string, error)

StorageSiteID survives host/domain changes and separates sites sharing a bucket.

Types

type DeleteResponse

type DeleteResponse struct {
	ID      string `json:"id"`
	Deleted bool   `json:"deleted"`
}

DeleteResponse is returned by DELETE /v1/media/{id}.

type Destination

type Destination struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Purpose   string `json:"purpose"`
	Secret    string `json:"secret"`
	UpdatedAt string `json:"updated_at"`
}

func LoadDestination

func LoadDestination(ctx context.Context, store *settings.Store, id string) (Destination, error)

func PrepareDestination

func PrepareDestination(name, purpose string, c S3Config, key []byte) (Destination, error)

PrepareDestination binds the location to its ID; credential rotation retains it.

type FetchJob

type FetchJob struct {
	URL  string     `json:"url"`
	Meta UploadMeta `json:"meta"`
}

FetchJob is the payload of FetchJobKind.

type ListQuery

type ListQuery struct {
	Search  string `json:"search"`
	Page    int    `json:"page"`
	PerPage int    `json:"per_page"`
	Sort    string `json:"sort"`
	Order   string `json:"order"`
}

ListQuery filters Service.List.

type LocalStorage

type LocalStorage struct {
	// contains filtered or unexported fields
}

LocalStorage stores files under a directory on disk; every access goes through os.Root so a key can never escape it.

func NewLocalStorage

func NewLocalStorage(dir string) *LocalStorage

NewLocalStorage returns a driver rooted at dir (created on first Put).

func (*LocalStorage) Delete

func (l *LocalStorage) Delete(_ context.Context, key string) error

Delete removes key; a missing file is not an error.

func (*LocalStorage) Open

func (l *LocalStorage) Open(_ context.Context, key string) (io.ReadCloser, error)

Open returns the file at key (an *os.File, so callers may Seek/Stat).

func (*LocalStorage) Put

func (l *LocalStorage) Put(_ context.Context, key string, r io.Reader, _ string) error

Put writes r to key atomically (temp file + rename).

func (*LocalStorage) URL

func (l *LocalStorage) URL(key string) string

URL returns the public URL of key.

type ManagedStorage

type ManagedStorage struct {
	// contains filtered or unexported fields
}

ManagedStorage keeps old destinations readable after upload activation changes. Only media-purpose destinations and the public media key grammar are accepted.

func (*ManagedStorage) Delete

func (s *ManagedStorage) Delete(ctx context.Context, key string) error

func (*ManagedStorage) Open

func (s *ManagedStorage) Open(ctx context.Context, key string) (io.ReadCloser, error)

func (*ManagedStorage) Put

func (s *ManagedStorage) Put(ctx context.Context, key string, r io.Reader, mime string) error

func (*ManagedStorage) URL

func (s *ManagedStorage) URL(key string) string

func (*ManagedStorage) UploadKey

func (s *ManagedStorage) UploadKey(ctx context.Context, key string) (string, error)

type Media

type Media struct {
	ID         string             `json:"id"`
	Filename   string             `json:"filename"`
	StorageKey string             `json:"storage_key"`
	URL        string             `json:"url"`
	Mime       string             `json:"mime"`
	Size       int64              `json:"size"`
	Width      int                `json:"width"`
	Height     int                `json:"height"`
	Alt        string             `json:"alt"`
	Caption    string             `json:"caption"`
	Title      string             `json:"title"`
	AuthorID   string             `json:"author_id"`
	Variants   map[string]Variant `json:"variants"`
	SourceURL  string             `json:"source_url"`
	Checksum   string             `json:"checksum"`
	CreatedAt  string             `json:"created_at"`
}

type Module

type Module struct{}

Module is the media kernel.Module.

func New

func New() *Module

New returns the media module.

func (*Module) Catalogs

func (*Module) Catalogs() fs.FS

Catalogs implements kernel.CatalogProvider.

func (*Module) Migrations

func (*Module) Migrations() fs.FS

Migrations implements kernel.Module.

func (*Module) Name

func (*Module) Name() string

Name implements kernel.Module.

func (*Module) Register

func (*Module) Register(d *kernel.Deps) error

Register publishes kernel.ServiceMedia, serves GET /media/*, and adds the admin library, picker, API operations and the fetch job.

type Patch

type Patch struct {
	Alt     *string `json:"alt,omitempty"`
	Caption *string `json:"caption,omitempty"`
	Title   *string `json:"title,omitempty"`
}

Patch updates only its non-nil fields.

type S3Config

type S3Config struct {
	Endpoint  string // https://<account>.r2.cloudflarestorage.com, https://s3.<region>.amazonaws.com
	Region    string // "auto" when empty (R2)
	Bucket    string
	AccessKey string
	SecretKey string
	PublicURL string // optional public base of the bucket (a CDN or R2 custom domain); else files are served from /media/
}

S3Config is an S3-compatible bucket (AWS S3, Cloudflare R2, MinIO…), addressed path-style.

func DestinationConfig

func DestinationConfig(d Destination, key []byte) (S3Config, error)

func (S3Config) Enabled

func (c S3Config) Enabled() bool

Enabled reports whether a bucket is configured.

type S3Storage

type S3Storage struct {
	// contains filtered or unexported fields
}

S3Storage stores new files in a bucket. Files stored before it was enabled stay on disk: Open falls back to the local driver and Delete removes both copies.

func NewS3Storage

func NewS3Storage(c S3Config, local *LocalStorage, insecure bool) (*S3Storage, error)

NewS3Storage validates c and returns the driver. Plain http endpoints are allowed only when insecure is set (dev, a local MinIO).

func (*S3Storage) Delete

func (s *S3Storage) Delete(ctx context.Context, key string) error

Delete removes key from the bucket and from disk; a missing object is not an error.

func (*S3Storage) Open

func (s *S3Storage) Open(ctx context.Context, key string) (io.ReadCloser, error)

Open returns the object at key, or the local file stored before the bucket was enabled.

func (*S3Storage) ProbeBucket

func (s *S3Storage) ProbeBucket(ctx context.Context, client *http.Client) error

ProbeBucket verifies signed read-only bucket access with the caller's guarded client. It does not change the active media driver or upload, list, or delete objects.

func (*S3Storage) Put

func (s *S3Storage) Put(ctx context.Context, key string, r io.Reader, mime string) error

Put uploads r to key.

func (*S3Storage) PutEncryptedBackup

func (s *S3Storage) PutEncryptedBackup(ctx context.Context, client *http.Client, key string, body []byte) error

PutEncryptedBackup accepts only private backup keys, outside the media route grammar. The caller encrypts and bounds the bytes before upload. ACLs are not used as privacy proof.

func (*S3Storage) URL

func (s *S3Storage) URL(key string) string

URL is the public URL of key: under PublicURL when set, else served by GoRun from /media/. SVG files are always served by GoRun, which adds their sandboxing CSP.

type Service

type Service interface {
	Upload(ctx context.Context, filename string, r io.Reader, meta UploadMeta) (*Media, error)
	FromURL(ctx context.Context, url string, meta UploadMeta) (*Media, error) // SSRF-safe, dedupes by source_url
	Get(ctx context.Context, id string) (*Media, error)
	List(ctx context.Context, q ListQuery) ([]Media, int, error)
	Update(ctx context.Context, id string, patch Patch) (*Media, error)
	Delete(ctx context.Context, id string) error
	URL(m *Media, variant string) string
	FindBySourceURL(ctx context.Context, url string) (*Media, error)
}

func NewService

func NewService(d *kernel.Deps, storage Storage) Service

NewService builds the media service over storage (used by Register and by tools that need the library outside a booted app).

type Storage

type Storage interface {
	Put(ctx context.Context, key string, r io.Reader, mime string) error
	Open(ctx context.Context, key string) (io.ReadCloser, error)
	Delete(ctx context.Context, key string) error
	URL(key string) string
}

type UploadMeta

type UploadMeta struct {
	GeneratedContentID string `json:"generated_content_id,omitempty"`
	GeneratedKind      string `json:"generated_kind,omitempty"`
	Alt                string `json:"alt"`
	Caption            string `json:"caption"`
	Title              string `json:"title"`
	AuthorID           string `json:"author_id"`
	SourceURL          string `json:"source_url"`
}

type UploadRequest

type UploadRequest struct {
	File    string `json:"file" format:"binary" doc:"the file part (multipart/form-data)"`
	Alt     string `json:"alt,omitempty"`
	Caption string `json:"caption,omitempty"`
	Title   string `json:"title,omitempty"`
}

UploadRequest documents POST /v1/media (multipart/form-data).

type Variant

type Variant struct {
	URL    string `json:"url"`
	Width  int    `json:"width"`
	Height int    `json:"height"`
}

Jump to

Keyboard shortcuts

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