server

package
v0.17.3 Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: Apache-2.0 Imports: 48 Imported by: 0

Documentation

Overview

Package server is the loopback HTTP mux for the web UI and the API (specs/000-product/contracts/api.md). Reads are assembled from the SQLite mirror. Write-through endpoints call Jira and re-read the issue into the mirror. Credentials are needed for those writes and for fetching attachment bytes that are not already on disk.

The server has no authentication: `gadak serve` refuses a non-loopback bind instead. Personal-state endpoints therefore never answer 401/403.

Index

Constants

This section is empty.

Variables

View Source
var Version = "0.0.0-dev"

Version is the gadak release string exposed on GET settings/ under runtime. cmd/gadak should assign this from its ldflags version var at startup:

server.Version = version

Until that wiring lands, the default below is what the UI shows.

Functions

func GuardBrowser added in v0.13.0

func GuardBrowser(next http.Handler, hostExempts ...func(*http.Request) bool) http.Handler

GuardBrowser wraps next so Host/Origin checks run before any route. Mount this on the top-level serve mux so routes registered outside Handler (/config.json, /healthz, /api/v1/workspaces, /w/) cannot skip the guard.

hostExempts widen only the Host check, for requests a later gate authenticates by credential instead of by name — today that is the paired origin passthrough (PairedOriginHostExempt) and the paired mirror allowlist (PairedMirrorHostExempt), whose Bearer requirements make the DNS-rebinding vector this check exists for unmountable (a browser cannot attach Authorization cross-origin without a preflight this server never answers). The Origin check is not exempted.

func MergedPRLinks(devLinks []store.DevLink, attachments []store.DetailAttachment) json.RawMessage

MergedPRLinks is ListLinkedPRs encoded as the linked_prs JSON array. Empty input returns nil so omitempty callers can drop the field.

func PairedMirrorHostExempt added in v0.17.3

func PairedMirrorHostExempt(dir func() string) func(*http.Request) bool

PairedMirrorHostExempt lets GuardBrowser pass a DNS-named Host for allowlisted mirror requests while active pairing tokens exist — the same probe shape as PairedOriginHostExempt: an empty-bearer Authorize answers "does the gate have anything to check" without accepting anything. VerdictOff (or an unreadable store, which fails closed) keeps today's forbidden_host, so an unpaired serve never widens for the phone. dir is resolved per request: pairing.json can appear while a serve is running.

func PairedOriginHostExempt added in v0.17.0

func PairedOriginHostExempt(dir func() string) func(*http.Request) bool

PairedOriginHostExempt lets GuardBrowser pass a DNS-named Host — which the rebinding check otherwise rejects — for origin-passthrough requests while active pairing tokens exist. Measured on a real tailnet (GDK-443): tailscale serve forwards the original `<machine>.<tailnet>.ts.net` Host upstream, so without this every paired request died as forbidden_host before the Bearer gate could speak. Authorize with an empty bearer answers "do tokens exist" without accepting anything: VerdictOff (or an error, which fails closed) keeps today's rejection, VerdictReject means pairingGate will demand the Bearer right after this. dir is resolved per request — pairing.json can appear while a serve is running.

func WebConfig

func WebConfig(cfg *config.Config) ([]byte, error)

WebConfig renders the config document the UI fetches before mount (`GadakConfig` in web/src/lib/config.ts). Credentials never appear in it.

func WebConfigBase

func WebConfigBase(cfg *config.Config, prefix string) ([]byte, error)

WebConfigBase is WebConfig with APIBase/AuthBase prefixed (e.g. "/w/work" for a workspace mount). prefix has no trailing slash; empty means root bases.

Types

type Handler

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

Handler is the HTTP API plus optional update-check control. It implements http.Handler; mount it at "/api/".

func New

func New(db *store.DB, cfg *config.Config) *Handler

New returns the API handler. Mount it at "/api/" — the patterns below carry their full paths, so nothing strips a prefix.

func NewWithCache

func NewWithCache(db *store.DB, cfg *config.Config, cache *attachcache.Cache) *Handler

NewWithCache is New plus an attachment byte cache. `gadak serve` passes one rooted under GADAK_HOME; tests pass nil when they do not exercise attachments.

func NewWorkspace

func NewWorkspace(db *store.DB, cfg *config.Config, cache *attachcache.Cache, profile string) *Handler

NewWorkspace is NewWithCache bound to a named profile (for /w/<name>/ mounts). profile is used for runtime paths and display; it does not re-read global config.

func (*Handler) BindOriginHandler added in v0.16.0

func (h *Handler) BindOriginHandler(next http.Handler)

BindOriginHandler pins the passthrough target. Tests use it so they can evict origin.live (simulating a second process) without reconstructing a second issuetap graph on the next request.

