Files
cameleer-saas/docker-compose.yml
hsiegeln 6f57e19c2a
All checks were successful
CI / build (push) Successful in 47s
CI / docker (push) Successful in 9s
fix: add CORS middleware for admin console origin on default tenant
The admin console (port 3002) calls the Management API on the
default tenant (port 443). Add Traefik CORS headers to allow
cross-origin requests from the admin console origin.

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

243 lines
9.3 KiB
YAML

services:
traefik-certs:
image: alpine:latest
restart: "no"
entrypoint: ["sh", "-c"]
command:
- |
if [ ! -f /certs/cert.pem ]; then
apk add --no-cache openssl >/dev/null 2>&1
openssl req -x509 -newkey rsa:4096 \
-keyout /certs/key.pem -out /certs/cert.pem \
-days 365 -nodes \
-subj "/CN=$$PUBLIC_HOST" \
-addext "subjectAltName=DNS:$$PUBLIC_HOST,DNS:*.$$PUBLIC_HOST"
echo "Generated self-signed cert for $$PUBLIC_HOST"
else
echo "Certs already exist, skipping"
fi
environment:
PUBLIC_HOST: ${PUBLIC_HOST:-localhost}
volumes:
- certs:/certs
traefik:
image: traefik:v3
restart: unless-stopped
depends_on:
traefik-certs:
condition: service_completed_successfully
ports:
- "80:80"
- "443:443"
- "3002:3002"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./docker/traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro
- certs:/etc/traefik/certs:ro
networks:
- cameleer
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-cameleer_saas}
POSTGRES_USER: ${POSTGRES_USER:-cameleer}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cameleer_dev}
volumes:
- pgdata:/var/lib/postgresql/data
- ./docker/init-databases.sh:/docker-entrypoint-initdb.d/init-databases.sh:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-cameleer} -d ${POSTGRES_DB:-cameleer_saas}"]
interval: 5s
timeout: 5s
retries: 5
networks:
- cameleer
logto:
image: ${LOGTO_IMAGE:-gitea.siegeln.net/cameleer/cameleer-logto}:${VERSION:-latest}
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
entrypoint: ["sh", "-c", "npm run cli db seed -- --swe && npm start"]
environment:
DB_URL: postgres://${POSTGRES_USER:-cameleer}:${POSTGRES_PASSWORD:-cameleer_dev}@postgres:5432/logto
ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}
ADMIN_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}:3002
TRUST_PROXY_HEADER: 1
NODE_TLS_REJECT_UNAUTHORIZED: "0" # dev only — accept self-signed cert for internal OIDC discovery
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))\""]
interval: 5s
timeout: 5s
retries: 30
start_period: 15s
labels:
- traefik.enable=true
- traefik.http.routers.logto.rule=PathPrefix(`/`)
- traefik.http.routers.logto.priority=1
- traefik.http.routers.logto.entrypoints=websecure
- traefik.http.routers.logto.tls=true
- traefik.http.routers.logto.middlewares=logto-cors
- traefik.http.middlewares.logto-cors.headers.accessControlAllowOriginList=${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}:3002
- traefik.http.middlewares.logto-cors.headers.accessControlAllowMethods=GET,POST,PUT,PATCH,DELETE,OPTIONS
- traefik.http.middlewares.logto-cors.headers.accessControlAllowHeaders=Authorization,Content-Type
- traefik.http.middlewares.logto-cors.headers.accessControlAllowCredentials=true
- traefik.http.services.logto.loadbalancer.server.port=3001
- traefik.http.routers.logto-console.rule=PathPrefix(`/`)
- traefik.http.routers.logto-console.entrypoints=admin-console
- traefik.http.routers.logto-console.tls=true
- traefik.http.routers.logto-console.service=logto-console
- traefik.http.services.logto-console.loadbalancer.server.port=3002
networks:
- cameleer
logto-bootstrap:
image: postgres:16-alpine
depends_on:
logto:
condition: service_healthy
cameleer3-server:
condition: service_healthy
restart: "no"
entrypoint: ["sh", "/scripts/logto-bootstrap.sh"]
environment:
LOGTO_ENDPOINT: http://logto:3001
LOGTO_ADMIN_ENDPOINT: http://logto:3002
LOGTO_PUBLIC_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}
PUBLIC_HOST: ${PUBLIC_HOST:-localhost}
PUBLIC_PROTOCOL: ${PUBLIC_PROTOCOL:-https}
PG_HOST: postgres
PG_USER: ${POSTGRES_USER:-cameleer}
PG_PASSWORD: ${POSTGRES_PASSWORD:-cameleer_dev}
PG_DB_SAAS: ${POSTGRES_DB:-cameleer_saas}
SAAS_ADMIN_USER: ${SAAS_ADMIN_USER:-admin}
SAAS_ADMIN_PASS: ${SAAS_ADMIN_PASS:-admin}
TENANT_ADMIN_USER: ${TENANT_ADMIN_USER:-camel}
TENANT_ADMIN_PASS: ${TENANT_ADMIN_PASS:-camel}
CAMELEER_AUTH_TOKEN: ${CAMELEER_AUTH_TOKEN:-default-bootstrap-token}
SERVER_ENDPOINT: http://cameleer3-server:8081
SERVER_UI_USER: ${CAMELEER_UI_USER:-admin}
SERVER_UI_PASS: ${CAMELEER_UI_PASSWORD:-admin}
volumes:
- ./docker/logto-bootstrap.sh:/scripts/logto-bootstrap.sh:ro
- bootstrapdata:/data
networks:
- cameleer
cameleer-saas:
image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest}
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
logto-bootstrap:
condition: service_completed_successfully
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- jardata:/data/jars
- bootstrapdata:/data/bootstrap:ro
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB:-cameleer_saas}
SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer}
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD:-cameleer_dev}
LOGTO_ENDPOINT: ${LOGTO_ENDPOINT:-http://logto:3001}
LOGTO_PUBLIC_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}
LOGTO_ISSUER_URI: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}/oidc
LOGTO_JWK_SET_URI: ${LOGTO_ENDPOINT:-http://logto:3001}/oidc/jwks
LOGTO_M2M_CLIENT_ID: ${LOGTO_M2M_CLIENT_ID:-}
LOGTO_M2M_CLIENT_SECRET: ${LOGTO_M2M_CLIENT_SECRET:-}
CAMELEER3_SERVER_ENDPOINT: http://cameleer3-server:8081
CLICKHOUSE_URL: jdbc:clickhouse://clickhouse:8123/cameleer
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
networks:
- cameleer
cameleer3-server:
image: ${CAMELEER3_SERVER_IMAGE:-gitea.siegeln.net/cameleer/cameleer3-server}:${VERSION:-latest}
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
clickhouse:
condition: service_started
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/cameleer3
SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer}
SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD:-cameleer_dev}
CLICKHOUSE_URL: jdbc:clickhouse://clickhouse:8123/cameleer
CAMELEER_AUTH_TOKEN: ${CAMELEER_AUTH_TOKEN:-default-bootstrap-token}
CAMELEER_JWT_SECRET: ${CAMELEER_JWT_SECRET:-cameleer-dev-jwt-secret-change-in-production}
CAMELEER_TENANT_ID: ${CAMELEER_TENANT_SLUG:-default}
CAMELEER_OIDC_ISSUER_URI: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}/oidc
CAMELEER_OIDC_JWK_SET_URI: ${LOGTO_ENDPOINT:-http://logto:3001}/oidc/jwks
CAMELEER_OIDC_TLS_SKIP_VERIFY: "true" # dev only — disable in production with real certs
CAMELEER_OIDC_AUDIENCE: ${CAMELEER_OIDC_AUDIENCE:-https://api.cameleer.local}
CAMELEER_CORS_ALLOWED_ORIGINS: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:8081/api/v1/health || exit 1"]
interval: 5s
timeout: 5s
retries: 30
start_period: 15s
labels:
- traefik.enable=false
networks:
- cameleer
cameleer3-server-ui:
image: ${CAMELEER3_SERVER_UI_IMAGE:-gitea.siegeln.net/cameleer/cameleer3-server-ui}:${VERSION:-latest}
restart: unless-stopped
depends_on:
cameleer3-server:
condition: service_healthy
environment:
CAMELEER_API_URL: http://cameleer3-server:8081
BASE_PATH: /server
labels:
- traefik.enable=true
- traefik.http.routers.server-ui.rule=PathPrefix(`/server`)
- traefik.http.routers.server-ui.entrypoints=websecure
- traefik.http.routers.server-ui.tls=true
- traefik.http.routers.server-ui.middlewares=server-ui-strip
- traefik.http.middlewares.server-ui-strip.stripprefix.prefixes=/server
- traefik.http.routers.server-ui.service=server-ui
- traefik.http.services.server-ui.loadbalancer.server.port=80
networks:
- cameleer
clickhouse:
image: clickhouse/clickhouse-server:latest
restart: unless-stopped
volumes:
- chdata:/var/lib/clickhouse
- ./docker/clickhouse-init.sql:/docker-entrypoint-initdb.d/init.sql:ro
- ./docker/clickhouse-users.xml:/etc/clickhouse-server/users.d/default-user.xml:ro
healthcheck:
test: ["CMD-SHELL", "clickhouse-client --query 'SELECT 1'"]
interval: 10s
timeout: 5s
retries: 3
networks:
- cameleer
networks:
cameleer:
driver: bridge
volumes:
pgdata:
chdata:
certs:
jardata:
bootstrapdata: