Files
FictionArchive/docker-compose.yml
Claude 78612ea29d
Some checks failed
CI / build-backend (pull_request) Failing after 1m12s
CI / build-frontend (pull_request) Successful in 28s
feat: implement authentication system for API Gateway and FileService
- Add JWT Bearer token validation to API Gateway with restricted CORS
- Add cookie-based JWT validation to FileService for browser image requests
- Create shared authentication infrastructure in FictionArchive.Service.Shared
- Update frontend to set fa_session cookie after OIDC login
- Add [Authorize] attributes to GraphQL mutations with role-based restrictions
- Configure OIDC settings for both services in docker-compose

Implements FA-17: Authentication for microservices architecture
2025-11-27 14:05:54 +00:00

202 lines
7.5 KiB
YAML

services:
# ===========================================
# Infrastructure
# ===========================================
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
rabbitmq:
image: rabbitmq:3-management-alpine
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-guest}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD:-guest}
volumes:
- rabbitmq_data:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "check_running"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
# ===========================================
# Backend Services
# ===========================================
novel-service:
image: git.orfl.xyz/conco/fictionarchive-novel-service:latest
environment:
ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_NovelService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres}
ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq
Novelpia__Username: ${NOVELPIA_USERNAME}
Novelpia__Password: ${NOVELPIA_PASSWORD}
NovelUpdateService__PendingImageUrl: https://files.fictionarchive.orfl.xyz/api/pendingupload.png
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
postgres:
condition: service_healthy
rabbitmq:
condition: service_healthy
restart: unless-stopped
translation-service:
image: git.orfl.xyz/conco/fictionarchive-translation-service:latest
environment:
ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_TranslationService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres}
ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq
DeepL__ApiKey: ${DEEPL_API_KEY}
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
postgres:
condition: service_healthy
rabbitmq:
condition: service_healthy
restart: unless-stopped
scheduler-service:
image: git.orfl.xyz/conco/fictionarchive-scheduler-service:latest
environment:
ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_SchedulerService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres}
ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
postgres:
condition: service_healthy
rabbitmq:
condition: service_healthy
restart: unless-stopped
user-service:
image: git.orfl.xyz/conco/fictionarchive-user-service:latest
environment:
ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_UserService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres}
ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
postgres:
condition: service_healthy
rabbitmq:
condition: service_healthy
restart: unless-stopped
authentication-service:
image: git.orfl.xyz/conco/fictionarchive-authentication-service:latest
environment:
ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
rabbitmq:
condition: service_healthy
restart: unless-stopped
file-service:
image: git.orfl.xyz/conco/fictionarchive-file-service:latest
environment:
ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq
S3__Endpoint: ${S3_ENDPOINT:-https://s3.orfl.xyz}
S3__Bucket: ${S3_BUCKET:-fictionarchive}
S3__AccessKey: ${S3_ACCESS_KEY}
S3__SecretKey: ${S3_SECRET_KEY}
Proxy__BaseUrl: https://files.orfl.xyz/api
OIDC__Authority: https://auth.orfl.xyz/application/o/fictionarchive/
OIDC__ClientId: fictionarchive-files
OIDC__Audience: fictionarchive-api
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"]
interval: 30s
timeout: 10s
retries: 3
labels:
- "traefik.enable=true"
- "traefik.http.routers.file-service.rule=Host(`files.orfl.xyz`)"
- "traefik.http.routers.file-service.entrypoints=websecure"
- "traefik.http.routers.file-service.tls.certresolver=letsencrypt"
- "traefik.http.services.file-service.loadbalancer.server.port=8080"
depends_on:
rabbitmq:
condition: service_healthy
restart: unless-stopped
# ===========================================
# API Gateway
# ===========================================
api-gateway:
image: git.orfl.xyz/conco/fictionarchive-api:latest
environment:
ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq
OIDC__Authority: https://auth.orfl.xyz/application/o/fictionarchive/
OIDC__ClientId: fictionarchive-api
OIDC__Audience: fictionarchive-api
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"]
interval: 30s
timeout: 10s
retries: 3
labels:
- "traefik.enable=true"
- "traefik.http.routers.api-gateway.rule=Host(`api.fictionarchive.orfl.xyz`)"
- "traefik.http.routers.api-gateway.entrypoints=websecure"
- "traefik.http.routers.api-gateway.tls.certresolver=letsencrypt"
- "traefik.http.services.api-gateway.loadbalancer.server.port=8080"
depends_on:
- novel-service
- translation-service
- scheduler-service
- user-service
- authentication-service
- file-service
restart: unless-stopped
# ===========================================
# Frontend
# ===========================================
frontend:
image: git.orfl.xyz/conco/fictionarchive-frontend:latest
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/"]
interval: 30s
timeout: 10s
retries: 3
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`fictionarchive.orfl.xyz`)"
- "traefik.http.routers.frontend.entrypoints=websecure"
- "traefik.http.routers.frontend.tls.certresolver=letsencrypt"
- "traefik.http.services.frontend.loadbalancer.server.port=80"
restart: unless-stopped
volumes:
postgres_data:
rabbitmq_data:
letsencrypt: