- 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
202 lines
7.5 KiB
YAML
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:
|