docs: update CLAUDE.md for per-tenant PG isolation and consolidated migrations
- TenantDatabaseService added to key classes - TenantDataCleanupService now ClickHouse-only - Per-tenant JDBC URL with currentSchema/ApplicationName in env vars table - Provisioning flow updated with DB creation step - Delete flow updated with schema+user drop - Database migrations section reflects consolidated V001 baseline Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
33
CLAUDE.md
33
CLAUDE.md
@@ -30,7 +30,7 @@ Agent-server protocol is defined in `cameleer3/cameleer3-common/PROTOCOL.md`. Th
|
||||
- `MeController.java` — GET /api/me (authenticated user, tenant list)
|
||||
|
||||
**tenant/** — Tenant data model
|
||||
- `TenantEntity.java` — JPA entity (id, name, slug, tier, status, logto_org_id, stripe IDs, settings JSONB)
|
||||
- `TenantEntity.java` — JPA entity (id, name, slug, tier, status, logto_org_id, stripe IDs, settings JSONB, db_password)
|
||||
|
||||
**vendor/** — Vendor console (platform:admin only)
|
||||
- `VendorTenantService.java` — orchestrates tenant creation (sync: DB + Logto + license, async: Docker provisioning + config push), suspend/activate, delete, restart server, upgrade server (force-pull + re-provision), license renewal
|
||||
@@ -44,8 +44,9 @@ Agent-server protocol is defined in `cameleer3/cameleer3-common/PROTOCOL.md`. Th
|
||||
|
||||
**provisioning/** — Pluggable tenant provisioning
|
||||
- `TenantProvisioner.java` — pluggable interface (like server's RuntimeOrchestrator)
|
||||
- `DockerTenantProvisioner.java` — Docker implementation, creates per-tenant server + UI containers. `upgrade(slug)` force-pulls latest images and removes server+UI containers (preserves app containers, volumes, networks) for re-provisioning. `remove(slug)` does full cleanup: label-based container removal, env networks, tenant network, JAR volume.
|
||||
- `TenantDataCleanupService.java` — GDPR data erasure on tenant delete: drops PostgreSQL `tenant_{slug}` schema, deletes ClickHouse data across all tables with `tenant_id` column
|
||||
- `DockerTenantProvisioner.java` — Docker implementation, creates per-tenant server + UI containers with per-tenant JDBC credentials (`currentSchema=tenant_{slug}&ApplicationName=tenant_{slug}`). `upgrade(slug)` force-pulls latest images and removes server+UI containers (preserves app containers, volumes, networks) for re-provisioning. `remove(slug)` does full cleanup: label-based container removal, env networks, tenant network, JAR volume.
|
||||
- `TenantDatabaseService.java` — creates/drops per-tenant PostgreSQL users (`tenant_{slug}`) and schemas; used during provisioning and delete
|
||||
- `TenantDataCleanupService.java` — GDPR data erasure on tenant delete: deletes ClickHouse data across all tables with `tenant_id` column (PostgreSQL cleanup handled by `TenantDatabaseService`)
|
||||
- `TenantProvisionerAutoConfig.java` — auto-detects Docker socket
|
||||
- `DockerCertificateManager.java` — file-based cert management with atomic `.wip` swap (Docker volume)
|
||||
- `DisabledCertificateManager.java` — no-op when certs dir unavailable
|
||||
@@ -177,6 +178,9 @@ These env vars are injected into provisioned per-tenant server containers:
|
||||
|
||||
| Env var | Value | Purpose |
|
||||
|---------|-------|---------|
|
||||
| `SPRING_DATASOURCE_URL` | `jdbc:postgresql://cameleer-postgres:5432/cameleer3?currentSchema=tenant_{slug}&ApplicationName=tenant_{slug}` | Per-tenant schema isolation + diagnostic query scoping |
|
||||
| `SPRING_DATASOURCE_USERNAME` | `tenant_{slug}` | Per-tenant PG user (owns only its schema) |
|
||||
| `SPRING_DATASOURCE_PASSWORD` | (generated, stored in `TenantEntity.dbPassword`) | Per-tenant PG password |
|
||||
| `CAMELEER_SERVER_SECURITY_OIDCISSUERURI` | `${PUBLIC_PROTOCOL}://${PUBLIC_HOST}/oidc` | Token issuer claim validation |
|
||||
| `CAMELEER_SERVER_SECURITY_OIDCJWKSETURI` | `http://cameleer-logto:3001/oidc/jwks` | Docker-internal JWK fetch |
|
||||
| `CAMELEER_SERVER_SECURITY_OIDCTLSSKIPVERIFY` | `true` (conditional) | Skip cert verify for OIDC discovery; only set when no `/certs/ca.pem` exists. When ca.pem exists, the server's `docker-entrypoint.sh` imports it into the JVM truststore instead. |
|
||||
@@ -296,9 +300,10 @@ When SaaS admin creates a tenant via `VendorTenantService`:
|
||||
6. Return immediately — UI shows provisioning spinner, polls via `refetchInterval`
|
||||
|
||||
**Asynchronous (in `provisionAsync`, `@Async`):**
|
||||
7. Create tenant-isolated Docker network (`cameleer-tenant-{slug}`)
|
||||
8. Create server container with env vars, Traefik labels (`traefik.docker.network`), health check, Docker socket bind, JAR volume, certs volume (ro)
|
||||
9. Create UI container with `CAMELEER_API_URL`, `BASE_PATH`, Traefik strip-prefix labels
|
||||
7. Create per-tenant PostgreSQL user + schema via `TenantDatabaseService.createTenantDatabase(slug, password)`, store `dbPassword` on entity
|
||||
8. Create tenant-isolated Docker network (`cameleer-tenant-{slug}`)
|
||||
9. Create server container with per-tenant JDBC URL (`currentSchema=tenant_{slug}&ApplicationName=tenant_{slug}`), Traefik labels (`traefik.docker.network`), health check, Docker socket bind, JAR volume, certs volume (ro)
|
||||
10. Create UI container with `CAMELEER_API_URL`, `BASE_PATH`, Traefik strip-prefix labels
|
||||
10. Wait for health check (`/api/v1/health`, not `/actuator/health` which requires auth)
|
||||
11. Push license token to server via M2M API
|
||||
12. Push OIDC config (Traditional Web App credentials + `additionalScopes: [urn:logto:scope:organizations, urn:logto:scope:organization_roles]`) to server for SSO
|
||||
@@ -314,7 +319,8 @@ When SaaS admin creates a tenant via `VendorTenantService`:
|
||||
|
||||
**Tenant delete** cleanup:
|
||||
- `DockerTenantProvisioner.remove(slug)` — label-based container removal (`cameleer.tenant={slug}`), env network cleanup, tenant network removal, JAR volume removal
|
||||
- `TenantDataCleanupService.cleanup(slug)` — drops PostgreSQL `tenant_{slug}` schema, deletes ClickHouse data (GDPR)
|
||||
- `TenantDatabaseService.dropTenantDatabase(slug)` — drops PostgreSQL `tenant_{slug}` schema + `tenant_{slug}` user
|
||||
- `TenantDataCleanupService.cleanupClickHouse(slug)` — deletes ClickHouse data across all tables with `tenant_id` column (GDPR)
|
||||
|
||||
**Password management** (tenant portal):
|
||||
- `POST /api/tenant/password` — tenant admin changes own Logto password (via `@AuthenticationPrincipal` JWT subject)
|
||||
@@ -324,18 +330,7 @@ When SaaS admin creates a tenant via `VendorTenantService`:
|
||||
## Database Migrations
|
||||
|
||||
PostgreSQL (Flyway): `src/main/resources/db/migration/`
|
||||
- V001 — tenants (id, name, slug, tier, status, logto_org_id, stripe IDs, settings JSONB)
|
||||
- V002 — licenses (id, tenant_id, tier, features JSONB, limits JSONB, expires_at)
|
||||
- V003 — environments (tenant -> environments 1:N)
|
||||
- V004 — api_keys (auth tokens for agent registration)
|
||||
- V005 — apps (Camel applications)
|
||||
- V006 — deployments (app versions, deployment history)
|
||||
- V007 — audit_log
|
||||
- V008 — app resource limits
|
||||
- V010 — cleanup of migrated tables
|
||||
- V011 — add provisioning fields (server_endpoint, provision_error)
|
||||
- V012 — certificates table + tenants.ca_applied_at
|
||||
- V013 — tenant_ca_certs (per-tenant CA certificates with PEM storage)
|
||||
- V001 — consolidated baseline: tenants (with db_password, server_endpoint, provision_error, ca_applied_at), licenses, audit_log, certificates, tenant_ca_certs
|
||||
|
||||
## Related Conventions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user