Documentation
¶
Overview ¶
Package server contains the HTTP server lifecycle, the first-run detector that bootstraps an admin user, and the boot banner.
Thumbnail backfill and repair — walks the file nodes in scope and draws the ones a selection asks for (thumb.Pipeline.Wanted).
⚠⚠ ONE walk, three front ends:
- `filex thumb backfill` (synchronous, prints progress);
- FILEX_THUMB_BACKFILL_ON_BOOT=once (background, fired from Start());
- Admin → Tools → Thumbnail repair, an ops job (thumb_repair.go) with progress, cancellation and a result.
Nothing else walks nodes to draw thumbnails: a second copy of this walk is how the CLI and the admin tool would come to disagree about which files "need repair".
Index ¶
Constants ¶
const ( AuditActionEntryAvailable = "storage.entry_available" )
Audit actions for an entry the storage could not answer for (issue #104), written when the entry's state CHANGES - not on every pass that gets the same answer.
const ( GapSyncRunning = "sync_running" GapSyncAborted = "sync_aborted" GapNeverSynced = "never_synced" )
Why a storage's catalogue cannot be trusted to hold its files (catalogueGap).
Variables ¶
var ErrNotIndexed = errors.New("thumb backfill: storage catalogue not ready")
ErrNotIndexed is returned (wrapped, after every other storage has been processed) when at least one targeted storage's catalogue cannot be trusted to hold its files.
⚠⚠ Why a backfill refuses instead of doing what it can. A backfill renders thumbnails for NODES — rows in the catalogue — because that is what a thumbnail is keyed on. Files written straight onto a storage's backend have no node until a sync writes one, and a sync answers 202 and writes them in the background. So this command, run against a storage nobody had synced, or one whose sync was still going, walked an empty (or half-written) catalogue and printed `{processed: 0, ok: 0, failed: 0, skipped: 0}` with exit 0. Measured 2026-09-14, and it is how a screenshot run produced a grid with no thumbnails while every step it took said "ok". A command that does nothing and reports success is worse than one that fails: nobody goes looking.
It cannot simply sync first: the CLI runs beside a live server, and two processes syncing one storage into one database is its own bug. So it says what is missing and what to do, and exits non-zero.
var ErrPathNeedsOneStorage = errors.New("thumb backfill: a path needs exactly one storage")
ErrPathNeedsOneStorage: a Path was given without exactly one storage.
var ErrPathNotFound = errors.New("thumb backfill: no file or folder at that path")
ErrPathNotFound: BackfillOptions.Path names nothing in the catalogue.
Functions ¶
func PrintBanner ¶
func PrintBanner(w io.Writer, cfg config.Config, fr FirstRunCredentials, caps map[string]model.ExternalServiceState, storages []*model.Storage)
PrintBanner writes the first-run banner to w (typically os.Stdout).
fr is the FirstRun result — when non-empty it adds the "first run detected" credentials section.
Types ¶
type BackfillOptions ¶
type BackfillOptions struct {
// StorageIDs, when non-empty, restricts the run to specific storages.
// Empty means "every enabled storage".
StorageIDs []int64
// Limit caps the number of files processed across all storages
// combined. 0 = unlimited.
Limit int
// RetryFailed includes nodes whose existing thumbnails row is in
// state="failed". Without this, failed rows are skipped (so a flaky
// office doc doesn't block every subsequent run).
RetryFailed bool
// RetrySkipped includes nodes whose existing thumbnails row is in
// state="skipped". Use when the pipeline has gained coverage for
// kinds that previously skipped (e.g. v0.1.7 generic fallback /
// audio waveform) — without this, old rows freeze the pipeline.
RetrySkipped bool
// Stale includes ready thumbnails drawn from other content (the file
// changed after its render), and failures or skips recorded on other
// content. `filex thumb backfill` turns it on unless --no-stale.
Stale bool
// All draws every file in scope, whatever its row says ("Rebuild").
All bool
// Path narrows the run to one file, or one folder with everything in it:
// storage-relative ("/Photos/2024"). "" or "/" is the whole storage. Only
// with exactly one storage in StorageIDs; a path that has no row answers
// ErrPathNotFound.
Path string
// Concurrency controls the worker pool size. <=0 → 4.
Concurrency int
// ProgressEvery determines how many processed nodes between an
// OnProgress callback. <=0 disables intermediate notifications.
ProgressEvery int
// OnProgress, if non-nil, is invoked with the running counters every
// ProgressEvery files. CLI uses this to print to stdout; the boot
// goroutine leaves it nil and lets the slog summary do the work.
OnProgress func(BackfillStats)
}
BackfillOptions tunes a backfill run.
type BackfillStats ¶
type BackfillStats struct {
Processed int
OK int
Failed int
Skipped int
// NotIndexed names the storages this run REFUSED, and why. See
// ErrNotIndexed.
NotIndexed []NotIndexedStorage
}
BackfillStats is the counter snapshot emitted via OnProgress and returned from BackfillThumbs.
type FirstRunCredentials ¶
type FirstRunCredentials struct {
AdminEmail string
AdminPassword string // plaintext — only ever shown ONCE (blank when preset)
WroteFile string // path of ~/.filex/.first-run.txt (blank when preset)
// Preset is true when the admin was created from FILEX_ADMIN_EMAIL /
// FILEX_ADMIN_PASSWORD — the operator already knows the password, so it is
// neither written to disk nor echoed in the banner.
Preset bool
}
FirstRunCredentials is what FirstRun returns when bootstrapping. Empty AdminEmail means no first-run was needed (a user already exists).
func FirstRun ¶
func FirstRun(ctx context.Context, store db.Store, dataDir, adminEmail, adminPassword string) (FirstRunCredentials, error)
FirstRun checks whether the users table is empty. If so it creates an admin user and records the timestamp in the settings table.
When adminEmail/adminPassword are supplied (from FILEX_ADMIN_* env) they are used as-is and nothing is written to disk. Otherwise it falls back to admin@local with a fresh random password written to a 0600 file at {dataDir}/.first-run.txt.
Returns zero-value FirstRunCredentials when no bootstrap was performed.
type NotIndexedStorage ¶
type NotIndexedStorage struct {
ID int64
Name string
Reason string
// Code is Reason for a client to say in its own language: one of the
// Gap* constants.
Code string
}
NotIndexedStorage is one storage a backfill would have reported success on while rendering nothing for it.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the high-level wrapper around HTTP + workers.
func (*Server) BackfillThumbs ¶
func (s *Server) BackfillThumbs(ctx context.Context, opts BackfillOptions) (BackfillStats, error)
BackfillThumbs walks the file nodes in scope and draws the ones opts selects (thumb.Pipeline.Wanted). Safe to call concurrently with normal operation — the pipeline itself is idempotent and uses UpsertThumbnail.
func (*Server) CountBackfill ¶ added in v0.50.0
CountBackfill is the number of files BackfillThumbs(opts) would draw right now: the same walk, drawing nothing. The repair tool shows it as the total.