httpapi

package
v0.189.2 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2026 License: MIT Imports: 50 Imported by: 0

Documentation

Overview

Package httpapi assembles the backend's HTTP surface as a plain net/http.Handler, independent of how it is served: cmd/lcatd wraps it in a listener, cmd/lcatd-lambda wraps it in the Lambda runtime. Handlers arrive in later tasks; this package owns routing, middleware, and response conventions.

Faceted filters over the works list: cataloger-shaped slices of the workindex summaries -- visibility, holdings, completeness gaps, controlled subjects, raw tags, and configured extras dimensions like provenance sources. Filters AND across groups and OR within one; each group's counts are computed with every other group's filters applied (self-excluding), the standard facet UX. Everything derives from fields the summary already carries -- no grain reads.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New(deps Deps) http.Handler

New assembles the routed, middleware-wrapped API handler.

Types

type Deps

type Deps struct {
	// Logger receives request logs and handler errors. nil disables logging.
	Logger *slog.Logger
	// Blob is the grain store. Record and export handlers (later tasks)
	// read and publish through it.
	Blob blob.Store
	// WorkIndex is the shared identity/summary index over the work grains
	//. Optional: New builds one over Blob when nil. A deployment
	// passes its own to share the index with services that write grains
	// outside httpapi (copycat, workers) or to warm it at boot.
	WorkIndex *workindex.Index
	// Verifier authenticates staff bearer tokens (an auth.Multi when both
	// SSO and local users are configured). nil leaves staff routes
	// unregistered.
	Verifier auth.TokenVerifier
	// AuthExchange, when set, serves POST /v1/auth/exchange -- the OIDC
	// PKCE token-exchange proxy for SPA logins against an external issuer.
	AuthExchange http.Handler
	// Local, when set, mounts built-in user auth (login/refresh/logout)
	// and, with Verifier, admin user management.
	Local *local.Service
	// Vocab, when set, mounts GET /v1/terms autocomplete over the loaded
	// controlled vocabularies.
	Vocab *vocab.Index
	// Suggest and Abuse together mount the anonymous suggestion surface
	// (challenge, submit, public counts).
	Suggest *suggest.Service
	Abuse   *suggest.Abuse
	// Authorities, when set, mounts the local-authority editing surface
	// and hooks the on-save auto-linker into record writes.
	Authorities *authoritiesvc.Service
	// Batch, when set, mounts batch operations, macros, and saved queries
	//.
	Batch *batch.Service
	// Profiles is the live editing-profile set the record/batch/authority
	// surfaces map through. New synthesizes a defaults-only, read-only
	// service when this is nil, so the field is optional for tests.
	Profiles *profilesvc.Service
	// Copycat, when set, mounts external search and staged imports
	//.
	Copycat *copycat.Service
	// Publisher, when set, carries approved decisions into the grain store
	// (POST /v1/publish and the review publish flag).
	Publisher GraphPublisher
	// DB is the document store backing drafts (and, with Blob and Verifier,
	// enables the record-editing surface).
	DB store.Store
	// Exports, when set, mounts the export-job surface.
	Exports *export.Service
	// OrgCode is the deployment's MARC organization code; MARC surfaces
	// derive each record's 040 from graph facts at decode time when set
	//.
	OrgCode string
	// Enrich, when set, mounts the admin enrichment surface.
	Enrich *enrich.Service
	// VocabSources, when set, mounts the authority-source registry, the
	// vocabulary download list, and the live suggest proxy.
	VocabSources *vocabsrc.Service
	// VocabUploadCapMB bounds hand-uploaded vocabulary dumps (0 = the
	// 512MB default). The install is synchronous and in-memory, so a
	// deployment sizes this to its own RAM appetite.
	VocabUploadCapMB int
	// UI, when set, serves the embedded cataloging SPA at "/" (API routes
	// keep priority under /v1/).
	UI http.Handler
	// ClientConfig is the JSON the SPA boots from (GET /config): auth
	// modes, issuers, vocab schemes, provider -- deployment facts, never
	// secrets.
	ClientConfig map[string]any
	// ExtraFacets lists the extras keys the works view facets on
	//, e.g. "sources" for provenance triage.
	ExtraFacets []string
	// ReadOnly puts the instance in demo mode: editorial and config writes are
	// rejected (paired with a read-only blob store), while authentication,
	// reads, search, and dry-run previews still work.
	ReadOnly bool
	// Health, when set, backs GET /v1/readyz. A nil Health reports ready
	// forever, which is what a non-orchestrated deployment wants.
	Health *Health
}

Deps carries the services handlers depend on. It grows as tasks land; everything in it is an interface so tests inject fakes.

type GraphPublisher

type GraphPublisher interface {
	PublishApproved(ctx context.Context, actor string) (publish.Result, error)
}

GraphPublisher is the publish pipeline seam (publish.Publisher in production; fakes in tests).

type Health added in v0.98.0

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

Health separates the two questions an orchestrator asks a replica, which are not the same question.

Liveness ("is this process wedged?") must stay true for as long as the process can answer at all. A liveness probe that fails restarts the container, so wiring it to anything but the process itself -- a datastore, a downstream service -- converts a dependency blip into a restart storm.

Readiness ("should this replica receive traffic?") is the one that has real work to do here, and its work is shutdown. Kubernetes removes a terminating pod from its Service endpoints concurrently with sending SIGTERM, not before it: for the width of that race a load balancer still routes to a server that has already stopped listening, and those requests fail. The fix is for the pod to fail readiness first, stay up long enough to be deregistered, and only then drain.

Readiness deliberately does not check store connectivity. Every replica shares one store, so a store blip would fail every replica's probe at once and the orchestrator would empty the Service of endpoints -- turning a degradation that still serves cached reads into a total outage. A probe whose failure mode is "remove all capacity" must depend on nothing shared.

func (*Health) Drain added in v0.98.0

func (h *Health) Drain()

Drain marks the replica as no longer accepting traffic. Readiness fails from the next probe onward; liveness is unaffected, because a draining server is working exactly as intended and must not be restarted out from under its in-flight requests.

func (*Health) Draining added in v0.98.0

func (h *Health) Draining() bool

Draining reports whether Drain has been called. A nil Health never drains, which is what tests and non-orchestrated deployments want.

type TagPromoter

type TagPromoter interface {
	PromoteTag(ctx context.Context, promo suggest.Promotion, actor string) (int, error)
}

TagPromoter is the promotion-execution capability of the publisher.

type WorkSaveHook

type WorkSaveHook interface {
	AutoLink(ctx context.Context, workID string, grain []byte) (int, error)
}

WorkSaveHook runs after a successful record write -- the seam the authority auto-linker plugs into. Hook failures never fail the save; the moderation queue is best-effort from the editor's perspective.

Jump to

Keyboard shortcuts

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