name: ${COMPOSE_PROJECT_NAME:-zhixing-system} services: server: build: context: ./zhixing-server target: production restart: unless-stopped environment: TZ: Asia/Shanghai ZHIXING_APP_ENV: production ZHIXING_DATABASE_URL: ${ZHIXING_DATABASE_URL:?Set ZHIXING_DATABASE_URL to the 1Panel PostgreSQL URL} ZHIXING_LOG_LEVEL: ${ZHIXING_LOG_LEVEL:-INFO} ZHIXING_MARKET_DATA_CSV_ROOT: /app/data/market-data ZHIXING_MARKET_DATA_COVERAGE_THRESHOLD: ${ZHIXING_MARKET_DATA_COVERAGE_THRESHOLD:-0.99} ZHIXING_MARKET_DATA_MAX_WORKERS: ${ZHIXING_MARKET_DATA_MAX_WORKERS:-8} ZHIXING_MARKET_DATA_MAX_RETRIES: ${ZHIXING_MARKET_DATA_MAX_RETRIES:-3} ZHIXING_MARKET_DATA_RETRY_BACKOFF_SECONDS: ${ZHIXING_MARKET_DATA_RETRY_BACKOFF_SECONDS:-1.0} ZHIXING_MARKET_DATA_REQUEST_INTERVAL_SECONDS: ${ZHIXING_MARKET_DATA_REQUEST_INTERVAL_SECONDS:-0.2} ZHIXING_MARKET_DATA_ADVISORY_LOCK_KEY: ${ZHIXING_MARKET_DATA_ADVISORY_LOCK_KEY:-7380521} ZHIXING_SELECTION_MAX_WORKERS: ${ZHIXING_SELECTION_MAX_WORKERS:-4} ZHIXING_SELECTION_BATCH_SIZE: ${ZHIXING_SELECTION_BATCH_SIZE:-200} ZHIXING_SELECTION_PATTERN_SCORING_ENABLED: ${ZHIXING_SELECTION_PATTERN_SCORING_ENABLED:-true} ZHIXING_TUSHARE_TOKEN: ${ZHIXING_TUSHARE_TOKEN:-} init: true expose: - "8000" healthcheck: test: - CMD - python - -c - "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz')" interval: 30s timeout: 5s retries: 3 start_period: 10s volumes: - market-data:/app/data/market-data networks: - default - 1panel-network web: build: context: ./zhixing-web target: production restart: unless-stopped depends_on: server: condition: service_healthy environment: TZ: Asia/Shanghai API_UPSTREAM: http://server:8000 init: true ports: - "127.0.0.1:${ZHIXING_WEB_PORT:-8111}:80" migrate: profiles: ["jobs"] build: context: ./zhixing-server target: production command: ["alembic", "upgrade", "head"] depends_on: market-data-init: condition: service_completed_successfully environment: TZ: Asia/Shanghai ZHIXING_DATABASE_URL: ${ZHIXING_DATABASE_URL:?Set ZHIXING_DATABASE_URL to the 1Panel PostgreSQL URL} networks: - 1panel-network # Named volumes are initialized as root; normalize ownership before app jobs run. market-data-init: profiles: ["jobs"] build: context: ./zhixing-server target: production user: "0:0" environment: TZ: Asia/Shanghai command: - sh - -c - mkdir -p /app/data/market-data && chown -R 10001:10001 /app/data/market-data volumes: - market-data:/app/data/market-data market-sync: profiles: ["jobs"] build: context: ./zhixing-server target: production command: ["market-data-sync"] depends_on: migrate: condition: service_completed_successfully environment: TZ: Asia/Shanghai ZHIXING_DATABASE_URL: ${ZHIXING_DATABASE_URL:?Set ZHIXING_DATABASE_URL to the 1Panel PostgreSQL URL} ZHIXING_MARKET_DATA_CSV_ROOT: /app/data/market-data ZHIXING_MARKET_DATA_COVERAGE_THRESHOLD: ${ZHIXING_MARKET_DATA_COVERAGE_THRESHOLD:-0.99} ZHIXING_MARKET_DATA_MAX_WORKERS: ${ZHIXING_MARKET_DATA_MAX_WORKERS:-8} ZHIXING_MARKET_DATA_MAX_RETRIES: ${ZHIXING_MARKET_DATA_MAX_RETRIES:-3} ZHIXING_MARKET_DATA_RETRY_BACKOFF_SECONDS: ${ZHIXING_MARKET_DATA_RETRY_BACKOFF_SECONDS:-1.0} ZHIXING_MARKET_DATA_REQUEST_INTERVAL_SECONDS: ${ZHIXING_MARKET_DATA_REQUEST_INTERVAL_SECONDS:-0.2} ZHIXING_MARKET_DATA_ADVISORY_LOCK_KEY: ${ZHIXING_MARKET_DATA_ADVISORY_LOCK_KEY:-7380521} ZHIXING_TUSHARE_TOKEN: ${ZHIXING_TUSHARE_TOKEN:-} volumes: - market-data:/app/data/market-data networks: - 1panel-network volumes: market-data: networks: 1panel-network: name: 1panel-network external: true