Commit Graph

479 Commits

Author SHA1 Message Date
hsiegeln
62b74d2d06 ci: remove sync-images workflow
All checks were successful
CI / build (push) Successful in 1m16s
CI / docker (push) Successful in 16s
Remote server will pull directly from the Gitea registry instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 01:25:56 +02:00
hsiegeln
3e2f035d97 fix(ci): use POSIX-compatible loop instead of bash arrays
All checks were successful
CI / build (push) Successful in 1m18s
CI / docker (push) Successful in 18s
The docker-builder container runs ash/sh, not bash — arrays with ()
are not supported. Use a simple for-in loop instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 00:32:12 +02:00
hsiegeln
9962ee99d9 fix(ci): drop ssh-keyscan, use StrictHostKeyChecking=accept-new instead
All checks were successful
CI / build (push) Successful in 1m16s
CI / docker (push) Successful in 17s
ssh-keyscan fails when the runner can't reach the host on port 22
during that step. Using accept-new on the ssh command itself is
equivalent for an ephemeral CI runner.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 00:29:52 +02:00
hsiegeln
b53840b77b ci: add manual workflow to sync Docker images to remote server
Some checks failed
CI / docker (push) Has been cancelled
CI / build (push) Has been cancelled
Pulls all :latest images from the Gitea registry and pipes them
via `docker save | ssh docker load` to the APP_HOST server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 00:28:39 +02:00
hsiegeln
9ed2cedc98 feat: self-service sign-up with email verification and onboarding
All checks were successful
CI / build (push) Successful in 1m14s
CI / docker (push) Successful in 1m15s
Complete sign-up pipeline: email registration via Logto Experience API,
SMTP email verification, and self-service trial tenant creation.

Layer 1 — Logto config:
- Bootstrap Phase 8b: SMTP email connector with branded HTML templates
- Bootstrap Phase 8c: enable SignInAndRegister (email+password sign-up)
- Dockerfile installs official Logto connectors (ensures SMTP available)
- SMTP env vars in docker-compose, installer templates, .env.example

Layer 2 — Experience API (ui/sign-in/experience-api.ts):
- Registration flow: initRegistration → sendVerificationCode → verifyCode
  → addProfile (password) → identifyUser → submit
- Sign-in auto-detects email vs username identifier

Layer 3 — Custom sign-in UI (ui/sign-in/SignInPage.tsx):
- Three-mode state machine: signIn / register / verifyCode
- Reads first_screen=register from URL query params
- Toggle links between sign-in and register views

Layer 4 — Post-registration onboarding:
- OnboardingService: reuses VendorTenantService.createAndProvision(),
  adds calling user to Logto org as owner, enforces one trial per user
- OnboardingController: POST /api/onboarding/tenant (authenticated only)
- OnboardingPage.tsx: org name + auto-slug form
- LandingRedirect: detects zero orgs → redirects to /onboarding
- RegisterPage.tsx: /platform/register initiates OIDC with firstScreen

Installers (install.sh + install.ps1):
- Both prompt for SMTP config in SaaS mode
- CLI args, env var capture, cameleer.conf persistence

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 00:21:07 +02:00
hsiegeln
dc7ac3a1ec feat: split auth domain — Logto gets dedicated AUTH_HOST
All checks were successful
CI / build (push) Successful in 1m22s
CI / docker (push) Successful in 48s
Support separate auth domain (e.g. auth.cameleer.io) for Logto while
keeping the SaaS app on PUBLIC_HOST (e.g. app.cameleer.io). AUTH_HOST
defaults to PUBLIC_HOST for backward-compatible single-domain setups.

- Logto routing: Host(AUTH_HOST) replaces PathPrefix('/') catch-all
- Root redirect moved from traefik-dynamic.yml to Docker labels with
  Host(PUBLIC_HOST) scope so it doesn't intercept auth domain
- Self-signed cert generates SANs for both domains
- Bootstrap Host header uses AUTH_HOST for Logto endpoint validation
- Spring issuer-uri and oidcissueruri use new authhost property
- Both installers (sh + ps1) prompt for AUTH_HOST in expert mode

