version: '3.7' services: # ========================================== # Frontend (Vite Dev Server) # ========================================== frontend: build: context: ./frontend ports: - "5173:5173" # Vite default port depends_on: - backend # Hot Reload requires binding volume locally volumes: - ./frontend:/app - /app/node_modules # Avoid overwriting node_modules environment: # Browser needs to access backend directly for CORS requests in Dev mode - VITE_API_BASE_URL=http://localhost:8000/api/v1 restart: always # ========================================== # Nginx (Frontend Production) # ========================================== nginx: build: context: ./frontend target: production-stage ports: - "80:80" - "443:443" depends_on: - backend volumes: - certs_data:/etc/nginx/certs restart: always # ========================================== # Backend (FastAPI) # ========================================== backend: build: context: ./backend ports: - "8000:8000" environment: - TZ=Asia/Shanghai - MYSQL_SERVER=db - MYSQL_PORT=3306 - MYSQL_USER=uap_user - MYSQL_PASSWORD=uap_pass - MYSQL_DB=uap_db - REDIS_HOST=redis - REDIS_PORT=6379 - HYDRA_ADMIN_URL=http://hydra:4445 # CORS: Add * to allow debugging from any origin if localhost fails # Also explictly allow 127.0.0.1 and localhost with port 5173 - BACKEND_CORS_ORIGINS=["http://localhost:5173", "http://127.0.0.1:5173", "http://frontend:5173"] depends_on: db: condition: service_healthy redis: condition: service_healthy hydra: condition: service_started db-migration: condition: service_completed_successfully volumes: - ./backend:/app # Hot Reload for Backend too - certs_data:/app/certs restart: always # ========================================== # Database (MySQL) # ========================================== db: image: mysql:8.0 container_name: uap_mysql command: --default-authentication-plugin=mysql_native_password restart: always environment: TZ: Asia/Shanghai MYSQL_ROOT_PASSWORD: root_password MYSQL_DATABASE: uap_db MYSQL_USER: uap_user MYSQL_PASSWORD: uap_pass ports: - "3308:3306" volumes: - db_data:/var/lib/mysql # - ./config/my.cnf:/etc/mysql/conf.d/my.cnf healthcheck: test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 # ========================================== # Database Version Migration (Flyway) # ========================================== db-migration: image: flyway/flyway:9-alpine container_name: uap_migration depends_on: db: condition: service_healthy command: -connectRetries=60 -baselineOnMigrate=true migrate environment: FLYWAY_URL: jdbc:mysql://db:3306/uap_db FLYWAY_USER: root FLYWAY_PASSWORD: root_password volumes: - ./sql:/flyway/sql # ========================================== # Database Auto Backup (Sidecar) # ========================================== db-backup: image: fradelg/mysql-cron-backup container_name: uap_backup restart: always depends_on: - db environment: - MYSQL_HOST=db - MYSQL_PORT=3306 - MYSQL_USER=root - MYSQL_PASS=root_password - CRON_TIME=0 3 * * * - MAX_BACKUPS=7 - GZIP_COMPRESSION=true volumes: - ./backups:/backup # ========================================== # Redis # ========================================== redis: image: redis:7-alpine ports: - "6379:6379" restart: always healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 # ========================================== # Ory Hydra Services # ========================================== hydra-migrate: image: oryd/hydra:v2.2.0 environment: - TZ=Asia/Shanghai - DSN=postgres://hydra:secret@postgresd:5432/hydra?sslmode=disable&max_conns=20&max_idle_conns=4 command: migrate sql -e --yes depends_on: postgresd: condition: service_healthy restart: on-failure hydra: image: oryd/hydra:v2.2.0 depends_on: hydra-migrate: condition: service_completed_successfully ports: - "4444:4444" - "4445:4445" - "5555:5555" command: serve all --dev environment: - TZ=Asia/Shanghai - DSN=postgres://hydra:secret@postgresd:5432/hydra?sslmode=disable&max_conns=20&max_idle_conns=4 - URLS_SELF_ISSUER=http://127.0.0.1:4444 - URLS_CONSENT=http://localhost:5173/consent - URLS_LOGIN=http://localhost:5173/login - URLS_LOGOUT=http://localhost:5173/login - SECRETS_SYSTEM=youReallyNeedToChangeThis - OIDC_SUBJECT_IDENTIFIERS_SUPPORTED_TYPES=public,pairwise - OIDC_SUBJECT_IDENTIFIERS_PAIRWISE_SALT=youReallyNeedToChangeThis - SERVE_COOKIES_SAME_SITE_MODE=Lax - SERVE_COOKIES_SAME_SITE_LEGACY_WORKAROUND=true - SERVE_PUBLIC_CORS_ENABLED=true - SERVE_PUBLIC_CORS_ALLOWED_ORIGINS=* - SERVE_PUBLIC_CORS_ALLOWED_METHODS=POST,GET,PUT,DELETE,PATCH,OPTIONS - SERVE_PUBLIC_CORS_ALLOWED_HEADERS=Authorization,Content-Type - SERVE_ADMIN_CORS_ENABLED=true - SERVE_ADMIN_CORS_ALLOWED_ORIGINS=* postgresd: image: postgres:15 environment: - TZ=Asia/Shanghai - POSTGRES_USER=hydra - POSTGRES_PASSWORD=secret - POSTGRES_DB=hydra volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U hydra"] interval: 10s timeout: 5s retries: 5 start_period: 10s volumes: db_data: postgres_data: certs_data: