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 ¶
- Variables
- func GuardBrowser(next http.Handler, hostExempts ...func(*http.Request) bool) http.Handler
- func MergedPRLinks(devLinks []store.DevLink, attachments []store.DetailAttachment) json.RawMessage
- func PairedMirrorHostExempt(dir func() string) func(*http.Request) bool
- func PairedOriginHostExempt(dir func() string) func(*http.Request) bool
- func WebConfig(cfg *config.Config) ([]byte, error)
- func WebConfigBase(cfg *config.Config, prefix string) ([]byte, error)
- type Handler
- func (h *Handler) BindOriginHandler(next http.Handler)
- func (h *Handler) CheckNow(ctx context.Context, cacheDir string) UpdateStatus
- func (h *Handler) Close() error
- func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (h *Handler) SetSyncStarter(f func())
- func (h *Handler) Shutdown(ctx context.Context) error
- func (h *Handler) SnapshotSync() progressResponse
- func (h *Handler) StartUpdateCheck(ctx context.Context, cacheDir string)
- func (h *Handler) SyncActivityHooks() (phase func(string), progress func(fetched, changed int))
- type LinkedPR
- type UpdateStatus
Constants ¶
This section is empty.
Variables ¶
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
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 ¶ added in v0.17.0
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
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
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.
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 ¶
New returns the API handler. Mount it at "/api/" — the patterns below carry their full paths, so nothing strips a prefix.
func NewWithCache ¶
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
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
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
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 ¶
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 ¶
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.