Local dev: AUTH_HOST=auth.localhost (resolves to 127.0.0.1, no hosts file)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 18:11:47 +02:00
hsiegeln
1fbafbb16d feat: add vendor tenant metrics dashboard
All checks were successful
CI / build (push) Successful in 1m24s
CI / docker (push) Successful in 1m0s
Fleet overview page at /vendor/metrics showing per-tenant operational
metrics (agents, CPU, heap, HTTP requests, ingestion drops, uptime).
Queries each tenant's server via the new POST /api/v1/admin/server-metrics/query
REST API instead of direct ClickHouse access, supporting future per-tenant
CH instances.

Backend: TenantMetricsService fires 11 metric queries per tenant
concurrently over a 5-minute window, assembles into a summary snapshot.
ServerApiClient.queryServerMetrics() handles the M2M authenticated POST.

Frontend: VendorMetricsPage with KPI strip (fleet totals) and per-tenant
table with color-coded badges and heap usage bars. Auto-refreshes every 60s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v1.0.0
2026-04-24 14:02:57 +02:00
hsiegeln
6c1241ed89 docs(docker): replace obsolete 504 workaround note with the real wiring
Some checks failed
CI / build (push) Successful in 1m23s
CI / docker (push) Successful in 18s
SonarQube Analysis / sonarqube (push) Failing after 1m20s
Pre-fix the paragraph claimed every dynamically-created container MUST
carry `traefik.docker.network=cameleer-traefik` to avoid a 504, because
Traefik's Docker provider pointed at `network: cameleer` (a literal
name that never matched any real network). After the one-line static
config fix (df64573), Traefik's provider targets `cameleer-traefik`
directly — the network every managed container already joins — so the
per-container label is just defense-in-depth, not required.

Rewritten to describe current behaviour and keep a short note about the
pre-fix 504 for operators who roll back to an old image.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:22:32 +02:00
hsiegeln
df64573bfb fix(traefik): point docker provider network at cameleer-traefik
All checks were successful
CI / build (push) Successful in 1m22s
CI / docker (push) Successful in 16s
The static config set `provider.docker.network: cameleer`, but no network
by that literal name exists. The `cameleer` network defined in the
compose file gets namespaced by compose to `cameleer_cameleer`, and
managed app containers created at runtime only ever attach to
`cameleer-traefik` (per `DockerNetworkManager.TRAEFIK_NETWORK`).

Symptom: when the Docker provider's preferred network doesn't match any
network on a container, Traefik picks an arbitrary container IP and may
route to one on a bridge Traefik itself isn't attached to — requests
hang until Traefik's upstream timeout fires (504 Gateway Timeout).

Fix is one line: match the network that `cameleer-server` actually
attaches its managed containers to.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:15:22 +02:00
hsiegeln
4526d97bda fix: generate CAMELEER_SERVER_SECURITY_JWTSECRET in installer and wire into containers
All checks were successful
CI / build (push) Successful in 1m16s
CI / docker (push) Successful in 59s
The server now requires a non-empty JWT secret. The installer (bash + ps1)
generates a random value for both SaaS and standalone modes, and the compose
templates map it into the respective containers. Also fixes container names
in generated INSTALL.md docs to use the cameleer- prefix consistently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 09:30:11 +02:00
hsiegeln
132143c083 refactor: decompose CLAUDE.md into directory-scoped files
Some checks failed
CI / build (push) Successful in 1m59s
CI / docker (push) Successful in 1m24s
SonarQube Analysis / sonarqube (push) Failing after 2m4s
Root CLAUDE.md reduced from 475 to 175 lines (75 excl. GitNexus).
Detailed context now loads automatically only when editing code in
the relevant directory:

- provisioning/CLAUDE.md — env vars, provisioning flow, lifecycle
- config/CLAUDE.md — auth, scopes, JWT, OIDC role extraction
- docker/CLAUDE.md — routing, networks, bootstrap, deployment pipeline
- installer/CLAUDE.md — deployment modes, compose templates, env naming
- ui/CLAUDE.md — frontend files, sign-in UI

