name: haixun-app x-backend-env: &backend-env HAIXUN_MONGO_URI: ${HAIXUN_MONGO_URI:?HAIXUN_MONGO_URI is required} HAIXUN_MONGO_DB: ${HAIXUN_MONGO_DB:-haixun} HAIXUN_REDIS_ADDR: ${HAIXUN_REDIS_ADDR:-redis:6379} HAIXUN_REDIS_PASSWORD: ${HAIXUN_REDIS_PASSWORD:?HAIXUN_REDIS_PASSWORD is required} HAIXUN_JWT_ACCESS_SECRET: ${HAIXUN_JWT_ACCESS_SECRET:?HAIXUN_JWT_ACCESS_SECRET is required} HAIXUN_JWT_REFRESH_SECRET: ${HAIXUN_JWT_REFRESH_SECRET:?HAIXUN_JWT_REFRESH_SECRET is required} HAIXUN_WORKER_SECRET: ${HAIXUN_WORKER_SECRET:?HAIXUN_WORKER_SECRET is required} HAIXUN_SECRETS_KEY: ${HAIXUN_SECRETS_KEY:?HAIXUN_SECRETS_KEY is required} HAIXUN_STORAGE_S3_ENDPOINT: ${HAIXUN_STORAGE_S3_ENDPOINT:-} HAIXUN_STORAGE_S3_PUBLIC_BASE_URL: ${HAIXUN_STORAGE_S3_PUBLIC_BASE_URL:-} HAIXUN_STORAGE_S3_REGION: ${HAIXUN_STORAGE_S3_REGION:-us-east-1} HAIXUN_STORAGE_S3_BUCKET: ${HAIXUN_STORAGE_S3_BUCKET:-} HAIXUN_STORAGE_S3_ACCESS_KEY: ${HAIXUN_STORAGE_S3_ACCESS_KEY:-} HAIXUN_STORAGE_S3_SECRET_KEY: ${HAIXUN_STORAGE_S3_SECRET_KEY:-} HAIXUN_STORAGE_S3_USE_PATH_STYLE: ${HAIXUN_STORAGE_S3_USE_PATH_STYLE:-true} HAIXUN_SMTP_HOST: ${HAIXUN_SMTP_HOST:-} HAIXUN_SMTP_PORT: ${HAIXUN_SMTP_PORT:-25} HAIXUN_SMTP_USERNAME: ${HAIXUN_SMTP_USERNAME:-} HAIXUN_SMTP_PASSWORD: ${HAIXUN_SMTP_PASSWORD:-} HAIXUN_SMTP_FROM: ${HAIXUN_SMTP_FROM:-} THREADS_APP_ID: ${THREADS_APP_ID:-} THREADS_APP_SECRET: ${THREADS_APP_SECRET:-} THREADS_REDIRECT_URI: ${THREADS_REDIRECT_URI:-} THREADS_OAUTH_SUCCESS_REDIRECT: ${THREADS_OAUTH_SUCCESS_REDIRECT:-} HAIXUN_KEYWORD_SEARCH_URL: http://node-tool-keyword-search:9101 HAIXUN_RESOLVE_MEDIA_URL: http://node-tool-resolve-media:9102 x-worker-env: &worker-env <<: *backend-env HAIXUN_JOB_WORKER_ENABLED: "true" HAIXUN_JOB_SCHEDULER_ENABLED: "${HAIXUN_JOB_SCHEDULER_ENABLED:-false}" HAIXUN_JOB_SCHEDULER_INTERVAL_SECONDS: "${HAIXUN_JOB_SCHEDULER_INTERVAL_SECONDS:-60}" HAIXUN_JOB_REAPER_ENABLED: "${HAIXUN_JOB_REAPER_ENABLED:-false}" HAIXUN_JOB_REAPER_INTERVAL_SECONDS: "${HAIXUN_JOB_REAPER_INTERVAL_SECONDS:-30}" HAIXUN_THREADS_TOKEN_REFRESH_ENABLED: "${HAIXUN_THREADS_TOKEN_REFRESH_ENABLED:-false}" HAIXUN_THREADS_TOKEN_REFRESH_INTERVAL_SECONDS: "${HAIXUN_THREADS_TOKEN_REFRESH_INTERVAL_SECONDS:-3600}" HAIXUN_THREADS_TOKEN_REFRESH_LEAD_DAYS: "${HAIXUN_THREADS_TOKEN_REFRESH_LEAD_DAYS:-7}" HAIXUN_THREADS_PUBLISH_QUEUE_ENABLED: "${HAIXUN_THREADS_PUBLISH_QUEUE_ENABLED:-false}" HAIXUN_THREADS_PUBLISH_QUEUE_INTERVAL_SECONDS: "${HAIXUN_THREADS_PUBLISH_QUEUE_INTERVAL_SECONDS:-60}" HAIXUN_THREADS_PUBLISH_QUEUE_BATCH_SIZE: "${HAIXUN_THREADS_PUBLISH_QUEUE_BATCH_SIZE:-20}" x-worker-service: &worker-service image: ${BACKEND_IMAGE:-ghcr.io/kupi-cat/haixun-backend}:${BACKEND_TAG:-v0.0.1} restart: unless-stopped command: ["/app/worker", "-f", "/app/etc/gateway.worker.prod.yaml"] networks: - app - haixun-infra services: web: build: context: .. dockerfile: backend/web/Dockerfile image: ${WEB_IMAGE:-ghcr.io/kupi-cat/haixun-web}:${WEB_TAG:-v0.0.1} restart: unless-stopped ports: - "${WEB_PORT:-80}:80" depends_on: - gateway networks: - app gateway: build: context: .. dockerfile: backend/Dockerfile image: ${BACKEND_IMAGE:-ghcr.io/kupi-cat/haixun-backend}:${BACKEND_TAG:-v0.0.1} restart: unless-stopped command: ["/app/gateway", "-f", "/app/etc/gateway.prod.yaml"] environment: <<: *backend-env expose: - "8890" networks: - app - haixun-infra worker-scheduler: <<: *worker-service environment: <<: *worker-env HAIXUN_JOB_WORKER_ENABLED: "false" HAIXUN_JOB_WORKER_TYPE: disabled HAIXUN_JOB_WORKER_ID: scheduler HAIXUN_JOB_SCHEDULER_ENABLED: "true" worker-reaper: <<: *worker-service environment: <<: *worker-env HAIXUN_JOB_WORKER_ENABLED: "false" HAIXUN_JOB_WORKER_TYPE: disabled HAIXUN_JOB_WORKER_ID: reaper HAIXUN_JOB_REAPER_ENABLED: "true" worker-token-sweeper: <<: *worker-service environment: <<: *worker-env HAIXUN_JOB_WORKER_ENABLED: "false" HAIXUN_JOB_WORKER_TYPE: disabled HAIXUN_JOB_WORKER_ID: token-sweeper HAIXUN_THREADS_TOKEN_REFRESH_ENABLED: "true" worker-publish-queue-sweeper: <<: *worker-service environment: <<: *worker-env HAIXUN_JOB_WORKER_ENABLED: "false" HAIXUN_JOB_WORKER_TYPE: disabled HAIXUN_JOB_WORKER_ID: publish-queue-sweeper HAIXUN_THREADS_PUBLISH_QUEUE_ENABLED: "true" worker-expand-graph: <<: *worker-service environment: <<: *worker-env HAIXUN_JOB_WORKER_TYPE: go-expand-graph HAIXUN_JOB_WORKER_ID: go-expand-graph worker-placement-scan: <<: *worker-service environment: <<: *worker-env HAIXUN_JOB_WORKER_TYPE: go-placement-scan HAIXUN_JOB_WORKER_ID: go-placement-scan worker-scan-viral: <<: *worker-service environment: <<: *worker-env HAIXUN_JOB_WORKER_TYPE: go-scan-viral HAIXUN_JOB_WORKER_ID: go-scan-viral worker-outreach-draft: <<: *worker-service environment: <<: *worker-env HAIXUN_JOB_WORKER_TYPE: go-generate-outreach-draft HAIXUN_JOB_WORKER_ID: go-generate-outreach-draft worker-refresh-threads-token: <<: *worker-service environment: <<: *worker-env HAIXUN_JOB_WORKER_TYPE: go-refresh-threads-token HAIXUN_JOB_WORKER_ID: go-refresh-threads-token worker-publish-analytics: <<: *worker-service environment: <<: *worker-env HAIXUN_JOB_WORKER_TYPE: go-publish-analytics HAIXUN_JOB_WORKER_ID: go-publish-analytics node-worker-style-8d: build: context: .. dockerfile: backend/worker/Dockerfile image: ${NODE_WORKER_IMAGE:-ghcr.io/kupi-cat/haixun-node-worker}:${NODE_WORKER_TAG:-v0.0.1} restart: unless-stopped command: ["npm", "run", "style-8d"] environment: HAIXUN_BACKEND_URL: http://gateway:8890 HAIXUN_WORKER_SECRET: ${HAIXUN_WORKER_SECRET:?HAIXUN_WORKER_SECRET is required} HAIXUN_NODE_WORKER_TYPE: node-style-8d HAIXUN_NODE_WORKER_ID: node-style-8d HAIXUN_WORKER_POLL_MS: ${HAIXUN_WORKER_POLL_MS:-3000} HAIXUN_8D_TARGET_SAMPLES: ${HAIXUN_8D_TARGET_SAMPLES:-4} PLAYWRIGHT_HEADLESS: "true" depends_on: - gateway networks: - app node-tool-keyword-search: image: ${NODE_WORKER_IMAGE:-ghcr.io/kupi-cat/haixun-node-worker}:${NODE_WORKER_TAG:-v0.0.1} restart: unless-stopped command: ["npm", "run", "keyword-search-server"] environment: PORT: "9101" PLAYWRIGHT_HEADLESS: "true" expose: - "9101" networks: - app node-tool-resolve-media: image: ${NODE_WORKER_IMAGE:-ghcr.io/kupi-cat/haixun-node-worker}:${NODE_WORKER_TAG:-v0.0.1} restart: unless-stopped command: ["npm", "run", "resolve-media-server"] environment: PORT: "9102" PLAYWRIGHT_HEADLESS: "true" expose: - "9102" networks: - app init: image: ${BACKEND_IMAGE:-ghcr.io/kupi-cat/haixun-backend}:${BACKEND_TAG:-v0.0.1} profiles: ["init"] command: ["/app/tool", "init", "-f", "/app/etc/gateway.prod.yaml"] environment: <<: *backend-env INIT_TENANT_ID: ${INIT_TENANT_ID:-default} INIT_ADMIN_EMAIL: ${INIT_ADMIN_EMAIL:?INIT_ADMIN_EMAIL is required} INIT_ADMIN_PASSWORD: ${INIT_ADMIN_PASSWORD:?INIT_ADMIN_PASSWORD is required} INIT_ADMIN_DISPLAY_NAME: ${INIT_ADMIN_DISPLAY_NAME:-Admin} networks: - haixun-infra networks: app: haixun-infra: external: true