b40eb3e88c
Backend: - Add cloudflare_tunnel.py service for creating/deleting tunnels via Cloudflare API - Add public_url and tunnel_id fields to ToolInstance model - Update start_instance to create Cloudflare tunnel after container starts - Update stop_instance to delete tunnel before stopping container - Update delete_instance to cleanup tunnel before deletion - Update restart_instance to recreate tunnel on restart - Create Alembic migration 0011 for tunnel fields - Add Cloudflare config settings (API token, zone ID, account ID, base domain) Infrastructure: - Add cloudflared service to docker-compose.traefik.yml - Mount shared cloudflared_config volume between API and cloudflared containers - Add Cloudflare env vars to API service Frontend: - Update instance Open button to handle both full URLs and proxy paths The instance URL is now set to the Cloudflare tunnel public URL when available, falling back to the API proxy path if tunnel creation fails.
149 lines
4.7 KiB
YAML
149 lines
4.7 KiB
YAML
# Docker Compose for deployment behind an existing Traefik reverse proxy
|
|
# All domains and the proxy web name are configurable via environment variables
|
|
|
|
services:
|
|
# PostgreSQL Database
|
|
postgres:
|
|
image: postgres:15-alpine
|
|
container_name: hq-postgres
|
|
environment:
|
|
POSTGRES_USER: ${POSTGRES_USER:-headquarter}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-headquarter}
|
|
POSTGRES_DB: ${POSTGRES_DB:-headquarter}
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-headquarter} -d ${POSTGRES_DB:-headquarter}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 10s
|
|
networks:
|
|
- backend
|
|
restart: unless-stopped
|
|
|
|
# Redis Cache
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: hq-redis
|
|
command: redis-server --appendonly yes
|
|
volumes:
|
|
- redis_data:/data
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 5s
|
|
networks:
|
|
- backend
|
|
restart: unless-stopped
|
|
|
|
# Web Frontend
|
|
headquarter-frontend:
|
|
build:
|
|
context: ./apps/web
|
|
dockerfile: Dockerfile
|
|
args:
|
|
VITE_API_BASE_URL: ${API_PUBLIC_URL:-https://${API_DOMAIN}}
|
|
VITE_APP_URL: ${WEB_PUBLIC_URL:-https://${WEB_DOMAIN}}
|
|
container_name: hq-web
|
|
environment:
|
|
VITE_API_BASE_URL: ${API_PUBLIC_URL:-https://${API_DOMAIN}}
|
|
VITE_APP_URL: ${WEB_PUBLIC_URL:-https://${WEB_DOMAIN}}
|
|
depends_on:
|
|
- headquarter-api
|
|
networks:
|
|
- backend
|
|
- traefik
|
|
restart: unless-stopped
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.headquarter-frontend.rule=Host(`${WEB_DOMAIN}`)"
|
|
- "traefik.http.routers.headquarter-frontend.entrypoints=websecure"
|
|
- "traefik.http.routers.headquarter-frontend.tls=true"
|
|
- "traefik.http.routers.headquarter-frontend.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-letsencrypt}"
|
|
- "traefik.http.services.headquarter-frontend.loadbalancer.server.port=80"
|
|
|
|
# API Service
|
|
headquarter-api:
|
|
build:
|
|
context: ./apps/api
|
|
dockerfile: Dockerfile
|
|
container_name: hq-api
|
|
environment:
|
|
APP_ENV: production
|
|
DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-headquarter}:${POSTGRES_PASSWORD:-headquarter}@postgres:5432/${POSTGRES_DB:-headquarter}
|
|
POSTGRES_HOST: postgres
|
|
POSTGRES_PORT: 5432
|
|
REDIS_URL: redis://redis:6379/0
|
|
SESSION_SECRET: ${SESSION_SECRET:-change-me-in-production}
|
|
SESSION_TTL_HOURS: ${SESSION_TTL_HOURS:-24}
|
|
REPO_BASE_PATH: /data/repos
|
|
INSTANCE_BASE_PATH: /data/instances
|
|
API_DOMAIN: ${API_DOMAIN}
|
|
WEB_DOMAIN: ${WEB_DOMAIN}
|
|
AUTHENTIK_DOMAIN: ${AUTHENTIK_DOMAIN}
|
|
API_PUBLIC_URL: ${API_PUBLIC_URL:-}
|
|
WEB_PUBLIC_URL: ${WEB_PUBLIC_URL:-}
|
|
AUTHENTIK_CLIENT_ID: ${AUTHENTIK_CLIENT_ID:-headquarter-web}
|
|
AUTHENTIK_CLIENT_SECRET: ${AUTHENTIK_CLIENT_SECRET}
|
|
AUTHENTIK_APPLICATION_SLUG: ${AUTHENTIK_APPLICATION_SLUG:-headquarter-web}
|
|
AUTHENTIK_AUTHORIZE_URL: ${AUTHENTIK_AUTHORIZE_URL:-}
|
|
AUTHENTIK_TOKEN_URL: ${AUTHENTIK_TOKEN_URL:-}
|
|
CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN}
|
|
CLOUDFLARE_ZONE_ID: ${CLOUDFLARE_ZONE_ID}
|
|
CLOUDFLARE_ACCOUNT_ID: ${CLOUDFLARE_ACCOUNT_ID}
|
|
CLOUDFLARE_BASE_DOMAIN: ${CLOUDFLARE_BASE_DOMAIN}
|
|
CLOUDFLARED_CONFIG_DIR: /etc/cloudflared
|
|
volumes:
|
|
- repo_data:/data/repos
|
|
- instance_data:/data/instances
|
|
- avatar_uploads:/app/uploads
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
- cloudflared_config:/etc/cloudflared
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
networks:
|
|
- backend
|
|
- traefik
|
|
restart: unless-stopped
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.headquarter-api.rule=Host(`${API_DOMAIN}`)"
|
|
- "traefik.http.routers.headquarter-api.entrypoints=websecure"
|
|
- "traefik.http.routers.headquarter-api.tls=true"
|
|
- "traefik.http.routers.headquarter-api.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-letsencrypt}"
|
|
- "traefik.http.services.headquarter-api.loadbalancer.server.port=8000"
|
|
|
|
# Cloudflare Tunnel
|
|
cloudflared:
|
|
image: cloudflare/cloudflared:latest
|
|
container_name: hq-cloudflared
|
|
command: tunnel --config /etc/cloudflared/config.yml run
|
|
volumes:
|
|
- cloudflared_config:/etc/cloudflared
|
|
networks:
|
|
- backend
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- headquarter-api
|
|
|
|
volumes:
|
|
postgres_data:
|
|
redis_data:
|
|
repo_data:
|
|
instance_data:
|
|
avatar_uploads:
|
|
cloudflared_config:
|
|
|
|
networks:
|
|
backend:
|
|
driver: bridge
|
|
traefik:
|
|
external: true
|
|
name: ${TRAEFIK_NETWORK:-traefik}
|