Documentation
¶
Overview ¶
Package thumbnail generates and caches low-resolution JPEG thumbnails for images on disk. Used by the file-browser endpoints to keep image grids snappy — the original raw bytes are too large to render dozens at once on a phone over a Tailscale link.
Cache layout: <TempDir>/kojo/thumb/<sha256(absPath|modtimeNano|filesize|size)>.jpg
The cache key includes modtime AND file size so an in-place edit invalidates the thumbnail automatically even when the editor preserves modtime (rare, but cheap insurance). Cache entries older than cacheMaxAge are swept by PurgeOld — invoke that periodically from the owning process; otherwise the OS reclaims tmpdir on reboot.
Index ¶
- Constants
- Variables
- func CacheDir() string
- func ClampSize(size int) int
- func Generate(srcPath string, size int) (string, error)
- func IsSupportedExt(ext string) bool
- func PurgeOld() (int, error)
- func ServeHTTP(w http.ResponseWriter, r *http.Request, srcPath string, size int) error
- func StartPurger(done <-chan struct{})
- type HTTPError
Constants ¶
const ( // MinSize / MaxSize bound the requested thumbnail edge (the longer // side after preserve-aspect scaling). MinSize = 16 MaxSize = 1024 // DefaultSize is what handlers use when no `size` query arg is given. // Chosen for a 2x-DPR 150-px grid tile. DefaultSize = 256 )
Variables ¶
var ( ErrUnsupportedFormat = errors.New("unsupported image format") // ErrSourceTooLarge guards against decode-bomb DoS. Callers should map // to HTTP 413. ErrSourceTooLarge = errors.New("source image exceeds size budget") )
ErrUnsupportedFormat is returned when the source can't be decoded as an image. Callers should map this to HTTP 415.
Functions ¶
func CacheDir ¶
func CacheDir() string
CacheDir returns the directory where thumbnails are stored. Created on demand by Generate.
func ClampSize ¶
ClampSize returns size clamped to [MinSize, MaxSize], or DefaultSize when size <= 0. Handlers should call this before passing user input through.
func Generate ¶
Generate returns the absolute path to a cached JPEG thumbnail for srcPath at the requested size (longer edge). srcPath must already be a validated, absolute path — this package does no access control.
Returns ErrUnsupportedFormat when the source can't be decoded, or ErrSourceTooLarge when the source exceeds the byte/pixel budget.
func IsSupportedExt ¶
IsSupportedExt reports whether ext (lower-case, leading dot) is a format we know how to thumbnail. Handlers can short-circuit obvious non-images before opening the file. Match the formats handled in decodeImage.
func PurgeOld ¶
PurgeOld removes cache entries whose mtime is older than cacheMaxAge. Safe to call concurrently with Generate — the worst case is that a just-written entry races a sweep and gets re-generated on the next request. Returns the number of files removed.
func ServeHTTP ¶ added in v0.101.2
ServeHTTP serves a thumbnail with Cache-Control: no-cache so the browser stores the body but revalidates via ETag on every request (→ 304 when unchanged). Use for sources whose URL is stable across content changes (avatars, blob thumbs without a version query).
Callers provide the raw HTTP request so http.ServeFile can handle If-None-Match → 304. Returns nil on success; a pre-stream failure is returned as *HTTPError carrying the mapped status (ErrUnsupportedFormat → 415, ErrSourceTooLarge → 413, os.IsNotExist → 404, else 500). The wrapped error keeps errors.Is working against the package sentinels.
func StartPurger ¶
func StartPurger(done <-chan struct{})
StartPurger runs PurgeOld every purgeInterval until ctx-style done channel closes. Callers should invoke this once at server start. The function returns immediately; the loop runs in its own goroutine.
Types ¶
type HTTPError ¶ added in v0.110.0
HTTPError carries an HTTP status and message for a pre-stream failure so that callers can deliver the error in their own format (e.g. the server's JSON envelope) instead of this package writing text/plain itself. It wraps the underlying error so errors.Is against the package sentinels still matches. Once streaming has begun the serving path can no longer change the status code, so late failures are logged rather than returned as HTTPError.
func NewHTTPError ¶ added in v0.110.0
NewHTTPError builds an HTTPError with the given status and message, wrapping err so errors.Is against the package sentinels keeps matching. Pass err=nil when the failure has no underlying error to preserve (e.g. a pre-check).