Spaces:
Sleeping
Sleeping
| set -e | |
| echo "=== Entrypoint Started ===" | |
| export TZ="Asia/Shanghai" | |
| CONFIG="/app/data/config.toml" | |
| mkdir -p /app/data /app/logs | |
| # ============================================================ | |
| # 1. 每次启动都重新生成 config.toml | |
| # ============================================================ | |
| cat > "$CONFIG" <<EOT | |
| [app] | |
| app_url = "__APP_URL__" | |
| app_key = "__APP_KEY__" | |
| api_key = "__API_KEY__" | |
| [proxy] | |
| base_proxy_url = "__PROXY_BASE_URL__" | |
| user_agent = "__PROXY_UA__" | |
| EOT | |
| sed -i "s|__APP_URL__|${APP_APP_URL:-}|g" "$CONFIG" | |
| sed -i "s|__APP_KEY__|${APP_KEY:-grok2api}|g" "$CONFIG" | |
| sed -i "s|__API_KEY__|${API_KEY:-}|g" "$CONFIG" | |
| sed -i "s|__PROXY_BASE_URL__|${PROXY_BASE_URL:-}|g" "$CONFIG" | |
| sed -i "s|__PROXY_UA__|${PROXY_UA:-}|g" "$CONFIG" | |
| echo "=== config.toml generated ===" | |
| cat "$CONFIG" | |
| # ============================================================ | |
| # 2. 修复 Supabase Pooler 兼容性 | |
| # | |
| # Supabase 连接模式: | |
| # - pooler:6543 = Transaction mode(不支持 prepared statements)❌ | |
| # - pooler:5432 = Session mode(支持 prepared statements,IPv4)✅ | |
| # - db.xxx:5432 = 直连(IPv6 only,HF Spaces 不支持)❌ | |
| # | |
| # grok2api 用 asyncpg,依赖 prepared statements | |
| # 所以必须用 Session mode (pooler:5432) | |
| # | |
| # 修复:自动将 pooler 端口 6543 → 5432(Session mode) | |
| # ============================================================ | |
| export SERVER_STORAGE_TYPE="${SERVER_STORAGE_TYPE:-pgsql}" | |
| export LOG_LEVEL="${LOG_LEVEL:-DEBUG}" | |
| if [ -n "$SERVER_STORAGE_URL" ]; then | |
| case "$SERVER_STORAGE_URL" in | |
| *pooler.supabase.com:6543*) | |
| export SERVER_STORAGE_URL=$(echo "$SERVER_STORAGE_URL" | sed 's|:6543/|:5432/|g') | |
| echo "=== Fixed Supabase: port 6543 (Transaction) -> 5432 (Session) ===" | |
| ;; | |
| esac | |
| echo "=== Storage URL: $(echo $SERVER_STORAGE_URL | sed 's|://.*@|://***@|') ===" | |
| fi | |
| # ============================================================ | |
| # 3. 启动前同步环境变量到数据库(如果表已存在) | |
| # ============================================================ | |
| if [ -n "$SERVER_STORAGE_URL" ]; then | |
| echo "=== Attempting to sync env vars to database ===" | |
| python3 << 'PYEOF' | |
| import asyncio, json, os, sys | |
| async def sync_config(): | |
| try: | |
| import asyncpg | |
| raw_url = os.environ.get("SERVER_STORAGE_URL", "") | |
| dsn = raw_url.replace("postgresql+asyncpg://", "postgresql://") | |
| conn = await asyncpg.connect(dsn) | |
| try: | |
| table_exists = await conn.fetchval(""" | |
| SELECT EXISTS ( | |
| SELECT 1 FROM information_schema.tables | |
| WHERE table_name = 'app_config' | |
| ) | |
| """) | |
| if not table_exists: | |
| print("app_config table not yet created, config.toml will be used at first startup") | |
| return | |
| overrides = [ | |
| ("app", "app_key", os.environ.get("APP_KEY", "")), | |
| ("app", "api_key", os.environ.get("API_KEY", "")), | |
| ("app", "app_url", os.environ.get("APP_APP_URL", "")), | |
| ("proxy", "base_proxy_url", os.environ.get("PROXY_BASE_URL", "")), | |
| ("proxy", "user_agent", os.environ.get("PROXY_UA", "")), | |
| ] | |
| for section, key, value in overrides: | |
| if not value: | |
| continue | |
| json_value = json.dumps(value) | |
| existing = await conn.fetchval( | |
| "SELECT value FROM app_config WHERE section = $1 AND key_name = $2", | |
| section, key | |
| ) | |
| if existing is not None: | |
| await conn.execute( | |
| "UPDATE app_config SET value = $1 WHERE section = $2 AND key_name = $3", | |
| json_value, section, key | |
| ) | |
| print(f" Updated [{section}].{key}") | |
| else: | |
| await conn.execute( | |
| "INSERT INTO app_config (section, key_name, value) VALUES ($1, $2, $3)", | |
| section, key, json_value | |
| ) | |
| print(f" Inserted [{section}].{key}") | |
| print("=== Database config synced successfully ===") | |
| finally: | |
| await conn.close() | |
| except Exception as e: | |
| print(f"=== DB sync warning (non-fatal): {e} ===", file=sys.stderr) | |
| asyncio.run(sync_config()) | |
| PYEOF | |
| fi | |
| # ============================================================ | |
| # 4. 启动服务 | |
| # ============================================================ | |
| if [ -f /app/scripts/init_storage.sh ]; then | |
| /app/scripts/init_storage.sh || echo "=== DB Init Skipped ===" | |
| else | |
| echo "=== init_storage.sh not found, skipping ===" | |
| fi | |
| echo "=== Starting Server ===" | |
| cd /app | |
| # 激活虚拟环境(如果存在) | |
| if [ -f /opt/venv/bin/activate ]; then | |
| echo "=== Activating virtual environment ===" | |
| . /opt/venv/bin/activate | |
| fi | |
| # 尝试多种方式启动 uvicorn | |
| if command -v uvicorn >/dev/null 2>&1; then | |
| exec uvicorn main:app --host 0.0.0.0 --port 8000 | |
| elif /opt/venv/bin/python3 -m uvicorn --version >/dev/null 2>&1; then | |
| exec /opt/venv/bin/python3 -m uvicorn main:app --host 0.0.0.0 --port 8000 | |
| elif command -v python3 >/dev/null 2>&1; then | |
| exec python3 -m uvicorn main:app --host 0.0.0.0 --port 8000 | |
| elif command -v python >/dev/null 2>&1; then | |
| exec python -m uvicorn main:app --host 0.0.0.0 --port 8000 | |
| else | |
| echo "ERROR: Neither uvicorn nor python found!" | |
| exit 1 | |
| fi | |