254 lines
9.2 KiB
YAML
254 lines
9.2 KiB
YAML
name: rf4-spotter
|
|
|
|
x-logging: &default-logging
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "5"
|
|
|
|
services:
|
|
proxy:
|
|
image: caddy:2.10.2-alpine
|
|
restart: unless-stopped
|
|
environment:
|
|
SITE_DOMAIN: ${SITE_DOMAIN:?Set SITE_DOMAIN}
|
|
FILES_DOMAIN: ${FILES_DOMAIN:?Set FILES_DOMAIN}
|
|
ACME_EMAIL: ${ACME_EMAIL:?Set ACME_EMAIL}
|
|
ADMIN_BASIC_USER: ${ADMIN_BASIC_USER:?Set ADMIN_BASIC_USER}
|
|
ADMIN_BASIC_PASSWORD_HASH: ${ADMIN_BASIC_PASSWORD_HASH:?Set ADMIN_BASIC_PASSWORD_HASH}
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
- "443:443/udp"
|
|
volumes:
|
|
- ./deploy/Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- caddy_data:/data
|
|
- caddy_config:/config
|
|
depends_on:
|
|
web:
|
|
condition: service_healthy
|
|
networks: [edge, backend]
|
|
security_opt: [no-new-privileges:true]
|
|
logging: *default-logging
|
|
deploy:
|
|
resources:
|
|
limits: {cpus: "0.50", memory: 256M}
|
|
|
|
db:
|
|
image: postgres:17-alpine
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_DB: ${POSTGRES_DB:-rf4_spotter}
|
|
POSTGRES_USER: ${POSTGRES_USER:-rf4}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-rf4} -d ${POSTGRES_DB:-rf4_spotter}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
networks: [backend]
|
|
security_opt: [no-new-privileges:true]
|
|
logging: *default-logging
|
|
deploy:
|
|
resources:
|
|
limits: {cpus: "1.00", memory: 1G}
|
|
|
|
minio:
|
|
image: minio/minio:RELEASE.2025-07-23T15-54-02Z
|
|
command: server /data
|
|
restart: unless-stopped
|
|
environment:
|
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER:?Set MINIO_ROOT_USER}
|
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?Set MINIO_ROOT_PASSWORD}
|
|
volumes:
|
|
- minio_data:/data
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-fsS", "http://localhost:9000/minio/health/live"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
networks: [backend]
|
|
security_opt: [no-new-privileges:true]
|
|
logging: *default-logging
|
|
deploy:
|
|
resources:
|
|
limits: {cpus: "0.75", memory: 1G}
|
|
|
|
minio-init:
|
|
image: minio/mc:RELEASE.2025-07-21T05-28-08Z
|
|
restart: "no"
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
command:
|
|
- >-
|
|
test "$$MINIO_ROOT_USER" != "$$S3_ACCESS_KEY" &&
|
|
test "$$MINIO_ROOT_PASSWORD" != "$$S3_SECRET_KEY" &&
|
|
mc alias set local http://minio:9000 "$$MINIO_ROOT_USER" "$$MINIO_ROOT_PASSWORD" >/dev/null &&
|
|
mc mb --ignore-existing "local/$$S3_BUCKET" >/dev/null &&
|
|
printf '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:GetBucketLocation","s3:ListBucket"],"Resource":["arn:aws:s3:::%s"]},{"Effect":"Allow","Action":["s3:GetObject","s3:PutObject","s3:DeleteObject"],"Resource":["arn:aws:s3:::%s/*"]}]}' "$$S3_BUCKET" "$$S3_BUCKET" > /tmp/rf4-app-policy.json &&
|
|
mc admin policy create local rf4-app-bucket /tmp/rf4-app-policy.json >/dev/null &&
|
|
(mc admin user info local "$$S3_ACCESS_KEY" >/dev/null 2>&1 || mc admin user add local "$$S3_ACCESS_KEY" "$$S3_SECRET_KEY") &&
|
|
(mc admin policy detach local readwrite --user "$$S3_ACCESS_KEY" >/dev/null 2>&1 || true) &&
|
|
mc admin policy attach local rf4-app-bucket --user "$$S3_ACCESS_KEY" >/dev/null &&
|
|
mc alias set app http://minio:9000 "$$S3_ACCESS_KEY" "$$S3_SECRET_KEY" >/dev/null &&
|
|
mc stat "app/$$S3_BUCKET" >/dev/null &&
|
|
! mc ls app >/dev/null 2>&1
|
|
environment:
|
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER:?Set MINIO_ROOT_USER}
|
|
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?Set MINIO_ROOT_PASSWORD}
|
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY:?Set S3_ACCESS_KEY}
|
|
S3_SECRET_KEY: ${S3_SECRET_KEY:?Set S3_SECRET_KEY}
|
|
S3_BUCKET: ${S3_BUCKET:-catch-screenshots}
|
|
depends_on:
|
|
minio: {condition: service_healthy}
|
|
networks: [backend]
|
|
security_opt: [no-new-privileges:true]
|
|
logging: *default-logging
|
|
|
|
api:
|
|
build:
|
|
context: .
|
|
dockerfile: apps/api/Dockerfile
|
|
restart: unless-stopped
|
|
environment:
|
|
DEPLOYMENT_ENVIRONMENT: production
|
|
APP_VERSION: ${APP_VERSION:-0.1.0}
|
|
APP_REVISION: ${APP_REVISION:-unknown}
|
|
DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL}
|
|
ADMIN_TOKEN: ${ADMIN_TOKEN:?Set ADMIN_TOKEN}
|
|
CORS_ORIGINS: '["https://${SITE_DOMAIN:?Set SITE_DOMAIN}"]'
|
|
S3_ENDPOINT_URL: http://minio:9000
|
|
S3_PUBLIC_ENDPOINT_URL: https://${FILES_DOMAIN:?Set FILES_DOMAIN}
|
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY:?Set S3_ACCESS_KEY}
|
|
S3_SECRET_KEY: ${S3_SECRET_KEY:?Set S3_SECRET_KEY}
|
|
S3_BUCKET: ${S3_BUCKET:-catch-screenshots}
|
|
OFFICIAL_RECORDS_URL: ${OFFICIAL_RECORDS_URL:-https://rf4game.de/records/region/RU/}
|
|
OFFICIAL_RECORDS_REGION: ${OFFICIAL_RECORDS_REGION:-RU}
|
|
OFFICIAL_RECORDS_CATEGORY: ${OFFICIAL_RECORDS_CATEGORY:-records}
|
|
OFFICIAL_IMPORT_REQUIRED: ${OFFICIAL_IMPORT_REQUIRED:-false}
|
|
SEED_DEMO_DATA: "false"
|
|
IMPORT_INTERVAL_SECONDS: ${IMPORT_INTERVAL_SECONDS:-3600}
|
|
PUBLIC_CACHE_SECONDS: ${PUBLIC_CACHE_SECONDS:-20}
|
|
RATE_LIMIT_SECRET: ${RATE_LIMIT_SECRET:?Set RATE_LIMIT_SECRET}
|
|
TRUSTED_PROXY_CIDRS: '${TRUSTED_PROXY_CIDRS:-"127.0.0.1/32,::1/128"}'
|
|
RETENTION_SUBMISSION_DAYS: ${RETENTION_SUBMISSION_DAYS:-1}
|
|
RETENTION_UNREVIEWED_DAYS: ${RETENTION_UNREVIEWED_DAYS:-30}
|
|
RETENTION_APPROVED_PERSONAL_DAYS: ${RETENTION_APPROVED_PERSONAL_DAYS:-180}
|
|
RETENTION_STAGING_DAYS: ${RETENTION_STAGING_DAYS:-90}
|
|
RETENTION_AUDIT_DAYS: ${RETENTION_AUDIT_DAYS:-365}
|
|
RETENTION_PUBLISHED_PAYLOAD_DAYS: ${RETENTION_PUBLISHED_PAYLOAD_DAYS:-365}
|
|
LOG_LEVEL: ${LOG_LEVEL:-INFO}
|
|
depends_on:
|
|
db: {condition: service_healthy}
|
|
migrate: {condition: service_completed_successfully}
|
|
minio: {condition: service_healthy}
|
|
minio-init: {condition: service_completed_successfully}
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/ready')"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 12
|
|
networks: [backend, edge]
|
|
security_opt: [no-new-privileges:true]
|
|
logging: *default-logging
|
|
deploy:
|
|
resources:
|
|
limits: {cpus: "1.00", memory: 1G}
|
|
|
|
migrate:
|
|
build:
|
|
context: .
|
|
dockerfile: apps/api/Dockerfile
|
|
restart: "no"
|
|
environment:
|
|
DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL}
|
|
depends_on:
|
|
db: {condition: service_healthy}
|
|
command: ["alembic", "upgrade", "head"]
|
|
networks: [backend]
|
|
security_opt: [no-new-privileges:true]
|
|
logging: *default-logging
|
|
deploy:
|
|
resources:
|
|
limits: {cpus: "0.50", memory: 256M}
|
|
|
|
web:
|
|
build:
|
|
context: ./apps/web
|
|
args:
|
|
PUBLIC_API_URL: https://${SITE_DOMAIN:?Set SITE_DOMAIN}
|
|
restart: unless-stopped
|
|
environment:
|
|
PUBLIC_API_URL: https://${SITE_DOMAIN:?Set SITE_DOMAIN}
|
|
API_INTERNAL_URL: http://api:8000
|
|
FILES_DOMAIN: ${FILES_DOMAIN:?Set FILES_DOMAIN}
|
|
FILES_ORIGIN: https://${FILES_DOMAIN:?Set FILES_DOMAIN}
|
|
depends_on:
|
|
api: {condition: service_healthy}
|
|
healthcheck:
|
|
test: ["CMD", "node", "-e", "fetch('http://localhost:4321').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
networks: [backend]
|
|
security_opt: [no-new-privileges:true]
|
|
logging: *default-logging
|
|
deploy:
|
|
resources:
|
|
limits: {cpus: "0.75", memory: 512M}
|
|
|
|
community-scheduler:
|
|
build:
|
|
context: .
|
|
dockerfile: apps/api/Dockerfile
|
|
restart: unless-stopped
|
|
environment:
|
|
DEPLOYMENT_ENVIRONMENT: production
|
|
DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL}
|
|
ADMIN_TOKEN: ${ADMIN_TOKEN:?Set ADMIN_TOKEN}
|
|
CORS_ORIGINS: '["https://${SITE_DOMAIN:?Set SITE_DOMAIN}"]'
|
|
S3_PUBLIC_ENDPOINT_URL: https://${FILES_DOMAIN:?Set FILES_DOMAIN}
|
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY:?Set S3_ACCESS_KEY}
|
|
S3_SECRET_KEY: ${S3_SECRET_KEY:?Set S3_SECRET_KEY}
|
|
SEED_DEMO_DATA: "false"
|
|
RATE_LIMIT_SECRET: ${RATE_LIMIT_SECRET:?Set RATE_LIMIT_SECRET}
|
|
TRUSTED_PROXY_CIDRS: '${TRUSTED_PROXY_CIDRS:-"127.0.0.1/32,::1/128"}'
|
|
COMMUNITY_IMPORT_INTERVAL_SECONDS: ${COMMUNITY_IMPORT_INTERVAL_SECONDS:-1800}
|
|
RF4MAP_POINT_URL: ${RF4MAP_POINT_URL:-https://rf4map.ru/points/275}
|
|
RF4POSTS_SPOT_URL: ${RF4POSTS_SPOT_URL:-https://rf4-posts.com/ru/spots/d0c6d9c6-4ebf-49a7-98a8-9a562553a8ee}
|
|
LOG_LEVEL: ${LOG_LEVEL:-INFO}
|
|
depends_on:
|
|
api: {condition: service_healthy}
|
|
command: ["python", "-m", "app.community_scheduler"]
|
|
networks: [backend, edge]
|
|
security_opt: [no-new-privileges:true]
|
|
logging: *default-logging
|
|
deploy:
|
|
resources:
|
|
limits: {cpus: "0.50", memory: 384M}
|
|
|
|
storage-tool:
|
|
image: minio/mc:RELEASE.2025-07-21T05-28-08Z
|
|
profiles: [tools]
|
|
environment:
|
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY:?Set S3_ACCESS_KEY}
|
|
S3_SECRET_KEY: ${S3_SECRET_KEY:?Set S3_SECRET_KEY}
|
|
S3_BUCKET: ${S3_BUCKET:-catch-screenshots}
|
|
volumes:
|
|
- ${BACKUP_DIRECTORY:-./backups}:/backup
|
|
networks: [backend]
|
|
security_opt: [no-new-privileges:true]
|
|
logging: *default-logging
|
|
|
|
networks:
|
|
edge:
|
|
backend:
|
|
internal: true
|
|
|
|
volumes:
|
|
postgres_data:
|
|
minio_data:
|
|
caddy_data:
|
|
caddy_config:
|