Documentation
¶
Overview ¶
Package admin is the server-rendered admin UI: html/template pages from backend/web, htmx for interactivity, the same session cookie and stores as the JSON API. It never imports internal/server; the server builds Deps from its services (server/admin_mount.go) and calls Mount.
Index ¶
- Constants
- Variables
- func BuilderPalette(cfg *siteconfig.Config) (colors, space, fs map[string]string)
- func CheckDangerPhrase(r *http.Request, dc ui.DangerConfirm) (ui.DangerConfirm, bool)
- func IsHTMX(r *http.Request) bool
- func Nonce(ctx context.Context) string
- func Redirect(w http.ResponseWriter, r *http.Request, target string)
- func SafeNextFor(prefix, raw string) string
- func Toast(w http.ResponseWriter, kind, message string)
- type APIKeyStore
- type AddressBanner
- type AddressLock
- type AddressService
- type AddressState
- type Admin
- func (a *Admin) AdminPath() string
- func (a *Admin) Error(w http.ResponseWriter, r *http.Request, status int, heading, message string)
- func (a *Admin) ErrorBack(w http.ResponseWriter, r *http.Request, status int, ...)
- func (a *Admin) Mount(r *httpx.Group)
- func (a *Admin) Nav() *Nav
- func (a *Admin) NewView(w http.ResponseWriter, r *http.Request, title string) *View
- func (a *Admin) Render(w http.ResponseWriter, r *http.Request, status int, page string, v *View)
- func (a *Admin) RenderPartial(name string, data any) (template.HTML, error)
- func (a *Admin) RequireProfile(next http.Handler) http.Handler
- func (a *Admin) RequireRole(roles ...string) func(http.Handler) http.Handler
- func (a *Admin) RequireSession(next http.Handler) http.Handler
- func (a *Admin) RequireSudo(next http.Handler) http.Handler
- func (a *Admin) SetBackups(svc BackupsService)
- func (a *Admin) SetFlash(w http.ResponseWriter, r *http.Request, kind, message string)
- func (a *Admin) SetSecondFactor(f SecondFactor)
- func (a *Admin) SetStats(svc StatsService)
- func (a *Admin) SetUpdates(svc UpdatesService, unavailable string)
- type AgentStats
- type ApplyRequest
- type ApplyResult
- type Assets
- type AuditEvent
- type AuditReader
- type AuditRow
- type Authenticator
- type AuthorStore
- type BackupInfo
- type BackupsService
- type BackupsState
- type ConfirmInfo
- type ContentDeps
- type CrawlerStore
- type Crumb
- type CurrentUser
- type Deps
- type EmailInput
- type EmailSettingsService
- type EmailSettingsView
- type EmailSource
- type EntityNavEntry
- type EntityStore
- type Flash
- type FormMailer
- type FormStore
- type GrowthDeps
- type ImportService
- type InsertOutcome
- type LinkingStore
- type Mailer
- type MediaStore
- type MenuStore
- type MissingSection
- type Nav
- type NavEntry
- type NavGroup
- type NavItem
- type OwnerCreator
- type PackageAdmin
- type PackageMeta
- type PackageSummary
- type PageAnswer
- type PageCacheService
- type PageCacheSettings
- type PageCacheStats
- type PasswordChanger
- type PeopleStore
- type PickOption
- type Pickers
- type PluginAdmin
- type PluginInfo
- type PluginNavEntry
- type PluginUIInfo
- type Preload
- type PreloadFailure
- type PreloadStatus
- type RecoveryCopy
- type RecoveryStore
- type RedirectStore
- type Referrals
- type Refusal
- type RegistryEntry
- type RegistryView
- type RouteResult
- type ScriptTag
- type SecondFactor
- type SectionStore
- type SessionStore
- type SettingsSection
- type SettingsTab
- type SetupCode
- type SiteStore
- type SpamService
- type StarterInstaller
- type StatsBanner
- type StatsService
- type StatsView
- type SudoStore
- type TemplateStore
- type TermStore
- type ThemeInfo
- type ThemeSection
- type TokenService
- type URLExplainer
- type URLExplanation
- type UpdateBanner
- type UpdateChip
- type UpdateChipBlock
- type UpdateChipRow
- type UpdatesBannerInput
- type UpdatesForm
- type UpdatesGlance
- type UpdatesHistoryRow
- type UpdatesLocks
- type UpdatesResult
- type UpdatesService
- type UpdatesState
- type UpdatesStatus
- type UserFinder
- type UserManager
- type UserReader
- type View
- func (v *View) AreaEntry() string
- func (v *View) AreaID() string
- func (v *View) AreaLabel() string
- func (v *View) AreaNav() []SettingsSection
- func (v *View) BootModules() []Preload
- func (v *View) CurrentSection() *SettingsSection
- func (v *View) EntitiesNav() []EntityNavEntry
- func (v *View) EntitiesNavManage() bool
- func (v *View) HTMXConfig() string
- func (v *View) HasSubnav() bool
- func (v *View) NavGroups() []NavGroup
- func (v *View) PluginsNav() []PluginNavEntry
- func (v *View) Preloads() []Preload
- func (v *View) SectionTabs() []ui.Tab
- func (v *View) SpeculationRules() template.HTML
- type WebhookStore
Constants ¶
const ( BackupRunning = "running" BackupOK = "ok" BackupFailed = "failed" BackupManual = "manual" BackupPreUpdate = "pre_update" )
Backup states and kinds as the page sees them. They mirror the engine's values; the admin does not import the engine, so the server adapter maps.
const DefaultAdminPath = "/admin"
DefaultAdminPath is where the admin lives unless Deps.AdminPath says otherwise. It is the one place the prefix is spelled out.
Variables ¶
var ( // ErrBackupRunning is returned by Start when a backup is already running. ErrBackupRunning = errors.New("a backup is already running") // ErrBackupNotFound is returned for an id that is not a backup. ErrBackupNotFound = errors.New("backup not found") )
var ( ErrPluginNotFound = errors.New("plugin not found") ErrBadGrant = errors.New("permission not requested by the plugin") // ErrCommunityGrant is a permission a community plugin may not be given. ErrCommunityGrant = errors.New("permission not allowed for a community plugin") )
ErrPluginNotFound, ErrBadGrant and ErrCommunityGrant are what PluginAdmin.Grant answers.
var ErrAddressRefused = errors.New("admin address refused")
ErrAddressRefused means the address was reserved, invalid, or weak without an acknowledgement. Nothing was stored and the old address keeps serving.
var ErrNoUpdate = errors.New("there is no update to install")
ErrNoUpdate is Apply's answer when the stored status names nothing to install.
var ErrThemeActive = errors.New("theme is active")
ErrThemeActive is what Uninstall answers for the active theme.
var ErrWrongCode = errors.New("admin: wrong second-factor code")
ErrWrongCode is what Verify returns for a code that is definitely wrong. Only this error counts toward the per-session cap. Any other error means the provider could not decide (an outage, a timeout) and is not counted.
Functions ¶
func BuilderPalette ¶
func BuilderPalette(cfg *siteconfig.Config) (colors, space, fs map[string]string)
BuilderPalette is the resolved palette of the site config for callers outside the admin (the MCP builder_design tool): colours by name, spacing by step and font sizes by name.
func CheckDangerPhrase ¶
func CheckDangerPhrase(r *http.Request, dc ui.DangerConfirm) (ui.DangerConfirm, bool)
CheckDangerPhrase is the server side of ui.DangerConfirm. It compares the posted confirm_phrase to dc.Phrase exactly: no trimming, no case folding, and an empty phrase never matches. On a mismatch it returns dc with Error set and false; the screen re-renders its page with that card and status 422. The browser's button state is only a hint, so every screen must call this.
func Redirect ¶
func Redirect(w http.ResponseWriter, r *http.Request, target string)
Redirect sends the browser to target after a successful action: a 303 for plain requests, HX-Redirect (a full navigation) for htmx ones.
func SafeNextFor ¶
SafeNextFor validates a post-login "next" target: a same-site path under the admin prefix only. Anything else (absolute URLs, //host, backslashes, control chars, other prefixes) becomes "" so the caller falls back to the default landing page.
The sudo page is never a target, but its own next is: a sign-in from an expired sudo page lands on the page the owner was headed to. That unwraps at most twice.
func Toast ¶
func Toast(w http.ResponseWriter, kind, message string)
Toast sets the HX-Trigger header that makes admin.js show a toast.
Types ¶
type APIKeyStore ¶
type APIKeyStore interface {
List(ctx context.Context) ([]*auth.APIKey, error)
Create(ctx context.Context, createdBy *uuid.UUID, in *auth.CreateAPIKeyInput) (*auth.APIKeyWithSecret, error)
Revoke(ctx context.Context, id uuid.UUID, revokedBy *uuid.UUID) error
Regenerate(ctx context.Context, id uuid.UUID) (string, error)
Delete(ctx context.Context, id uuid.UUID) error
}
APIKeyStore is auth.APIKeyService as the API-keys screen uses it.
type AddressBanner ¶
type AddressBanner struct {
Path string
ChooseHref, KeepHref string
// KeepLabel is "Keep <address>…".
KeepLabel string
}
AddressBanner is the owner notice for a weak admin address nobody accepted.
type AddressLock ¶
type AddressLock string
AddressLock says why the admin address cannot be changed from the admin.
const ( // AddressUnlocked: an owner may change the address. AddressUnlocked AddressLock = "" // AddressLockedEnv: the server environment pins the address. AddressLockedEnv AddressLock = "env" // AddressLockedDemo: the public demo does not allow changes. AddressLockedDemo AddressLock = "demo" )
type AddressService ¶
type AddressService interface {
State() AddressState
// Assess ranks a candidate against the reserved names and the site.
Assess(ctx context.Context, path string) adminpath.Result
// Generate returns a new random strong candidate. It stores nothing.
Generate() string
// Apply stores req.Path and serves the admin there from now on.
Apply(ctx context.Context, req ApplyRequest) (ApplyResult, error)
// Keep records that an owner knowingly accepted the current address.
Keep(ctx context.Context, by string) error
}
AddressService is the admin's view of the live admin address. The server implements it: changing the address rebuilds the admin and swaps it in.
type AddressState ¶
type AddressState struct {
Path string
Level adminpath.Level
Reasons []adminpath.Reason
// SetBy and SetAt are nil for an address the server generated itself and
// for installs that predate the record.
SetBy string
SetAt *time.Time
// AckBy and AckAt are set when an owner knowingly accepted a weak address.
AckBy string
AckAt *time.Time
Lock AddressLock
// Pinned is the environment variable name behind AddressLockedEnv. It is
// data from the server so no template spells the address or its source.
Pinned string
}
AddressState is what the admin shows about the live admin address. The service keeps it cached, so reading it costs no database round trip.
type Admin ¶
type Admin struct {
// contains filtered or unexported fields
}
Admin holds the parsed templates and assets and the HTTP handlers.
func New ¶
New parses every template and bundles the assets. A parse failure is returned so `serve` refuses to boot rather than 500 on every page.
func (*Admin) Error ¶
Error answers with an error page, or, for an htmx fragment request, a toast plus HX-Reswap: none so the current page stays put.
func (*Admin) ErrorBack ¶
func (a *Admin) ErrorBack(w http.ResponseWriter, r *http.Request, status int, heading, message, back, backLabel string)
ErrorBack is Error with a way back that suits the screen the error is on.
func (*Admin) Mount ¶
Mount registers every /admin route on r. Registration order is irrelevant: ServeMux picks the most specific pattern.
func (*Admin) NewView ¶
NewView builds the root view for a request: chrome data from the request context and the site config, and the flash message (consumed here).
func (*Admin) Render ¶
Render writes page with status. Normal requests get the whole document; an htmx request targeting a region gets only the page's "main" block. A template error becomes a 500 page, never a half-written response.
func (*Admin) RenderPartial ¶
RenderPartial executes one named partial (tests, and handlers that return a fragment such as a table row after an htmx action).
func (*Admin) RequireProfile ¶
RequireProfile sends a signed-in user whose person has no name or no photo to the complete-profile step. It runs after RequireSession and never writes: "complete" is derived from the person, so there is no flag to keep in step. A GET navigation carries next= so the step ends where the user was headed; an htmx request gets HX-Redirect (next is the page it came from); anything else (a POST) is redirected without next, since replaying it is not safe.
func (*Admin) RequireRole ¶
RequireRole allows only the listed roles; others get the 403 page.
func (*Admin) RequireSession ¶
RequireSession authenticates the hatamoto_session cookie (independent of ENABLE_COOKIE_AUTH, which only gates the JSON API) and puts the user, their claims and the DB context on the request. Failures redirect to the login page (htmx: 401 + HX-Redirect so the whole page navigates). It never rotates the session: parallel htmx requests would race the rotation.
func (*Admin) RequireSudo ¶
RequireSudo lets a request through only when its session confirmed the password in the last sudoTTL. Put it after RequireSession. Anyone else is sent to the confirmation page; a POST is never replayed, so the user submits the form again once confirmed.
func (*Admin) SetBackups ¶
func (a *Admin) SetBackups(svc BackupsService)
SetBackups hands the admin the backup service once the engine is known: the server mounts the admin before it exists. Call it before serving starts.
func (*Admin) SetFlash ¶
SetFlash stores a message for the next page view (60 s, HMAC-signed so it cannot be forged into the page).
func (*Admin) SetSecondFactor ¶
func (a *Admin) SetSecondFactor(f SecondFactor)
SetSecondFactor registers the provider sudo mode asks for a code. Pass nil to remove it. It is safe to call while the admin serves requests.
func (*Admin) SetStats ¶ added in v1.0.2
func (a *Admin) SetStats(svc StatsService)
SetStats sets the install stats service after New, like SetUpdates. Call it only with a non-nil service: a typed nil would defeat the nil checks.
func (*Admin) SetUpdates ¶
func (a *Admin) SetUpdates(svc UpdatesService, unavailable string)
SetUpdates sets the updates service (or why there is none) after New. The server mounts the admin before the update agent exists; call it before the server starts serving.
type AgentStats ¶
type AgentStats interface {
agents.StatsStore
FirstCrawls(ctx context.Context, rg agents.Range, p agents.Page) ([]agents.FirstCrawlRow, int, error)
FirstCrawlAggregates(ctx context.Context, rg agents.Range) (agents.FirstCrawlAgg, error)
PublishedPages(ctx context.Context) (int, error)
FetchesBySchema(ctx context.Context, rg agents.Range) ([]agents.SeriesPoint, error)
HealthSeries(ctx context.Context, rg agents.Range) ([]agents.HealthDay, error)
NeverFetchedBySchema(ctx context.Context) ([]agents.SchemaCount, error)
agents.RobotsStore
}
AgentStats is the read side of AI-crawler analytics (agents.StatsStore plus first crawls) and the robots policy.
type ApplyRequest ¶
type ApplyRequest struct {
Path string
// By is the display name or email of the owner choosing it.
By string
// Ack is true when the owner typed the confirmation for a weak address.
Ack bool
}
ApplyRequest asks for a new admin address. Path is already normalised.
type ApplyResult ¶
type ApplyResult struct{ Path string }
ApplyResult is what a successful Apply reports; Path is the live address.
type Assets ¶
type Assets struct {
CSSURL string
Importmap template.HTML
Tokens []byte
// contains filtered or unexported fields
}
Assets is the fingerprinted CSS/JS bundle plus the vendored scripts, built once at boot. All URLs live under <prefix>/_/ so the edge needs no rule.
type AuditEvent ¶
AuditEvent is one owner action for the admin audit log. Detail must carry no secrets.
type AuditReader ¶
type AuditReader interface {
Recent(ctx context.Context, limit, offset int) ([]AuditRow, int, error)
Latest(ctx context.Context, limit int) ([]AuditRow, error)
}
AuditReader lists stored audit events newest first. Recent also counts the stored events (for pagination); Latest skips that count for callers that only want the newest few.
type AuditRow ¶
AuditRow is one stored audit event as the Audit log page and the Overview Activity card read it.
type Authenticator ¶
type Authenticator interface {
LoginSession(ctx context.Context, email, password, userAgent, ip string) (*user.User, string, error)
IssueSession(ctx context.Context, userID uuid.UUID, userAgent, ip string) (string, error)
}
Authenticator signs people in (auth.Service).
type AuthorStore ¶
type AuthorStore interface {
List(ctx context.Context) ([]*person.Person, error)
Resolve(ctx context.Context, name string) (entity.Byline, error)
}
AuthorStore feeds the author picker: the people a byline can credit, and the resolver behind "Or add a new author". A name that is the site organisation resolves to Org, never to a person.
type BackupInfo ¶
type BackupInfo struct {
ID string
Kind string // BackupManual or BackupPreUpdate
Target string // the version a pre-update backup was taken for
State string // BackupRunning, BackupOK or BackupFailed
Percent int
Error string
Created time.Time
Size int64
Parts []string // archive members, e.g. core.dump, content.tar.gz
Plugins []string
MediaMissing int // referenced media files that were not on disk
}
BackupInfo is one backup as the page shows it.
type BackupsService ¶
type BackupsService interface {
List(ctx context.Context) (BackupsState, error)
// Start begins a manual backup in the background and returns its id.
// It returns ErrBackupRunning when one is already running.
Start(ctx context.Context, actor string) (string, error)
// Open returns the archive for download. The caller closes it.
Open(ctx context.Context, id string) (io.ReadSeekCloser, BackupInfo, error)
// Delete removes a finished backup; ErrBackupNotFound for an unknown id.
Delete(ctx context.Context, id string) error
}
BackupsService is the backup engine as the admin uses it. The server adapter implements it; Start never runs the backup on the request context.
type BackupsState ¶
type BackupsState struct {
Rows []BackupInfo // newest first
Running bool
FreeBytes uint64 // free space where backups are written; 0 when unknown
KeepManual int
KeepPreUpdate int
}
BackupsState is everything the page needs in one read.
type ConfirmInfo ¶
type ConfirmInfo struct {
Name string
Downgrade, SignerChange bool
// WidenedAccess is set when an update can call web addresses the installed
// version could not; it also clears the plugin's grants.
WidenedAccess bool
NewHosts []string
NewFetchAny bool
NewIsland bool
NewScripts bool
OldVersion, NewVersion string
OldSigner, NewSigner string
// Missing are section types the active theme would lose.
Missing []MissingSection
}
ConfirmInfo is what replacing an installed package puts at stake: the owner must confirm it. SignerChange and WidenedAccess also clear the plugin's grants.
type ContentDeps ¶
type ContentDeps struct {
Entities EntityStore
Terms TermStore
Media MediaStore
Authors AuthorStore
// PublicPath is the public URL path of an entity ("" when unknown).
PublicPath func(ctx context.Context, e *entity.Entity) string
// PublicPaths is PublicPath for many entities of one schema at once (one
// ancestor query for all of rows, none without nested URLs). routable is
// false, with nil paths, when the schema has no public URL; rows may be nil
// to ask only that. Nil falls back to PublicPath per entity.
PublicPaths func(ctx context.Context, sc *entity.EntitySchema, rows []*entity.Entity) (paths []string, routable bool)
// Theme lists the active theme's section types; nil (or empty) when page
// rendering is off, which leaves the page editor without "Add section" and
// without a preview.
Theme func() []ThemeSection
// Preview renders unsaved attributes: it answers a POST with the JSON body
// of /api/v1/render/preview (entity_id, schema, attributes, path, canvas)
// with the full HTML document. Nil turns the live preview off.
Preview http.Handler
// RenderNodes applies builder ops to a draft page doc and renders node
// fragments for the canvas: it answers a POST with the JSON body of the
// node render request (the preview body plus ops, render, css, seq).
// Nil turns the in-place canvas patching off (the editor reloads the frame).
RenderNodes http.Handler
// Templates are the section templates; nil hides saving and inserting them.
Templates TemplateStore
// Crawlers reads AI-crawler activity for the blog editor's "AI crawlers"
// card; nil hides the card.
Crawlers CrawlerStore
// Recovery keeps the unsaved work of a published post or page per user, so
// it can be offered back after a closed tab. Nil turns recovery copies
// off: the routes answer 204 and the editors get no copy.
Recovery RecoveryStore
}
ContentDeps are the services behind the entity, schema, taxonomy and media screens. Any may be nil only in route-table builds.
type CrawlerStore ¶
type CrawlerStore interface {
EntityActivity(ctx context.Context, entityID uuid.UUID, agentIDs []string, today time.Time) (agents.EntityActivityData, error)
}
CrawlerStore reads AI-crawler activity for one entity (agents.StatsRepository).
type CurrentUser ¶
CurrentUser is the signed-in user as templates see them.
func UserFromContext ¶
func UserFromContext(ctx context.Context) (CurrentUser, bool)
UserFromContext is the signed-in user of an admin request (false outside one).
func (*CurrentUser) Initials ¶
func (u *CurrentUser) Initials() string
Initials is the avatar text: the first letters of the first and last word of the name ("Hana Sato" gives "HS"), the first two letters of a single word, "?" for an empty name.
type Deps ¶
type Deps struct {
Logger *slog.Logger
// Sessions, Users and Site may be nil only in route-table builds: the
// handlers are never invoked there.
Sessions SessionStore
Users UserReader
Site SiteStore
Auth Authenticator
Tokens TokenService
Finder UserFinder
Mailer Mailer
// BaseURL returns the site address the owner or operator configured
// (PUBLIC_BASE_URL, else the Site address setting); "" when none is set.
// Emailed links use only this, never the request's headers.
BaseURL func(*http.Request) string
// CookieSecure is ADMIN_COOKIE_SECURE: a floor for the Secure cookie
// attribute (TLS seen on the request also turns it on).
CookieSecure bool
Manager UserManager
Passwords PasswordChanger
APIKeys APIKeyStore
// Webhooks is the outgoing-webhook service behind Settings > Webhooks; nil
// answers that section with 404. Set it only when non-nil: a typed nil
// would defeat the check.
Webhooks WebhookStore
// WebhookDeliveryOn is true when a job sends queued webhooks; false shows
// the "delivery is switched off" notice.
WebhookDeliveryOn bool
// WebhooksPluginLeft says a plugin named webhooks is still installed (one
// that was not moved into core); true shows the notice on the webhooks page.
WebhooksPluginLeft func(context.Context) bool
// Invalidate clears the rendered-page cache after settings change.
Invalidate func(context.Context)
// BuilderTokens reports whether the live theme opted in to the design
// tokens (theme.json builder.tokens); nil means no.
BuilderTokens func() bool
// BuilderParts lists the layout slots (header, footer, single, archive) the
// live theme renders parts for (theme.json builder.parts); nil means none.
BuilderParts func() []string
// PageCache is the rendered-page cache behind Settings > Performance;
// nil answers that page with 503.
PageCache PageCacheService
// Spam is the spam protection settings behind Settings > Forms > Spam
// protection; nil answers that page with 503.
Spam SpamService
// EmailSettings is the SMTP configuration behind Settings > Email and the
// setup wizard's Email step; nil answers those pages with 503.
EmailSettings EmailSettingsService
// Updates is the automatic-update service behind Settings > Updates and
// the owner banner; nil renders that page as unavailable and no banner.
// Set it only when non-nil: a typed nil would defeat the check.
Updates UpdatesService
// Stats is the opt-in install stats service behind the Install stats card
// on Settings > Updates, the setup wizard step and the owner banner; nil
// hides all three. Set it only when non-nil.
Stats StatsService
UpdatesUnavailable string
// Backups is the backup service behind Settings > Backups; nil answers
// that page with 503. Set it only when non-nil.
Backups BackupsService
// Audit records an owner action in the admin audit log; nil skips it.
Audit func(ctx context.Context, e AuditEvent)
// AuditLog reads the audit log back; nil leaves the Audit log page
// unmounted. Set it only when non-nil.
AuditLog AuditReader
Owners OwnerCreator
Starter StarterInstaller
// SetupCode gates the first owner; nil disables the wizard's first step.
SetupCode *SetupCode
// NeedsSetup reports a fresh install (no users yet); /admin/login then
// sends visitors to the setup wizard. Nil means never.
NeedsSetup func(context.Context) bool
// FlashKey signs the flash cookie (derive it from the server secret).
FlashKey []byte
// SiteNameFallback is shown until the site config has a name.
SiteNameFallback string
// RateLimit wraps the unauthenticated auth screens; nil means none.
RateLimit func(http.Handler) http.Handler
// Growth holds the services of the menus, forms, redirects, dashboards,
// linking and plugins screens (see growth.go).
Growth GrowthDeps
// Content holds the services behind the entity, schema, taxonomy and media
// screens (v1-11).
Content ContentDeps
// People is the person behind each user account: the profile pages and
// the sign-in profile gate. Nil skips the gate.
People PeopleStore
// PersonPath is the public path of a person by slug (the person content
// type's URL pattern); nil or "" falls back to /author/{slug}.
PersonPath func(ctx context.Context, slug string) string
// Import runs the WordPress import wizard (importjob.Manager); nil answers 503.
Import ImportService
// Dir, when set, replaces the embedded templates/CSS/JS with files from
// disk and rebuilds them per request (dev loop, HATAMOTO_ADMIN_DIR).
Dir string
// PublicAPIPrefix is the extra public API prefix (PUBLIC_API_PREFIX), empty
// when only /api/v1/public is served. Form addresses shown in the admin use it.
PublicAPIPrefix string
// Dev turns on the development-only pages (the styleguide, HATAMOTO_DEV=1).
Dev bool
// Demo is the public demo mode (HATAMOTO_DEMO=1): a fixed banner, the
// public login on the sign-in page and the refusals of config.DemoConfig.
Demo config.DemoConfig
// AdminPath is the URL prefix every admin route is served under. Empty
// means DefaultAdminPath; otherwise it is "/" plus one lowercase [a-z0-9-]
// segment of 1 to 64 characters, not starting or ending with "-". Reserved
// names and the spec's length rules come in PR 2. Demo mode requires the
// default.
AdminPath string
// Address is the live admin address: its state, the picker's checks and
// the change itself. Nil hides the address screens (route-table builds).
Address AddressService
// Sudo, when set, holds the sudo grants. The server builds one and passes
// it to every Admin it swaps in, so an owner keeps sudo across a change.
// Nil means this Admin keeps its own.
Sudo *SudoStore
}
Deps is everything the admin needs. Interfaces are declared here (not in the services) so handler tests run on fakes without a database.
type EmailInput ¶
type EmailInput struct {
Provider string
SESRegion string
Host string
Port int
Security email.Security
Username string
Password string
FromAddress string
FromName string
}
EmailInput is one submitted SMTP form after validation. Host is already derived for the SES and Gmail presets. Password is empty when the owner left it blank (see keepPassword on Check and Save).
type EmailSettingsService ¶
type EmailSettingsService interface {
Get(ctx context.Context) (EmailSettingsView, error)
// Check connects and signs in with in without sending a message. A
// *email.CheckError explains a failure. keepPassword uses the saved
// password instead of in.Password.
Check(ctx context.Context, in EmailInput, keepPassword bool) error
// Save stores in (sealing the password) and applies it at once.
// keepPassword keeps the saved password instead of in.Password.
Save(ctx context.Context, in EmailInput, keepPassword bool) error
// Reset deletes the saved settings so the server's SMTP_* apply again.
Reset(ctx context.Context) error
}
EmailSettingsService is the SMTP configuration saved in the admin and applied to the running mail service without a restart.
type EmailSettingsView ¶
type EmailSettingsView struct {
// Saved reports whether a settings row exists. The form fields below are
// defaults (custom, 587, STARTTLS) when it does not.
Saved bool
Provider string
SESRegion string
Host string
Port int
Security email.Security
Username string
FromAddress string
FromName string
// PasswordSaved: a password is stored and can be read.
PasswordSaved bool
// PasswordLost: a password is stored but cannot be read (the server
// SECRET changed); the owner must type it again.
PasswordLost bool
Source EmailSource
// Configured: the live service can send.
Configured bool
// Disabled: EMAIL_DISABLED=true switches all sending off.
Disabled bool
}
EmailSettingsView is what the Email tab and wizard step show: the saved values (never the password) and how the live configuration was chosen.
type EmailSource ¶
type EmailSource string
EmailSource says where the live email configuration comes from.
const ( // EmailSourceSaved: settings saved in Settings > Email. EmailSourceSaved EmailSource = "saved" // EmailSourceServer: the server's SMTP_* environment. EmailSourceServer EmailSource = "server" // EmailSourceNone: nothing usable is configured. EmailSourceNone EmailSource = "none" )
type EntityNavEntry ¶
type EntityNavEntry struct {
EntityNavEntry is one link of the Entities subnav. ID is the entity type's slug and the no-JS select's option value: empty for "All entities" (never a valid slug, so it selects the unfiltered list).
type EntityStore ¶
type EntityStore interface {
ListSchemas(ctx context.Context) ([]*entity.EntitySchema, error)
GetSchema(ctx context.Context, id uuid.UUID) (*entity.EntitySchema, error)
CreateSchema(ctx context.Context, in *entity.CreateSchemaInput) (*entity.EntitySchema, error)
UpdateSchema(ctx context.Context, id uuid.UUID, in *entity.UpdateSchemaInput) (*entity.EntitySchema, error)
DeleteSchema(ctx context.Context, id uuid.UUID) error
GetEntity(ctx context.Context, id uuid.UUID) (*entity.Entity, error)
CreateEntity(ctx context.Context, userID *uuid.UUID, in *entity.CreateEntityInput) (*entity.Entity, error)
UpdateEntity(ctx context.Context, id uuid.UUID, userID *uuid.UUID, in *entity.UpdateEntityInput) (*entity.Entity, error)
UpdateEntityStatus(ctx context.Context, id uuid.UUID, userID *uuid.UUID, status entity.EntityStatus) (*entity.Entity, error)
ScheduleEntity(ctx context.Context, id uuid.UUID, userID *uuid.UUID, at time.Time) (*entity.Entity, error)
DeleteEntity(ctx context.Context, id uuid.UUID) error
ListEntitiesWithOffset(ctx context.Context, f *entity.ListEntitiesFilter, p *entity.OffsetPage) (*entity.OffsetPagedResult[*entity.Entity], error)
CountEntitiesBySchemaStatus(ctx context.Context) ([]entity.SchemaStatusCount, error)
CountEntitiesByParent(ctx context.Context, limit int) ([]entity.ParentCount, error)
CountEntityChildren(ctx context.Context, parentIDs []uuid.UUID) (map[uuid.UUID]int, error)
// RefUsers lists what embeds a block (a block's usage list).
RefUsers(ctx context.Context, blockID uuid.UUID, limit int) (users []entity.RefUser, capped bool, err error)
ListVersions(ctx context.Context, id uuid.UUID, limit int) ([]*entity.EntityVersion, error)
GetVersion(ctx context.Context, id uuid.UUID, version int) (*entity.EntityVersion, error)
RestoreVersion(ctx context.Context, id uuid.UUID, userID *uuid.UUID, version int) (*entity.Entity, error)
}
EntityStore is entity.Service as the entity and schema screens use it.
type Flash ¶
type Flash struct {
Kind string `json:"k"` // success | error | warning | info
Message string `json:"m"`
Expires int64 `json:"e"`
Inline bool `json:"-"` // stay beside the form instead of becoming a toast
}
Flash is a one-shot message carried across a redirect.
type FormMailer ¶
type FormMailer interface {
Configured() bool
Forward(ctx context.Context, f *form.Form, sub *form.FormSubmission, to []string, by string) error
}
FormMailer forwards a submission by email (the "Email this" action).
type FormStore ¶
type FormStore interface {
ListForms(ctx context.Context, status string) ([]*form.Form, error)
GetForm(ctx context.Context, id uuid.UUID) (*form.Form, error)
CreateForm(ctx context.Context, in *form.CreateFormInput) (*form.Form, error)
UpdateForm(ctx context.Context, id uuid.UUID, in *form.UpdateFormInput) (*form.Form, error)
DeleteForm(ctx context.Context, id uuid.UUID) error
ListSubmissions(ctx context.Context, formID uuid.UUID, p *form.ListSubmissionsParams) ([]*form.FormSubmission, int, error)
GetSubmission(ctx context.Context, id uuid.UUID) (*form.FormSubmission, error)
UpdateSubmission(ctx context.Context, id uuid.UUID, in *form.UpdateSubmissionInput) (*form.FormSubmission, error)
DeleteSubmission(ctx context.Context, id uuid.UUID) error
}
FormStore is form.Service as the forms screens use it.
type GrowthDeps ¶
type GrowthDeps struct {
Menus MenuStore
Forms FormStore
Redirects RedirectStore
Explain URLExplainer
Pickers Pickers
Sections SectionStore
Agents AgentStats
Referrals Referrals
Linking LinkingStore
Plugins PluginAdmin
Packages PackageAdmin
FormMail FormMailer
// AgentRegistry names the crawlers (agents.Default() when nil).
AgentRegistry *agents.Registry
}
GrowthDeps groups the services of the site and growth screens.
type ImportService ¶
type ImportService interface {
Begin(ctx context.Context, actor importjob.Actor, src importjob.Source) (uuid.UUID, error)
Get(ctx context.Context, id uuid.UUID) (*importjob.Run, error)
Recent(ctx context.Context, limit int) ([]importjob.Run, error)
Start(ctx context.Context, id uuid.UUID, actor importjob.Actor, host string, o importjob.Options) error
Resume(ctx context.Context, id uuid.UUID, actor importjob.Actor, host string, cred *importjob.Source) error
Cancel(ctx context.Context, id uuid.UUID, actor importjob.Actor) error
}
ImportService is importjob.Manager as the WordPress import wizard uses it. Nothing on it can lift the engine's private-host guard: that is a flag of the command line only.
type InsertOutcome ¶
InsertOutcome reports what Insert did.
type LinkingStore ¶
type LinkingStore interface {
ListRules(ctx context.Context, f *linkingv2.RuleFilter) (*linkingv2.PaginatedRulesResult, error)
GetRule(ctx context.Context, id uuid.UUID) (*linkingv2.LinkRule, error)
CreateRule(ctx context.Context, in *linkingv2.CreateRuleInput) (*linkingv2.LinkRule, error)
UpdateRule(ctx context.Context, id uuid.UUID, in *linkingv2.UpdateRuleInput) (*linkingv2.LinkRule, error)
DeleteRules(ctx context.Context, ids []uuid.UUID) (int, error)
DeleteRulesBySource(ctx context.Context, source string) (int, error)
DeleteAllRules(ctx context.Context) (int, error)
PreviewRule(ctx context.Context, phrase string, target uuid.UUID) (linkingv2.RulePreviewResult, error)
Suggest(ctx context.Context, source uuid.UUID) ([]linkingv2.Suggestion, error)
Equity(ctx context.Context) (*linkingv2.EquityReport, error)
Inbound(ctx context.Context, target uuid.UUID) ([]linkingv2.EquityDetailRow, error)
// EntityName is the display name of an entity, "" when it does not exist.
EntityName(ctx context.Context, id uuid.UUID) string
// Insert wraps the first usable mention of anchor in source's content with a
// link to target (the suggestion panel's "Insert").
Insert(ctx context.Context, source, target uuid.UUID, anchor string) (InsertOutcome, error)
// SuggestIn is Suggest against the editor's live (unsaved) content html.
SuggestIn(ctx context.Context, source uuid.UUID, html string) ([]linkingv2.Suggestion, error)
// WrapIn links the first usable mention of anchor in html to target and
// returns the new html. Nothing is stored: the editor saves it as usual.
WrapIn(ctx context.Context, target uuid.UUID, anchor, html string) (string, InsertOutcome, error)
}
LinkingStore is internal linking v2 as the linking screens use it.
type Mailer ¶
type Mailer interface {
IsConfigured() bool
SendPasswordReset(ctx context.Context, to string, data email.ResetMailData) error
SendInvite(ctx context.Context, to string, data email.InviteMailData) error
SendTest(ctx context.Context, to, siteName string) error
SendAdminAddressChanged(ctx context.Context, to string, data email.AdminAddressChangedData) error
SendAdminAddressSetup(ctx context.Context, to string, data email.AdminAddressSetupData) error
}
Mailer sends the auth emails (email.Service).
type MediaStore ¶
type MediaStore interface {
Upload(ctx context.Context, userID *uuid.UUID, file io.Reader, filename, mimeType string, size int64) (*media.Media, error)
Get(ctx context.Context, id uuid.UUID) (*media.Media, error)
List(ctx context.Context, f *media.ListMediaFilter, limit, offset int) (*media.PagedResult, error)
Update(ctx context.Context, id uuid.UUID, in *media.UpdateMediaInput) (*media.Media, error)
Delete(ctx context.Context, id uuid.UUID) error
Usage(ctx context.Context, m *media.Media) ([]media.Usage, error)
Config() media.Config
}
MediaStore is media.Service as the library and the picker use it.
type MenuStore ¶
type MenuStore interface {
ListMenus(ctx context.Context) ([]*menu.Menu, error)
GetMenuWithItems(ctx context.Context, id uuid.UUID) (*menu.Menu, error)
CreateMenu(ctx context.Context, in *menu.CreateMenuInput) (*menu.Menu, error)
ReplaceMenu(ctx context.Context, id uuid.UUID, fields *menu.UpdateMenuInput, items []menu.ReplaceItem) (*menu.Menu, error)
DeleteMenu(ctx context.Context, id uuid.UUID) error
}
MenuStore is menu.Service as the menu screens use it.
type MissingSection ¶
MissingSection is a section type the content uses that a theme lacks.
type Nav ¶
type Nav struct {
// contains filtered or unexported fields
}
Nav is the sidebar registry. Core registers its six primary items; none has children (plugin pages live in the Plugins subnav).
func (*Nav) AddProvider ¶
AddProvider registers a function whose items join the registered ones on every render.
type NavEntry ¶
type NavEntry struct {
NavEntry is a NavItem resolved for one user and path. Current is the aria-current value: "page" on the link of the page, "" otherwise.
type NavGroup ¶
type NavGroup struct {
}
NavGroup is a run of consecutive entries under one title ("" for none).
type NavItem ¶
type NavItem struct {
NavItem is one sidebar link. Roles empty means every signed-in user.
type OwnerCreator ¶
type OwnerCreator interface {
CreateFirstOwner(ctx context.Context, name, email, password string) (*user.User, error)
}
OwnerCreator creates the first owner (auth.Bootstrap).
type PackageAdmin ¶
type PackageAdmin interface {
Registry(ctx context.Context) RegistryView
// Fetch downloads kind/name from the registry (hash-checked).
Fetch(ctx context.Context, kind, name string) ([]byte, error)
// Inspect parses a package for the permission prompt.
Inspect(ctx context.Context, data []byte) (PackageSummary, error)
// Install stores and applies the package; grant is the approved permission
// list (nil keeps the earlier grant); confirm is the owner's yes to a
// Summary.Confirm. Reasons the owner should read come back as *Refusal.
Install(ctx context.Context, data []byte, source string, grant []string, confirm bool) error
SetEnabled(ctx context.Context, name string, on bool) error
Uninstall(ctx context.Context, kind, name string, dropData bool) error
AllowUnsigned(ctx context.Context) bool
SetAllowUnsigned(ctx context.Context, on bool) error
Themes(ctx context.Context) ([]ThemeInfo, error)
MissingSections(ctx context.Context, theme string) ([]MissingSection, error)
// Activate switches the site to the theme ("default" is the built-in one).
Activate(ctx context.Context, theme string) error
ThemeSettingsSchema(ctx context.Context, theme string) []byte
ThemeSettings(ctx context.Context, theme string) (map[string]any, error)
SaveThemeSettings(ctx context.Context, theme string, values map[string]any) (schemaform.Errors, error)
// PreviewURL is the public-site URL that starts a preview of the theme.
PreviewURL(theme string) string
// UIFile serves a file of the plugin package's ui/ directory; it reports
// false when the plugin is not loaded or has no such file.
UIFile(w http.ResponseWriter, r *http.Request, plugin, file, csp, cache string) bool
// CallRoute runs one of the plugin's own routes as the signed-in user.
CallRoute(ctx context.Context, plugin, method, path string, query url.Values, body []byte, user CurrentUser) RouteResult
// CallHost runs one host function of the plugin (its permissions apply);
// the result is the sdk.CallResponse JSON.
CallHost(ctx context.Context, plugin, fn string, args json.RawMessage) json.RawMessage
}
PackageAdmin is the package manager as the Plugins and Themes pages use it.
type PackageMeta ¶
type PackageMeta struct {
Enabled bool
// WidgetOnly is set for a package that ships widgets and no plugin code:
// the runtime has no status for it, and that is healthy.
WidgetOnly bool
Signed bool
Signer string
// FirstParty is set when the package is signed by one of the project's own keys
// (whether that key is still trusted is Blocked's business).
FirstParty bool
// WebhooksImported is set (webhooks row only) when the boot step has recorded that
// the plugin's subscriptions were copied into core.
WebhooksImported bool
Source string
// Update is the newer version the registry offers ("" when none).
Update string
// UpdateNew lists the permissions that update asks for and the installed
// version does not; empty when Update is, or when it asks for nothing new.
UpdateNew []string
// Blocked says why policy keeps the package from loading (a signer that is
// no longer trusted, a dev package in production); "" when it is not.
Blocked string
// ScriptsBlocked is set when the plugin ships browser JavaScript (an
// assets/plugin.js, or script tags for the page head) that is not loaded
// because the owner has not granted site.scripts.
ScriptsBlocked bool
// MarkupStopped lists the sections and partials that are not registered
// because they carry scripts and the plugin lacks site.scripts.
MarkupStopped []string
// AsksScripts: its manifest lists site.scripts, so the owner can grant it.
AsksScripts bool
// Uninstalling is set while an uninstall is unfinished (retry it).
Uninstalling bool
}
PackageMeta is what the packages table knows about an installed plugin.
type PackageSummary ¶
type PackageSummary struct {
Kind, Name, Version, Description string
Permissions, AllowedHosts []string
Signed bool
Signer string
// Problem says why installing would be refused (untrusted signer, bad
// signature, unsigned while unsigned packages are off); "" when it would go ahead.
Problem string
// Updating is set when a package of that name is installed already.
Updating bool
InstalledVersion string
Granted []string
// Confirm is set when replacing the installed package needs the owner's
// confirmation (older version, other signer, lost sections).
Confirm *ConfirmInfo
}
PackageSummary is a package as shown on the permission prompt.
type PageAnswer ¶
type PageAnswer struct {
Kind string // entity | home | archive | ""
Name string
Schema string
Path string // canonical path
EntityID string
}
PageAnswer names what serves a public path.
type PageCacheService ¶
type PageCacheService interface {
Stats(ctx context.Context) PageCacheStats
Settings(ctx context.Context) (PageCacheSettings, error)
Save(ctx context.Context, s PageCacheSettings) error
Clear(ctx context.Context) (pages int, err error)
PreloadNow(ctx context.Context) (started bool, err error)
PreloadStatus() PreloadStatus
}
PageCacheService is the rendered-page cache as Settings > Performance uses it. The server implements it and owns persistence; the admin only validates the form and calls through.
type PageCacheSettings ¶
type PageCacheSettings struct {
Enabled bool
TTLSeconds, MaxMB int
PreloadEnabled bool
PreloadIntervalSeconds int
// PreloadMode is "missing" (skip cached pages) or "all" (refresh).
PreloadMode string
// PreloadAfterChanges schedules a preload shortly after content changes.
PreloadAfterChanges bool
}
PageCacheSettings are the owner's stored choices. TTLSeconds 0 means "use the server default", MaxMB 0 means no limit, PreloadIntervalSeconds 0 means "same as the lifetime".
type PageCacheStats ¶
type PageCacheStats struct {
Pages int
Bytes, CapBytes int64
EffectiveTTL time.Duration
ServerDefaultTTL time.Duration
TTLSource string
ServerDefaultFromEnv bool
BaseURLPinned bool
SchedulerAvailable bool
}
PageCacheStats is the live state shown next to the form. TTLSource is "admin" or "server".
type PasswordChanger ¶
type PasswordChanger interface {
ChangePassword(ctx context.Context, userID uuid.UUID, current, newPassword string, keepSession *uuid.UUID) error
}
PasswordChanger changes a user's own password (auth.UserTokens).
type PeopleStore ¶
type PeopleStore interface {
// ForUser returns the person a user owns, person.ErrNotFound when none.
ForUser(ctx context.Context, userID uuid.UUID) (*person.Person, error)
// Save writes the profile form, making the person from the typed name when
// the user has none; invalid input is a *person.ValidationError.
Save(ctx context.Context, userID uuid.UUID, in person.ProfileInput) (*person.Person, error)
// List returns every person by name.
List(ctx context.Context) ([]*person.Person, error)
// PostCounts is the number of published entities credited to each person.
PostCounts(ctx context.Context) (map[uuid.UUID]int, error)
// LinkedUsers maps a person to the user that owns it (people without a
// login are absent).
LinkedUsers(ctx context.Context) (map[uuid.UUID]uuid.UUID, error)
// Create makes a person without a login; a bad name is a *person.ValidationError.
Create(ctx context.Context, by *uuid.UUID, name string) (*person.Person, error)
// MergePeople folds drop into keep (person.ErrMergeBothLogins when both
// have a login).
MergePeople(ctx context.Context, keepID, dropID uuid.UUID) (*person.MergeResult, error)
}
PeopleStore is *person.Profiles as the profile pages and the People screens use it. Nil only in route-table builds and in tests that do not touch people; the profile gate then lets every request through.
type PickOption ¶
type PickOption struct {
Value string // entity id, or a term's archive path
Label string
Hint string // schema, slug, kind
Slug string // an entity's slug (empty for terms)
}
PickOption is one result of an entity or term search.
type Pickers ¶
type Pickers interface {
SearchEntities(ctx context.Context, q string, limit int) ([]PickOption, error)
SearchTerms(ctx context.Context, q string, limit int) ([]PickOption, error)
// EntityName is the display name of an entity, "" when it does not exist.
EntityName(ctx context.Context, id uuid.UUID) string
}
Pickers search the things a menu item or a rule can point at. This is the minimal search-select backend; the entity and media pickers of v1-11 replace it.
type PluginAdmin ¶
type PluginAdmin interface {
Plugins() []PluginInfo
Grant(ctx context.Context, name string, permissions []string) error
// Settings is the stored settings with schema defaults merged and write-only
// values masked.
Settings(ctx context.Context, name string) (map[string]any, error)
// StoredSecrets names the writeOnly settings that hold a value; the form
// says "stored" for them and never shows the value.
StoredSecrets(ctx context.Context, name string) (map[string]bool, error)
// SaveSettings validates against the plugin's schema; problems come back as
// field errors (dotted paths), not as an error.
SaveSettings(ctx context.Context, name string, values map[string]any) (schemaform.Errors, error)
}
PluginAdmin is the plugin runtime as the plugins page uses it.
type PluginInfo ¶
type PluginInfo struct {
Name, Version, SDK, Description, Error, DataError string
Loaded bool
Hooks, Permissions, Granted, AllowedHosts []string
// Refused is the part of Permissions this plugin may never be given (a
// community plugin and its scripts); the page shows them unticked.
Refused []string
Routes, Cron, Cookies, Funcs []string // one human line each
SettingsSchema []byte
// Package is set for a plugin installed from a package (nil for one loaded
// from HATAMOTO_PLUGIN_DIR); UI for one that ships an admin page.
Package *PackageMeta
UI *PluginUIInfo
}
PluginInfo is one plugin as the plugins page shows it.
type PluginNavEntry ¶
type PluginNavEntry struct {
PluginNavEntry is one link of the Plugins subnav. ID is the no-JS select's option value: empty for the list (never a valid plugin name, so a plugin named "all" stays selectable) and the plugin's name otherwise.
type PluginUIInfo ¶
type PluginUIInfo struct {
Title string
// Entry is the HTML file under the package's ui/ directory.
Entry string
Roles []string
// Functions are the host functions its page may call: what its granted
// permissions allow.
Functions []string
}
PluginUIInfo is a plugin's declared admin page.
type PreloadFailure ¶
type PreloadFailure struct {
Path, Reason string
}
PreloadFailure names a page the last preload could not build and why.
type PreloadStatus ¶
type PreloadStatus struct {
Running bool
Done, Total int
HasResult bool
Finished time.Time
Pages, Errors int
Duration time.Duration
StoppedEarly bool
Reason string
Skipped int // pages already cached and left alone
Failures []PreloadFailure
}
PreloadStatus is the in-memory state of the preloader and its last result.
type RecoveryCopy ¶
RecoveryCopy is one saved recovery copy (see internal/recovery).
type RecoveryStore ¶
type RecoveryStore interface {
Put(ctx context.Context, user, entity uuid.UUID, c RecoveryCopy) error
// Get returns nil, nil when there is no copy.
Get(ctx context.Context, user, entity uuid.UUID) (*RecoveryCopy, error)
Delete(ctx context.Context, user, entity uuid.UUID) error
}
RecoveryStore keeps recovery copies, one per (user, entity).
type RedirectStore ¶
type RedirectStore interface {
ListRedirects(ctx context.Context, p redirect.RedirectListParams) ([]*redirect.Redirect, int, error)
GetRedirect(ctx context.Context, id uuid.UUID) (*redirect.Redirect, error)
CreateRedirect(ctx context.Context, in *redirect.CreateRedirectInput) (*redirect.Redirect, error)
UpdateRedirect(ctx context.Context, id uuid.UUID, in *redirect.UpdateRedirectInput) (*redirect.Redirect, error)
DeleteRedirect(ctx context.Context, id uuid.UUID) error
List404Entries(ctx context.Context, p redirect.NotFoundListParams) ([]*redirect.NotFoundEntry, int, error)
ListRecent404Entries(ctx context.Context, since time.Time, limit int) ([]*redirect.NotFoundEntry, error)
Ignore404Entry(ctx context.Context, id uuid.UUID) error
Delete404Entry(ctx context.Context, id uuid.UUID) error
}
RedirectStore is redirect.Service as the redirect screens use it.
type Referrals ¶
type Referrals interface {
AIReferrals(ctx context.Context, from, to time.Time) ([]analytics.ReferralDay, error)
Pageviews(ctx context.Context, from, to time.Time) ([]analytics.DayCount, error)
}
Referrals reads the daily AI-referral and pageview rollup.
type Refusal ¶
type Refusal struct{ Msg string }
Refusal is a package that was refused for a reason the owner should read (bad signature, unsigned, unknown permission, a theme that does not render).
type RegistryEntry ¶
type RegistryEntry struct {
Kind, Name, Version, Description, Homepage string
Permissions []string
Installed bool
InstalledVersion string
// Update is set when the registry's version is newer than the installed one.
Update bool
}
RegistryEntry is one package the registry lists.
type RegistryView ¶
type RegistryView struct {
Entries []RegistryEntry
// Off means no registry is configured; Err says why a configured one failed.
Off bool
Err string
// Host is the registry's host name, shown in the empty state when Err is set.
Host string
}
RegistryView is what browsing the registry returned.
type RouteResult ¶
RouteResult is a plugin route's response, relayed by the bridge.
type SecondFactor ¶
type SecondFactor interface {
// Has reports whether the user enrolled a second factor. Return an error
// when you cannot tell: sudo then refuses rather than skip the code.
Has(ctx context.Context, userID uuid.UUID) (bool, error)
// Verify checks the code the user typed. Return ErrWrongCode (or an error
// wrapping it) for a wrong code, and any other error when you cannot tell.
// Core only caps wrong codes per session (a handful per 15 minutes). A
// provider should also count and lock its own failures per user, because a
// session cap does not stop an attacker who opens new sessions.
Verify(ctx context.Context, userID uuid.UUID, code string) error
}
SecondFactor is the seam a second-factor provider (an authenticator app, say) plugs into. Core ships none: without one, sudo asks for the password only.
type SectionStore ¶
type SectionStore interface {
List(ctx context.Context, f *sectiontemplate.ListFilter, limit, offset int) (*sectiontemplate.ListResult, error)
Get(ctx context.Context, id uuid.UUID) (*sectiontemplate.SectionTemplate, error)
Update(ctx context.Context, id uuid.UUID, in *sectiontemplate.UpdateInput) (*sectiontemplate.SectionTemplate, error)
Delete(ctx context.Context, id uuid.UUID) error
}
SectionStore is sectiontemplate.Service as the section template screens use it.
type SessionStore ¶
type SessionStore interface {
Validate(ctx context.Context, token string) (*session.Session, error)
Revoke(ctx context.Context, id uuid.UUID) error
RevokeAllForUser(ctx context.Context, userID uuid.UUID) error
}
SessionStore is the slice of session.Store the admin uses.
type SettingsSection ¶
type SettingsSection struct {
ID, Label, Icon, Desc, Href string
// Group is the sidebar heading the section sits under (Site, Content,
// System); empty for areas that show no headings.
Group string
Tabs []SettingsTab
}
SettingsSection is one entry of the Settings secondary sidebar. Href is the first tab the role may open.
type SettingsTab ¶
SettingsTab is one page of a Settings section. Roles empty means every signed-in user, exactly as the route behind it is gated.
type SetupCode ¶
type SetupCode struct {
// contains filtered or unexported fields
}
SetupCode gates the creation of the first owner: whoever reaches a fresh install first must still prove access to the server (the code is in its log, or in HATAMOTO_SETUP_CODE). Only a hash is kept in memory.
func NewSetupCode ¶
NewSetupCode generates a random 8-character code and returns it for the log line ("XXXX-XXXX"); the SetupCode keeps only its hash.
func SetupCodeFrom ¶
SetupCodeFrom wraps an operator-chosen code (HATAMOTO_SETUP_CODE).
type SiteStore ¶
type SiteStore interface {
Get(ctx context.Context) (*siteconfig.Config, error)
Update(ctx context.Context, in siteconfig.UpdateInput) (*siteconfig.Config, error)
}
SiteStore reads and writes the singleton site config.
type SpamService ¶
type SpamService interface {
Settings(ctx context.Context) (spam.Settings, error)
Save(ctx context.Context, s spam.Settings) error
SealSecret(secret string) ([]byte, error)
// SecretReadable reports whether the saved secret can be opened.
SecretReadable(s spam.Settings) bool
// TestSecret checks the saved secret with the provider.
TestSecret(ctx context.Context, s spam.Settings) (valid bool, detail string, err error)
}
SpamService is the spam protection settings and the provider check behind Settings > Forms > Spam protection. The captcha secret only ever goes in (SealSecret) and is checked against the provider (TestSecret); it never comes back out.
type StarterInstaller ¶
type StarterInstaller interface {
Install(ctx context.Context, o starter.Options) (starter.Result, error)
}
StarterInstaller installs the optional starter content.
type StatsBanner ¶ added in v1.0.2
type StatsBanner struct {
PreviewHref string // Settings > Updates, at the card that shows what is sent
}
StatsBanner is the owner's one-time question about install stats on an install that has not answered it yet.
type StatsService ¶ added in v1.0.2
type StatsService interface {
// View is the stored choice, whether the server configuration forces it
// off, and the exact payload the daily ping would send.
View(ctx context.Context) (StatsView, error)
// Unanswered reports whether the owner still has to answer: the server
// configuration does not turn stats off and the choice is unset. It must
// be cheap, the banner asks it on every owner page.
Unanswered(ctx context.Context) (bool, error)
// ForcedOff reports HATAMOTO_TELEMETRY=off. It reads no state.
ForcedOff() bool
// Choose stores the owner's answer. Yes creates the install ID, No (or
// switching off) deletes it.
Choose(ctx context.Context, yes bool) error
// Reset replaces the install ID with a new one.
Reset(ctx context.Context) error
}
StatsService is the opt-in install stats as Settings > Updates, the setup wizard and the owner banner use it. The server implements it on top of the installstats package; the admin only validates forms and calls through.
type StatsView ¶ added in v1.0.2
type StatsView struct {
Consent string // "unset", "yes" or "no"
ForcedOff bool // HATAMOTO_TELEMETRY=off: the owner cannot change it
PreviewJSON string // the exact request body
LastSent *time.Time // time of the last successful report
}
StatsView is what the admin shows about install stats.
type SudoStore ¶
type SudoStore struct {
// contains filtered or unexported fields
}
SudoStore is the sudo-mode grants of a process. Share one between Admin values to keep elevations when the admin is rebuilt (the address change swaps in a new Admin). Use NewSudoStore.
type TemplateStore ¶
type TemplateStore interface {
List(ctx context.Context, f *sectiontemplate.ListFilter, limit, offset int) (*sectiontemplate.ListResult, error)
Get(ctx context.Context, id uuid.UUID) (*sectiontemplate.SectionTemplate, error)
Create(ctx context.Context, userID *uuid.UUID, in *sectiontemplate.CreateInput) (*sectiontemplate.SectionTemplate, error)
RecordUsage(ctx context.Context, id uuid.UUID) error
}
TemplateStore is sectiontemplate.Service as the page editor uses it: saving a section as a template and inserting one.
type TermStore ¶
type TermStore interface {
ListTerms(ctx context.Context, kind, search string, page, perPage int) (*taxonomy.TermListResult, error)
GetTerm(ctx context.Context, id uuid.UUID) (*taxonomy.Term, error)
CreateTerm(ctx context.Context, in taxonomy.CreateTermInput) (*taxonomy.Term, error)
UpdateTerm(ctx context.Context, in taxonomy.UpdateTermInput) (*taxonomy.Term, error)
DeleteTerm(ctx context.Context, id uuid.UUID) error
MergeTerms(ctx context.Context, from, into uuid.UUID) error
ListTermsForEntity(ctx context.Context, entityID uuid.UUID) ([]*taxonomy.Term, error)
ReplaceEntityTerms(ctx context.Context, entityID uuid.UUID, termIDs []uuid.UUID) error
}
TermStore is taxonomy.Service as the taxonomy screens and the term picker use it.
type ThemeInfo ¶
type ThemeInfo struct {
Name, Version, Description string
Builtin, Active bool
Signed bool
Signer, Source string
HasSettings bool
Update string
// Blocked says why policy keeps an installed theme from running ("" when it is not).
Blocked string
}
ThemeInfo is one theme on the Themes page.
type ThemeSection ¶
type ThemeSection struct {
Type, Title, Description string
Schema json.RawMessage
}
ThemeSection is one section type of the active theme: what the page editor offers in "Add section" and builds the section form from.
type TokenService ¶
type TokenService interface {
IssueReset(ctx context.Context, userID uuid.UUID) (string, error)
ResetValid(ctx context.Context, token string) bool
ConsumeReset(ctx context.Context, token, newPassword string) (*user.User, error)
PeekInvite(ctx context.Context, token string) (*auth.Invite, error)
ConsumeInvite(ctx context.Context, token, name, password string) (*user.User, error)
IssueInvite(ctx context.Context, email string, role user.Role, invitedBy uuid.UUID) (string, *auth.Invite, error)
ListInvites(ctx context.Context) ([]auth.Invite, error)
RevokeInvite(ctx context.Context, id uuid.UUID) error
}
TokenService is the reset/invite token flow (auth.UserTokens).
type URLExplainer ¶
type URLExplainer interface {
ExplainURL(ctx context.Context, path string) (URLExplanation, error)
}
URLExplainer resolves a path the way the public site does.
type URLExplanation ¶
type URLExplanation struct {
Redirects *redirect.Answer
// Page is what serves the path when no redirect fires; Kind is "" when
// nothing does (a 404).
Page PageAnswer
}
URLExplanation answers "what happens to this URL": the redirect providers in order (the first match wins) and, when none redirects, the page that serves it.
type UpdateBanner ¶
type UpdateBanner struct {
Kind string // rolled_back | blocked | backup_failed
Message string
Href string
Label string
}
UpdateBanner is the owner-only notice in the admin shell.
func PickBannerFor ¶
func PickBannerFor(prefix string, in UpdatesBannerInput, _ time.Time) *UpdateBanner
PickBannerFor returns the single most urgent banner, or nil. Order: rolled back, blocked, backup failed: only what needs the owner's action. A major release and updates being off for 90+ days are told by the top-bar chip. A blocked update is not announced while updates are off (the page says "Updates are off"). A dev build never gets one.
func (*UpdateBanner) CalloutKind ¶
func (b *UpdateBanner) CalloutKind() string
CalloutKind is the hm/callout kind that styles the banner.
func (*UpdateBanner) DismissKey ¶
func (b *UpdateBanner) DismissKey() string
DismissKey is the session-scoped dismissal key; a danger banner (a rollback or a failed backup) is never dismissible because it is an error the owner has to act on.
type UpdateChip ¶
type UpdateChip struct {
ID string // the popover's element id
Version string // "v1.4.2", or "dev build"
Title string // "Up to date", "v1.4.3 is ready", "v1.4.3 rolled back"
Channel string // "Stable channel", "" on a dev build
Note string // one sentence under the title (a major release, updates off for 90+ days)
Name string // the trigger's accessible name
Blocks []UpdateChipBlock
Rows []UpdateChipRow
Href string
Label string
}
UpdateChip is the owner's always-visible update status in the top bar: a few tiny release blocks, the running version and a popover with the latest releases. Built from the cached UpdatesGlance, never from View.
type UpdateChipBlock ¶
type UpdateChipBlock struct {
Kind string // success | danger | info | neutral
}
UpdateChipBlock is one tiny release block, oldest first.
type UpdateChipRow ¶
type UpdateChipRow struct {
Text string // "Updated to v1.4.2"
When string
Badge string // badge kind
}
UpdateChipRow is one release switch in the popover.
type UpdatesBannerInput ¶
type UpdatesBannerInput struct {
Status UpdatesStatus
Mode string
Running string
Dev bool
}
UpdatesBannerInput is what PickBanner needs: the stored status and the effective mode.
type UpdatesForm ¶
UpdatesForm is the policy an owner submitted. Locked fields are ignored by the service. A nil hour means "any time".
type UpdatesGlance ¶
type UpdatesGlance struct {
Running string
Dev bool
Mode string
Channel string
Status UpdatesStatus
Applying bool // an Update now run is in flight; read live, never cached
OffSince *time.Time
History []UpdatesHistoryRow // newest first, at most 20
}
UpdatesGlance is the small, cached subset of UpdatesState the chip needs.
type UpdatesHistoryRow ¶
UpdatesHistoryRow is one release switch the supervisor made.
type UpdatesLocks ¶
UpdatesLocks marks fields the server's environment sets, which the page shows read-only and a post cannot change.
type UpdatesResult ¶
type UpdatesResult struct {
Status UpdatesStatus
Target string // Apply: the version being installed
Started bool // Apply: a background run was started
Busy bool // another check or update is already running
}
UpdatesResult reports a Check or an Apply.
type UpdatesService ¶
type UpdatesService interface {
// View is everything the page shows: running version, effective policy,
// env locks, stored status and history.
View(ctx context.Context) (UpdatesState, error)
// Save stores the policy. It returns field errors (keyed by the form
// element id: mode, channel, window-start, defer) when the updater rejects
// a value, and ignores locked fields.
Save(ctx context.Context, in UpdatesForm) (map[string]string, error)
// Check fetches and decides without downloading anything.
Check(ctx context.Context) (UpdatesResult, error)
// Apply starts an update in the background to the target the service
// derives from its own stored status (ErrNoUpdate when there is none).
// The caller never names a version.
Apply(ctx context.Context) (UpdatesResult, error)
// ApplyWithoutBackup is Apply for an update the pre-update backup
// stopped: the same run, with the backup skipped for this version only.
ApplyWithoutBackup(ctx context.Context) (UpdatesResult, error)
// Unpin clears the stored pin.
Unpin(ctx context.Context) error
// Banner is the owner banner, nil when nothing needs attention.
Banner(ctx context.Context) (*UpdateBanner, error)
// Glance is the short, cached read behind the top-bar chip and the
// Overview: it never writes and never costs more than one read per cache
// window, so every owner page may ask for it.
Glance(ctx context.Context) (UpdatesGlance, error)
}
UpdatesService is the automatic-update engine as Settings > Updates and the owner banner use it. The server implements it on top of the updater; the admin only validates forms and calls through. Every method that changes something goes through the same updater path as the scheduled run.
type UpdatesState ¶
type UpdatesState struct {
Running string
Dev bool // not a release build: updates are disabled
Mode string
Channel string
Channels []string
WindowStart *int
WindowEnd *int
DeferDays int
Pin string
Locks UpdatesLocks
Status UpdatesStatus
Applying bool // an update started from here is still running
OffSince *time.Time
History []UpdatesHistoryRow
}
UpdatesState is the Updates page data. Mode, Channel and the window are the effective values (environment over stored over defaults).
func (UpdatesState) ApplyIsMajor ¶
func (s UpdatesState) ApplyIsMajor() bool
ApplyIsMajor reports that Update now would install a major release, which asks for a confirmation first: it can change how data is stored and the site cannot go back past it.
func (UpdatesState) ApplyTarget ¶
func (s UpdatesState) ApplyTarget() string
ApplyTarget is what "Update now" would install on this install: nothing on a dev build or while the environment locks updates off (an operator's switch-off is not overridden from the page), else the status target.
func (UpdatesState) ApplyWithoutBackupTarget ¶
func (s UpdatesState) ApplyWithoutBackupTarget() string
ApplyWithoutBackupTarget is the version "Update without a backup" installs: the target of an update the pre-update backup stopped, "" in any other state. Like ApplyTarget it comes from the stored status, never a form.
func (UpdatesState) Current ¶
func (s UpdatesState) Current() UpdatesStatus
Current is the status the page shows: a dev build and an off mode override whatever the last stored check said, and a failed check shows as such.
type UpdatesStatus ¶
type UpdatesStatus struct {
LastCheckAt *time.Time
LastResult string // ok | error | skipped | ""
Decision string // one of the updater's decisions, "" before the first check
Reason string
Target string
AvailableMajor string
}
UpdatesStatus is the outcome of the last update check.
func (UpdatesStatus) ApplyTarget ¶
func (st UpdatesStatus) ApplyTarget(running string) string
ApplyTarget is the version "Update now" installs: a major the owner has to start by hand, a scheduled or notified release that skips the ramp, window and defer, or the target of an Update now that did not finish (so it can be tried again). "" when there is nothing to apply. The service derives the target from its stored status with this and never from the form.
type UserFinder ¶
type UserFinder interface {
GetByEmail(ctx context.Context, email string) (*user.User, error)
Count(ctx context.Context) (int, error)
}
UserFinder looks users up by address (forgot-password) and counts them.
type UserManager ¶
type UserManager interface {
GetByID(ctx context.Context, id uuid.UUID) (*user.User, error)
List(ctx context.Context, limit, offset int) ([]*user.User, error)
Create(ctx context.Context, in *user.CreateUserInput, passwordHash string) (*user.User, error)
Update(ctx context.Context, id uuid.UUID, in *user.UpdateUserInput) (*user.User, error)
Delete(ctx context.Context, id uuid.UUID) error
}
UserManager is the user repository the Users screens use.
type UserReader ¶
UserReader loads the signed-in user.
type View ¶
type View struct {
Title string
Crumbs []Crumb
SiteName string
// SiteURL is the public site the sidebar's "Open site" button opens.
SiteURL string
Lang string
Theme string
Path string
Nonce string
CSRF string
Assets *Assets
Scripts []string // extra vendored scripts, e.g. "sortablejs"
// AreaCarry is the query the secondary sidebar's links and phone select
// keep (the analytics range and compare switch); empty elsewhere.
AreaCarry url.Values
// Modules are the page's entry modules beyond "@hm/admin", declared by the
// page template's "modules" block; Render fills them in. The head preloads
// their static import closure.
Modules []string
Flash *Flash
User *CurrentUser
// Section and Tab place the page in the Settings frame (IDs from
// settingsRegistry); empty means the page is not a Settings page.
Section, Tab string
Wide bool // bare layout: wider panel
// Width is the page column in the shell: "narrow" (one readable form
// column), "compact" (the same column, kept at that width inside settings),
// "wide" (tables, grids, charts; the default) or "full"
// (editors). Render normalises anything else to "wide".
Width string
// Refresh, in seconds, reloads the page without JavaScript (a live status
// page); htmx pages poll instead.
Refresh int
// NoSiteAddress asks the owner to set the site address (emails need it).
NoSiteAddress bool
// SetupPending reminds the owner that the wizard was not finished.
SetupPending bool
// SkipMarkupHooks keeps the markup scan from adding modules: a page whose
// markup carries hooks it must not load code for (the builder shell, whose
// no-JS outline sits in <noscript>) declares what it needs itself.
SkipMarkupHooks bool
// UpdateBanner is the owner's update notice (nil when nothing needs
// attention); NewView fills it for owner GET requests off the Updates page.
UpdateBanner *UpdateBanner
// UpdateChip is the owner's top-bar update status (nil for editors and
// sites without an update service); NewView fills it for GET requests.
UpdateChip *UpdateChip
// AddressBanner nags the owner about a weak admin address nobody accepted
// (nil otherwise); NewView fills it for owner GET requests off the Security pages.
AddressBanner *AddressBanner
// StatsBanner asks the owner of an existing install about install stats
// (nil otherwise); NewView fills it for owner GET requests off the Updates page.
StatsBanner *StatsBanner
// DemoBanner is the public demo notice ("" outside demo mode).
DemoBanner string
Data any // page-specific data
// contains filtered or unexported fields
}
View is the root template data of every page. Partials never read it implicitly: they take explicit dict arguments.
func (*View) AreaNav ¶
func (v *View) AreaNav() []SettingsSection
AreaNav is the secondary sidebar of the signed-in user; the shell renders it around a page that set View.Section.
func (*View) BootModules ¶
BootModules are the page's own screen modules (top-level "@hm/name" specifiers) as <script type="module"> tags. The nested editor modules ("@hm/editor/kit", "@hm/canvas/host") are only preloaded: the screen module imports them itself.
func (*View) CurrentSection ¶
func (v *View) CurrentSection() *SettingsSection
CurrentSection is the section the page belongs to, if the role may see it.
func (*View) EntitiesNav ¶
func (v *View) EntitiesNav() []EntityNavEntry
EntitiesNav is the Entities subnav the shell renders on the entity list (nil on every other page).
func (*View) EntitiesNavManage ¶
EntitiesNavManage reports whether the subnav links to the entity types settings page (owners only; editors cannot open it).
func (*View) HTMXConfig ¶
HTMXConfig is the content of <meta name="htmx-config">: nonces for what htmx injects, no eval, no script tags, no history snapshots (stale CSRF / secrets).
func (*View) PluginsNav ¶
func (v *View) PluginsNav() []PluginNavEntry
PluginsNav is the Plugins subnav the shell renders on /admin/plugins* pages (nil elsewhere and for signed-out requests).
func (*View) Preloads ¶
Preloads is the modulepreload list of the page: the static import closure of the admin entry plus the page's declared entry modules (see BootModules).
func (*View) SectionTabs ¶
SectionTabs are the link tabs of the current section (none when it has one).
func (*View) SpeculationRules ¶
SpeculationRules is the template accessor for speculationRules.
type WebhookStore ¶
type WebhookStore interface {
List(ctx context.Context) ([]webhook.ListedSubscription, error)
// Create stores the subscription and returns it with its new signing secret.
Create(ctx context.Context, in webhook.Subscription) (webhook.Subscription, string, error)
Get(ctx context.Context, id string) (*webhook.Subscription, error)
Update(ctx context.Context, id string, p webhook.SubscriptionPatch) error
SetActive(ctx context.Context, id string, active bool) error
// RegenerateSecret replaces the signing secret and returns the new one.
RegenerateSecret(ctx context.Context, id string) (string, error)
Delete(ctx context.Context, id string) error
// SendTest sends a test delivery now and returns its recorded outcome.
SendTest(ctx context.Context, id string) (*webhook.DeliveryRow, error)
// ListDeliveries is one page of the delivery log (subscriptionID "" = all)
// and the total row count. Rows never carry the payload.
ListDeliveries(ctx context.Context, subscriptionID string, limit, offset int) ([]webhook.DeliveryRow, int, error)
// RetryDelivery re-queues a failed delivery; false means it had not failed.
RetryDelivery(ctx context.Context, id string) (bool, error)
// CountFailedSince is how many deliveries queued since the time have failed.
CountFailedSince(ctx context.Context, since time.Time) (int, error)
}
WebhookStore is the webhook service as the Settings > Webhooks screens use it. A list never carries a signing secret.
Source Files
¶
- accept.go
- address.go
- address_apply.go
- address_form.go
- address_notify.go
- admin.go
- adminguard.go
- adminpath.go
- analytics.go
- analytics_agent.go
- analytics_content.go
- analytics_crawlers.go
- analytics_health.go
- analytics_legacy.go
- analytics_overview.go
- analytics_referrals.go
- analytics_robots.go
- analytics_view.go
- apikeys.go
- assets.go
- audit.go
- audit_log.go
- authmw.go
- avatar.go
- backups.go
- blog_editor.go
- blogseo.go
- builder_component.go
- builder_editor.go
- builder_node_versions.go
- builder_outline.go
- builder_paste.go
- builder_render.go
- builder_reuse.go
- builder_save.go
- builder_stylespec.go
- builder_templates.go
- confirm.go
- content.go
- content_deps.go
- cookies.go
- created.go
- csp.go
- csrf.go
- dangerconfirm.go
- dashboard.go
- dashboard_activity.go
- dashboard_health.go
- demo.go
- design_presets.go
- design_tokens.go
- email_settings.go
- email_validate.go
- entities.go
- entities_nav.go
- entity_audit.go
- entity_draft.go
- entity_edit.go
- errors.go
- export_settings.go
- flash.go
- format.go
- forms.go
- frameticket.go
- general.go
- growth.go
- htmx.go
- icons.go
- icons_lucide.go
- import.go
- layout_parts.go
- legacy_redirects.go
- linking.go
- login.go
- media.go
- menus.go
- nav.go
- packages.go
- page_editor.go
- partialfuncs.go
- people.go
- people_deps.go
- performance.go
- performance_types.go
- performance_validate.go
- plugins.go
- plugins_install.go
- plugins_nav.go
- plugins_update.go
- pluginui.go
- profile.go
- profilegate.go
- recover.go
- recovery_handlers.go
- redirects.go
- render.go
- schemas.go
- sections.go
- security.go
- security_change.go
- security_keep.go
- settings.go
- settings_more.go
- settings_nav.go
- setup.go
- setup_address.go
- setupcode.go
- setuphelp.go
- siteform.go
- spam.go
- spam_validate.go
- speculation.go
- stats.go
- style_titles.go
- styleguide.go
- sudo.go
- sudo_factor.go
- sudo_factor_set.go
- sudo_links.go
- sudo_mw.go
- sudo_store.go
- taxonomies.go
- terms_tree.go
- theme_blocks.go
- themes.go
- updates.go
- updates_chip.go
- users.go
- versions.go
- versions_nodes.go
- webhooks.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package a11y is the no-browser accessibility checker run over every golden page and partial: structural rules on the HTML (names, landmarks, ids, headings, tables, dialogs, CSP-hostile attributes) plus contrast and CSS lint on the tokens.
|
Package a11y is the no-browser accessibility checker run over every golden page and partial: structural rules on the HTML (names, landmarks, ids, headings, tables, dialogs, CSP-hostile attributes) plus contrast and CSS lint on the tokens. |
|
Package schemaform renders a JSON Schema as an HTML form and reads the form back into JSON.
|
Package schemaform renders a JSON Schema as an HTML form and reads the form back into JSON. |
|
Package starter installs the optional starter content of the setup wizard: a home page with sections, About and Contact pages, two blog posts, header and footer menus and a contact form.
|
Package starter installs the optional starter content of the setup wizard: a home page with sections, About and Contact pages, two blog posts, header and footer menus and a contact form. |
|
Package ui holds the typed shapes of the admin partials (web/admin/partials).
|
Package ui holds the typed shapes of the admin partials (web/admin/partials). |