func (*Handler) CheckNow added in v0.16.0

func (h *Handler) CheckNow(ctx context.Context, cacheDir string) UpdateStatus

CheckNow bypasses the 24h disk cache, hits GitHub once, and records the result for GET update/ and for bootstrap/delta. Background checks stay silent; this path is the one that reports current / error / dev.

func (*Handler) Close added in v0.16.0

func (h *Handler) Close() error

Close is Shutdown with a 3s bound — the same window cmd/gadak/serve.go uses for http.Server.Shutdown.

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

func (*Handler) SetSyncStarter

func (h *Handler) SetSyncStarter(f func())

SetSyncStarter registers a function that starts the background sync loop after the first credential is saved via onboarding connect. Fired at most once. cmdServe registers this when serve starts without a credential.

func (*Handler) Shutdown added in v0.16.0

func (h *Handler) Shutdown(ctx context.Context) error

Shutdown cancels background startSyncJob work and waits for those goroutines to return, or until ctx is done. A timed-out wait returns ctx.Err(); the job may still be running and still hold a database connection. Idempotent.

Returning from the job goroutine is not enough: database/sql rolls a cancelled Tx back from a helper goroutine (Tx.awaitDone), and that helper can still hold the pool connection after runSyncJob has returned. Waiting for InUse==0 is waiting for that writer, which is the WAL leak GDK-270 actually is.

func (*Handler) SnapshotSync added in v0.16.0

func (h *Handler) SnapshotSync() progressResponse

SnapshotSync is the debug document for "what background work is running right now": the same one-shot job + activity picture that GET /api/v1/issues/sync/progress/ already returns. No new endpoint — that GET already carries it; this is the in-process form.

func (*Handler) StartUpdateCheck

func (h *Handler) StartUpdateCheck(ctx context.Context, cacheDir string)

StartUpdateCheck runs a GitHub release lookup immediately and every 24h. Results feed latest_version / release_url on bootstrap and delta when the running build is older. Records cacheDir even when disabled so a later user-initiated CheckNow still knows where the file lives. The background loop is a no-op when cfg.UpdateCheckEnabled() is false. Safe with no credential (Jira-independent). Background errors are silent.

func (*Handler) SyncActivityHooks

func (h *Handler) SyncActivityHooks() (phase func(string), progress func(fetched, changed int))

SyncActivityHooks returns the Phase and Progress callbacks a background watch loop should report through, so the UI can say what the mirror is fetching and how far along. Safe for concurrent use; nil Handler returns nil funcs (callers pass them straight into sync.Options).

type LinkedPR added in v0.17.0

type LinkedPR struct {
	Number int     `json:"number"`
	Title  string  `json:"title"`
	URL    string  `json:"url"`
	State  string  `json:"state"`
	Repo   *string `json:"repo"`
	Author *string `json:"author"`
	// LinkedBy / LinkedByID name who attached the link (dev_links actor,
	// GDK-589) — a different axis from Author: a bot linking a human's PR
	// keeps both. Absent for URL attachments, which carry no actor.
	LinkedBy   *string `json:"linked_by,omitempty"`
	LinkedByID *string `json:"linked_by_id,omitempty"`
}

prLinksFromAttachments derives the linked_prs payload from mirrored URL attachments when no plugin enrichment supplies one. The enrichment (kind='prs') stays the winner: it can carry state and author, which a bare URL cannot. LinkedPR is one GitHub pull request derived from the mirror (dev_links and/or a PR-shaped URL attachment).

func ListLinkedPRs added in v0.17.0

func ListLinkedPRs(devLinks []store.DevLink, attachments []store.DetailAttachment) []LinkedPR

ListLinkedPRs merges the two mirrored PR sources: dev_links (the origin's development panel, GDK-497 — carries a state) and PR-shaped URL attachments (GDK-495 — carry none). Deduped by URL, dev_links winning, because a stated status beats an inferred blank.

type UpdateStatus added in v0.16.0

type UpdateStatus struct {
	Current         string `json:"current"`
	Latest          string `json:"latest,omitempty"`
	URL             string `json:"release_url,omitempty"`
	Notes           string `json:"release_notes,omitempty"`
	NotesLen        int    `json:"release_notes_len"`
	CheckedAt       string `json:"checked_at,omitempty"`
	Newer           bool   `json:"newer,omitempty"`
	Status          string `json:"status,omitempty"` // newer|current|error|dev — this CheckNow
	Error           string `json:"error,omitempty"`
	LastUserCheckAt string `json:"last_user_check_at,omitempty"`
	LastUserStatus  string `json:"last_user_status,omitempty"`
}

UpdateStatus is GET/POST update/ and CheckNow: what the server currently knows about the latest published release, plus the outcome of a user-initiated check when one has run.

Jump to

Keyboard shortcuts

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