admin

package
v0.71.2 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 48 Imported by: 0

Documentation

Overview

Package admin is the back-office battery for GoFastr apps, stock operator screens on top of the data and controls the framework already exposes. It is NOT read-only: today it owns queue/audit ops dashboards, full entity CRUD (proxied through each entity's own CrudHandler so validation, owner/tenant scope, hooks, and audit apply exactly as on the public JSON API), the role→permission and user→role RBAC screens, and the process-module operator lifecycle (enable / disable / bump-generation / per-grant revoke).

Every surface, SSR screens and RPC/form routes alike, is behind the admin default-deny gate (b.gate), which requires an authenticated admin (role "admin" by default, or whatever Config.Authorize decides). Unauthenticated requests are 401 (or redirected to Config.LoginPath); authenticated-but- unauthorized are 403. There is no unauthenticated or self-service path.

The ops dashboards (queue, audit) are self-contained server-rendered HTML that work without a UI host. The entity CRUD screens render THROUGH the host's mounted UI host (runtime.js hydration, islands) and require one. All inputs that reach a SQL surface are bound as parameters, never interpolated; role, permission, and module names from operator forms are $n-bound in their stores.

Every mutation (grant, revoke, assign-roles, module lifecycle, entity write) records a security/compliance audit row; a failed audit write is logged, not swallowed, because the mutation is already durable.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SortDirOf

func SortDirOf(v string) string

SortDirOf normalizes a direction query value to "asc" (default) or "desc".

Types

type Battery

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

Battery is the framework Battery implementation.

func New

func New(cfg Config) *Battery

New constructs the Admin battery with the supplied config. Pass the result to framework.App.RegisterBattery.

func (*Battery) Init

func (b *Battery) Init(app *framework.App) error

Init implements framework.Battery. Mounts the three admin pages on the App's router under cfg.PathPrefix.

func (*Battery) Name

func (b *Battery) Name() string

Name implements framework.Battery.

func (*Battery) RegisterRoutes

func (b *Battery) RegisterRoutes(r *router.Router)

RegisterRoutes mounts the three admin pages under cfg.PathPrefix on the supplied router. Exposed so apps that compose their own router can mount the admin without going through the battery lifecycle.

func (*Battery) ReservedEmbedPrefixes added in v0.49.0

func (b *Battery) ReservedEmbedPrefixes() []string

ReservedEmbedPrefixes reports the prefix this battery actually mounted, so an embed grant can never reach the back office even when the app relocated it with Config.PathPrefix. See framework.EmbedReserving.

type Config

type Config struct {
	// PathPrefix is the URL prefix under which admin pages mount.
	// Defaults to "/admin".
	PathPrefix string

	// Title is the title shown at the top of every admin page.
	// Defaults to "Admin".
	Title string

	// Theme supplies the shared design tokens (--color-* / --font-*) the admin
	// renders from, so the back-office matches the surface that mounts it
	// instead of looking like a separate tool. When zero, the framework
	// DefaultTheme is used. Pass the same theme the app's UI host uses for a
	// coherent experience; override any token to restyle.
	Theme style.Theme

	// FontFaceCSS is raw @font-face CSS for the app's fonts. The admin renders
	// standalone pages (its own <head>), so without this it would reference the
	// theme's --font-* families but never load their files. Pass the same
	// @font-face rules the UI host serves so the admin loads identical fonts.
	FontFaceCSS string

	// Queue is the optional Browsable queue. When set, /admin/queue is
	// active and appears in overview/navigation. When nil, it is hidden;
	// the direct page returns a "no queue wired" diagnostic so the route
	// never 404s ambiguously.
	Queue queue.Browsable

	// DB is the database connection used to read the audit log table and,
	// when entity admin is enabled, overrides the app DB for those operations.
	// When nil, Init uses the app DB; without either, /admin/audit returns a
	// "no audit log wired" stub.
	DB *sql.DB

	// AuditTable is the audit log table name. Defaults to "audit_log".
	AuditTable string

	// QueueListLimit caps rows on /admin/queue. Default 200.
	QueueListLimit int

	// AuditListLimit caps rows on /admin/audit. Default 200.
	AuditListLimit int

	// Entities lists the entity names to expose as editable CRUD screens
	// under <PathPrefix>/e/<table>. When empty (default) NOTHING is
	// exposed, an admin dropped into an app must name what it manages.
	// Set AllEntities for the whole-back-office behavior. Naming an
	// entity explicitly also works for a CRUD=false one, if you really
	// mean to. Screens proxy to each entity's own CrudHandler, so
	// validation, owner/tenant scope, hooks, and events all apply exactly
	// as on the JSON API.
	Entities []string

	// AllEntities exposes EVERY registered entity whose CRUD is enabled,
	// the explicit "generate the whole back-office" opt-in (previously the
	// implicit default when Entities was empty). CRUD-disabled entities
	// (e.g. battery/auth's users/sessions, which ship CRUD=false) are
	// skipped, so this never exposes credential tables. Ignored when
	// Entities is non-empty.
	AllEntities bool

	// Authorize gates every admin surface, both the SSR screens (via the UI
	// host's policy chain) and the RPC/form routes (via middleware). It returns
	// true to allow the request. When nil, the default authorizer requires an
	// authenticated user that holds the AdminRole (see below), a user whose
	// GetRoles() []string includes it. Supply a custom predicate to override
	// the role check entirely (e.g. a permission lookup, an allow-list).
	Authorize func(ctx context.Context) bool

	// AdminRole is the role the default authorizer requires (when Authorize is
	// nil). Defaults to "admin". Ignored when Authorize is set.
	AdminRole string

	// EntityListLimit caps rows per page on an entity list screen. Default 50.
	EntityListLimit int

	// LoginPath, when set, redirects an UNAUTHENTICATED GET to a configured
	// login page (`LoginPath?next=<requested path>`) instead of returning a
	// bare 401. An authenticated user lacking the admin role still gets 403,
	// they're signed in, just not allowed. Empty (default) keeps the 401.
	LoginPath string

	// Policy is the RBAC role policy the admin screens manage. When set
	// alongside GrantStore, the role→permission matrix screen is active
	// at <PathPrefix>/rbac/roles and grant/revoke persists across restarts.
	Policy *access.RolePolicy

	// GrantStore persists role→permission grants to the database. When
	// set alongside Policy, grant/revoke via the admin screens writes to
	// both the live policy and the DB. Wire it with
	// framework.NewGrantStore(db, policy) + EnsureSchema + LoadInto at boot.
	GrantStore *access.GrantStore

	// Auth is the auth manager used for the user→role assignment screen.
	// When set, the user roles screen is active at <PathPrefix>/rbac/users.
	// The underlying UserStore must implement UserLister (for listing) and
	// UpdateRoles (for assignment). EntityUserStore does both.
	Auth *auth.AuthManager

	// EffectiveRoles optionally resolves additional roles for the user roles
	// screen. Direct auth_users.roles are always included with origin "direct";
	// hook results are unioned with them and labeled by their supplied origin.
	// When nil, the screen keeps its direct-roles-only rendering.
	EffectiveRoles func(ctx context.Context, userID string) []access.RoleWithOrigin

	// ProcessModules is the process-module supervisor the operator lifecycle
	// screen manages. When set, /admin/modules is active: list every module's
	// state (404-vs-503 surfaced in copy) plus enable/disable, bump-generation
	// (the circuit-reset / recovery lever, design §8), and per-grant revoke.
	// When nil, the screen is not mounted and the route 404s. Wire it with
	// app.ProcessModules(), the real *framework.ProcessModuleSupervisor
	// satisfies the processModuleController interface.
	ProcessModules processModuleController

	// Logger receives operational warnings a handler can't surface to the
	// caller, notably a security/compliance audit-row write failing AFTER
	// its mutation already committed (grant, revoke, role-assign, module
	// lifecycle). The mutation is not rolled back (it took effect), but a
	// lost audit record is a silent security gap, so it MUST be logged.
	// Default: slog.Default(). Inject a *slog.Logger (e.g. the app's) so the
	// line lands wherever the host's structured logs go.
	Logger *slog.Logger
}

Config configures the Admin battery.

Jump to

Keyboard shortcuts

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