Documentation
¶
Overview ¶
Package setup provides a first-run setup flow for self-hosted GoFastr apps. An operator deploying a binary against an empty database gets a guided bootstrap: create the initial admin, verify adapter health, and any other step the app declares — either headlessly (all values in env) or through an SSR wizard.
The package exports Config, Field, Step, and constructors for shipped steps (AdminStep, HealthStep). A Config becomes a Runner via New; the Runner implements framework.SetupRunner and is wired onto an App via framework.WithSetup.
Index ¶
- type Config
- type Field
- type Runner
- func (r *Runner) CanRunHeadless(_ context.Context) (bool, error)
- func (r *Runner) Handler(swap func(), healthz, readyz http.HandlerFunc) http.Handler
- func (r *Runner) Incomplete(ctx context.Context) (bool, error)
- func (r *Runner) RunSteps(ctx context.Context) error
- func (r *Runner) SetupURL(addr string) string
- type Step
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
Steps []Step
// Complete reports whether setup has already finished (e.g. "at
// least one user exists"). This is a DERIVED check — never a marker
// file — so a crash mid-setup re-enters setup on next boot. Required.
Complete func(ctx context.Context) (bool, error)
// DisableToken opts out of the one-time setup token. Use only on
// trusted networks where the first operator to reach /setup is
// guaranteed to be authorised.
DisableToken bool
// Title overrides the wizard page title. Defaults to "Setup".
Title string
// HostName overrides the host in the setup URL printed at boot.
// Defaults to the bound listen address.
HostName string
// Theme supplies the shared design tokens (--color-* / --font-*)
// the wizard renders from. When zero, the framework DefaultTheme
// is used. Pass the same theme the app uses for a coherent look.
Theme style.Theme
}
Config is the top-level setup configuration.
type Field ¶
type Field struct {
// Name is the form field name AND the default env-var suffix
// (prefixed with GOFASTR_). e.g. "ADMIN_EMAIL" → form name
// "ADMIN_EMAIL", env GOFASTR_ADMIN_EMAIL.
Name string
// Label is the human-readable label shown in the wizard.
Label string
// EnvVar is the full environment variable name (e.g.
// "GOFASTR_ADMIN_EMAIL"). Empty means the field cannot be resolved
// from env — it can only come through the wizard.
EnvVar string
// Secret renders as a password input. Secret values are never
// pre-filled from env (the wizard shows an empty password field even
// when GOFASTR_ADMIN_PASSWORD is set) and never logged.
Secret bool
// Validate optionally checks the value before the step's Run fires.
// Returning an error aborts the step and re-renders the field with
// the message.
Validate func(value string) error
}
Field describes one input the operator provides during setup.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner implements framework.SetupRunner. Construct via New.
func New ¶
New constructs a Runner from cfg. Panics if cfg.Complete is nil — without a Complete predicate the framework can't tell whether setup is done, and a derived check (not a marker file) is the contract.
func (*Runner) CanRunHeadless ¶
CanRunHeadless reports whether every field across all steps resolves from the environment. A field resolves when its EnvVar is non-empty AND the variable is actually set.
func (*Runner) Handler ¶
func (r *Runner) Handler(swap func(), healthz, readyz http.HandlerFunc) http.Handler
Handler implements framework.SetupRunner.Handler. It returns the interactive setup surface: the wizard + /healthz + /readyz; every other path returns 503 with a short "setup required" body.
The returned handler owns:
- Token exchange: GET /setup?token=<t> sets a cookie and redirects to /setup; wrong/missing token → 403.
- Wizard navigation: GET /setup renders the current step; POST /setup validates, runs the step, and advances or re-renders on error.
- Atomic exit: when the final step succeeds, swap() is called to switch to the real app handler. Step execution is serialized and Complete is re-checked under the mutex so two racing submissions can't both run the steps.
func (*Runner) Incomplete ¶
Incomplete reports whether setup has not yet finished.
type Step ¶
type Step struct {
Name string
Fields []Field
// Run executes the step with the resolved field values. Called once
// per step in order. An error aborts the flow (headless) or
// re-renders the step (interactive).
Run func(ctx context.Context, values map[string]string) error
}
Step is one unit of the setup flow.
func AdminStep ¶
func AdminStep(m *auth.AuthManager, db *sql.DB, usersTable string) (Step, func(context.Context) (bool, error))
AdminStep builds the "create initial admin" step plus the Complete predicate ("at least one user exists").
The step collects an email + password, validates the password against the auth battery's strength policy (RecommendedMinPasswordBytes), hashes it with the same bcrypt hasher the battery uses, and creates a user with roles ["admin"].
The Complete predicate probes the users table directly — auth.UserStore has no count/list surface and the brief forbids adding methods to battery/auth. The narrowest exported surface is the *sql.DB the host already holds (the same one passed to auth.NewEntityUserStore).
usersTable MUST match the table name passed to auth.NewEntityUserStore (typically "auth_users"). The identifier is validated with query.SafeIdent and quoted with query.QuoteIdent since it can't be parameterized. An empty or unsafe name panics — fail loud, never silently fall back to a hardcoded default.
EnvVars: GOFASTR_ADMIN_EMAIL / GOFASTR_ADMIN_PASSWORD.
func HealthStep ¶
HealthStep builds a "verify adapters" step with no fields. Its Run executes the app's registered readiness checks via the exported RunReadinessChecks method and fails with a per-check, actionable error list. In the wizard this renders as a "verify adapters" step.