Files
skills/docs/api-stack/api/api-plugin-site/README.md
2026-05-29 08:20:56 +01:00

9.5 KiB

@suaveplan/api-plugin-site - Multi-site / multi-domain management plugin for @suaveplan/api. Stores site records with name / slug / theme / settings / push config, resolves Host headers to a site through DomainResolverService, exposes public + private OpenAPI route trees, ships an injectable domain-resolver Hono middleware, emits site:settings:updated events, optionally auto-creates a default site on organization:created. 14 SITE.* error codes, 5 OTel instruments, owner-scoped queries, server-tier.

Multi-site / multi-domain management plugin for @suaveplan/api. Stores site records (name, slug, theme, settings, push config) in SurrealDB, resolves incoming Host headers to a site via DomainResolverService, exposes public + private OpenAPI Hono route trees, and emits the site:settings:updated event whenever a site's settings change so downstream caches / CDNs can react.

Documentation tree

  • error-codes — Owns the stable SITE.* error-code registry and the SiteError class used by every public API path in `@suaveplan/api…
    • error-codes — Owns the stable SITE.* error-code registry and the SiteError class used by every public API path in `@suaveplan/api…
  • integration — MIDDLEWARE_RECOMMENDATIONS is a frozen, typed bundle of recommended rate-limit and audit configuration for each docum…
    • middleware-recommendations — MIDDLEWARE_RECOMMENDATIONS is a frozen, typed bundle of recommended rate-limit and audit configuration for each docum…
  • middleware — Factory that builds a Hono middleware which resolves the incoming request's Host header to a site + organization…
    • domain-resolver — Factory that builds a Hono middleware which resolves the incoming request's Host header to a site + organization…
  • otel — Owns the lazy-built OpenTelemetry instrument bundle (SiteInstruments) that every observable code path in `@suaveplan/…
    • instruments — Owns the lazy-built OpenTelemetry instrument bundle (SiteInstruments) that every observable code path in `@suaveplan/…
  • plugin — Owns createSitePlugin, the canonical factory that produces the PluginDescriptor consumed by @suaveplan/api-plugin…
    • plugin — Owns createSitePlugin, the canonical factory that produces the PluginDescriptor consumed by @suaveplan/api-plugin…
  • private-routes — Builds the authenticated Hono route tree for site CRUD and domain management. The factory is intentionally auth-agnosti…
  • public-routes — Builds the public (no-auth) Hono route tree that the plugin mounts under the configured routePrefix. The two endpoint…
  • domain — Zod schemas for every domain-shaped value: the validated domain literal itself, the request/response shapes for `addDom…
  • options — Owns the plugin-options schema and the mergeConfig / DEFAULT_CONFIG helpers used by createSitePlugin(...). Every…
  • push — Schemas and a pure validator for the per-site push-notification configuration. The site plugin treats push config as op…
  • site — Defines the Zod schemas that describe the canonical site entity, its create/update inputs, the public-facing slice, a…
  • db-port — Declares SiteDb, the structural database port that SiteService and DomainResolverService consume. suaveplan's found…
  • domain-resolver — DomainResolverService maps a request's Host (or arbitrary domain string) to a persisted site record and the ownin…
  • normalize-domain — Pure helper that mirrors the SurrealDB fn::site::domain::normalize function in TypeScript so application-tier code (t…
  • site — SiteService is the application-tier owner of every CRUD path against the SurrealDB site table. It mediates between…
  • services — Augments the ServiceRegistry interface from @suaveplan/api-plugin so the three slots the site plugin owns — `siteSe…
    • registry-augmentation — Augments the ServiceRegistry interface from @suaveplan/api-plugin so the three slots the site plugin owns — `siteSe…
  • types — Runtime-free type module that defines every shared shape used by the site plugin: persisted Site / SiteSettings / `…
    • types — Runtime-free type module that defines every shared shape used by the site plugin: persisted Site / SiteSettings / `…

Install

bun add @suaveplan/api-plugin-site

Quick start

import { createPluginRegistry } from "@suaveplan/api-plugin";
import { createDatabasePlugin } from "@suaveplan/api-plugin-database";
import { createSitePlugin } from "@suaveplan/api-plugin-site";

const registry = createPluginRegistry();
registry.register(createDatabasePlugin({ /* surrealdb config */ }));
registry.register(createSitePlugin({
    maxSitesPerOrg: 10,
    maxDomainsPerSite: 5,
    autoCreateDefaultSite: true,
    defaultDomainPattern: "{slug}.suaveplan.app",
}));
await registry.start();

After registry.start(), the site plugin exposes three services on ServiceRegistry:

  • siteService: SiteService — CRUD + settings + domain management.
  • domainResolver: DomainResolverService — resolve(domain) returning {success, site, organization}.
  • domainResolverMiddleware: Hono Middleware — request-scoped resolver that injects site / organization / siteId / organizationId into c.

What it does

  • Persists site records under SurrealDB with per-table SurQL migration shipped in surql/tables/site.surql.
  • Enforces per-organization and per-site caps (maxSitesPerOrg, maxDomainsPerSite).
  • Public route: GET /sites/resolve/{domain} — domain → site/org lookup.
  • Public route: GET /sites/{id}/public — sanitised SitePublicInfo projection (no push secrets).
  • Private routes: POST /sites, PATCH /sites/{id}, POST /sites/{id}/domains, DELETE /sites/{id}/domains.
  • Optional organization:created listener that auto-creates a default site per new organisation, with site:bootstrap:failed events on failure.
  • 14 stable SITE.* error codes raised through SiteError extends SuaveplanError.
  • OTel instruments: site.sites_created_total{outcome}, site.sites_updated_total{kind}, site.domain_resolutions_total{outcome}, site.domain_resolution_duration_ms, site.bootstrap_failed_total.

Modules

Middleware composition

The site plugin ships zero runtime dependencies on @suaveplan/api-plugin-rate-limit and @suaveplan/api-plugin-audit. Composition stays in the host application via Hono's app.use(prefix, middleware) pattern.