diff --git a/.gitignore b/.gitignore index a928ab0..ed65d4a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ Thumbs.db .playwright-mcp/ .gitnexus +# Installer output (generated by install.sh / install.ps1) +installer/cameleer/ + # Generated by postinstall from @cameleer/design-system ui/public/favicon.svg docker/runtime-base/agent.jar diff --git a/CLAUDE.md b/CLAUDE.md index 9a1f336..17f7351 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -288,6 +288,13 @@ The installer (`installer/install.sh`) supports two deployment modes: Standalone mode generates a simpler compose with the server running directly. No Logto, no SaaS management plane, no bootstrap. The admin logs in with local credentials at `/`. +The installer uses static docker-compose templates in `installer/templates/`. Templates are copied to the install directory and composed via `COMPOSE_FILE` in `.env`: +- `docker-compose.yml` — shared infrastructure (traefik, postgres, clickhouse) +- `docker-compose.saas.yml` — SaaS mode (logto, cameleer-saas) +- `docker-compose.server.yml` — standalone mode (server, server-ui) +- `docker-compose.tls.yml` — overlay: custom TLS cert volume +- `docker-compose.monitoring.yml` — overlay: external monitoring network + ### Tenant Provisioning Flow When SaaS admin creates a tenant via `VendorTenantService`: diff --git a/installer/cameleer/.env.bak b/installer/cameleer/.env.bak deleted file mode 100644 index 230e96b..0000000 --- a/installer/cameleer/.env.bak +++ /dev/null @@ -1,33 +0,0 @@ -# Cameleer SaaS Configuration -# Generated by installer v1.0.0 on 2026-04-15 08:55:30 UTC - -VERSION=latest - -PUBLIC_HOST=desktop-fb5vgj9.siegeln.internal -PUBLIC_PROTOCOL=https - -HTTP_PORT=80 -HTTPS_PORT=443 -LOGTO_CONSOLE_PORT=3002 - -# PostgreSQL -POSTGRES_USER=cameleer -POSTGRES_PASSWORD=dwnyYXj3bVe6kFcOHERr57SkrkD9476a -POSTGRES_DB=cameleer_saas - -# ClickHouse -CLICKHOUSE_PASSWORD=SshXE61qZqB1kVoZpQLbr2mDYokw1ZgJ - -# Admin user -SAAS_ADMIN_USER=admin -SAAS_ADMIN_PASS=1J3TrbgIYZbxjav1K14uy5DX8nil6Bdi - -# TLS -NODE_TLS_REJECT=0 -# Docker -DOCKER_SOCKET=/var/run/docker.sock -DOCKER_GID=0 - -# Provisioning images -CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=gitea.siegeln.net/cameleer/cameleer-server:latest -CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=gitea.siegeln.net/cameleer/cameleer-server-ui:latest \ No newline at end of file diff --git a/installer/cameleer/INSTALL.md b/installer/cameleer/INSTALL.md deleted file mode 100644 index afb5bf7..0000000 --- a/installer/cameleer/INSTALL.md +++ /dev/null @@ -1,95 +0,0 @@ -# Cameleer SaaS -- Installation Documentation - -## Installation Summary - -| | | -|---|---| -| **Version** | latest | -| **Date** | 2026-04-15 08:55:55 UTC | -| **Installer** | v1.0.0 | -| **Install Directory** | C:\Users\Hendrik\Documents\projects\cameleer-saas\installer\cameleer | -| **Hostname** | desktop-fb5vgj9.siegeln.internal | -| **TLS** | Self-signed (auto-generated) | - -## Service URLs - -- **Platform UI:** https://desktop-fb5vgj9.siegeln.internal/platform/ -- **API Endpoint:** https://desktop-fb5vgj9.siegeln.internal/platform/api/ -- **Logto Admin Console:** https://desktop-fb5vgj9.siegeln.internal:3002 - -## First Steps - -1. Open the Platform UI in your browser -2. Log in as admin with the credentials from `credentials.txt` -3. Create tenants from the admin console -4. The platform will provision a dedicated server instance for each tenant - -## Architecture - -| Container | Purpose | -|---|---| -| `traefik` | Reverse proxy, TLS termination, routing | -| `postgres` | PostgreSQL database (SaaS + Logto + tenant schemas) | -| `clickhouse` | Time-series storage (traces, metrics, logs) | -| `logto` | OIDC identity provider + bootstrap | -| `cameleer-saas` | SaaS platform (Spring Boot + React) | - -Per-tenant `cameleer-server` and `cameleer-server-ui` containers are provisioned dynamically. - -## Networking - -| Port | Service | -|---|---| -| 80 | HTTP (redirects to HTTPS) | -| 443 | HTTPS (main entry point) | -| 3002 | Logto Admin Console | - - -## TLS - -**Mode:** Self-signed (auto-generated) - -The platform generated a self-signed certificate on first boot. To replace it: -1. Log in as admin and navigate to **Certificates** in the admin console -2. Upload your certificate and key via the UI -3. Activate the new certificate (zero-downtime swap) - -## Data & Backups - -| Docker Volume | Contains | -|---|---| -| `cameleer-pgdata` | PostgreSQL data (tenants, licenses, audit) | -| `cameleer-chdata` | ClickHouse data (traces, metrics, logs) | -| `cameleer-certs` | TLS certificates | -| `cameleer-bootstrapdata` | Logto bootstrap results | - -### Backup Commands - -```bash -docker compose -p cameleer-saas exec cameleer-postgres pg_dump -U cameleer cameleer_saas > backup.sql -docker compose -p cameleer-saas exec cameleer-clickhouse clickhouse-client --query "SELECT * FROM cameleer.traces FORMAT Native" > traces.native -``` - -## Upgrading - -```powershell -.\install.ps1 -InstallDir C:\Users\Hendrik\Documents\projects\cameleer-saas\installer\cameleer -Version NEW_VERSION -``` - -## Troubleshooting - -| Issue | Command | -|---|---| -| Service not starting | `docker compose -p cameleer-saas logs SERVICE_NAME` | -| Bootstrap failed | `docker compose -p cameleer-saas logs cameleer-logto` | -| Routing issues | `docker compose -p cameleer-saas logs cameleer-traefik` | -| Database issues | `docker compose -p cameleer-saas exec cameleer-postgres psql -U cameleer -d cameleer_saas` | - -## Uninstalling - -```powershell -Set-Location C:\Users\Hendrik\Documents\projects\cameleer-saas\installer\cameleer -docker compose -p cameleer-saas down -docker compose -p cameleer-saas down -v -Remove-Item -Recurse -Force C:\Users\Hendrik\Documents\projects\cameleer-saas\installer\cameleer -``` \ No newline at end of file diff --git a/installer/cameleer/cameleer.conf b/installer/cameleer/cameleer.conf deleted file mode 100644 index e862f96..0000000 --- a/installer/cameleer/cameleer.conf +++ /dev/null @@ -1,18 +0,0 @@ -# Cameleer installation config -# Generated by installer v1.0.0 on 2026-04-15 08:55:30 UTC - -install_dir=C:\Users\Hendrik\Documents\projects\cameleer-saas\installer\cameleer -public_host=desktop-fb5vgj9.siegeln.internal -public_protocol=https -admin_user=admin -tls_mode=self-signed -http_port=80 -https_port=443 -logto_console_port=3002 -logto_console_exposed=true -monitoring_network= -version=latest -compose_project=cameleer-saas -docker_socket=/var/run/docker.sock -node_tls_reject=0 -deployment_mode=saas \ No newline at end of file diff --git a/installer/cameleer/credentials.txt b/installer/cameleer/credentials.txt deleted file mode 100644 index db47b3c..0000000 --- a/installer/cameleer/credentials.txt +++ /dev/null @@ -1,16 +0,0 @@ -=========================================== - CAMELEER PLATFORM CREDENTIALS - Generated: 2026-04-15 08:55:55 UTC - - SECURE THIS FILE AND DELETE AFTER NOTING - THESE CREDENTIALS CANNOT BE RECOVERED -=========================================== - -Admin Console: https://desktop-fb5vgj9.siegeln.internal/platform/ -Admin User: admin -Admin Password: 1J3TrbgIYZbxjav1K14uy5DX8nil6Bdi - -PostgreSQL: cameleer / dwnyYXj3bVe6kFcOHERr57SkrkD9476a -ClickHouse: default / SshXE61qZqB1kVoZpQLbr2mDYokw1ZgJ - -Logto Console: https://desktop-fb5vgj9.siegeln.internal:3002 \ No newline at end of file diff --git a/installer/install.ps1 b/installer/install.ps1 index c6ac684..f8a1887 100644 --- a/installer/install.ps1 +++ b/installer/install.ps1 @@ -607,26 +607,37 @@ BOOTSTRAP_TOKEN=$bt # Docker DOCKER_SOCKET=$($c.DockerSocket) DOCKER_GID=$gid + +POSTGRES_IMAGE=postgres:16-alpine "@ if ($c.TlsMode -eq 'custom') { $content += "`nCERT_FILE=/user-certs/cert.pem" $content += "`nKEY_FILE=/user-certs/key.pem" if ($c.CaFile) { $content += "`nCA_FILE=/user-certs/ca.pem" } } + $composeFile = 'docker-compose.yml:docker-compose.server.yml' + if ($c.TlsMode -eq 'custom') { $composeFile += ':docker-compose.tls.yml' } + if ($c.MonitoringNetwork) { $composeFile += ':docker-compose.monitoring.yml' } + $content += "`n`n# Compose file assembly`nCOMPOSE_FILE=$composeFile" + if ($c.MonitoringNetwork) { + $content += "`n`n# Monitoring`nMONITORING_NETWORK=$($c.MonitoringNetwork)" + } } else { + $consoleBind = if ($c.LogtoConsoleExposed -eq 'true') { '0.0.0.0' } else { '127.0.0.1' } $content = @" # Cameleer SaaS Configuration # Generated by installer v${CAMELEER_INSTALLER_VERSION} on $ts - + VERSION=$($c.Version) - + PUBLIC_HOST=$($c.PublicHost) PUBLIC_PROTOCOL=$($c.PublicProtocol) - + HTTP_PORT=$($c.HttpPort) HTTPS_PORT=$($c.HttpsPort) LOGTO_CONSOLE_PORT=$($c.LogtoConsolePort) - +LOGTO_CONSOLE_BIND=$consoleBind + # PostgreSQL POSTGRES_USER=cameleer POSTGRES_PASSWORD=$($c.PostgresPassword) @@ -658,6 +669,13 @@ CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=${REGISTRY}/cameleer-server:$($c.Version) CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=${REGISTRY}/cameleer-server-ui:$($c.Version) "@ $content += $provisioningBlock + $composeFile = 'docker-compose.yml:docker-compose.saas.yml' + if ($c.TlsMode -eq 'custom') { $composeFile += ':docker-compose.tls.yml' } + if ($c.MonitoringNetwork) { $composeFile += ':docker-compose.monitoring.yml' } + $content += "`n`n# Compose file assembly`nCOMPOSE_FILE=$composeFile" + if ($c.MonitoringNetwork) { + $content += "`n`n# Monitoring`nMONITORING_NETWORK=$($c.MonitoringNetwork)" + } } Write-Utf8File $f $content @@ -665,443 +683,33 @@ CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=${REGISTRY}/cameleer-server-ui:$($c.Ver Log-Info 'Generated .env' } -# --- Docker Compose generation --- -# Rule: '@ and "@ closing delimiters must ALWAYS be alone at column 0 on their own line. +# --- Copy docker-compose templates --- -function Generate-ComposeFile { - $c = $script:cfg - if ($c.DeploymentMode -eq 'standalone') { Generate-ComposeFileStandalone; return } - - $f = Join-Path $c.InstallDir 'docker-compose.yml' - $gid = Get-DockerGid $c.DockerSocket - $out = New-Object System.Collections.Generic.List[string] - - $out.Add(@' -# Cameleer SaaS Platform -# Generated by Cameleer installer -- do not edit manually - -services: - cameleer-traefik: - image: ${TRAEFIK_IMAGE:-gitea.siegeln.net/cameleer/cameleer-traefik}:${VERSION:-latest} - restart: unless-stopped - ports: - - "${HTTP_PORT:-80}:80" - - "${HTTPS_PORT:-443}:443" -'@ - ) - if ($c.LogtoConsoleExposed -eq 'true') { - $out.Add(' - "${LOGTO_CONSOLE_PORT:-3002}:3002"') - } - $out.Add(@' - environment: - PUBLIC_HOST: ${PUBLIC_HOST:-localhost} - CERT_FILE: ${CERT_FILE:-} - KEY_FILE: ${KEY_FILE:-} - CA_FILE: ${CA_FILE:-} - volumes: - - cameleer-certs:/certs - - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock:ro -'@ - ) - if ($c.TlsMode -eq 'custom') { $out.Add(' - ./certs:/user-certs:ro') } - $out.Add(@' - networks: - - cameleer - - cameleer-traefik -'@ - ) - if ($c.MonitoringNetwork) { - $out.Add(" - $($c.MonitoringNetwork)") - $out.Add(@' - labels: - - "prometheus.io/scrape=true" - - "prometheus.io/port=8082" - - "prometheus.io/path=/metrics" -'@ - ) - } - - $out.Add(@' - - cameleer-postgres: - image: ${POSTGRES_IMAGE:-gitea.siegeln.net/cameleer/cameleer-postgres}:${VERSION:-latest} - restart: unless-stopped - environment: - POSTGRES_DB: cameleer_saas - POSTGRES_USER: ${POSTGRES_USER:-cameleer} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - volumes: - - cameleer-pgdata:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-cameleer} -d cameleer_saas"] - interval: 5s - timeout: 5s - retries: 5 - networks: - - cameleer -'@ - ) - if ($c.MonitoringNetwork) { $out.Add(" - $($c.MonitoringNetwork)") } - - $out.Add(@' - - cameleer-clickhouse: - image: ${CLICKHOUSE_IMAGE:-gitea.siegeln.net/cameleer/cameleer-clickhouse}:${VERSION:-latest} - restart: unless-stopped - environment: - CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} - volumes: - - cameleer-chdata:/var/lib/clickhouse - healthcheck: - test: ["CMD-SHELL", "clickhouse-client --password $${CLICKHOUSE_PASSWORD} --query 'SELECT 1'"] - interval: 10s - timeout: 5s - retries: 3 - networks: - - cameleer -'@ - ) - if ($c.MonitoringNetwork) { - $out.Add(" - $($c.MonitoringNetwork)") - $out.Add(@' - labels: - - "prometheus.io/scrape=true" - - "prometheus.io/port=9363" - - "prometheus.io/path=/metrics" -'@ - ) - } - - $out.Add(@' - - 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 -'@ - ) - if ($c.LogtoConsoleExposed -eq 'true') { - $out.Add(@' - - 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 -'@ - ) - } - - $out.Add(@' - volumes: - - cameleer-bootstrapdata:/data - networks: - - cameleer - - cameleer-saas: - image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest} - restart: unless-stopped - depends_on: - cameleer-logto: - condition: service_healthy - environment: - SPRING_DATASOURCE_URL: jdbc:postgresql://cameleer-postgres:5432/cameleer_saas - SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer} - SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD} - CAMELEER_SAAS_IDENTITY_LOGTOENDPOINT: http://cameleer-logto:3001 - CAMELEER_SAAS_IDENTITY_LOGTOPUBLICENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} - 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 -'@ - ) - if ($c.MonitoringNetwork) { - $out.Add(@' - - "prometheus.io/scrape=true" - - "prometheus.io/port=8080" - - "prometheus.io/path=/platform/actuator/prometheus" -'@ - ) - } - $out.Add(@' - volumes: - - cameleer-bootstrapdata:/data/bootstrap:ro - - cameleer-certs:/certs - - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock - networks: - - cameleer -'@ - ) - if ($c.MonitoringNetwork) { $out.Add(" - $($c.MonitoringNetwork)") } - $out.Add(" group_add:") - $out.Add(" - `"$gid`"") - - $out.Add(@' - -volumes: - cameleer-pgdata: - cameleer-chdata: - cameleer-certs: - cameleer-bootstrapdata: - -networks: - cameleer: - driver: bridge - cameleer-traefik: - name: cameleer-traefik - driver: bridge -'@ - ) - if ($c.MonitoringNetwork) { - $out.Add(" $($c.MonitoringNetwork):") - $out.Add(' external: true') - } - - Write-Utf8File $f ($out -join "`n") - Log-Info 'Generated docker-compose.yml' -} - -function Generate-ComposeFileStandalone { +function Copy-Templates { $c = $script:cfg - $f = Join-Path $c.InstallDir 'docker-compose.yml' - $gid = Get-DockerGid $c.DockerSocket - $out = New-Object System.Collections.Generic.List[string] - - $out.Add(@' -# Cameleer Server (standalone) -# Generated by Cameleer installer -- do not edit manually - -services: - cameleer-traefik: - image: ${TRAEFIK_IMAGE:-gitea.siegeln.net/cameleer/cameleer-traefik}:${VERSION:-latest} - restart: unless-stopped - ports: - - "${HTTP_PORT:-80}:80" - - "${HTTPS_PORT:-443}:443" - environment: - PUBLIC_HOST: ${PUBLIC_HOST:-localhost} - CERT_FILE: ${CERT_FILE:-} - KEY_FILE: ${KEY_FILE:-} - CA_FILE: ${CA_FILE:-} - volumes: - - cameleer-certs:/certs - - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock:ro - - ./traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro -'@ - ) - if ($c.TlsMode -eq 'custom') { $out.Add(' - ./certs:/user-certs:ro') } - $out.Add(@' - networks: - - cameleer - - cameleer-traefik -'@ - ) - if ($c.MonitoringNetwork) { $out.Add(" - $($c.MonitoringNetwork)") } - - $out.Add(@' - - cameleer-postgres: - image: postgres:16-alpine - restart: unless-stopped - environment: - POSTGRES_DB: ${POSTGRES_DB:-cameleer} - POSTGRES_USER: ${POSTGRES_USER:-cameleer} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - volumes: - - cameleer-pgdata:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-cameleer} -d $${POSTGRES_DB:-cameleer}"] - interval: 5s - timeout: 5s - retries: 5 - networks: - - cameleer -'@ - ) - if ($c.MonitoringNetwork) { $out.Add(" - $($c.MonitoringNetwork)") } - - $out.Add(@' - - cameleer-clickhouse: - image: ${CLICKHOUSE_IMAGE:-gitea.siegeln.net/cameleer/cameleer-clickhouse}:${VERSION:-latest} - restart: unless-stopped - environment: - CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} - volumes: - - cameleer-chdata:/var/lib/clickhouse - healthcheck: - test: ["CMD-SHELL", "clickhouse-client --password $${CLICKHOUSE_PASSWORD} --query 'SELECT 1'"] - interval: 10s - timeout: 5s - retries: 3 - networks: - - cameleer -'@ - ) - if ($c.MonitoringNetwork) { $out.Add(" - $($c.MonitoringNetwork)") } - - # Server block: double-quoted so $gid expands; compose ${VAR} uses backtick-dollar - $serverBlock = @" - - cameleer-server: - image: `${SERVER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-server}:`${VERSION:-latest} - container_name: cameleer-server - restart: unless-stopped - depends_on: - cameleer-postgres: - condition: service_healthy - environment: - CAMELEER_SERVER_TENANT_ID: default - SPRING_DATASOURCE_URL: jdbc:postgresql://cameleer-postgres:5432/`${POSTGRES_DB:-cameleer}?currentSchema=tenant_default - SPRING_DATASOURCE_USERNAME: `${POSTGRES_USER:-cameleer} - SPRING_DATASOURCE_PASSWORD: `${POSTGRES_PASSWORD} - CAMELEER_SERVER_CLICKHOUSE_URL: jdbc:clickhouse://cameleer-clickhouse:8123/cameleer - CAMELEER_SERVER_CLICKHOUSE_USERNAME: default - CAMELEER_SERVER_CLICKHOUSE_PASSWORD: `${CLICKHOUSE_PASSWORD} - CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN: `${BOOTSTRAP_TOKEN} - CAMELEER_SERVER_SECURITY_UIUSER: `${SERVER_ADMIN_USER:-admin} - CAMELEER_SERVER_SECURITY_UIPASSWORD: `${SERVER_ADMIN_PASS:?SERVER_ADMIN_PASS must be set in .env} - CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS: `${PUBLIC_PROTOCOL:-https}://`${PUBLIC_HOST:-localhost} - CAMELEER_SERVER_RUNTIME_ENABLED: "true" - CAMELEER_SERVER_RUNTIME_SERVERURL: http://cameleer-server:8081 - CAMELEER_SERVER_RUNTIME_ROUTINGDOMAIN: `${PUBLIC_HOST:-localhost} - CAMELEER_SERVER_RUNTIME_ROUTINGMODE: path - CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH: /data/jars - CAMELEER_SERVER_RUNTIME_DOCKERNETWORK: cameleer-apps - CAMELEER_SERVER_RUNTIME_JARDOCKERVOLUME: cameleer-jars - CAMELEER_SERVER_RUNTIME_BASEIMAGE: gitea.siegeln.net/cameleer/cameleer-runtime-base:`${VERSION:-latest} - labels: - - traefik.enable=true - - traefik.http.routers.server-api.rule=PathPrefix(``/api``) - - traefik.http.routers.server-api.entrypoints=websecure - - traefik.http.routers.server-api.tls=true - - traefik.http.services.server-api.loadbalancer.server.port=8081 - - traefik.docker.network=cameleer-traefik - healthcheck: - test: ["CMD-SHELL", "curl -sf http://localhost:8081/api/v1/health || exit 1"] - interval: 10s - timeout: 5s - retries: 30 - start_period: 30s - volumes: - - jars:/data/jars - - cameleer-certs:/certs:ro - - `${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock - group_add: - - "$gid" - networks: - - cameleer - - cameleer-traefik - - cameleer-apps - - cameleer-server-ui: - image: `${SERVER_UI_IMAGE:-gitea.siegeln.net/cameleer/cameleer-server-ui}:`${VERSION:-latest} - restart: unless-stopped - depends_on: - cameleer-server: - condition: service_healthy - environment: - CAMELEER_API_URL: http://cameleer-server:8081 - BASE_PATH: "" - labels: - - traefik.enable=true - - traefik.http.routers.ui.rule=PathPrefix(``/``) - - traefik.http.routers.ui.priority=1 - - traefik.http.routers.ui.entrypoints=websecure - - traefik.http.routers.ui.tls=true - - traefik.http.services.ui.loadbalancer.server.port=80 - - traefik.docker.network=cameleer-traefik - networks: - - cameleer-traefik -"@ - $out.Add($serverBlock) - - $out.Add(@' - -volumes: - cameleer-pgdata: - cameleer-chdata: - cameleer-certs: - jars: - -networks: - cameleer: - driver: bridge - cameleer-traefik: - name: cameleer-traefik - driver: bridge - cameleer-apps: - name: cameleer-apps - driver: bridge -'@ - ) - if ($c.MonitoringNetwork) { - $out.Add(" $($c.MonitoringNetwork):") - $out.Add(' external: true') + $src = Join-Path $PSScriptRoot 'templates' + + # Base infra -- always copied + Copy-Item (Join-Path $src 'docker-compose.yml') (Join-Path $c.InstallDir 'docker-compose.yml') -Force + Copy-Item (Join-Path $src '.env.example') (Join-Path $c.InstallDir '.env.example') -Force + + # Mode-specific + if ($c.DeploymentMode -eq 'standalone') { + Copy-Item (Join-Path $src 'docker-compose.server.yml') (Join-Path $c.InstallDir 'docker-compose.server.yml') -Force + Copy-Item (Join-Path $src 'traefik-dynamic.yml') (Join-Path $c.InstallDir 'traefik-dynamic.yml') -Force + } else { + Copy-Item (Join-Path $src 'docker-compose.saas.yml') (Join-Path $c.InstallDir 'docker-compose.saas.yml') -Force } - - Write-Utf8File $f ($out -join "`n") - - $traefikDyn = @' -tls: - stores: - default: - defaultCertificate: - certFile: /certs/cert.pem - keyFile: /certs/key.pem -'@ - Write-Utf8File (Join-Path $c.InstallDir 'traefik-dynamic.yml') $traefikDyn - - Log-Info 'Generated docker-compose.yml (standalone)' + + # Optional overlays + if ($c.TlsMode -eq 'custom') { + Copy-Item (Join-Path $src 'docker-compose.tls.yml') (Join-Path $c.InstallDir 'docker-compose.tls.yml') -Force + } + if ($c.MonitoringNetwork) { + Copy-Item (Join-Path $src 'docker-compose.monitoring.yml') (Join-Path $c.InstallDir 'docker-compose.monitoring.yml') -Force + } + + Log-Info "Copied docker-compose templates to $($c.InstallDir)" } # --- Docker operations --- @@ -1713,7 +1321,7 @@ function Handle-Rerun { Merge-Config $script:cfg.InstallDir = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath( $script:cfg.InstallDir) - Generate-ComposeFile + Copy-Templates Invoke-ComposePull Invoke-ComposeDown Invoke-ComposeUp @@ -1743,7 +1351,7 @@ function Handle-Rerun { docker compose -p $proj down -v 2>$null } catch {} finally { Pop-Location } - foreach ($fname in @('.env','.env.bak','docker-compose.yml','cameleer.conf','credentials.txt','INSTALL.md','traefik-dynamic.yml')) { + foreach ($fname in @('.env','.env.bak','.env.example','docker-compose.yml','docker-compose.saas.yml','docker-compose.server.yml','docker-compose.tls.yml','docker-compose.monitoring.yml','traefik-dynamic.yml','cameleer.conf','credentials.txt','INSTALL.md')) { $fp = Join-Path $c.InstallDir $fname if (Test-Path $fp) { Remove-Item $fp -Force } } @@ -1795,7 +1403,7 @@ function Main { if ($script:cfg.TlsMode -eq 'custom') { Copy-Certs } Generate-EnvFile - Generate-ComposeFile + Copy-Templates Write-ConfigFile Invoke-ComposePull diff --git a/installer/install.sh b/installer/install.sh index cc7dfc9..f106c27 100644 --- a/installer/install.sh +++ b/installer/install.sh @@ -603,12 +603,22 @@ BOOTSTRAP_TOKEN=$(generate_password) # Docker DOCKER_SOCKET=${DOCKER_SOCKET} DOCKER_GID=$(stat -c '%g' "${DOCKER_SOCKET}" 2>/dev/null || echo "0") + +POSTGRES_IMAGE=postgres:16-alpine + +# Compose file assembly +COMPOSE_FILE=docker-compose.yml:docker-compose.server.yml$([ "$TLS_MODE" = "custom" ] && echo ":docker-compose.tls.yml")$([ -n "$MONITORING_NETWORK" ] && echo ":docker-compose.monitoring.yml") EOF if [ "$TLS_MODE" = "custom" ]; then echo "CERT_FILE=/user-certs/cert.pem" >> "$f" echo "KEY_FILE=/user-certs/key.pem" >> "$f" [ -n "$CA_FILE" ] && echo "CA_FILE=/user-certs/ca.pem" >> "$f" fi + if [ -n "$MONITORING_NETWORK" ]; then + echo "" >> "$f" + echo "# Monitoring" >> "$f" + echo "MONITORING_NETWORK=${MONITORING_NETWORK}" >> "$f" + fi log_info "Generated .env" cp "$f" "$INSTALL_DIR/.env.bak" return @@ -629,6 +639,7 @@ PUBLIC_PROTOCOL=${PUBLIC_PROTOCOL} HTTP_PORT=${HTTP_PORT} HTTPS_PORT=${HTTPS_PORT} LOGTO_CONSOLE_PORT=${LOGTO_CONSOLE_PORT} +LOGTO_CONSOLE_BIND=$([ "$LOGTO_CONSOLE_EXPOSED" = "true" ] && echo "0.0.0.0" || echo "127.0.0.1") # PostgreSQL POSTGRES_USER=cameleer @@ -665,473 +676,46 @@ DOCKER_GID=$(stat -c '%g' "${DOCKER_SOCKET}" 2>/dev/null || echo "0") # Provisioning images CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=${REGISTRY}/cameleer-server:${VERSION} CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=${REGISTRY}/cameleer-server-ui:${VERSION} + +# Compose file assembly +COMPOSE_FILE=docker-compose.yml:docker-compose.saas.yml$([ "$TLS_MODE" = "custom" ] && echo ":docker-compose.tls.yml")$([ -n "$MONITORING_NETWORK" ] && echo ":docker-compose.monitoring.yml") EOF + if [ -n "$MONITORING_NETWORK" ]; then + echo "" >> "$f" + echo "# Monitoring" >> "$f" + echo "MONITORING_NETWORK=${MONITORING_NETWORK}" >> "$f" + fi + log_info "Generated .env" cp "$f" "$INSTALL_DIR/.env.bak" } -generate_compose_file() { +copy_templates() { + local src + src="$(cd "$(dirname "$0")" && pwd)/templates" + + # Base infra — always copied + cp "$src/docker-compose.yml" "$INSTALL_DIR/docker-compose.yml" + cp "$src/.env.example" "$INSTALL_DIR/.env.example" + + # Mode-specific if [ "$DEPLOYMENT_MODE" = "standalone" ]; then - generate_compose_file_standalone - return - fi - local f="$INSTALL_DIR/docker-compose.yml" - : > "$f" - - cat >> "$f" << 'EOF' -# Cameleer SaaS Platform -# Generated by Cameleer installer � do not edit manually - -services: - cameleer-traefik: - image: ${TRAEFIK_IMAGE:-gitea.siegeln.net/cameleer/cameleer-traefik}:${VERSION:-latest} - restart: unless-stopped - ports: - - "${HTTP_PORT:-80}:80" - - "${HTTPS_PORT:-443}:443" -EOF - - if [ "$LOGTO_CONSOLE_EXPOSED" = "true" ]; then - cat >> "$f" << 'EOF' - - "${LOGTO_CONSOLE_PORT:-3002}:3002" -EOF + cp "$src/docker-compose.server.yml" "$INSTALL_DIR/docker-compose.server.yml" + cp "$src/traefik-dynamic.yml" "$INSTALL_DIR/traefik-dynamic.yml" + else + cp "$src/docker-compose.saas.yml" "$INSTALL_DIR/docker-compose.saas.yml" fi - cat >> "$f" << 'EOF' - environment: - PUBLIC_HOST: ${PUBLIC_HOST:-localhost} - CERT_FILE: ${CERT_FILE:-} - KEY_FILE: ${KEY_FILE:-} - CA_FILE: ${CA_FILE:-} - volumes: - - cameleer-certs:/certs - - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock:ro -EOF - + # Optional overlays if [ "$TLS_MODE" = "custom" ]; then - cat >> "$f" << 'EOF' - - ./certs:/user-certs:ro -EOF + cp "$src/docker-compose.tls.yml" "$INSTALL_DIR/docker-compose.tls.yml" fi - - cat >> "$f" << 'EOF' - networks: - - cameleer - - cameleer-traefik -EOF - if [ -n "$MONITORING_NETWORK" ]; then - echo " - ${MONITORING_NETWORK}" >> "$f" - cat >> "$f" << 'EOF' - labels: - - "prometheus.io/scrape=true" - - "prometheus.io/port=8082" - - "prometheus.io/path=/metrics" -EOF + cp "$src/docker-compose.monitoring.yml" "$INSTALL_DIR/docker-compose.monitoring.yml" fi - cat >> "$f" << 'EOF' - - cameleer-postgres: - image: ${POSTGRES_IMAGE:-gitea.siegeln.net/cameleer/cameleer-postgres}:${VERSION:-latest} - restart: unless-stopped - environment: - POSTGRES_DB: cameleer_saas - POSTGRES_USER: ${POSTGRES_USER:-cameleer} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - volumes: - - cameleer-pgdata:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-cameleer} -d cameleer_saas"] - interval: 5s - timeout: 5s - retries: 5 - networks: - - cameleer -EOF - - if [ -n "$MONITORING_NETWORK" ]; then - echo " - ${MONITORING_NETWORK}" >> "$f" - fi - - cat >> "$f" << 'EOF' - - cameleer-clickhouse: - image: ${CLICKHOUSE_IMAGE:-gitea.siegeln.net/cameleer/cameleer-clickhouse}:${VERSION:-latest} - restart: unless-stopped - environment: - CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} - volumes: - - cameleer-chdata:/var/lib/clickhouse - healthcheck: - test: ["CMD-SHELL", "clickhouse-client --password $${CLICKHOUSE_PASSWORD} --query 'SELECT 1'"] - interval: 10s - timeout: 5s - retries: 3 - networks: - - cameleer -EOF - - if [ -n "$MONITORING_NETWORK" ]; then - echo " - ${MONITORING_NETWORK}" >> "$f" - cat >> "$f" << 'EOF' - labels: - - "prometheus.io/scrape=true" - - "prometheus.io/port=9363" - - "prometheus.io/path=/metrics" -EOF - fi - - cat >> "$f" << 'EOF' - - 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 -EOF - - if [ "$LOGTO_CONSOLE_EXPOSED" = "true" ]; then - cat >> "$f" << 'EOF' - - 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 -EOF - fi - - cat >> "$f" << 'EOF' - volumes: - - cameleer-bootstrapdata:/data - networks: - - cameleer - - 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 -EOF - - if [ -n "$MONITORING_NETWORK" ]; then - cat >> "$f" << 'EOF' - - "prometheus.io/scrape=true" - - "prometheus.io/port=8080" - - "prometheus.io/path=/platform/actuator/prometheus" -EOF - fi - - cat >> "$f" << 'EOF' - volumes: - - cameleer-bootstrapdata:/data/bootstrap:ro - - cameleer-certs:/certs - - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock - networks: - - cameleer -EOF - - if [ -n "$MONITORING_NETWORK" ]; then - echo " - ${MONITORING_NETWORK}" >> "$f" - fi - - # Detect Docker socket GID for container access - local docker_gid - docker_gid=$(stat -c '%g' "${DOCKER_SOCKET:-/var/run/docker.sock}" 2>/dev/null || echo "0") - cat >> "$f" << EOF - group_add: - - "${docker_gid}" - -volumes: -EOF - cat >> "$f" << 'EOF' - cameleer-pgdata: - cameleer-chdata: - cameleer-certs: - cameleer-bootstrapdata: - -networks: - cameleer: - driver: bridge - cameleer-traefik: - name: cameleer-traefik - driver: bridge -EOF - - if [ -n "$MONITORING_NETWORK" ]; then - cat >> "$f" << EOF - ${MONITORING_NETWORK}: - external: true -EOF - fi - - log_info "Generated docker-compose.yml" -} - -generate_compose_file_standalone() { - local f="$INSTALL_DIR/docker-compose.yml" - : > "$f" - - cat >> "$f" << 'COMPOSEEOF' -# Cameleer Server (standalone) -# Generated by Cameleer installer — do not edit manually - -services: - cameleer-traefik: - image: ${TRAEFIK_IMAGE:-gitea.siegeln.net/cameleer/cameleer-traefik}:${VERSION:-latest} - restart: unless-stopped - ports: - - "${HTTP_PORT:-80}:80" - - "${HTTPS_PORT:-443}:443" - environment: - PUBLIC_HOST: ${PUBLIC_HOST:-localhost} - CERT_FILE: ${CERT_FILE:-} - KEY_FILE: ${KEY_FILE:-} - CA_FILE: ${CA_FILE:-} - volumes: - - cameleer-certs:/certs - - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock:ro - - ./traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro -COMPOSEEOF - - if [ "$TLS_MODE" = "custom" ]; then - echo " - ./certs:/user-certs:ro" >> "$f" - fi - - cat >> "$f" << 'COMPOSEEOF' - networks: - - cameleer - - cameleer-traefik -COMPOSEEOF - - if [ -n "$MONITORING_NETWORK" ]; then - echo " - ${MONITORING_NETWORK}" >> "$f" - fi - - cat >> "$f" << 'COMPOSEEOF' - - cameleer-postgres: - image: postgres:16-alpine - restart: unless-stopped - environment: - POSTGRES_DB: ${POSTGRES_DB:-cameleer} - POSTGRES_USER: ${POSTGRES_USER:-cameleer} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - volumes: - - cameleer-pgdata:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-cameleer} -d $${POSTGRES_DB:-cameleer}"] - interval: 5s - timeout: 5s - retries: 5 - networks: - - cameleer -COMPOSEEOF - - if [ -n "$MONITORING_NETWORK" ]; then - echo " - ${MONITORING_NETWORK}" >> "$f" - fi - - cat >> "$f" << 'COMPOSEEOF' - - cameleer-clickhouse: - image: ${CLICKHOUSE_IMAGE:-gitea.siegeln.net/cameleer/cameleer-clickhouse}:${VERSION:-latest} - restart: unless-stopped - environment: - CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} - volumes: - - cameleer-chdata:/var/lib/clickhouse - healthcheck: - test: ["CMD-SHELL", "clickhouse-client --password $${CLICKHOUSE_PASSWORD} --query 'SELECT 1'"] - interval: 10s - timeout: 5s - retries: 3 - networks: - - cameleer -COMPOSEEOF - - if [ -n "$MONITORING_NETWORK" ]; then - echo " - ${MONITORING_NETWORK}" >> "$f" - fi - - # Detect Docker socket GID - local docker_gid - docker_gid=$(stat -c '%g' "${DOCKER_SOCKET:-/var/run/docker.sock}" 2>/dev/null || echo "0") - - cat >> "$f" << COMPOSEEOF - - cameleer-server: - image: \${SERVER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-server}:\${VERSION:-latest} - container_name: cameleer-server - restart: unless-stopped - depends_on: - cameleer-postgres: - condition: service_healthy - environment: - CAMELEER_SERVER_TENANT_ID: default - SPRING_DATASOURCE_URL: jdbc:postgresql://cameleer-postgres:5432/\${POSTGRES_DB:-cameleer}?currentSchema=tenant_default - SPRING_DATASOURCE_USERNAME: \${POSTGRES_USER:-cameleer} - SPRING_DATASOURCE_PASSWORD: \${POSTGRES_PASSWORD} - CAMELEER_SERVER_CLICKHOUSE_URL: jdbc:clickhouse://cameleer-clickhouse:8123/cameleer - CAMELEER_SERVER_CLICKHOUSE_USERNAME: default - CAMELEER_SERVER_CLICKHOUSE_PASSWORD: \${CLICKHOUSE_PASSWORD} - CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN: \${BOOTSTRAP_TOKEN} - CAMELEER_SERVER_SECURITY_UIUSER: \${SERVER_ADMIN_USER:-admin} - CAMELEER_SERVER_SECURITY_UIPASSWORD: \${SERVER_ADMIN_PASS:?SERVER_ADMIN_PASS must be set in .env} - CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS: \${PUBLIC_PROTOCOL:-https}://\${PUBLIC_HOST:-localhost} - CAMELEER_SERVER_RUNTIME_ENABLED: "true" - CAMELEER_SERVER_RUNTIME_SERVERURL: http://cameleer-server:8081 - CAMELEER_SERVER_RUNTIME_ROUTINGDOMAIN: \${PUBLIC_HOST:-localhost} - CAMELEER_SERVER_RUNTIME_ROUTINGMODE: path - CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH: /data/jars - CAMELEER_SERVER_RUNTIME_DOCKERNETWORK: cameleer-apps - CAMELEER_SERVER_RUNTIME_JARDOCKERVOLUME: cameleer-jars - CAMELEER_SERVER_RUNTIME_BASEIMAGE: gitea.siegeln.net/cameleer/cameleer-runtime-base:\${VERSION:-latest} - labels: - - traefik.enable=true - - traefik.http.routers.server-api.rule=PathPrefix(\`/api\`) - - traefik.http.routers.server-api.entrypoints=websecure - - traefik.http.routers.server-api.tls=true - - traefik.http.services.server-api.loadbalancer.server.port=8081 - - traefik.docker.network=cameleer-traefik - healthcheck: - test: ["CMD-SHELL", "curl -sf http://localhost:8081/api/v1/health || exit 1"] - interval: 10s - timeout: 5s - retries: 30 - start_period: 30s - volumes: - - jars:/data/jars - - cameleer-certs:/certs:ro - - \${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock - group_add: - - "${docker_gid}" - networks: - - cameleer - - cameleer-traefik - - cameleer-apps - - cameleer-server-ui: - image: \${SERVER_UI_IMAGE:-gitea.siegeln.net/cameleer/cameleer-server-ui}:\${VERSION:-latest} - restart: unless-stopped - depends_on: - cameleer-server: - condition: service_healthy - environment: - CAMELEER_API_URL: http://cameleer-server:8081 - BASE_PATH: "" - labels: - - traefik.enable=true - - traefik.http.routers.ui.rule=PathPrefix(\`/\`) - - traefik.http.routers.ui.priority=1 - - traefik.http.routers.ui.entrypoints=websecure - - traefik.http.routers.ui.tls=true - - traefik.http.services.ui.loadbalancer.server.port=80 - - traefik.docker.network=cameleer-traefik - networks: - - cameleer-traefik -COMPOSEEOF - - cat >> "$f" << 'COMPOSEEOF' - -volumes: - cameleer-pgdata: - cameleer-chdata: - cameleer-certs: - jars: - -networks: - cameleer: - driver: bridge - cameleer-traefik: - name: cameleer-traefik - driver: bridge - cameleer-apps: - name: cameleer-apps - driver: bridge -COMPOSEEOF - - if [ -n "$MONITORING_NETWORK" ]; then - cat >> "$f" << EOF - ${MONITORING_NETWORK}: - external: true -EOF - fi - - # Generate standalone traefik dynamic config (overrides baked-in redirect) - cat > "$INSTALL_DIR/traefik-dynamic.yml" << 'TRAEFIKEOF' -tls: - stores: - default: - defaultCertificate: - certFile: /certs/cert.pem - keyFile: /certs/key.pem -TRAEFIKEOF - - log_info "Generated docker-compose.yml (standalone)" + log_info "Copied docker-compose templates to $INSTALL_DIR" } # --- Docker operations --- @@ -1700,7 +1284,7 @@ handle_rerun() { load_config_file "$INSTALL_DIR/cameleer.conf" load_env_overrides merge_config - generate_compose_file + copy_templates docker_compose_pull docker_compose_down docker_compose_up @@ -1725,9 +1309,12 @@ handle_rerun() { log_info "Reinstalling..." docker_compose_down 2>/dev/null || true (cd "$INSTALL_DIR" && docker compose -p "${COMPOSE_PROJECT:-cameleer-saas}" down -v 2>/dev/null || true) - rm -f "$INSTALL_DIR/.env" "$INSTALL_DIR/docker-compose.yml" \ + rm -f "$INSTALL_DIR/.env" "$INSTALL_DIR/.env.bak" "$INSTALL_DIR/.env.example" \ + "$INSTALL_DIR/docker-compose.yml" "$INSTALL_DIR/docker-compose.saas.yml" \ + "$INSTALL_DIR/docker-compose.server.yml" "$INSTALL_DIR/docker-compose.tls.yml" \ + "$INSTALL_DIR/docker-compose.monitoring.yml" "$INSTALL_DIR/traefik-dynamic.yml" \ "$INSTALL_DIR/cameleer.conf" "$INSTALL_DIR/credentials.txt" \ - "$INSTALL_DIR/INSTALL.md" "$INSTALL_DIR/.env.bak" + "$INSTALL_DIR/INSTALL.md" rm -rf "$INSTALL_DIR/certs" IS_RERUN=false return @@ -1788,7 +1375,7 @@ main() { # Generate configuration files generate_env_file - generate_compose_file + copy_templates write_config_file # Pull and start diff --git a/installer/templates/.env.example b/installer/templates/.env.example new file mode 100644 index 0000000..4833cda --- /dev/null +++ b/installer/templates/.env.example @@ -0,0 +1,88 @@ +# Cameleer Configuration +# Copy this file to .env and fill in the values. +# The installer generates .env automatically — this file is for reference. + +# ============================================================ +# Compose file assembly (set by installer) +# ============================================================ +# SaaS: docker-compose.yml:docker-compose.saas.yml +# Standalone: docker-compose.yml:docker-compose.server.yml +# Add :docker-compose.tls.yml for custom TLS certificates +# Add :docker-compose.monitoring.yml for external monitoring network +COMPOSE_FILE=docker-compose.yml:docker-compose.saas.yml + +# ============================================================ +# Image version +# ============================================================ +VERSION=latest + +# ============================================================ +# Public access +# ============================================================ +PUBLIC_HOST=localhost +PUBLIC_PROTOCOL=https + +# ============================================================ +# Ports +# ============================================================ +HTTP_PORT=80 +HTTPS_PORT=443 +# Set to 0.0.0.0 to expose Logto admin console externally (default: localhost only) +# LOGTO_CONSOLE_BIND=0.0.0.0 +LOGTO_CONSOLE_PORT=3002 + +# ============================================================ +# PostgreSQL +# ============================================================ +POSTGRES_USER=cameleer +POSTGRES_PASSWORD=CHANGE_ME +# SaaS: cameleer_saas, Standalone: cameleer +POSTGRES_DB=cameleer_saas + +# ============================================================ +# ClickHouse +# ============================================================ +CLICKHOUSE_PASSWORD=CHANGE_ME + +# ============================================================ +# Admin credentials (SaaS mode) +# ============================================================ +SAAS_ADMIN_USER=admin +SAAS_ADMIN_PASS=CHANGE_ME + +# ============================================================ +# Admin credentials (standalone mode) +# ============================================================ +# SERVER_ADMIN_USER=admin +# SERVER_ADMIN_PASS=CHANGE_ME +# BOOTSTRAP_TOKEN=CHANGE_ME + +# ============================================================ +# TLS +# ============================================================ +# Set to 1 to reject unauthorized TLS certificates (production) +NODE_TLS_REJECT=0 +# Custom TLS certificate paths (inside container, set by installer) +# CERT_FILE=/user-certs/cert.pem +# KEY_FILE=/user-certs/key.pem +# CA_FILE=/user-certs/ca.pem + +# ============================================================ +# Docker +# ============================================================ +DOCKER_SOCKET=/var/run/docker.sock +# GID of the docker socket — detected by installer, used for container group_add +DOCKER_GID=0 + +# ============================================================ +# Provisioning images (SaaS mode only) +# ============================================================ +# CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=gitea.siegeln.net/cameleer/cameleer-server:latest +# CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=gitea.siegeln.net/cameleer/cameleer-server-ui:latest + +# ============================================================ +# Monitoring (optional) +# ============================================================ +# External Docker network name for Prometheus scraping. +# Only needed when docker-compose.monitoring.yml is in COMPOSE_FILE. +# MONITORING_NETWORK=prometheus diff --git a/installer/templates/docker-compose.monitoring.yml b/installer/templates/docker-compose.monitoring.yml new file mode 100644 index 0000000..ddbb723 --- /dev/null +++ b/installer/templates/docker-compose.monitoring.yml @@ -0,0 +1,7 @@ +# External monitoring network overlay +# Overrides the noop monitoring bridge with a real external network + +networks: + monitoring: + external: true + name: ${MONITORING_NETWORK:?MONITORING_NETWORK must be set in .env} diff --git a/installer/cameleer/docker-compose.yml b/installer/templates/docker-compose.saas.yml similarity index 72% rename from installer/cameleer/docker-compose.yml rename to installer/templates/docker-compose.saas.yml index 7d30034..a568760 100644 --- a/installer/cameleer/docker-compose.yml +++ b/installer/templates/docker-compose.saas.yml @@ -1,58 +1,7 @@ -# Cameleer SaaS Platform -# Generated by Cameleer installer -- do not edit manually - +# Cameleer SaaS — Logto + management plane +# Loaded in SaaS deployment mode + services: - cameleer-traefik: - image: ${TRAEFIK_IMAGE:-gitea.siegeln.net/cameleer/cameleer-traefik}:${VERSION:-latest} - restart: unless-stopped - ports: - - "${HTTP_PORT:-80}:80" - - "${HTTPS_PORT:-443}:443" - - "${LOGTO_CONSOLE_PORT:-3002}:3002" - environment: - PUBLIC_HOST: ${PUBLIC_HOST:-localhost} - CERT_FILE: ${CERT_FILE:-} - KEY_FILE: ${KEY_FILE:-} - CA_FILE: ${CA_FILE:-} - volumes: - - cameleer-certs:/certs - - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock:ro - networks: - - cameleer - - cameleer-traefik - - cameleer-postgres: - image: ${POSTGRES_IMAGE:-gitea.siegeln.net/cameleer/cameleer-postgres}:${VERSION:-latest} - restart: unless-stopped - environment: - POSTGRES_DB: cameleer_saas - POSTGRES_USER: ${POSTGRES_USER:-cameleer} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - volumes: - - cameleer-pgdata:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-cameleer} -d cameleer_saas"] - interval: 5s - timeout: 5s - retries: 5 - networks: - - cameleer - - cameleer-clickhouse: - image: ${CLICKHOUSE_IMAGE:-gitea.siegeln.net/cameleer/cameleer-clickhouse}:${VERSION:-latest} - restart: unless-stopped - environment: - CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} - volumes: - - cameleer-chdata:/var/lib/clickhouse - healthcheck: - test: ["CMD-SHELL", "clickhouse-client --password $${CLICKHOUSE_PASSWORD} --query 'SELECT 1'"] - interval: 10s - timeout: 5s - retries: 3 - networks: - - cameleer - cameleer-logto: image: ${LOGTO_IMAGE:-gitea.siegeln.net/cameleer/cameleer-logto}:${VERSION:-latest} restart: unless-stopped @@ -104,7 +53,8 @@ services: - cameleer-bootstrapdata:/data networks: - cameleer - + - monitoring + cameleer-saas: image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest} restart: unless-stopped @@ -112,11 +62,14 @@ services: 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 @@ -132,24 +85,22 @@ services: - 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 - group_add: - - "0" - + - monitoring + volumes: - cameleer-pgdata: - cameleer-chdata: - cameleer-certs: cameleer-bootstrapdata: - + networks: - cameleer: - driver: bridge - cameleer-traefik: - name: cameleer-traefik - driver: bridge \ No newline at end of file + monitoring: + name: cameleer-monitoring-noop diff --git a/installer/templates/docker-compose.server.yml b/installer/templates/docker-compose.server.yml new file mode 100644 index 0000000..6f541b1 --- /dev/null +++ b/installer/templates/docker-compose.server.yml @@ -0,0 +1,97 @@ +# Cameleer Server (standalone) +# Loaded in standalone deployment mode + +services: + cameleer-traefik: + volumes: + - ./traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro + + cameleer-postgres: + image: postgres:16-alpine + environment: + POSTGRES_DB: ${POSTGRES_DB:-cameleer} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-cameleer} -d $${POSTGRES_DB:-cameleer}"] + + cameleer-server: + image: ${SERVER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-server}:${VERSION:-latest} + container_name: cameleer-server + restart: unless-stopped + depends_on: + cameleer-postgres: + condition: service_healthy + environment: + CAMELEER_SERVER_TENANT_ID: default + SPRING_DATASOURCE_URL: jdbc:postgresql://cameleer-postgres:5432/${POSTGRES_DB:-cameleer}?currentSchema=tenant_default + SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer} + SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD} + CAMELEER_SERVER_CLICKHOUSE_URL: jdbc:clickhouse://cameleer-clickhouse:8123/cameleer + CAMELEER_SERVER_CLICKHOUSE_USERNAME: default + CAMELEER_SERVER_CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} + CAMELEER_SERVER_SECURITY_BOOTSTRAPTOKEN: ${BOOTSTRAP_TOKEN:?BOOTSTRAP_TOKEN must be set in .env} + CAMELEER_SERVER_SECURITY_UIUSER: ${SERVER_ADMIN_USER:-admin} + CAMELEER_SERVER_SECURITY_UIPASSWORD: ${SERVER_ADMIN_PASS:?SERVER_ADMIN_PASS must be set in .env} + CAMELEER_SERVER_SECURITY_CORSALLOWEDORIGINS: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} + CAMELEER_SERVER_RUNTIME_ENABLED: "true" + CAMELEER_SERVER_RUNTIME_SERVERURL: http://cameleer-server:8081 + CAMELEER_SERVER_RUNTIME_ROUTINGDOMAIN: ${PUBLIC_HOST:-localhost} + CAMELEER_SERVER_RUNTIME_ROUTINGMODE: path + CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH: /data/jars + CAMELEER_SERVER_RUNTIME_DOCKERNETWORK: cameleer-apps + CAMELEER_SERVER_RUNTIME_JARDOCKERVOLUME: cameleer-jars + CAMELEER_SERVER_RUNTIME_BASEIMAGE: gitea.siegeln.net/cameleer/cameleer-runtime-base:${VERSION:-latest} + labels: + - traefik.enable=true + - traefik.http.routers.server-api.rule=PathPrefix(`/api`) + - traefik.http.routers.server-api.entrypoints=websecure + - traefik.http.routers.server-api.tls=true + - traefik.http.services.server-api.loadbalancer.server.port=8081 + - traefik.docker.network=cameleer-traefik + healthcheck: + test: ["CMD-SHELL", "curl -sf http://localhost:8081/api/v1/health || exit 1"] + interval: 10s + timeout: 5s + retries: 30 + start_period: 30s + volumes: + - jars:/data/jars + - cameleer-certs:/certs:ro + - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock + group_add: + - "${DOCKER_GID:-0}" + networks: + - cameleer + - cameleer-traefik + - cameleer-apps + - monitoring + + cameleer-server-ui: + image: ${SERVER_UI_IMAGE:-gitea.siegeln.net/cameleer/cameleer-server-ui}:${VERSION:-latest} + restart: unless-stopped + depends_on: + cameleer-server: + condition: service_healthy + environment: + CAMELEER_API_URL: http://cameleer-server:8081 + BASE_PATH: "" + labels: + - traefik.enable=true + - traefik.http.routers.ui.rule=PathPrefix(`/`) + - traefik.http.routers.ui.priority=1 + - traefik.http.routers.ui.entrypoints=websecure + - traefik.http.routers.ui.tls=true + - traefik.http.services.ui.loadbalancer.server.port=80 + - traefik.docker.network=cameleer-traefik + networks: + - cameleer-traefik + - monitoring + +volumes: + jars: + +networks: + cameleer-apps: + name: cameleer-apps + driver: bridge + monitoring: + name: cameleer-monitoring-noop diff --git a/installer/templates/docker-compose.tls.yml b/installer/templates/docker-compose.tls.yml new file mode 100644 index 0000000..489d08c --- /dev/null +++ b/installer/templates/docker-compose.tls.yml @@ -0,0 +1,7 @@ +# Custom TLS certificates overlay +# Adds user-supplied certificate volume to traefik + +services: + cameleer-traefik: + volumes: + - ./certs:/user-certs:ro diff --git a/installer/templates/docker-compose.yml b/installer/templates/docker-compose.yml new file mode 100644 index 0000000..99a93b4 --- /dev/null +++ b/installer/templates/docker-compose.yml @@ -0,0 +1,79 @@ +# Cameleer Infrastructure +# Shared base — always loaded. Mode-specific services in separate compose files. + +services: + cameleer-traefik: + image: ${TRAEFIK_IMAGE:-gitea.siegeln.net/cameleer/cameleer-traefik}:${VERSION:-latest} + restart: unless-stopped + ports: + - "${HTTP_PORT:-80}:80" + - "${HTTPS_PORT:-443}:443" + - "${LOGTO_CONSOLE_BIND:-127.0.0.1}:${LOGTO_CONSOLE_PORT:-3002}:3002" + environment: + PUBLIC_HOST: ${PUBLIC_HOST:-localhost} + CERT_FILE: ${CERT_FILE:-} + KEY_FILE: ${KEY_FILE:-} + CA_FILE: ${CA_FILE:-} + volumes: + - cameleer-certs:/certs + - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock:ro + labels: + - "prometheus.io/scrape=true" + - "prometheus.io/port=8082" + - "prometheus.io/path=/metrics" + networks: + - cameleer + - cameleer-traefik + - monitoring + + cameleer-postgres: + image: ${POSTGRES_IMAGE:-gitea.siegeln.net/cameleer/cameleer-postgres}:${VERSION:-latest} + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB:-cameleer_saas} + POSTGRES_USER: ${POSTGRES_USER:-cameleer} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set in .env} + volumes: + - cameleer-pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-cameleer} -d $${POSTGRES_DB:-cameleer_saas}"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - cameleer + - monitoring + + cameleer-clickhouse: + image: ${CLICKHOUSE_IMAGE:-gitea.siegeln.net/cameleer/cameleer-clickhouse}:${VERSION:-latest} + restart: unless-stopped + environment: + CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:?CLICKHOUSE_PASSWORD must be set in .env} + volumes: + - cameleer-chdata:/var/lib/clickhouse + healthcheck: + test: ["CMD-SHELL", "clickhouse-client --password $${CLICKHOUSE_PASSWORD} --query 'SELECT 1'"] + interval: 10s + timeout: 5s + retries: 3 + labels: + - "prometheus.io/scrape=true" + - "prometheus.io/port=9363" + - "prometheus.io/path=/metrics" + networks: + - cameleer + - monitoring + +volumes: + cameleer-pgdata: + cameleer-chdata: + cameleer-certs: + +networks: + cameleer: + driver: bridge + cameleer-traefik: + name: cameleer-traefik + driver: bridge + monitoring: + name: cameleer-monitoring-noop diff --git a/installer/templates/traefik-dynamic.yml b/installer/templates/traefik-dynamic.yml new file mode 100644 index 0000000..b2a8787 --- /dev/null +++ b/installer/templates/traefik-dynamic.yml @@ -0,0 +1,6 @@ +tls: + stores: + default: + defaultCertificate: + certFile: /certs/cert.pem + keyFile: /certs/key.pem