Files
tenant-registry/migrations/0001_init.up.sql
T
sharang 6a6cd76426
ci / shared (pull_request) Successful in 4s
ci / test (pull_request) Has been skipped
ci / image (pull_request) Has been skipped
feat(server): tenant-registry skeleton boots against dev stack
Minimal Go service so platform/portal has something to resolve in local
dev. Stdlib net/http with Go 1.22 enhanced ServeMux (method+path
patterns); no third-party deps yet.

Layout:
  cmd/server/main.go               entry point with graceful shutdown
  internal/config/                 env-driven config (APP_ENV, ADDR, KC issuer)
  internal/server/                 http handlers + request-logging middleware
  internal/store/memory.go         in-memory tenant store, seeded with acme
  migrations/0001_init.up.sql      schema for the M4.1 follow-up (unapplied)
  Makefile                         dev/test/build/lint/docker targets
  Dockerfile                       multi-stage distroless build

Endpoints (under :8080 in dev):
  GET /healthz
  GET /v1/tenants/by-slug/{slug}   200 acme | 404
  GET /v1/tenants/{id}             200 by uuid | 404

JWT validation and the real Postgres-backed store land in the M4.1
follow-up PR — keeping this PR strictly to 'boots, replies, tests pass'.

Refs: M4.1 (skeleton)
2026-05-18 22:40:49 +02:00

53 lines
1.9 KiB
SQL

-- Placeholder for the M4.1 schema (see PLATFORM_ARCHITECTURE.md §5c).
-- The skeleton uses an in-memory store; this file lands the table shape
-- the real M4.1 PR will use, so the schema review can happen alongside
-- the rest of the boot scaffolding.
-- enums --------------------------------------------------------------------
CREATE TYPE tenant_status AS ENUM ('trial', 'active', 'frozen', 'archived', 'demo');
CREATE TYPE tenant_kind AS ENUM ('customer', 'demo', 'stage', 'internal');
-- tenants ------------------------------------------------------------------
CREATE TABLE tenants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug TEXT UNIQUE NOT NULL CHECK (slug ~ '^[a-z0-9-]{2,40}$'),
name TEXT NOT NULL,
status tenant_status NOT NULL DEFAULT 'trial',
kind tenant_kind NOT NULL DEFAULT 'customer',
plan TEXT NOT NULL DEFAULT 'starter',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
trial_ends_at TIMESTAMPTZ
);
CREATE INDEX tenants_status_idx ON tenants (status);
-- tenant ↔ product entitlements -------------------------------------------
CREATE TABLE tenant_products (
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
product TEXT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (tenant_id, product)
);
-- audit log (Retraced-shape; PRODUCT_INTEGRATION_SPEC.md §8.4) ------------
CREATE TABLE audit_log (
id BIGSERIAL PRIMARY KEY,
tenant_id UUID REFERENCES tenants(id),
actor_id TEXT,
actor_name TEXT,
action TEXT NOT NULL,
target_id TEXT,
target_type TEXT,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
source_ip INET,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX audit_log_tenant_idx ON audit_log (tenant_id, created_at DESC);