From 5e5bc97bf548f2469a889a0f74f59b766b787605 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Mon, 13 Apr 2026 16:30:32 +0200 Subject: [PATCH] feat(installer): add .env and docker-compose.yml generation --- installer/install.sh | 324 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) diff --git a/installer/install.sh b/installer/install.sh index ac7dcbb..ecc3e7e 100644 --- a/installer/install.sh +++ b/installer/install.sh @@ -553,3 +553,327 @@ generate_passwords() { log_info "Generated vendor password." fi } + +# --- File generation --- + +copy_certs() { + local certs_dir="$INSTALL_DIR/certs" + mkdir -p "$certs_dir" + cp "$CERT_FILE" "$certs_dir/cert.pem" + cp "$KEY_FILE" "$certs_dir/key.pem" + if [ -n "$CA_FILE" ]; then + cp "$CA_FILE" "$certs_dir/ca.pem" + fi + log_info "Copied TLS certificates to $certs_dir/" +} + +generate_env_file() { + local f="$INSTALL_DIR/.env" + cat > "$f" << EOF +# Cameleer SaaS Configuration +# Generated by installer v${CAMELEER_INSTALLER_VERSION} on $(date -u '+%Y-%m-%d %H:%M:%S UTC') + +# Image version +VERSION=${VERSION} + +# Public access +PUBLIC_HOST=${PUBLIC_HOST} +PUBLIC_PROTOCOL=${PUBLIC_PROTOCOL} + +# Ports +HTTP_PORT=${HTTP_PORT} +HTTPS_PORT=${HTTPS_PORT} +LOGTO_CONSOLE_PORT=${LOGTO_CONSOLE_PORT} + +# PostgreSQL +POSTGRES_USER=cameleer +POSTGRES_PASSWORD=${POSTGRES_PASSWORD} +POSTGRES_DB=cameleer_saas + +# ClickHouse +CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD} + +# Admin user +SAAS_ADMIN_USER=${ADMIN_USER} +SAAS_ADMIN_PASS=${ADMIN_PASS} + +# TLS +NODE_TLS_REJECT=${NODE_TLS_REJECT} +EOF + + if [ "$TLS_MODE" = "custom" ]; then + cat >> "$f" << 'EOF' +CERT_FILE=/user-certs/cert.pem +KEY_FILE=/user-certs/key.pem +EOF + if [ -n "$CA_FILE" ]; then + echo "CA_FILE=/user-certs/ca.pem" >> "$f" + fi + fi + + cat >> "$f" << EOF + +# Vendor account +VENDOR_SEED_ENABLED=${VENDOR_ENABLED} +VENDOR_USER=${VENDOR_USER} +VENDOR_PASS=${VENDOR_PASS:-} + +# Docker +DOCKER_SOCKET=${DOCKER_SOCKET} + +# Provisioning images +CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=${REGISTRY}/cameleer3-server:${VERSION} +CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=${REGISTRY}/cameleer3-server-ui:${VERSION} +EOF + + log_info "Generated .env" + cp "$f" "$INSTALL_DIR/.env.bak" +} + +generate_compose_file() { + local f="$INSTALL_DIR/docker-compose.yml" + : > "$f" + + cat >> "$f" << 'EOF' +# Cameleer SaaS Platform +# Generated by Cameleer installer — do not edit manually + +services: + 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 + fi + + cat >> "$f" << 'EOF' + environment: + PUBLIC_HOST: ${PUBLIC_HOST:-localhost} + CERT_FILE: ${CERT_FILE:-} + KEY_FILE: ${KEY_FILE:-} + CA_FILE: ${CA_FILE:-} + volumes: + - certs:/certs + - ${DOCKER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock:ro +EOF + + if [ "$TLS_MODE" = "custom" ]; then + cat >> "$f" << 'EOF' + - ./certs:/user-certs:ro +EOF + 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 + fi + + cat >> "$f" << 'EOF' + + 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: + - 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' + + clickhouse: + image: ${CLICKHOUSE_IMAGE:-gitea.siegeln.net/cameleer/cameleer-clickhouse}:${VERSION:-latest} + restart: unless-stopped + environment: + CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} + volumes: + - 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' + + logto: + image: ${LOGTO_IMAGE:-gitea.siegeln.net/cameleer/cameleer-logto}:${VERSION:-latest} + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + DB_URL: postgres://${POSTGRES_USER:-cameleer}:${POSTGRES_PASSWORD}@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://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} + PG_DB_SAAS: cameleer_saas + SAAS_ADMIN_USER: ${SAAS_ADMIN_USER:-admin} + SAAS_ADMIN_PASS: ${SAAS_ADMIN_PASS:-admin} + VENDOR_SEED_ENABLED: "${VENDOR_SEED_ENABLED:-false}" + VENDOR_USER: ${VENDOR_USER:-vendor} + VENDOR_PASS: ${VENDOR_PASS:-vendor} + 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.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.service=logto + - traefik.http.routers.logto.middlewares=logto-cors + - "traefik.http.middlewares.logto-cors.headers.accessControlAllowOriginList=${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}:${LOGTO_CONSOLE_PORT:-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 +EOF + + if [ "$LOGTO_CONSOLE_EXPOSED" = "true" ]; then + cat >> "$f" << 'EOF' + - 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 +EOF + fi + + cat >> "$f" << 'EOF' + volumes: + - bootstrapdata:/data + networks: + - cameleer + + cameleer-saas: + image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest} + restart: unless-stopped + depends_on: + logto: + condition: service_healthy + environment: + SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/cameleer_saas + SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer} + SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD} + CAMELEER_SAAS_IDENTITY_LOGTOENDPOINT: http://logto:3001 + CAMELEER_SAAS_IDENTITY_LOGTOPUBLICENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} + CAMELEER_SAAS_PROVISIONING_PUBLICPROTOCOL: ${PUBLIC_PROTOCOL:-https} + CAMELEER_SAAS_PROVISIONING_PUBLICHOST: ${PUBLIC_HOST:-localhost} + CAMELEER_SAAS_PROVISIONING_NETWORKNAME: ${COMPOSE_PROJECT_NAME:-cameleer-saas}_cameleer + CAMELEER_SAAS_PROVISIONING_TRAEFIKNETWORK: cameleer-traefik + CAMELEER_SAAS_PROVISIONING_SERVERIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERIMAGE:-gitea.siegeln.net/cameleer/cameleer3-server:latest} + CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE: ${CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE:-gitea.siegeln.net/cameleer/cameleer3-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: + - bootstrapdata:/data/bootstrap:ro + - 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 + + cat >> "$f" << 'EOF' + group_add: + - "0" + +volumes: + pgdata: + chdata: + certs: + 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" +}