refactor(deploy-vercel): streamline deployment to Vercel, remove Docker/local deployment files, and harden path handling

This commit is contained in:
cto-new[bot]
2026-01-23 18:47:39 +00:00
parent affd9b2da9
commit af9be25f20
11 changed files with 192 additions and 198 deletions

86
app.py
View File

@@ -2,6 +2,8 @@ import base64
import ctypes
import json
import logging
import os
import sys
import queue
import random
import re
@@ -16,20 +18,45 @@ from fastapi.responses import JSONResponse, StreamingResponse
from fastapi.templating import Jinja2Templates
from wasmtime import Linker, Module, Store
# -------------------------- 获取项目根目录 --------------------------
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
IS_VERCEL = bool(os.getenv("VERCEL")) or bool(os.getenv("NOW_REGION"))
def resolve_path(env_key: str, default_rel: str) -> str:
raw = os.getenv(env_key)
if raw:
return raw if os.path.isabs(raw) else os.path.join(BASE_DIR, raw)
return os.path.join(BASE_DIR, default_rel)
# -------------------------- 初始化 tokenizer --------------------------
chat_tokenizer_dir = "./"
chat_tokenizer_dir = resolve_path("DS2API_TOKENIZER_DIR", "")
tokenizer = transformers.AutoTokenizer.from_pretrained(
chat_tokenizer_dir, trust_remote_code=True
)
# -------------------------- 日志配置 --------------------------
logging.basicConfig(
level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s"
level=os.getenv("LOG_LEVEL", "INFO").upper(),
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[logging.StreamHandler(sys.stdout)],
force=True,
)
logger = logging.getLogger("main")
app = FastAPI()
@app.exception_handler(Exception)
async def unhandled_exception_handler(request: Request, exc: Exception):
logger.exception(f"[unhandled_exception] {request.method} {request.url.path}: {exc}")
return JSONResponse(
status_code=500,
content={"error": {"type": "api_error", "message": "Internal Server Error"}},
)
# 添加 CORS 中间件,允许所有来源
app.add_middleware(
CORSMiddleware,
@@ -40,34 +67,68 @@ app.add_middleware(
)
# 模板目录
templates = Jinja2Templates(directory="templates")
templates = Jinja2Templates(directory=resolve_path("DS2API_TEMPLATES_DIR", "templates"))
# ----------------------------------------------------------------------
# (1) 配置文件的读写函数
# ----------------------------------------------------------------------
CONFIG_PATH = "config.json"
CONFIG_PATH = resolve_path("DS2API_CONFIG_PATH", "config.json")
def load_config():
"""从 config.json 加载配置,出错则返回空 dict"""
"""加载配置。
优先从环境变量读取:
- DS2API_CONFIG_JSON / CONFIG_JSON: 直接 JSON 字符串,或 base64 编码后的 JSON
若未提供环境变量,再从 CONFIG_PATH 指向的文件读取。
"""
raw_cfg = os.getenv("DS2API_CONFIG_JSON") or os.getenv("CONFIG_JSON")
if raw_cfg:
try:
return json.loads(raw_cfg)
except json.JSONDecodeError:
try:
decoded = base64.b64decode(raw_cfg).decode("utf-8")
return json.loads(decoded)
except Exception as e:
logger.warning(f"[load_config] 环境变量配置解析失败: {e}")
return {}
try:
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
logger.warning(f"[load_config] 无法读取配置文件: {e}")
logger.warning(f"[load_config] 无法读取配置文件({CONFIG_PATH}): {e}")
return {}
def save_config(cfg):
"""将配置写回 config.json"""
"""将配置写回 config.json
Vercel 环境文件系统通常是只读的;且如果配置来自环境变量,也无法回写。
所以这里失败不应影响主流程。
"""
if os.getenv("DS2API_CONFIG_JSON") or os.getenv("CONFIG_JSON"):
logger.info("[save_config] 配置来自环境变量,跳过写回")
return
try:
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
json.dump(cfg, f, ensure_ascii=False, indent=2)
except PermissionError as e:
logger.warning(f"[save_config] 配置文件不可写({CONFIG_PATH}): {e}")
except Exception as e:
logger.error(f"[save_config] 写入 config.json 失败: {e}")
logger.exception(f"[save_config] 写入 config.json 失败: {e}")
CONFIG = load_config()
if not CONFIG:
logger.warning(
"[config] 未加载到有效配置,请提供 config.json路径可用 DS2API_CONFIG_PATH 指定)或设置环境变量 DS2API_CONFIG_JSON"
)
# -------------------------- 全局账号队列 --------------------------
account_queue = [] # 维护所有可用账号
@@ -116,7 +177,7 @@ BASE_HEADERS = {
CLAUDE_DEFAULT_MODEL = "claude-sonnet-4-20250514" # Claude统一默认模型
# WASM 模块文件路径
WASM_PATH = "sha3_wasm_bg.7b9ca65ddd.wasm"
WASM_PATH = resolve_path("DS2API_WASM_PATH", "sha3_wasm_bg.7b9ca65ddd.wasm")
# ----------------------------------------------------------------------
@@ -1914,9 +1975,10 @@ def index(request: Request):
# ----------------------------------------------------------------------
# 启动 FastAPI 应用
# 启动 FastAPI 应用(仅本地运行)
# ----------------------------------------------------------------------
if __name__ == "__main__":
if __name__ == "__main__" and not IS_VERCEL:
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5001)
port = int(os.getenv("PORT", "5001"))
uvicorn.run(app, host="0.0.0.0", port=port)