No information lost — everything moved, nothing deleted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 09:30:21 +02:00
hsiegeln
b824942408 docs: fix scope breakdown and add missing InfrastructurePage
All checks were successful
CI / build (push) Successful in 2m12s
CI / docker (push) Successful in 19s
- OAuth2 scopes: 1 platform + 9 tenant + 3 server (not "10 platform")
- Add InfrastructurePage.tsx to vendor pages list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 09:21:28 +02:00
hsiegeln
31e8dd05f0 docs: update CLAUDE.md for runtime base image, env var accuracy
All checks were successful
CI / build (push) Successful in 1m21s
CI / docker (push) Successful in 3m2s
- Fix OIDC env var names (OIDC_ISSUERURI not OIDCISSUERURI)
- Fix CAMELEER_SERVER_TENANT_ID value (slug, not UUID)
- Add missing env vars (ClickHouse, JWT secret, license token, base image)
- Complete provisioning properties table (was 6/16, now all listed)
- Add semantic note: CAMELEER_SAAS_PROVISIONING_* = "forwarded to tenant"
- Update runtime-base description (log appender JAR, entrypoint override,
  runtime type detection, PropertiesLauncher version handling)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 09:16:55 +02:00
hsiegeln
eba9f560ac fix: name JAR volume explicitly to match JARDOCKERVOLUME env var
Some checks failed
CI / build (push) Successful in 1m17s
CI / docker (push) Successful in 19s
SonarQube Analysis / sonarqube (push) Failing after 1m23s
The compose volume `jars` gets created as `<project>_jars` by Docker
Compose, but JARDOCKERVOLUME tells the server to mount `cameleer-jars`
on deployed app containers. These are different Docker volumes, so
the app JAR was never visible inside the app container — causing
ClassNotFoundException on startup.

