From f5165add13620ae844a312706d03586c29543a62 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Mon, 13 Apr 2026 16:19:29 +0200 Subject: [PATCH] feat: consolidate docker-compose.yml for baked-in images Remove all bind-mounted config files and init containers. Services reduced from 7 to 5. All configuration via environment variables. --- .env.example | 46 ++++++--- docker-compose.yml | 192 +++++++++++------------------------ docker/clickhouse-config.xml | 9 -- docker/clickhouse-init.sql | 1 - docker/clickhouse-users.xml | 10 -- docker/init-databases.sh | 9 -- docker/traefik-dynamic.yml | 24 ----- docker/vendor-seed.sh | 135 ------------------------ traefik.yml | 24 ----- 9 files changed, 87 insertions(+), 363 deletions(-) delete mode 100644 docker/clickhouse-config.xml delete mode 100644 docker/clickhouse-init.sql delete mode 100644 docker/clickhouse-users.xml delete mode 100644 docker/init-databases.sh delete mode 100644 docker/traefik-dynamic.yml delete mode 100644 docker/vendor-seed.sh delete mode 100644 traefik.yml diff --git a/.env.example b/.env.example index 9498c75..0fc6113 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,18 @@ -# Cameleer SaaS Environment Variables -# Copy to .env and fill in values +# Cameleer SaaS — Environment Configuration +# Copy to .env and fill in values for production -# Application version +# Image version VERSION=latest +# Public access +PUBLIC_HOST=localhost +PUBLIC_PROTOCOL=https + +# Ports +HTTP_PORT=80 +HTTPS_PORT=443 +LOGTO_CONSOLE_PORT=3002 + # PostgreSQL POSTGRES_USER=cameleer POSTGRES_PASSWORD=change_me_in_production @@ -12,19 +21,24 @@ POSTGRES_DB=cameleer_saas # ClickHouse CLICKHOUSE_PASSWORD=change_me_in_production -# Public domain (used by Traefik, Logto, and SaaS provisioning) -PUBLIC_HOST=localhost -PUBLIC_PROTOCOL=https +# Admin user (created by bootstrap) +SAAS_ADMIN_USER=admin +SAAS_ADMIN_PASS=change_me_in_production -# Logto Identity Provider (infrastructure — used by logto-bootstrap init container) -LOGTO_ENDPOINT=http://logto:3001 -LOGTO_DB_PASSWORD=change_me_in_production +# TLS (leave empty for self-signed) +# NODE_TLS_REJECT=0 # Set to 1 when using real certificates +# CERT_FILE= +# KEY_FILE= +# CA_FILE= -# SaaS Identity (Logto M2M credentials — usually auto-provisioned by bootstrap) -CAMELEER_SAAS_IDENTITY_M2MCLIENTID= -CAMELEER_SAAS_IDENTITY_M2MCLIENTSECRET= -CAMELEER_SAAS_IDENTITY_SPACLIENTID= +# Vendor account (optional) +VENDOR_SEED_ENABLED=false +# VENDOR_USER=vendor +# VENDOR_PASS=change_me -# SaaS Provisioning -CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=gitea.siegeln.net/cameleer/cameleer3-server:latest -CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=gitea.siegeln.net/cameleer/cameleer3-server-ui:latest +# Docker images (override for custom registries) +# TRAEFIK_IMAGE=gitea.siegeln.net/cameleer/cameleer-traefik +# POSTGRES_IMAGE=gitea.siegeln.net/cameleer/cameleer-postgres +# CLICKHOUSE_IMAGE=gitea.siegeln.net/cameleer/cameleer-clickhouse +# LOGTO_IMAGE=gitea.siegeln.net/cameleer/cameleer-logto +# CAMELEER_IMAGE=gitea.siegeln.net/cameleer/cameleer-saas diff --git a/docker-compose.yml b/docker-compose.yml index fecf6f0..751bffe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,58 +1,11 @@ services: - traefik-certs: - image: alpine:latest - restart: "no" - entrypoint: ["sh", "-c"] - command: - - | - if [ -f /certs/cert.pem ]; then - echo "Certs already exist, skipping" - exit 0 - fi - - # Option 1: User-supplied certificate - if [ -n "$$CERT_FILE" ] && [ -n "$$KEY_FILE" ]; then - apk add --no-cache openssl >/dev/null 2>&1 - cp "$$CERT_FILE" /certs/cert.pem - cp "$$KEY_FILE" /certs/key.pem - if [ -n "$$CA_FILE" ]; then - cp "$$CA_FILE" /certs/ca.pem - fi - # Validate: key matches cert - CERT_MOD=$$(openssl x509 -noout -modulus -in /certs/cert.pem 2>/dev/null | md5sum) - KEY_MOD=$$(openssl rsa -noout -modulus -in /certs/key.pem 2>/dev/null | md5sum) - if [ "$$CERT_MOD" != "$$KEY_MOD" ]; then - echo "ERROR: Certificate and key do not match!" - rm -f /certs/cert.pem /certs/key.pem /certs/ca.pem - exit 1 - fi - SELF_SIGNED=false - echo "Installed user-supplied certificate" - else - # Option 2: Generate self-signed - 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" - SELF_SIGNED=true - echo "Generated self-signed cert for $$PUBLIC_HOST" - fi - - # Write metadata for SaaS app to seed DB - SUBJECT=$$(openssl x509 -noout -subject -in /certs/cert.pem 2>/dev/null | sed 's/subject=//') - FINGERPRINT=$$(openssl x509 -noout -fingerprint -sha256 -in /certs/cert.pem 2>/dev/null | sed 's/.*=//') - NOT_BEFORE=$$(openssl x509 -noout -startdate -in /certs/cert.pem 2>/dev/null | sed 's/notBefore=//') - NOT_AFTER=$$(openssl x509 -noout -enddate -in /certs/cert.pem 2>/dev/null | sed 's/notAfter=//') - HAS_CA=false - [ -f /certs/ca.pem ] && HAS_CA=true - cat > /certs/meta.json </dev/null || true + 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:-} @@ -60,28 +13,13 @@ services: CA_FILE: ${CA_FILE:-} 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 - cameleer-traefik postgres: - image: postgres:16-alpine + image: ${POSTGRES_IMAGE:-gitea.siegeln.net/cameleer/cameleer-postgres}:${VERSION:-latest} restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB:-cameleer_saas} @@ -89,7 +27,6 @@ services: 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 @@ -98,54 +35,37 @@ services: networks: - cameleer + clickhouse: + image: ${CLICKHOUSE_IMAGE:-gitea.siegeln.net/cameleer/cameleer-clickhouse}:${VERSION:-latest} + restart: unless-stopped + environment: + CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-cameleer_ch} + volumes: + - chdata:/var/lib/clickhouse + healthcheck: + test: ["CMD-SHELL", "clickhouse-client --password ${CLICKHOUSE_PASSWORD:-cameleer_ch} --query 'SELECT 1'"] + interval: 10s + timeout: 5s + retries: 3 + labels: + - prometheus.scrape=true + - prometheus.path=/metrics + - prometheus.port=9363 + 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 + ADMIN_ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}:${LOGTO_CONSOLE_PORT:-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.service=logto - - 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 - restart: "no" - entrypoint: ["sh", "/scripts/logto-bootstrap.sh"] - environment: + 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} @@ -157,8 +77,34 @@ services: PG_DB_SAAS: ${POSTGRES_DB:-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 + - 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 volumes: - - ./docker/logto-bootstrap.sh:/scripts/logto-bootstrap.sh:ro - bootstrapdata:/data networks: - cameleer @@ -167,10 +113,8 @@ services: image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest} restart: unless-stopped depends_on: - postgres: + logto: condition: service_healthy - logto-bootstrap: - condition: service_completed_successfully volumes: - bootstrapdata:/data/bootstrap:ro - certs:/certs @@ -193,28 +137,6 @@ services: networks: - cameleer - clickhouse: - image: clickhouse/clickhouse-server:latest - restart: unless-stopped - environment: - CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-cameleer_ch} - 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 - - ./docker/clickhouse-config.xml:/etc/clickhouse-server/config.d/prometheus.xml:ro - healthcheck: - test: ["CMD-SHELL", "clickhouse-client --password ${CLICKHOUSE_PASSWORD:-cameleer_ch} --query 'SELECT 1'"] - interval: 10s - timeout: 5s - retries: 3 - labels: - - prometheus.scrape=true - - prometheus.path=/metrics - - prometheus.port=9363 - networks: - - cameleer - networks: cameleer: driver: bridge diff --git a/docker/clickhouse-config.xml b/docker/clickhouse-config.xml deleted file mode 100644 index 446b7ef..0000000 --- a/docker/clickhouse-config.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - /metrics - 9363 - true - true - true - - diff --git a/docker/clickhouse-init.sql b/docker/clickhouse-init.sql deleted file mode 100644 index f745780..0000000 --- a/docker/clickhouse-init.sql +++ /dev/null @@ -1 +0,0 @@ -CREATE DATABASE IF NOT EXISTS cameleer; diff --git a/docker/clickhouse-users.xml b/docker/clickhouse-users.xml deleted file mode 100644 index af57303..0000000 --- a/docker/clickhouse-users.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - ::/0 - - - - diff --git a/docker/init-databases.sh b/docker/init-databases.sh deleted file mode 100644 index 51829de..0000000 --- a/docker/init-databases.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e - -psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL - CREATE DATABASE logto; - CREATE DATABASE cameleer3; - GRANT ALL PRIVILEGES ON DATABASE logto TO $POSTGRES_USER; - GRANT ALL PRIVILEGES ON DATABASE cameleer3 TO $POSTGRES_USER; -EOSQL diff --git a/docker/traefik-dynamic.yml b/docker/traefik-dynamic.yml deleted file mode 100644 index d20cd3e..0000000 --- a/docker/traefik-dynamic.yml +++ /dev/null @@ -1,24 +0,0 @@ -http: - routers: - root-redirect: - rule: "Path(`/`)" - priority: 100 - entryPoints: - - websecure - tls: {} - middlewares: - - root-to-platform - service: saas@docker - middlewares: - root-to-platform: - redirectRegex: - regex: "^(https?://[^/]+)/?$" - replacement: "${1}/platform/" - permanent: false - -tls: - stores: - default: - defaultCertificate: - certFile: /etc/traefik/certs/cert.pem - keyFile: /etc/traefik/certs/key.pem diff --git a/docker/vendor-seed.sh b/docker/vendor-seed.sh deleted file mode 100644 index 64025fa..0000000 --- a/docker/vendor-seed.sh +++ /dev/null @@ -1,135 +0,0 @@ -#!/bin/sh -set -e - -# Cameleer SaaS — Vendor Seed Script -# Creates the saas-vendor global role and vendor user. -# Run ONCE on the hosted SaaS environment AFTER standard bootstrap. -# NOT part of docker-compose.yml — invoked manually or by CI. - -LOGTO_ENDPOINT="${LOGTO_ENDPOINT:-http://logto:3001}" -MGMT_API_RESOURCE="https://default.logto.app/api" -API_RESOURCE_INDICATOR="https://api.cameleer.local" -PG_HOST="${PG_HOST:-postgres}" -PG_USER="${PG_USER:-cameleer}" -PG_DB_LOGTO="logto" - -# Vendor credentials (override via env vars) -VENDOR_USER="${VENDOR_USER:-vendor}" -VENDOR_PASS="${VENDOR_PASS:-vendor}" -VENDOR_NAME="${VENDOR_NAME:-SaaS Vendor}" - -log() { echo "[vendor-seed] $1"; } -pgpass() { PGPASSWORD="${PG_PASSWORD:-cameleer_dev}"; export PGPASSWORD; } - -# Install jq + curl -apk add --no-cache jq curl >/dev/null 2>&1 - -# ============================================================ -# Get Management API token -# ============================================================ - -log "Reading M2M credentials from bootstrap file..." -BOOTSTRAP_FILE="/data/logto-bootstrap.json" -if [ ! -f "$BOOTSTRAP_FILE" ]; then - log "ERROR: Bootstrap file not found at $BOOTSTRAP_FILE — run standard bootstrap first" - exit 1 -fi - -M2M_ID=$(jq -r '.m2mClientId' "$BOOTSTRAP_FILE") -M2M_SECRET=$(jq -r '.m2mClientSecret' "$BOOTSTRAP_FILE") - -if [ -z "$M2M_ID" ] || [ "$M2M_ID" = "null" ] || [ -z "$M2M_SECRET" ] || [ "$M2M_SECRET" = "null" ]; then - log "ERROR: M2M credentials not found in bootstrap file" - exit 1 -fi - -log "Getting Management API token..." -TOKEN_RESPONSE=$(curl -s -X POST "${LOGTO_ENDPOINT}/oidc/token" \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "grant_type=client_credentials&client_id=${M2M_ID}&client_secret=${M2M_SECRET}&resource=${MGMT_API_RESOURCE}&scope=all") -TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token' 2>/dev/null) -[ -z "$TOKEN" ] || [ "$TOKEN" = "null" ] && { log "ERROR: Failed to get token"; exit 1; } -log "Got Management API token." - -api_get() { curl -s -H "Authorization: Bearer $TOKEN" "${LOGTO_ENDPOINT}${1}" 2>/dev/null || echo "[]"; } -api_post() { curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "$2" "${LOGTO_ENDPOINT}${1}" 2>/dev/null || true; } - -# ============================================================ -# Create saas-vendor global role -# ============================================================ - -log "Checking for saas-vendor role..." -EXISTING_ROLES=$(api_get "/api/roles") -VENDOR_ROLE_ID=$(echo "$EXISTING_ROLES" | jq -r '.[] | select(.name == "saas-vendor" and .type == "User") | .id') - -if [ -n "$VENDOR_ROLE_ID" ]; then - log "saas-vendor role exists: $VENDOR_ROLE_ID" -else - # Collect all API resource scope IDs - EXISTING_RESOURCES=$(api_get "/api/resources") - API_RESOURCE_ID=$(echo "$EXISTING_RESOURCES" | jq -r ".[] | select(.indicator == \"$API_RESOURCE_INDICATOR\") | .id") - ALL_SCOPE_IDS=$(api_get "/api/resources/$API_RESOURCE_ID/scopes" | jq '[.[].id]') - - log "Creating saas-vendor role with all scopes..." - VENDOR_ROLE_RESPONSE=$(api_post "/api/roles" "{ - \"name\": \"saas-vendor\", - \"description\": \"SaaS vendor — full platform control across all tenants\", - \"type\": \"User\", - \"scopeIds\": $ALL_SCOPE_IDS - }") - VENDOR_ROLE_ID=$(echo "$VENDOR_ROLE_RESPONSE" | jq -r '.id') - log "Created saas-vendor role: $VENDOR_ROLE_ID" -fi - -# ============================================================ -# Create vendor user -# ============================================================ - -log "Checking for vendor user '$VENDOR_USER'..." -VENDOR_USER_ID=$(api_get "/api/users?search=$VENDOR_USER" | jq -r ".[] | select(.username == \"$VENDOR_USER\") | .id") - -if [ -n "$VENDOR_USER_ID" ]; then - log "Vendor user exists: $VENDOR_USER_ID" -else - log "Creating vendor user '$VENDOR_USER'..." - VENDOR_RESPONSE=$(api_post "/api/users" "{ - \"username\": \"$VENDOR_USER\", - \"password\": \"$VENDOR_PASS\", - \"name\": \"$VENDOR_NAME\" - }") - VENDOR_USER_ID=$(echo "$VENDOR_RESPONSE" | jq -r '.id') - log "Created vendor user: $VENDOR_USER_ID" -fi - -# Assign saas-vendor role -if [ -n "$VENDOR_ROLE_ID" ] && [ "$VENDOR_ROLE_ID" != "null" ]; then - api_post "/api/users/$VENDOR_USER_ID/roles" "{\"roleIds\": [\"$VENDOR_ROLE_ID\"]}" >/dev/null 2>&1 - log "Assigned saas-vendor role." -fi - -# ============================================================ -# Add vendor to all existing organizations with owner role -# ============================================================ - -log "Adding vendor to all organizations..." -ORG_OWNER_ROLE_ID=$(api_get "/api/organization-roles" | jq -r '.[] | select(.name == "owner") | .id') -ORGS=$(api_get "/api/organizations") -ORG_COUNT=$(echo "$ORGS" | jq 'length') - -for i in $(seq 0 $((ORG_COUNT - 1))); do - ORG_ID=$(echo "$ORGS" | jq -r ".[$i].id") - ORG_NAME=$(echo "$ORGS" | jq -r ".[$i].name") - api_post "/api/organizations/$ORG_ID/users" "{\"userIds\": [\"$VENDOR_USER_ID\"]}" >/dev/null 2>&1 - if [ -n "$ORG_OWNER_ROLE_ID" ] && [ "$ORG_OWNER_ROLE_ID" != "null" ]; then - curl -s -X PUT -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ - -d "{\"organizationRoleIds\": [\"$ORG_OWNER_ROLE_ID\"]}" \ - "${LOGTO_ENDPOINT}/api/organizations/$ORG_ID/users/$VENDOR_USER_ID/roles" >/dev/null 2>&1 - fi - log " Added to org '$ORG_NAME' ($ORG_ID) with owner role." -done - -log "" -log "=== Vendor seed complete! ===" -log " Vendor user: $VENDOR_USER / $VENDOR_PASS" -log " Role: saas-vendor (global) + owner (in all orgs)" -log " This user has platform:admin scope and cross-tenant access." diff --git a/traefik.yml b/traefik.yml deleted file mode 100644 index ee265b1..0000000 --- a/traefik.yml +++ /dev/null @@ -1,24 +0,0 @@ -api: - dashboard: false - -entryPoints: - web: - address: ":80" - http: - redirections: - entryPoint: - to: websecure - scheme: https - websecure: - address: ":443" - admin-console: - address: ":3002" - -providers: - docker: - endpoint: "unix:///var/run/docker.sock" - exposedByDefault: false - network: cameleer - file: - filename: /etc/traefik/dynamic.yml -