diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9ad5966 --- /dev/null +++ b/.env.example @@ -0,0 +1,25 @@ +# Cameleer SaaS Environment Variables +# Copy to .env and fill in values + +# Application version +VERSION=latest + +# PostgreSQL +POSTGRES_USER=cameleer +POSTGRES_PASSWORD=change_me_in_production +POSTGRES_DB=cameleer_saas + +# Logto Identity Provider +LOGTO_ENDPOINT=http://logto:3001 +LOGTO_ISSUER_URI=http://logto:3001/oidc +LOGTO_JWK_SET_URI=http://logto:3001/oidc/jwks +LOGTO_DB_PASSWORD=change_me_in_production +LOGTO_M2M_CLIENT_ID= +LOGTO_M2M_CLIENT_SECRET= + +# Ed25519 Keys (mount PEM files) +CAMELEER_JWT_PRIVATE_KEY_PATH=/etc/cameleer/keys/ed25519.key +CAMELEER_JWT_PUBLIC_KEY_PATH=/etc/cameleer/keys/ed25519.pub + +# Domain (for Traefik TLS) +DOMAIN=localhost diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..574d1b4 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,21 @@ +# Development overrides: exposes ports for direct access +# Usage: docker compose -f docker-compose.yml -f docker-compose.dev.yml up +services: + postgres: + ports: + - "5432:5432" + + logto: + ports: + - "3001:3001" + - "3002:3002" + + cameleer-saas: + ports: + - "8080:8080" + environment: + SPRING_PROFILES_ACTIVE: dev + + clickhouse: + ports: + - "8123:8123" diff --git a/docker-compose.yml b/docker-compose.yml index e6ea78e..089ee5a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,122 @@ services: + traefik: + image: traefik:v3 + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./traefik.yml:/etc/traefik/traefik.yml:ro + - acme:/etc/traefik/acme + networks: + - cameleer + postgres: image: postgres:16-alpine + restart: unless-stopped environment: - POSTGRES_DB: cameleer_saas - POSTGRES_USER: cameleer - POSTGRES_PASSWORD: cameleer_dev - ports: - - "5432:5432" + 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}"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - cameleer + + logto: + image: ghcr.io/logto-io/logto: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: ${LOGTO_ENDPOINT:-http://localhost:3001} + ADMIN_ENDPOINT: ${LOGTO_ADMIN_ENDPOINT:-http://localhost:3002} + TRUST_PROXY_HEADER: 1 + labels: + - traefik.enable=true + - traefik.http.routers.logto.rule=PathPrefix(`/oidc`) || PathPrefix(`/interaction`) + - traefik.http.services.logto.loadbalancer.server.port=3001 + networks: + - cameleer + + cameleer-saas: + image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest} + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./keys:/etc/cameleer/keys: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_ISSUER_URI: ${LOGTO_ISSUER_URI:-http://logto:3001/oidc} + LOGTO_JWK_SET_URI: ${LOGTO_JWK_SET_URI:-http://logto:3001/oidc/jwks} + LOGTO_M2M_CLIENT_ID: ${LOGTO_M2M_CLIENT_ID:-} + LOGTO_M2M_CLIENT_SECRET: ${LOGTO_M2M_CLIENT_SECRET:-} + CAMELEER_JWT_PRIVATE_KEY_PATH: ${CAMELEER_JWT_PRIVATE_KEY_PATH:-} + CAMELEER_JWT_PUBLIC_KEY_PATH: ${CAMELEER_JWT_PUBLIC_KEY_PATH:-} + labels: + - traefik.enable=true + - traefik.http.routers.api.rule=PathPrefix(`/api`) + - traefik.http.services.api.loadbalancer.server.port=8080 + - traefik.http.routers.forwardauth.rule=Path(`/auth/verify`) + - traefik.http.services.forwardauth.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/${POSTGRES_DB:-cameleer_saas} + CLICKHOUSE_URL: jdbc:clickhouse://clickhouse:8123/cameleer + labels: + - traefik.enable=true + - traefik.http.routers.observe.rule=PathPrefix(`/observe`) + - traefik.http.routers.observe.middlewares=forward-auth + - traefik.http.middlewares.forward-auth.forwardauth.address=http://cameleer-saas:8080/auth/verify + - traefik.http.middlewares.forward-auth.forwardauth.authResponseHeaders=X-Tenant-Id,X-User-Id,X-User-Email + - traefik.http.services.observe.loadbalancer.server.port=8080 + networks: + - cameleer + + clickhouse: + image: clickhouse/clickhouse-server:latest + restart: unless-stopped + volumes: + - chdata:/var/lib/clickhouse + healthcheck: + test: ["CMD-SHELL", "clickhouse-client --query 'SELECT 1'"] + interval: 10s + timeout: 5s + retries: 3 + networks: + - cameleer + +networks: + cameleer: + driver: bridge volumes: pgdata: + chdata: + acme: diff --git a/docker/init-databases.sh b/docker/init-databases.sh new file mode 100644 index 0000000..1c0b212 --- /dev/null +++ b/docker/init-databases.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL + CREATE DATABASE logto; + GRANT ALL PRIVILEGES ON DATABASE logto TO $POSTGRES_USER; +EOSQL diff --git a/traefik.yml b/traefik.yml new file mode 100644 index 0000000..e98a95f --- /dev/null +++ b/traefik.yml @@ -0,0 +1,14 @@ +api: + dashboard: false + +entryPoints: + web: + address: ":80" + websecure: + address: ":443" + +providers: + docker: + endpoint: "unix:///var/run/docker.sock" + exposedByDefault: false + network: cameleer