From 3c343f94417eaa63a8b576cfbdc9e7c2b9d1609d Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:59:00 +0200 Subject: [PATCH] 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) --- installer/templates/docker-compose.saas.yml | 106 ++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 installer/templates/docker-compose.saas.yml diff --git a/installer/templates/docker-compose.saas.yml b/installer/templates/docker-compose.saas.yml new file mode 100644 index 0000000..a568760 --- /dev/null +++ b/installer/templates/docker-compose.saas.yml @@ -0,0 +1,106 @@ +# Cameleer SaaS — Logto + management plane +# Loaded in SaaS deployment mode + +services: + cameleer-logto: + image: ${LOGTO_IMAGE:-gitea.siegeln.net/cameleer/cameleer-logto}:${VERSION:-latest} + restart: unless-stopped + depends_on: + cameleer-postgres: + condition: service_healthy + environment: + DB_URL: postgres://${POSTGRES_USER:-cameleer}:${POSTGRES_PASSWORD}@cameleer-postgres:5432/logto + ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} + ADMIN_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}:${LOGTO_CONSOLE_PORT:-3002} + TRUST_PROXY_HEADER: 1 + NODE_TLS_REJECT_UNAUTHORIZED: "${NODE_TLS_REJECT:-0}" + LOGTO_ENDPOINT: http://cameleer-logto:3001 + LOGTO_ADMIN_ENDPOINT: http://cameleer-logto:3002 + LOGTO_PUBLIC_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} + PUBLIC_HOST: ${PUBLIC_HOST:-localhost} + PUBLIC_PROTOCOL: ${PUBLIC_PROTOCOL:-https} + PG_HOST: cameleer-postgres + PG_USER: ${POSTGRES_USER:-cameleer} + PG_PASSWORD: ${POSTGRES_PASSWORD} + PG_DB_SAAS: cameleer_saas + SAAS_ADMIN_USER: ${SAAS_ADMIN_USER:-admin} + SAAS_ADMIN_PASS: ${SAAS_ADMIN_PASS:?SAAS_ADMIN_PASS must be set in .env} + healthcheck: + test: ["CMD-SHELL", "node -e \"require('http').get('http://localhost:3001/oidc/.well-known/openid-configuration', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))\" && test -f /data/logto-bootstrap.json"] + interval: 10s + timeout: 5s + retries: 60 + start_period: 30s + labels: + - traefik.enable=true + - traefik.http.routers.cameleer-logto.rule=PathPrefix(`/`) + - traefik.http.routers.cameleer-logto.priority=1 + - traefik.http.routers.cameleer-logto.entrypoints=websecure + - traefik.http.routers.cameleer-logto.tls=true + - traefik.http.routers.cameleer-logto.service=cameleer-logto + - traefik.http.routers.cameleer-logto.middlewares=cameleer-logto-cors + - "traefik.http.middlewares.cameleer-logto-cors.headers.accessControlAllowOriginList=${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}:${LOGTO_CONSOLE_PORT:-3002}" + - traefik.http.middlewares.cameleer-logto-cors.headers.accessControlAllowMethods=GET,POST,PUT,PATCH,DELETE,OPTIONS + - traefik.http.middlewares.cameleer-logto-cors.headers.accessControlAllowHeaders=Authorization,Content-Type + - traefik.http.middlewares.cameleer-logto-cors.headers.accessControlAllowCredentials=true + - traefik.http.services.cameleer-logto.loadbalancer.server.port=3001 + - traefik.http.routers.cameleer-logto-console.rule=PathPrefix(`/`) + - traefik.http.routers.cameleer-logto-console.entrypoints=admin-console + - traefik.http.routers.cameleer-logto-console.tls=true + - traefik.http.routers.cameleer-logto-console.service=cameleer-logto-console + - traefik.http.services.cameleer-logto-console.loadbalancer.server.port=3002 + volumes: + - cameleer-bootstrapdata:/data + networks: + - cameleer + - monitoring + + cameleer-saas: + image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest} + restart: unless-stopped + depends_on: + cameleer-logto: + condition: service_healthy + environment: + # SaaS database + SPRING_DATASOURCE_URL: jdbc:postgresql://cameleer-postgres:5432/cameleer_saas + SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer} + SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD} + # Identity (Logto) + CAMELEER_SAAS_IDENTITY_LOGTOENDPOINT: http://cameleer-logto:3001 + CAMELEER_SAAS_IDENTITY_LOGTOPUBLICENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} + # Provisioning — passed to per-tenant server containers + CAMELEER_SAAS_PROVISIONING_PUBLICHOST: ${PUBLIC_HOST:-localhost} + CAMELEER_SAAS_PROVISIONING_PUBLICPROTOCOL: ${PUBLIC_PROTOCOL:-https} + CAMELEER_SAAS_PROVISIONING_NETWORKNAME: ${COMPOSE_PROJECT_NAME:-cameleer-saas}_cameleer + CAMELEER_SAAS_PROVISIONING_TRAEFIKNETWORK: cameleer-traefik + CAMELEER_SAAS_PROVISIONING_DATASOURCEUSERNAME: ${POSTGRES_USER:-cameleer} + CAMELEER_SAAS_PROVISIONING_DATASOURCEPASSWORD: ${POSTGRES_PASSWORD} + CAMELEER_SAAS_PROVISIONING_CLICKHOUSEPASSWORD: ${CLICKHOUSE_PASSWORD} + CAMELEER_SAAS_PROVISIONING_SERVERIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERIMAGE:-gitea.siegeln.net/cameleer/cameleer-server:latest} + CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE:-gitea.siegeln.net/cameleer/cameleer-server-ui:latest} + labels: + - traefik.enable=true + - traefik.http.routers.saas.rule=PathPrefix(`/platform`) + - traefik.http.routers.saas.entrypoints=websecure + - traefik.http.routers.saas.tls=true + - traefik.http.services.saas.loadbalancer.server.port=8080 + - "prometheus.io/scrape=true" + - "prometheus.io/port=8080" + - "prometheus.io/path=/platform/actuator/prometheus" + volumes: + - cameleer-bootstrapdata:/data/bootstrap:ro + - cameleer-certs:/certs + - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock + group_add: + - "${DOCKER_GID:-0}" + networks: + - cameleer + - monitoring + +volumes: + cameleer-bootstrapdata: + +networks: + monitoring: + name: cameleer-monitoring-noop