Add an explicit registry with capability hooks for migrations, routes, workflows, config, and UI assets. Move FedWiki fully and Stripe's separable store, workflow, and webhook pieces under internal/integrations. Drive startup wiring from declarations, including config validation, secret file pairs, CSRF exemptions, UI composition, and workflow startup. Move integration DB roles and grants into their owning migration streams, and route outbox writes through a shared enqueue helper.
109 lines
5.6 KiB
Go
109 lines
5.6 KiB
Go
// Package integrations is the composition root's view of the installed
|
|
// integration set. Each integration lives in its own subtree
|
|
// (internal/integrations/<slug>) and is listed once in registry.go's
|
|
// All(). Adding an integration is one tree + one registry line — no
|
|
// init()-time self-registration.
|
|
//
|
|
// Import-direction rule (mirrors the FK rule for schemas): integration
|
|
// trees MAY import core packages (internal/db, internal/integration,
|
|
// internal/server's exported deps, ...); core packages MUST NOT import
|
|
// integration trees. Only composition roots — cmd/start.go and
|
|
// internal/migrate/sources.go — consume this package.
|
|
//
|
|
// Cycle note: integration subpackages (e.g. internal/integrations/fedwiki)
|
|
// deliberately do NOT import this package. Their New() constructors return
|
|
// a concrete type that structurally satisfies Integration; registry.go
|
|
// (in this package) is the only place the concrete types are named as the
|
|
// interface. This keeps the dependency edge one-directional — this
|
|
// package imports the subpackages, never the reverse — which a two-way
|
|
// import would turn into a compile-time cycle.
|
|
//
|
|
// This package deliberately does NOT define a routes capability interface
|
|
// (see internal/server.RouteProvider instead): internal/migrate imports
|
|
// this package (Sources loops All()), and internal/server's own DB-backed
|
|
// tests (internal test files, package server) import internal/migrate —
|
|
// so if this package imported internal/server to reference its Deps type,
|
|
// internal/server's test binary would cycle (server -> migrate ->
|
|
// integrations -> server). Defining RouteProvider at the point of use
|
|
// (internal/server, which cmd/start.go already imports to build Config)
|
|
// avoids that without weakening the capability-discovery pattern: the
|
|
// composition root still type-asserts each Integration from All() against
|
|
// server.RouteProvider exactly as it would against a hook declared here.
|
|
//
|
|
// A workflows capability interface is excluded for the same reason:
|
|
// internal/workflows.WorkflowProvider is declared in internal/workflows
|
|
// instead of here, so that internal/workflows (which this package's
|
|
// registered integrations import, e.g. internal/integrations/stripe ->
|
|
// internal/workflows/stripe) never has to import this package to
|
|
// reference its own capability type — keeping the design's import-
|
|
// direction rule (internal/server and internal/workflows end this change
|
|
// with zero integration imports; see design.md Decision 3) true by
|
|
// construction rather than by incidental non-cycling. cmd/start.go, which
|
|
// already imports both packages to build Config and WorkerConfig,
|
|
// type-asserts against workflows.WorkflowProvider exactly as it does
|
|
// against server.RouteProvider.
|
|
package integrations
|
|
|
|
import (
|
|
"io/fs"
|
|
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/db"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/integration"
|
|
)
|
|
|
|
// Integration is the mandatory capability every registered integration
|
|
// implements. Optional capabilities (routes, workflows, config, UI
|
|
// assets) are discovered by type-asserting an Integration against the
|
|
// interfaces below; an integration lacking a capability simply doesn't
|
|
// implement the corresponding interface.
|
|
type Integration interface {
|
|
// Slug is the integration's short identifier (e.g. "fedwiki",
|
|
// "stripe"). It must match the Slug in Provider()'s manifest.
|
|
Slug() string
|
|
|
|
// Provider returns the provider-registry manifest source, consumed by
|
|
// internal/integration.RegisterProviders at boot.
|
|
Provider() integration.ProviderSource
|
|
|
|
// MigrationSource returns the integration's migration source,
|
|
// consumed by internal/migrate.Sources.
|
|
MigrationSource() db.MigrationSource
|
|
}
|
|
|
|
// Routes capability: see internal/server.RouteProvider. An integration
|
|
// implements it by defining RegisterRoutes(mux *http.ServeMux, deps
|
|
// server.Deps) error and CSRFExemptPaths() []string — the same shape this
|
|
// package's other capability interfaces follow — but the interface itself
|
|
// is declared in internal/server to avoid the import cycle documented in
|
|
// this file's package comment.
|
|
|
|
// Workflows capability: see internal/workflows.WorkflowProvider. An
|
|
// integration implements it by defining RegisterWorkflows(w worker.Worker,
|
|
// database *sql.DB, logger *slog.Logger) and Startup(ctx, c client.Client,
|
|
// taskQueue string, database *sql.DB, logger *slog.Logger) error — again
|
|
// the same shape this package's other capability interfaces follow, but
|
|
// declared in internal/workflows for the same import-cycle reason as
|
|
// RouteProvider above (see internal/workflows.WorkflowProvider's doc
|
|
// comment for the concrete cycle it avoids).
|
|
|
|
// ConfigProvider capability: see internal/config.ConfigProvider. An
|
|
// integration implements it by defining ConfigSpec() []config.ConfigKey —
|
|
// the same shape this package's other capability interfaces follow, but
|
|
// declared in internal/config rather than here: that package is the one
|
|
// that actually consumes ConfigKey values (its ValidateStart), and
|
|
// cmd/start.go already imports it for that call, so declaring the type
|
|
// there means internal/config never has to import this package (and,
|
|
// transitively, every concrete adapter's own dependencies — internal/
|
|
// server, internal/workflows/*, the Temporal client) merely to reference a
|
|
// plain declaration struct. Same reasoning as the Routes/Workflows
|
|
// capability pointers above.
|
|
|
|
// UIProvider is implemented by integrations that ship templates and/or
|
|
// static assets. Both filesystems are slug-namespaced: template files,
|
|
// defined template names, and static asset paths must be prefixed with
|
|
// the integration's slug.
|
|
type UIProvider interface {
|
|
Templates() fs.FS
|
|
Static() fs.FS
|
|
}
|