name: travel-knowledge-graph services: postgres: image: postgres:16 container_name: travel-kg-postgres restart: unless-stopped environment: POSTGRES_USER: ${POSTGRES_USER:-admin} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} POSTGRES_DB: ${POSTGRES_DB:-kg_admin} ports: - "${POSTGRES_HOST_BIND:-127.0.0.1}:${POSTGRES_PORT:-5433}:5432" volumes: - postgres-data:/var/lib/postgresql/data - ./snapshots/postgres/kg_admin_new2.dump:/snapshots/kg_admin_new2.dump:ro - ./docker/postgres-init/01-restore-snapshot.sh:/docker-entrypoint-initdb.d/01-restore-snapshot.sh:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] interval: 10s timeout: 5s retries: 10 mysql: image: mysql:8.4 container_name: travel-kg-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-change-this-root-password} MYSQL_DATABASE: ${MYSQL_DATABASE:-platform_control} MYSQL_USER: ${MYSQL_USER:-data_center} MYSQL_PASSWORD: ${MYSQL_PASSWORD:-change-this-data-password} MYSQL_ACCESS_BROKER_USER: ${MYSQL_ACCESS_BROKER_USER:-access_broker} MYSQL_ACCESS_BROKER_PASSWORD: ${MYSQL_ACCESS_BROKER_PASSWORD:-change-this-access-broker-credential} command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_0900_ai_ci - --default-time-zone=+00:00 - --partial-revokes=ON - --local-infile=OFF - --secure-file-priv=NULL - --skip-name-resolve=ON - --skip-symbolic-links - --server-id=1 - --log-bin=mysql-bin - --binlog-format=ROW - --binlog-expire-logs-seconds=604800 - --sync-binlog=1 - --innodb-flush-log-at-trx-commit=1 ports: - "${MYSQL_HOST_BIND:-127.0.0.1}:${MYSQL_PORT:-3307}:3306" volumes: - mysql-data:/var/lib/mysql - ./docker/mysql-init/01-data-center-grants.sh:/docker-entrypoint-initdb.d/01-data-center-grants.sh:ro healthcheck: test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p\"$${MYSQL_ROOT_PASSWORD}\" --silent"] interval: 10s timeout: 5s retries: 15 falkordb-seed: image: busybox:1.36 restart: "no" command: - sh - -c - | set -eu if [ ! -f /data/dump.rdb ]; then cp /snapshot/dump.rdb /data/dump.rdb fi ls -lh /data/dump.rdb volumes: - falkordb-data:/data - ./snapshots/falkordb/dump.rdb:/snapshot/dump.rdb:ro falkordb: image: falkordb/falkordb:latest container_name: travel-kg-falkordb restart: unless-stopped ports: - "${FALKORDB_HOST_BIND:-127.0.0.1}:${FALKORDB_PORT:-6380}:6379" - "${FALKORDB_BROWSER_HOST_BIND:-127.0.0.1}:${FALKORDB_BROWSER_PORT:-3002}:3000" volumes: - falkordb-data:/var/lib/falkordb/data depends_on: falkordb-seed: condition: service_completed_successfully healthcheck: test: ["CMD", "redis-cli", "-p", "6379", "PING"] interval: 10s timeout: 5s retries: 10 dbeaver-gateway: build: context: ./deploy/dbeaver-gateway image: travel-knowledge-graph-dbeaver-gateway:local container_name: travel-kg-dbeaver-gateway restart: unless-stopped ports: - "${DATA_MYSQL_SSH_BIND:-127.0.0.1}:${DATA_MYSQL_SSH_PORT:-2222}:22" volumes: - dbeaver-access:/var/lib/travel-kg/dbeaver-access - dbeaver-ssh-host-keys:/etc/ssh/host-keys depends_on: mysql: condition: service_healthy healthcheck: test: ["CMD-SHELL", "nc -z 127.0.0.1 22 && nc -z 127.0.0.1 3307"] interval: 10s timeout: 5s retries: 10 api: build: context: . dockerfile: Dockerfile network: host image: travel-knowledge-graph-api:local container_name: travel-kg-api restart: unless-stopped environment: DATABASE_URL: postgresql://${POSTGRES_USER:-admin}:${POSTGRES_PASSWORD:-password}@postgres:5432/${POSTGRES_DB:-kg_admin} DB_SCHEMA: ${DB_SCHEMA:-kg_admin_new2} DB_MIGRATIONS_ENABLED: ${DOCKER_DB_MIGRATIONS_ENABLED:-false} DATA_MYSQL_URL: mysql://${MYSQL_USER:-data_center}:${MYSQL_PASSWORD:-change-this-data-password}@mysql:3306/${MYSQL_DATABASE:-platform_control} DATA_MYSQL_REQUIRED: "true" DATA_MYSQL_POOL_MIN_SIZE: ${DATA_MYSQL_POOL_MIN_SIZE:-2} DATA_MYSQL_POOL_MAX_SIZE: ${DATA_MYSQL_POOL_MAX_SIZE:-20} DATA_SQL_CONSOLE_WRITE_ENABLED: ${DATA_SQL_CONSOLE_WRITE_ENABLED:-false} INTERFACE_API_SECRET: ${INTERFACE_API_SECRET:-change-me-interface-api-secret} DATA_MYSQL_DIRECT_ACCESS_ENABLED: ${DATA_MYSQL_DIRECT_ACCESS_ENABLED:-true} DATA_MYSQL_PUBLIC_HOST: ${DATA_MYSQL_PUBLIC_HOST:-} DATA_MYSQL_PUBLIC_PORT: ${DATA_MYSQL_PUBLIC_PORT:-0} DATA_MYSQL_DIRECT_TRANSPORT: ${DATA_MYSQL_DIRECT_TRANSPORT:-SSH 隧道(强制)} DATA_MYSQL_SSH_TUNNEL_REQUIRED: ${DATA_MYSQL_SSH_TUNNEL_REQUIRED:-true} DATA_MYSQL_SSH_HOST: ${DATA_MYSQL_SSH_HOST:-} DATA_MYSQL_SSH_PORT: ${DATA_MYSQL_SSH_PORT:-2222} DATA_MYSQL_SSH_USERNAME: ${DATA_MYSQL_SSH_USERNAME:-dbeaver} DATA_MYSQL_SSH_AUTH_METHOD: ${DATA_MYSQL_SSH_AUTH_METHOD:-SSH 私钥 / SSH Agent} DATA_MYSQL_MANAGED_ACCESS_ENABLED: ${DATA_MYSQL_MANAGED_ACCESS_ENABLED:-true} DATA_MYSQL_SSH_AUTHORIZED_KEYS_FILE: ${DATA_MYSQL_SSH_AUTHORIZED_KEYS_FILE:-/var/lib/travel-kg/dbeaver-access/authorized_keys} DATA_MYSQL_SSH_HOST_PUBLIC_KEY_FILE: ${DATA_MYSQL_SSH_HOST_PUBLIC_KEY_FILE:-/var/lib/travel-kg/dbeaver-access/ssh_host_ed25519_key.pub} DATA_MYSQL_PROVISIONER_USER: ${MYSQL_ACCESS_BROKER_USER:-access_broker} DATA_MYSQL_PROVISIONER_PASSWORD: ${MYSQL_ACCESS_BROKER_PASSWORD:-change-this-access-broker-credential} DATA_MYSQL_ADMIN_HOST: ${DATA_MYSQL_ADMIN_HOST:-127.0.0.1} DATA_MYSQL_ADMIN_PORT: ${DATA_MYSQL_ADMIN_PORT:-${MYSQL_PORT:-3307}} DATA_MYSQL_ADMIN_ACCOUNT_POLICY: ${DATA_MYSQL_ADMIN_ACCOUNT_POLICY:-一人一号 · 单库授权 · 禁止 root} DATA_MYSQL_AUDIT_ENABLED: ${DATA_MYSQL_AUDIT_ENABLED:-false} MYSQL_HOST_BIND: ${MYSQL_HOST_BIND:-127.0.0.1} DATA_BACKUP_ENABLED: ${DATA_BACKUP_ENABLED:-false} DATA_BACKUP_ENCRYPTION_REQUIRED: ${DATA_BACKUP_ENCRYPTION_REQUIRED:-true} DATA_BACKUP_RETENTION_DAYS: ${DATA_BACKUP_RETENTION_DAYS:-30} FALKORDB_HOST: falkordb FALKORDB_PORT: "6379" FALKORDB_GRAPH: ${FALKORDB_GRAPH:-guiyang_new2} FALKORDB_PASSWORD: ${FALKORDB_PASSWORD:-} AUTH_SECRET: ${AUTH_SECRET:-change-me-32-chars-minimum-secret} AUTH_ALGORITHM: ${AUTH_ALGORITHM:-HS256} AUTH_ISSUER: ${AUTH_ISSUER:-zn-kg-admin} AUTH_AUDIENCE: ${AUTH_AUDIENCE:-zn-kg-admin-web} AUTH_TOKEN_EXPIRE_MINUTES: ${AUTH_TOKEN_EXPIRE_MINUTES:-60} AUTH_DEFAULT_USERNAME: ${AUTH_DEFAULT_USERNAME:-admin@example.com} AUTH_DEFAULT_PASSWORD: ${AUTH_DEFAULT_PASSWORD:-change-me} AUTH_LOGIN_MAX_ATTEMPTS: ${AUTH_LOGIN_MAX_ATTEMPTS:-5} AUTH_LOGIN_WINDOW_SECONDS: ${AUTH_LOGIN_WINDOW_SECONDS:-300} AUTH_LOGIN_LOCK_SECONDS: ${AUTH_LOGIN_LOCK_SECONDS:-900} INTERFACE_API_DEFAULT_EXPIRY_DAYS: ${INTERFACE_API_DEFAULT_EXPIRY_DAYS:-30} INTERFACE_API_MAX_EXPIRY_DAYS: ${INTERFACE_API_MAX_EXPIRY_DAYS:-90} INTERFACE_API_RATE_LIMIT_PER_MINUTE: ${INTERFACE_API_RATE_LIMIT_PER_MINUTE:-120} APP_ENVIRONMENT: ${APP_ENVIRONMENT:-development} SECURITY_STRICT_MODE: ${SECURITY_STRICT_MODE:-false} CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-http://localhost:8102,http://127.0.0.1:8102} TRUSTED_HOSTS: ${TRUSTED_HOSTS:-*} SECURITY_HEADERS_ENABLED: ${SECURITY_HEADERS_ENABLED:-true} DEFAULT_TENANT: ${DEFAULT_TENANT:-CityGraph-new2} DEFAULT_PROJECT: ${DEFAULT_PROJECT:-CityGraph-new2} INGEST_API_KEYS: ${INGEST_API_KEYS:-dev-key-1} LLM_PROVIDER: ${LLM_PROVIDER:-openai-compatible} LLM_API_BASE: ${LLM_API_BASE:-} LLM_API_KEY: ${LLM_API_KEY:-} LLM_MODEL: ${LLM_MODEL:-deepseek-chat} LLM_TIMEOUT_SECONDS: ${LLM_TIMEOUT_SECONDS:-30} LLM_EXTRACTION_ENABLED: ${LLM_EXTRACTION_ENABLED:-false} AMAP_WEB_KEY: ${AMAP_WEB_KEY:-} AMAP_JS_KEY: ${AMAP_JS_KEY:-} AMAP_SECURITY_JSCODE: ${AMAP_SECURITY_JSCODE:-} GAODE_CRAWLER_PATH: ${GAODE_CRAWLER_PATH:-} ports: - "${API_HOST_BIND:-0.0.0.0}:${API_PORT:-8102}:8000" volumes: - dbeaver-access:/var/lib/travel-kg/dbeaver-access depends_on: postgres: condition: service_healthy mysql: condition: service_healthy falkordb: condition: service_healthy dbeaver-gateway: condition: service_healthy healthcheck: test: [ "CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/v1/admin/health', timeout=5).read()", ] interval: 30s timeout: 5s start_period: 30s retries: 3 volumes: postgres-data: mysql-data: falkordb-data: dbeaver-access: dbeaver-ssh-host-keys: