Documentation
¶
Overview ¶
Package api is the composition root's one dependency: an http.Handler satisfying api/openapi.yaml, serving the aggregated dashboard snapshot and the static frontend. It lives under internal/ because the API this service offers is its endpoints, not its Go packages — see go.md's "a server keeps everything in internal/ and its commands in cmd/".
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewMux ¶
NewMux wires the handlers registered against api/openapi.yaml: liveness, passkey registration/login/session, per-user settings, the aggregated dashboard snapshot, and the static frontend that consumes it.
The dashboard itself, and the page that renders it, require a valid session; registration, login and the handful of static assets a login page needs (its own HTML/JS, the shared stylesheet, the favicon) don't.
Types ¶
type AdminUser ¶ added in v0.9.0
type AdminUser struct {
Username string `json:"username"`
DisplayName string `json:"displayName"`
IsAdmin bool `json:"isAdmin"`
CreatedAt string `json:"createdAt"`
}
AdminUser matches components.schemas.AdminUser in api/openapi.yaml.
type BotPrUpdatesResponse ¶ added in v0.67.0
type BotPrUpdatesResponse struct {
AllowBotPrUpdates bool `json:"allowBotPrUpdates"`
}
BotPrUpdatesResponse matches components.schemas.BotPrUpdatesResponse. Deliberately not folded into SettingsResponse's own GET: the dashboard page (app.js) needs only this one field on every load, and reusing handleSettingsGet for that would carry its EnsureWebhookCredentials side effect along too — silently provisioning webhook credentials (and flipping GET /api/dashboard/stream from 404 to 200) for a user who only ever visited the dashboard and never opened Settings at all. This handler reads the saved row without ever creating one.
type CredentialInfo ¶ added in v0.73.0
type CredentialInfo struct {
ID string `json:"id"`
Label string `json:"label"`
CreatedAt time.Time `json:"createdAt"`
}
CredentialInfo matches components.schemas.Credential — one passkey's own display metadata, never the credential itself.
type Deps ¶ added in v0.8.0
type Deps struct {
// Version is the release tag this binary was built from ("dev" for a
// build outside the release pipeline) — see cmd/forge-dashboard's own
// version var for how it's set.
Version string
AuthService *auth.Service
AuthStore *auth.Store
SettingsStore *settings.Store
SharingStore *sharing.Store
// Manager holds each signed-in user's own Aggregator, built from
// their saved Credentials via BuildSources.
Manager *dashboard.Manager
BuildSources func(settings.Credentials) []dashboard.Source
// PublicOrigin is this instance's own externally-reachable origin
// (RP_ORIGIN — the same value WebAuthn's relying-party config
// already requires be set correctly, reused rather than adding a
// second "what's my own address" env var). handleWebhookEnsure uses
// it to build the exact URL a webhook it creates should target;
// nothing else here needs it, since matching an existing hook
// (dashboard.WebhookTargetsPath) only ever compares by path.
PublicOrigin string
// AppContext is the process's own long-lived context (canceled on
// shutdown), not any one request's — a handler that starts a
// Manager-owned background refresh goroutine has to root it here, not
// in the *http.Request context that dies the moment that handler
// returns.
AppContext context.Context
}
Deps is everything NewMux needs — passed as one struct rather than a long parameter list, since it's grown one dependency per feature slice (auth, then per-user settings) and reads better named than positional.
type Health ¶
type Health struct {
Status string `json:"status"`
}
Health matches components.schemas.Health in api/openapi.yaml.
type SessionUser ¶ added in v0.7.0
type SessionUser struct {
Username string `json:"username"`
DisplayName string `json:"displayName"`
IsAdmin bool `json:"isAdmin"`
}
SessionUser matches components.schemas.SessionUser in api/openapi.yaml.
type SettingsResponse ¶ added in v0.8.0
type SettingsResponse struct {
GitHubUsername string `json:"githubUsername"`
GitHubTokenSet bool `json:"githubTokenSet"`
ForgejoURL string `json:"forgejoUrl"`
ForgejoUsername string `json:"forgejoUsername"`
ForgejoTokenSet bool `json:"forgejoTokenSet"`
// WebhookToken and WebhookSecret, unlike the forge tokens above, are
// ours to hand back in the clear — the user has to paste them into
// the forge's own webhook setup, so a "set" flag alone wouldn't do.
WebhookToken string `json:"webhookToken"`
WebhookSecret string `json:"webhookSecret"`
// AllowBotPrUpdates and RenovateRebaseLabel — see settings.Credentials'
// own doc comments; both round-trip as plain values, unlike the forge
// tokens above, since neither is a secret.
AllowBotPrUpdates bool `json:"allowBotPrUpdates"`
RenovateRebaseLabel string `json:"renovateRebaseLabel"`
// Theme — see settings.Credentials' own doc comment. Round-tripped
// here too so the Settings page's own save confirms what it just set,
// even though the lightweight GET /api/settings/theme below is what
// every other page actually polls on load.
Theme string `json:"theme"`
}
SettingsResponse matches components.schemas.SettingsResponse in api/openapi.yaml. Tokens are never sent back to the browser once saved — *Set reports whether one is on file, not what it is.
type SharedUser ¶ added in v0.10.0
type SharedUser struct {
}
SharedUser matches components.schemas.SharedUser in api/openapi.yaml.
type SharingResponse ¶ added in v0.10.0
type SharingResponse struct {
}
SharingResponse matches components.schemas.SharingResponse.
type ThemeResponse ¶ added in v0.73.0
type ThemeResponse struct {
Theme string `json:"theme"`
}
ThemeResponse matches components.schemas.ThemeResponse. Same reasoning as BotPrUpdatesResponse above: every page needs this on load, and none of them should trigger EnsureWebhookCredentials just to read one field.
type Token ¶ added in v0.64.0
type Token struct {
ID string `json:"id"`
Label string `json:"label"`
CreatedAt time.Time `json:"createdAt"`
ExpiresAt time.Time `json:"expiresAt"`
LastUsedAt *time.Time `json:"lastUsedAt,omitempty"`
}
Token matches components.schemas.APIToken — a token's own metadata, never the raw value. Named Token, not APIToken, to avoid stuttering as api.APIToken from outside the package; auth.APIToken (a different package) keeps its own fuller name since auth.Token would collide with nothing here but reads less clearly on its own.