Fix: add `name: cameleer-jars` to the volume definition so both the
server and deployed app containers share the same named volume.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 00:03:48 +02:00
hsiegeln
3c2bf4a9b1 fix: pass self-reference in VendorTenantServiceTest for async proxy
All checks were successful
CI / build (push) Successful in 1m17s
CI / docker (push) Successful in 44s
The @Lazy self-proxy pattern requires a non-null reference in tests.
Construct the instance then re-create with itself as the self param.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 23:33:07 +02:00
hsiegeln
97b2235914 fix: update tests for ProvisioningProperties runtimeBaseImage field
Some checks failed
CI / build (push) Failing after 1m22s
CI / docker (push) Has been skipped
Add missing runtimeBaseImage arg to ProvisioningProperties constructor
calls in tests. Also add missing self-proxy arg to VendorTenantService
constructor (pre-existing from async provisioning commit).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 23:27:53 +02:00
hsiegeln
338db5dcda fix: forward runtime base image to provisioned tenant servers
Some checks failed
CI / build (push) Failing after 59s
CI / docker (push) Has been skipped
CAMELEER_SERVER_RUNTIME_BASEIMAGE was never set on provisioned
per-tenant server containers, causing them to fall back to the
server's hardcoded default. Added CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE
as a configurable property that gets forwarded during provisioning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 23:20:46 +02:00
hsiegeln
fd50a147a2 fix: make tenant provisioning truly async via self-proxy
Some checks failed
CI / build (push) Failing after 41s
CI / docker (push) Has been skipped
@Async on provisionAsync() was bypassed because all call sites were
internal (this.provisionAsync), skipping the Spring proxy. Inject self
via @Lazy to route through the proxy so provisioning runs in a
background thread and the API returns immediately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:32:05 +02:00
hsiegeln
0dd52624b7 fix: use semicolon as COMPOSE_FILE separator on Windows
All checks were successful
CI / build (push) Successful in 1m59s
CI / docker (push) Successful in 46s
Windows Docker Compose uses ; not : as the path separator in COMPOSE_FILE.
The colon was being interpreted as part of the filename, causing CreateFile errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:11:34 +02:00
hsiegeln
1ce0ea411d chore: update design-system to 0.1.54
Some checks failed
CI / build (push) Successful in 1m25s
CI / docker (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:08:17 +02:00
hsiegeln
81be25198c chore: update design-system to 0.1.53
All checks were successful
CI / build (push) Successful in 1m16s
CI / docker (push) Successful in 1m32s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:56:14 +02:00
hsiegeln
dc4ea33c9b feat: externalize docker-compose templates from installer scripts
All checks were successful
CI / build (push) Successful in 1m16s
CI / docker (push) Successful in 20s
Replace inline heredoc compose generation with static template files.
Templates are copied to the install dir and composed via COMPOSE_FILE in .env.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:53:26 +02:00
hsiegeln
186f7639ad docs: update CLAUDE.md with template-based installer architecture
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:11:04 +02:00
hsiegeln
6c7895b0d6 chore(installer): remove generated install output, add to gitignore
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:09:30 +02:00
hsiegeln
6170f61eeb refactor(installer): replace ps1 compose generation with template copying
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:08:34 +02:00
hsiegeln
2ed527ac74 refactor(installer): replace sh compose generation with template copying
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:03:01 +02:00
hsiegeln
cb1f6b8ccf feat(installer): add .env.example with documented variables
Reference .env file documenting all configuration variables across both
deployment modes, with section headers for compose assembly, public access,
credentials, TLS, Docker, provisioning, and monitoring.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:59:15 +02:00
hsiegeln
758585cc9a feat(installer): add TLS and monitoring overlay templates
Optional compose overlays: TLS overlay mounts user-supplied certs into
traefik, monitoring overlay replaces the noop bridge with an external
Docker network for Prometheus scraping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:59:10 +02:00
hsiegeln
141b44048c feat(installer): add standalone docker-compose and traefik templates
Standalone mode: server + server-ui services with postgres image override
to stock postgres:16-alpine. Includes traefik-dynamic.yml for default TLS
certificate store configuration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:59:05 +02:00
hsiegeln
3c343f9441 feat(installer): add SaaS docker-compose template
Logto identity provider and cameleer-saas management plane services.
Includes Traefik labels, CORS config, bootstrap healthcheck, and all
provisioning env vars parameterized from .env.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:59:00 +02:00
hsiegeln
bdb24f8de6 feat(installer): add infra base docker-compose template
Shared infrastructure base (traefik, postgres, clickhouse) always loaded
regardless of deployment mode. Uses parameterized images, fail-if-unset
password variables, and a noop monitoring network bridge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:58:54 +02:00
hsiegeln
933b56f68f docs: add implementation plan for externalizing compose templates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:54:31 +02:00
hsiegeln
19c463051a docs: add design spec for externalizing docker compose templates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:47:14 +02:00
hsiegeln
41052d01e8 fix: replace admin password fallback defaults with fail-if-unset
All checks were successful
CI / build (push) Successful in 1m15s
CI / docker (push) Successful in 16s
Docker compose templates defaulted to admin/admin when .env was missing.
Now uses :? to fail with a clear error instead of silently using weak creds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 20:17:46 +02:00
hsiegeln
99e75b0a4e fix: update sign-in UI to design-system 0.1.51
All checks were successful
CI / build (push) Successful in 1m15s
CI / docker (push) Successful in 1m31s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:33:00 +02:00
hsiegeln
eb6897bf10 chore: update design-system to 0.1.51 (renamed assets)
Some checks failed
CI / build (push) Failing after 1m9s
CI / docker (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:30:50 +02:00
hsiegeln
63c194dab7 chore: rename cameleer3 to cameleer
Some checks failed
CI / build (push) Failing after 18s
CI / docker (push) Has been skipped
Rename Java packages from net.siegeln.cameleer3 to net.siegeln.cameleer,
update all references in workflows, Docker configs, docs, and bootstrap.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 15:28:44 +02:00
hsiegeln
44a0e413e9 fix: include cameleer3-log-appender.jar in runtime base image
All checks were successful
CI / build (push) Successful in 1m20s
CI / docker (push) Successful in 13s
The log appender JAR was missing from the cameleer-runtime-base Docker
image, causing agent log forwarding to silently fail with "No supported
logging framework found, log forwarding disabled". This meant only
container stdout logs (source=container) were captured — no application
or agent logs reached ClickHouse.

CI now downloads the appender JAR from the Maven registry alongside the
agent JAR, and the Dockerfile COPYs it to /app/cameleer3-log-appender.jar
where the server's Docker entrypoint expects it (-Dloader.path for
Spring Boot, -cp for plain Java).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:11:04 +02:00
hsiegeln
15306dddc0 fix: force-pull images on install and fix provisioning test assertions
All checks were successful
CI / build (push) Successful in 1m11s
CI / docker (push) Successful in 47s
Installers now use `--pull always --force-recreate` on `docker compose up`
to ensure fresh images are used on every install/reinstall, preventing
stale containers from missing schema changes like db_password.

Fix VendorTenantServiceTest to expect two repository saves in provisioning
tests (one for dbPassword, one for final status).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 08:50:40 +02:00
hsiegeln
6eb848f353 fix: add missing TenantDatabaseService mock to VendorTenantServiceTest
Some checks failed
CI / build (push) Failing after 58s
CI / docker (push) Has been skipped
Constructor gained an 11th parameter (TenantDatabaseService) but the
test was not updated, breaking CI compilation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 08:32:14 +02:00
hsiegeln
d53afe43cc docs: update CLAUDE.md for per-tenant PG isolation and consolidated migrations
Some checks failed
CI / build (push) Failing after 42s
CI / docker (push) Has been skipped
SonarQube Analysis / sonarqube (push) Failing after 33s
- 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>
2026-04-15 00:26:36 +02:00
hsiegeln
24a443ef30 refactor: consolidate Flyway migrations into single V001 baseline
Some checks failed
CI / build (push) Failing after 51s
CI / docker (push) Has been skipped
Replace 14 incremental migrations (V001-V015) with a single V001__init.sql
representing the final schema. Tables that were created and later dropped
(environments, api_keys, apps, deployments) are excluded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 00:24:25 +02:00
hsiegeln
d7eb700860 refactor: move PG cleanup to TenantDatabaseService, keep only ClickHouse
TenantDataCleanupService now handles only ClickHouse GDPR erasure;
the dropPostgresSchema private method is removed and the public method
renamed cleanupClickHouse(). VendorTenantService updated accordingly
with the TODO comment removed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 00:17:00 +02:00
hsiegeln
c1458e4995 feat: create per-tenant PG database during provisioning, drop on delete
Inject TenantDatabaseService; call createTenantDatabase() at the start
of provisionAsync() (stores generated password on TenantEntity), and
dropTenantDatabase() in delete() before GDPR data erasure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 00:16:06 +02:00
hsiegeln
b79a7fe405 feat: construct per-tenant JDBC URL with currentSchema and ApplicationName
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 00:14:35 +02:00
hsiegeln
6d6c1f3562 feat: add TenantDatabaseService for per-tenant PG user+schema
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 00:13:34 +02:00
hsiegeln
0e3f383cf4 feat: add dbPassword to TenantProvisionRequest 2026-04-15 00:13:27 +02:00
hsiegeln
cd6dd1e5af feat: add dbPassword field to TenantEntity 2026-04-15 00:13:12 +02:00
hsiegeln
dfa2a6bfa2 feat: add db_password column to tenants table (V015) 2026-04-15 00:13:11 +02:00
hsiegeln
a7196ff4c1 docs: per-tenant PostgreSQL isolation implementation plan
8-task plan covering migration, entity change, TenantDatabaseService,
provisioner JDBC URL construction, VendorTenantService integration,
and TenantDataCleanupService refactor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 00:11:34 +02:00