TorbatYar/docker-compose.yml
Mortezakoohjani 800b0ba2c5 Deploy TorbatYar for torbatyar.ir with nginx multi-tenant routing.
Wire production domain, CORS for tenant subdomains, celery volume mounts, and nginx reverse proxy configs for apex, API, identity, auth, and wildcard tenants.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 21:43:33 +03:30

214 lines
6.6 KiB
YAML

# ============================================================
# SuperApp SaaS Platform - Docker Compose
# postgres, redis, keycloak, core-service, identity-access,
# celery-worker, celery-beat, frontend
# ============================================================
services:
postgres:
image: postgres:15-alpine
container_name: superapp_postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports:
- "${POSTGRES_PORT:-5432}:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./infrastructure/postgres/init-dbs.sql:/docker-entrypoint-initdb.d/02-init-dbs.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- superapp_net
redis:
image: redis:7-alpine
container_name: superapp_redis
restart: unless-stopped
ports:
- "${REDIS_PORT:-6379}:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- superapp_net
keycloak:
image: quay.io/keycloak/keycloak:24.0
container_name: superapp_keycloak
restart: unless-stopped
command: start-dev --import-realm --features=token-exchange
environment:
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN:-admin}
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD:-admin}
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB}
KC_DB_USERNAME: ${POSTGRES_USER}
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
KC_HTTP_ENABLED: "true"
KC_HOSTNAME_STRICT: "false"
KC_PROXY: edge
KC_HOSTNAME: ${KEYCLOAK_HOSTNAME:-auth.torbatyar.ir}
KC_HOSTNAME_STRICT_HTTPS: ${KEYCLOAK_HOSTNAME_STRICT_HTTPS:-false}
JAVA_OPTS_KC_HEAP: "-Xms256m -Xmx512m"
ports:
- "8080:8080"
volumes:
- ./infrastructure/keycloak/realm:/opt/keycloak/data/import
- ./infrastructure/keycloak/themes:/opt/keycloak/themes
depends_on:
postgres:
condition: service_healthy
networks:
- superapp_net
core-service:
build:
context: .
dockerfile: backend/core-service/Dockerfile.dev
container_name: superapp_core_service
restart: unless-stopped
env_file:
- .env
ports:
- "8000:8000"
volumes:
- ./backend/core-service:/app
- ./backend/shared-lib:/shared-lib
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
keycloak:
condition: service_started
command: >
sh -c "alembic upgrade 0001_initial &&
alembic stamp head &&
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload"
networks:
- superapp_net
identity-access-service:
build:
context: .
dockerfile: backend/services/identity-access/Dockerfile.dev
container_name: superapp_identity_service
restart: unless-stopped
env_file:
- .env
environment:
DATABASE_URL: ${IDENTITY_DATABASE_URL}
DATABASE_URL_SYNC: ${IDENTITY_DATABASE_URL_SYNC}
IDENTITY_KEYCLOAK_CLIENT_ID: identity-access-service
KEYCLOAK_IDENTITY_CLIENT_SECRET: ${KEYCLOAK_IDENTITY_CLIENT_SECRET:-change-me-identity}
PLATFORM_ADMIN_MOBILES: ${PLATFORM_ADMIN_MOBILES:-}
KEYCLOAK_SERVER_URL: http://keycloak:8080
KEYCLOAK_PUBLIC_URL: ${KEYCLOAK_PUBLIC_URL:-http://localhost:8080}
CORE_SERVICE_URL: ${CORE_SERVICE_URL:-http://core-service:8000}
CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000}
ports:
- "8001:8001"
volumes:
- ./backend/services/identity-access:/app
- ./backend/shared-lib:/shared-lib
depends_on:
postgres:
condition: service_healthy
keycloak:
condition: service_started
core-service:
condition: service_started
command: >
sh -c "python scripts/ensure_db.py &&
alembic upgrade head &&
python scripts/enable_mobile_auth_client.py || true &&
uvicorn app.main:app --host 0.0.0.0 --port 8001 --reload"
networks:
- superapp_net
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
container_name: superapp_frontend
restart: unless-stopped
ports:
- "${FRONTEND_PORT:-3000}:3000"
environment:
NEXT_PUBLIC_BACKEND_URL: ${NEXT_PUBLIC_BACKEND_URL:-http://localhost:8000}
NEXT_PUBLIC_API_BASE_URL: ${NEXT_PUBLIC_API_BASE_URL:-http://localhost:8000}
NEXT_PUBLIC_IDENTITY_API_URL: ${NEXT_PUBLIC_IDENTITY_API_URL:-http://localhost:8001}
NEXT_PUBLIC_KEYCLOAK_URL: ${NEXT_PUBLIC_KEYCLOAK_URL:-http://localhost:8080}
NEXT_PUBLIC_KEYCLOAK_REALM: ${NEXT_PUBLIC_KEYCLOAK_REALM:-superapp}
NEXT_PUBLIC_KEYCLOAK_CLIENT_ID: ${NEXT_PUBLIC_KEYCLOAK_CLIENT_ID:-superapp-frontend}
# polling برای hot-reload پایدار روی volume mount ویندوز
WATCHPACK_POLLING: "true"
CHOKIDAR_USEPOLLING: "true"
volumes:
- ./frontend:/app
- frontend_node_modules:/app/node_modules
depends_on:
core-service:
condition: service_started
identity-access-service:
condition: service_started
networks:
- superapp_net
celery-worker:
build:
context: .
dockerfile: backend/core-service/Dockerfile.dev
container_name: superapp_celery_worker
restart: unless-stopped
env_file:
- .env
volumes:
- ./backend/core-service:/app
- ./backend/shared-lib:/shared-lib
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
command: celery -A app.workers.celery_app.celery_app worker --loglevel=INFO -Q default,core_events,notifications,webhooks
networks:
- superapp_net
celery-beat:
build:
context: .
dockerfile: backend/core-service/Dockerfile.dev
container_name: superapp_celery_beat
restart: unless-stopped
env_file:
- .env
volumes:
- ./backend/core-service:/app
- ./backend/shared-lib:/shared-lib
depends_on:
redis:
condition: service_healthy
command: celery -A app.workers.celery_app.celery_app beat --loglevel=INFO
networks:
- superapp_net
volumes:
postgres_data:
redis_data:
frontend_node_modules:
networks:
superapp_net:
driver: bridge