diff --git a/.env.example b/.env.example index 8038cb9..76abbcf 100644 --- a/.env.example +++ b/.env.example @@ -8,12 +8,14 @@ DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/adiuva REDIS_URL=redis://localhost:6379/0 # ── Auth (JWT RS256) ────────────────────────────────────────────────────────── -# Public key for optional local JWT verification (Traefik ForwardAuth handles -# this in production — services trust X-User-* headers from Traefik). # Generate keypair: # openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048 # openssl rsa -in private.pem -pubout -out public.pem # Paste PEM content with literal \n for newlines. +# +# Private key — ONLY used by the Auth Service (JWT signing). +JWT_PRIVATE_KEY= +# Public key — used by all services / Traefik ForwardAuth (JWT verification). JWT_PUBLIC_KEY= JWT_ACCESS_TOKEN_EXPIRE_MINUTES=30 JWT_REFRESH_TOKEN_EXPIRE_DAYS=30 @@ -53,4 +55,13 @@ QDRANT_API_KEY= # ── Langfuse (observability) ───────────────────────────────────────────────── LANGFUSE_SECRET_KEY=sk-lf-... LANGFUSE_PUBLIC_KEY=pk-lf-... -LANGFUSE_HOST=https://cloud.langfuse.com # or self-hosted URL \ No newline at end of file +LANGFUSE_HOST=https://cloud.langfuse.com # or self-hosted URL + +# ── Cloudflare (Traefik ACME DNS-01 challenge) ─────────────────────────────── +CF_DNS_API_TOKEN= +ACME_EMAIL= + +# ── PostgreSQL (used by docker-compose) ────────────────────────────────────── +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=adiuva \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index c54bd25..68e68a9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,27 +1,34 @@ +# ── Adiuva Microservices ───────────────────────────────────────────── +# docker compose up --build +# docker compose up --build auth ws-gateway chat # subset + services: - app: - build: . + + # ═══════════════════════════════════════════════════════════════════ + # Infrastructure + # ═══════════════════════════════════════════════════════════════════ + + traefik: + image: traefik:v3.1 ports: - - "8080:8000" - env_file: - - path: .env - required: false + - "80:80" + - "443:443" + - "8080:8080" # dashboard (dev only) environment: - DATABASE_URL: postgresql+asyncpg://postgres:postgres@db:5432/adiuva - GITHUB_COPILOT_TOKEN_DIR: /root/.config/litellm/github_copilot + CF_DNS_API_TOKEN: ${CF_DNS_API_TOKEN:-} volumes: - - copilot_tokens:/root/.config/litellm/github_copilot - depends_on: - db: - condition: service_healthy + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro + - ./traefik/dynamic:/etc/traefik/dynamic:ro + - traefik_acme:/etc/traefik/acme restart: unless-stopped db: image: pgvector/pgvector:pg16 environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: adiuva + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} + POSTGRES_DB: ${POSTGRES_DB:-adiuva} volumes: - postgres_data:/var/lib/postgresql/data healthcheck: @@ -31,42 +38,161 @@ services: retries: 5 restart: unless-stopped - # Optional Redis for future rate-limit or caching needs - # redis: - # image: redis:7-alpine - # restart: unless-stopped - - # ── Local S3-compatible storage (MinIO) ── - minio: - image: minio/minio:latest - command: server /data --console-address ":9001" - ports: - - "9000:9000" - - "9001:9001" - environment: - MINIO_ROOT_USER: minioadmin - MINIO_ROOT_PASSWORD: minioadmin + redis: + image: redis:7-alpine + command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru volumes: - - minio_data:/data + - redis_data:/data healthcheck: - test: ["CMD", "mc", "ready", "local"] + test: ["CMD", "redis-cli", "ping"] interval: 5s - timeout: 5s + timeout: 3s retries: 5 restart: unless-stopped - # ── Local vector store (Qdrant) ── - qdrant: - image: qdrant/qdrant:latest - ports: - - "6333:6333" - - "6334:6334" - volumes: - - qdrant_data:/qdrant/storage + # ── Optional infrastructure (uncomment as needed) ──────────────── + + # minio: + # image: minio/minio:latest + # command: server /data --console-address ":9001" + # ports: + # - "9000:9000" + # - "9001:9001" + # environment: + # MINIO_ROOT_USER: minioadmin + # MINIO_ROOT_PASSWORD: minioadmin + # volumes: + # - minio_data:/data + # healthcheck: + # test: ["CMD", "mc", "ready", "local"] + # interval: 5s + # timeout: 5s + # retries: 5 + # restart: unless-stopped + + # qdrant: + # image: qdrant/qdrant:latest + # ports: + # - "6333:6333" + # - "6334:6334" + # volumes: + # - qdrant_data:/qdrant/storage + # restart: unless-stopped + + # ═══════════════════════════════════════════════════════════════════ + # Migrations (run once, then exit) + # ═══════════════════════════════════════════════════════════════════ + + migrate: + build: + context: . + dockerfile: Dockerfile + command: ["python", "-m", "alembic", "upgrade", "head"] + env_file: + - path: .env + required: false + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-adiuva} + depends_on: + db: + condition: service_healthy + restart: "no" + + # ═══════════════════════════════════════════════════════════════════ + # Application Services + # ═══════════════════════════════════════════════════════════════════ + + auth: + build: + context: . + dockerfile: services/auth/Dockerfile + env_file: + - path: .env + required: false + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-adiuva} + REDIS_URL: redis://redis:6379/0 + depends_on: + db: + condition: service_healthy + migrate: + condition: service_completed_successfully + restart: unless-stopped + + ws-gateway: + build: + context: . + dockerfile: services/ws-gateway/Dockerfile + env_file: + - path: .env + required: false + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-adiuva} + REDIS_URL: redis://redis:6379/0 + depends_on: + redis: + condition: service_healthy + auth: + condition: service_started + restart: unless-stopped + + chat: + build: + context: . + dockerfile: services/chat/Dockerfile + env_file: + - path: .env + required: false + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-adiuva} + REDIS_URL: redis://redis:6379/0 + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + migrate: + condition: service_completed_successfully + restart: unless-stopped + + batch-agent: + build: + context: . + dockerfile: services/batch-agent/Dockerfile + env_file: + - path: .env + required: false + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-adiuva} + REDIS_URL: redis://redis:6379/0 + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + migrate: + condition: service_completed_successfully + restart: unless-stopped + + billing: + build: + context: . + dockerfile: services/billing/Dockerfile + env_file: + - path: .env + required: false + environment: + DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-adiuva} + depends_on: + db: + condition: service_healthy + migrate: + condition: service_completed_successfully restart: unless-stopped volumes: postgres_data: - minio_data: - qdrant_data: - copilot_tokens: + redis_data: + traefik_acme: + # minio_data: + # qdrant_data: