api

package
v0.121.0 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: AGPL-3.0 Imports: 23 Imported by: 0

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

func NewMux(deps Deps) http.Handler

NewMux wires the handlers registered against api/openapi.yaml: liveness, readiness, 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 AdminInvite added in v0.86.0

type AdminInvite struct {
	ID          string `json:"id"`
	Username    string `json:"username"`
	DisplayName string `json:"displayName"`
	ExpiresAt   string `json:"expiresAt"`
}

AdminInvite matches components.schemas.AdminInvite — an outstanding invite's own metadata for the admin area's list. ID is the invite's own stable identifier (its stored token hash — see auth.Invite's own doc comment for why that's safe to hand back), never the raw token itself; it's what a revoke call passes back in the {token} path segment.

type AdminInviteCreateResponse added in v0.86.0

type AdminInviteCreateResponse struct {
	Token       string `json:"token"`
	Username    string `json:"username"`
	DisplayName string `json:"displayName"`
	ExpiresAt   string `json:"expiresAt"`
}

AdminInviteCreateResponse matches components.schemas.AdminInviteCreateResponse — the one and only response that ever carries the raw invite token, shown once at creation time; only its hash is stored, so it can't be recovered from here again (the same "show once" handling APITokenCreateResponse's own token field gets).

type AdminRequestLogEntry added in v0.90.0

type AdminRequestLogEntry struct {
	LoggedAt   string               `json:"loggedAt"`
	Forge      string               `json:"forge"`
	Account    string               `json:"account,omitempty"`
	Method     string               `json:"method"`
	Endpoint   string               `json:"endpoint"`
	StatusCode int                  `json:"statusCode,omitempty"`
	Outcome    string               `json:"outcome"`
	RateLimit  *dashboard.RateLimit `json:"rateLimit,omitempty"`
}

AdminRequestLogEntry matches components.schemas.RequestLogEntry.

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 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 DatabasePinger added in v0.109.0

type DatabasePinger interface {
	PingContext(ctx context.Context) error
}

DatabasePinger is the slice of *sql.DB readiness needs. Deps.Database is satisfied by cmd/forge-dashboard's schema-aware wrapper in production.

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

	// Database and Dashboard back GET /readyz. Either may be nil, in which
	// case that check is skipped.
	Database  DatabasePinger
	Dashboard FirstRefreshGate

	// Manager holds each signed-in user's own Aggregator, built from
	// their saved Credentials via BuildSources.
	Manager *dashboard.Manager
	// BuildSources takes the signed-in user's own ID (#482: threaded
	// through so the forge clients it builds can record outbound
	// requests under the right account) alongside their saved
	// Credentials.
	BuildSources func(userID []byte, c settings.Credentials) []dashboard.Source

	// RequestLog is the admin-only outbound-request log's own read path
	// — every account's forge clients record into the same underlying
	// store (see cmd/forge-dashboard's buildSourcesForUser), so this one
	// instance, with no account bound to it, is enough to list or export
	// across all of them.
	RequestLog *requestlog.SQLiteRecorder

	// 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

	// GitHubAppConfigured is whether this deployment has a GitHub App set
	// up at all (#620) — server-wide GITHUB_APP_ID/
	// GITHUB_APP_PRIVATE_KEY_BASE64, read once at startup, never a
	// per-user setting. handleSettingsGet/handleSettingsPut use it to
	// explain and enforce why a saved githubAppInstallationId can't work
	// on a server with no App configured.
	GitHubAppConfigured bool

	// 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 FirstRefreshGate added in v0.109.0

type FirstRefreshGate interface {
	FirstRefreshComplete() bool
}

FirstRefreshGate reports whether the dashboard has finished its first refresh. *dashboard.Manager satisfies it.

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"`
	// GitHubAppInstallationID and GitHubAppConfigured (#620) — see their
	// SettingsResponse doc comments in api/openapi.yaml for the full
	// precedence/validation story.
	GitHubAppInstallationID int64  `json:"githubAppInstallationId"`
	GitHubAppConfigured     bool   `json:"githubAppConfigured"`
	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"`
	// RenovateRebaseLabel — see settings.Credentials' own doc comment;
	// round-trips as a plain value, unlike the forge tokens above, since
	// it isn't a secret.
	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 {
	Username    string `json:"username"`
	DisplayName string `json:"displayName"`
}

SharedUser matches components.schemas.SharedUser in api/openapi.yaml.

type SharingResponse added in v0.10.0

type SharingResponse struct {
	SharedWith   []SharedUser `json:"sharedWith"`
	SharedWithMe []SharedUser `json:"sharedWithMe"`
}

SharingResponse matches components.schemas.SharingResponse.

type ThemeResponse added in v0.73.0

type ThemeResponse struct {
	Theme string `json:"theme"`
}

ThemeResponse matches components.schemas.ThemeResponse. Every page needs this on load, and none of them should trigger EnsureWebhookCredentials just to read one field — the same reasoning that gave Theme its own dedicated GET rather than folding it into handleSettingsGet.

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.

type Version added in v0.15.0

type Version struct {
	Version string `json:"version"`
}

Version matches components.schemas.Version in api/openapi.yaml.

Jump to

Keyboard shortcuts

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