services: db: image: postgres:15-alpine pull_policy: if_not_present container_name: ctms_db restart: always environment: POSTGRES_USER: ctms_user POSTGRES_PASSWORD: secret_password POSTGRES_DB: ctms_db volumes: # 持久化数据到 Linux 本地,防止重启丢失 - ./pg_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ctms_user -d ctms_db"] interval: 5s timeout: 5s retries: 5 networks: - ctms_net backend: build: context: ./backend dockerfile: Dockerfile pull: false container_name: ctms_backend restart: always environment: DATABASE_URL: postgresql+asyncpg://ctms_user:secret_password@db/ctms_db ENV: ${ENV:-development} JWT_SECRET_KEY: ${JWT_SECRET_KEY:-dev-secret} FRONTEND_PUBLIC_URL: ${FRONTEND_PUBLIC_URL:-http://localhost:8888} LOGIN_RSA_PRIVATE_KEY: ${LOGIN_RSA_PRIVATE_KEY:-} LOGIN_RSA_KEY_ID: ${LOGIN_RSA_KEY_ID:-default} MONITORING_ACCESS_LOG_RETENTION_DAYS: ${MONITORING_ACCESS_LOG_RETENTION_DAYS:-90} MONITORING_METRIC_RETENTION_DAYS: ${MONITORING_METRIC_RETENTION_DAYS:-400} MONITORING_RETENTION_INTERVAL_SECONDS: ${MONITORING_RETENTION_INTERVAL_SECONDS:-86400} USER_LOGIN_ACTIVITY_RETENTION_DAYS: ${USER_LOGIN_ACTIVITY_RETENTION_DAYS:-180} USER_SESSION_ONLINE_SECONDS: ${USER_SESSION_ONLINE_SECONDS:-300} TRUSTED_PROXY_CIDRS: ${TRUSTED_PROXY_CIDRS:-127.0.0.1/32,::1/128,172.16.0.0/12,192.168.0.0/16} MONITORING_SERVER_PUBLIC_IP: ${MONITORING_SERVER_PUBLIC_IP:-} MONITORING_PUBLIC_IP_DISCOVERY_URLS: ${MONITORING_PUBLIC_IP_DISCOVERY_URLS:-https://api64.ipify.org,https://icanhazip.com} MONITORING_IP_GEO_FALLBACK_ENABLED: ${MONITORING_IP_GEO_FALLBACK_ENABLED:-true} MONITORING_IP_GEO_FALLBACK_API_KEY: ${MONITORING_IP_GEO_FALLBACK_API_KEY:-} MONITORING_IP_GEO_FALLBACK_TIMEOUT_SECONDS: ${MONITORING_IP_GEO_FALLBACK_TIMEOUT_SECONDS:-2.5} MONITORING_IP_GEO_FALLBACK_CACHE_SECONDS: ${MONITORING_IP_GEO_FALLBACK_CACHE_SECONDS:-604800} MONITORING_IP_GEO_FALLBACK_MAX_LOOKUPS: ${MONITORING_IP_GEO_FALLBACK_MAX_LOOKUPS:-10} ONLYOFFICE_ENABLED: ${ONLYOFFICE_ENABLED:-false} ONLYOFFICE_JWT_SECRET: ${ONLYOFFICE_JWT_SECRET:-} ONLYOFFICE_INTERNAL_URL: ${ONLYOFFICE_INTERNAL_URL:-http://onlyoffice} ONLYOFFICE_STORAGE_BASE_URL: ${ONLYOFFICE_STORAGE_BASE_URL:-http://backend:8000} ONLYOFFICE_INSTANCE_ID: ${ONLYOFFICE_INSTANCE_ID:-} ONLYOFFICE_CONFIG_TTL_SECONDS: ${ONLYOFFICE_CONFIG_TTL_SECONDS:-300} COLLABORATION_MAX_FILE_BYTES: ${COLLABORATION_MAX_FILE_BYTES:-52428800} depends_on: db: condition: service_healthy volumes: # 文档版本和附件必须跨容器重建保留;数据库中的 file_uri 统一指向 /code/app/uploads。 - ./backend/app/uploads:/code/app/uploads healthcheck: test: - CMD - python - -c - import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/readyz', timeout=2).read() interval: 10s timeout: 3s retries: 5 start_period: 20s networks: - ctms_net onlyoffice: build: context: ./onlyoffice dockerfile: Dockerfile args: ONLYOFFICE_IMAGE: ${ONLYOFFICE_IMAGE:-onlyoffice/documentserver:9.4.0.1} image: ctms-onlyoffice:9.4.0.1 container_name: ctms_onlyoffice restart: always profiles: ["office"] environment: JWT_ENABLED: "true" JWT_SECRET: ${ONLYOFFICE_JWT_SECRET:-} JWT_HEADER: AuthorizationJwt JWT_IN_BODY: "false" EXAMPLE_ENABLED: "false" WOPI_ENABLED: "false" PLUGINS_ENABLED: "false" ALLOW_PRIVATE_IP_ADDRESS: "true" shm_size: "2gb" stop_grace_period: 60s volumes: - onlyoffice_data:/var/www/onlyoffice/Data - onlyoffice_lib:/var/lib/onlyoffice - onlyoffice_logs:/var/log/onlyoffice healthcheck: test: ["CMD", "curl", "-fsS", "http://127.0.0.1/healthcheck"] interval: 15s timeout: 5s retries: 12 start_period: 60s networks: - ctms_net backend-init: build: context: ./backend dockerfile: Dockerfile pull: false command: python scripts/init_production.py environment: DATABASE_URL: postgresql+asyncpg://ctms_user:secret_password@db/ctms_db ENV: ${ENV:-development} JWT_SECRET_KEY: ${JWT_SECRET_KEY:-dev-secret} FRONTEND_PUBLIC_URL: ${FRONTEND_PUBLIC_URL:-http://localhost:8888} LOGIN_RSA_PRIVATE_KEY: ${LOGIN_RSA_PRIVATE_KEY:-} LOGIN_RSA_KEY_ID: ${LOGIN_RSA_KEY_ID:-default} depends_on: db: condition: service_healthy profiles: ["init"] networks: - ctms_net nginx: build: context: . dockerfile: nginx/Dockerfile pull: false args: NPM_CONFIG_REGISTRY: ${NPM_CONFIG_REGISTRY:-https://registry.npmmirror.com} VITE_RUNTIME_ENV: ${ENV:-production} VITE_ALLOW_INSECURE_DEV_LOGIN: ${VITE_ALLOW_INSECURE_DEV_LOGIN:-false} container_name: ctms_nginx restart: always ports: - "8888:80" volumes: - ./nginx/certs:/etc/nginx/certs:ro - ./frontend/public/favicon.ico:/usr/share/nginx/html/favicon.ico:ro depends_on: backend: condition: service_healthy networks: - ctms_net networks: ctms_net: driver: bridge volumes: onlyoffice_data: onlyoffice_lib: onlyoffice_logs: