mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-02 07:25:26 +08:00
refactor(deploy-vercel): streamline deployment to Vercel, remove Docker/local deployment files, and harden path handling
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
config.json
|
||||
docker-compose.yml
|
||||
.gitignore
|
||||
.github
|
||||
vercel.json
|
||||
.releaserc.json
|
||||
CHANGELOG.md
|
||||
version.txt
|
||||
README.MD
|
||||
49
.gitignore
vendored
49
.gitignore
vendored
@@ -1,2 +1,49 @@
|
||||
*.bak
|
||||
config.json
|
||||
config.json
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# Virtual environments
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
.venv
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
uvicorn.log
|
||||
|
||||
# Vercel
|
||||
.vercel
|
||||
|
||||
11
Dockerfile
11
Dockerfile
@@ -1,11 +0,0 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . /app
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
EXPOSE 5001
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
153
README.MD
153
README.MD
@@ -13,10 +13,7 @@
|
||||
* [免责声明](#免责声明)
|
||||
* [接入准备](#接入准备)
|
||||
* [多账号接入](#多账号接入)
|
||||
* [Docker部署](#Docker部署)
|
||||
* [Docker-compose部署](#Docker-compose部署)
|
||||
* [Vercel部署](#Vercel部署)
|
||||
* [原生部署](#原生部署)
|
||||
* [接口列表](#接口列表)
|
||||
* [模型列表](#模型列表)
|
||||
* [对话补全](#对话补全)
|
||||
@@ -48,18 +45,28 @@
|
||||
|
||||
每次请求服务会从中挑选一个。
|
||||
|
||||
## Docker部署
|
||||
## Vercel部署
|
||||
|
||||
请准备一台具有公网IP的服务器并将5001端口开放。
|
||||
> [!NOTE]
|
||||
> Vercel免费账户的请求响应超时时间为10秒,但接口响应通常较久,可能会遇到Vercel返回的504超时错误!
|
||||
|
||||
配置 config.json
|
||||
[](https://vercel.com/import/project?template=https://github.com/iidamie/deepseek2api)
|
||||
|
||||
### 部署步骤
|
||||
|
||||
1. **点击上方按钮一键部署到 Vercel**
|
||||
|
||||
2. **配置方式选择(二选一)**
|
||||
|
||||
#### 方式 A:使用配置文件(推荐用于开发测试)
|
||||
|
||||
部署完成后,返回你的 Github 仓库,创建/编辑 `config.json` 文件(可参考 `config.example.json`):
|
||||
|
||||
在 `deepseek` 目录下,创建 config.json文件
|
||||
```json
|
||||
{
|
||||
"keys": [
|
||||
"key1",
|
||||
"key2"
|
||||
"your-api-key-1",
|
||||
"your-api-key-2"
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
@@ -76,126 +83,48 @@
|
||||
"mobile": "12345678901",
|
||||
"password": "password3",
|
||||
"token": ""
|
||||
},
|
||||
{
|
||||
"mobile": "12345678901",
|
||||
"password": "password4",
|
||||
"token": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
* keys - 你的 API 鉴权密钥
|
||||
* accounts - DeepSeek 账号列表,支持多个账号轮换,避免单账号受限
|
||||
|
||||
拉取镜像并启动服务。
|
||||
|
||||
```shell
|
||||
docker run -d -p 5001:5001 -v "$(pwd)/config.json:/app/config.json" --name deepseek2api ghcr.io/iidamie/deepseek2api:latest
|
||||
```
|
||||
|
||||
查看服务实时日志
|
||||
|
||||
```shell
|
||||
docker logs -f deepseek2api
|
||||
```
|
||||
|
||||
重启服务
|
||||
|
||||
```shell
|
||||
docker restart deepseek2api
|
||||
```
|
||||
|
||||
停止服务
|
||||
|
||||
```shell
|
||||
docker stop deepseek2api
|
||||
```
|
||||
|
||||
## Docker-compose部署
|
||||
|
||||
拉取该项目
|
||||
```shell
|
||||
git clone https://github.com/iidamie/deepseek2api.git
|
||||
cd deepseek2api
|
||||
```
|
||||
配置 config.json
|
||||
|
||||
在当前目录下,修改 config.json 文件
|
||||
* keys - 你的 API 鉴权密钥
|
||||
* accounts - DeepSeek 账号列表,支持多个账号轮换,避免单账号受限
|
||||
|
||||
启动服务
|
||||
|
||||
```shell
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
查看服务实时日志
|
||||
|
||||
```shell
|
||||
docker logs -f deepseek2api
|
||||
```
|
||||
|
||||
重启服务
|
||||
|
||||
```shell
|
||||
docker restart deepseek2api
|
||||
```
|
||||
|
||||
停止服务
|
||||
|
||||
```shell
|
||||
docker stop deepseek2api
|
||||
```
|
||||
|
||||
## Vercel部署
|
||||
|
||||
> [!NOTE]
|
||||
> Vercel免费账户的请求响应超时时间为10秒,但接口响应通常较久,可能会遇到Vercel返回的504超时错误!
|
||||
|
||||
[](https://vercel.com/import/project?template=https://github.com/iidamie/deepseek2api)
|
||||
|
||||
部署完后,返回你的 Github 仓库配置 config.json
|
||||
* keys - 你的 API 鉴权密钥
|
||||
* accounts - DeepSeek 账号列表,支持多个账号轮换,避免单账号受限
|
||||
配置完成后,返回 Vercel 控制台,点击 "Redeploy" 重新部署即可。
|
||||
|
||||
> [!WARNING]
|
||||
> 一定要是私库,防止信息泄露!
|
||||
|
||||
配置完后,返回 Vercel 控制台,重新部署即可
|
||||
#### 方式 B:使用环境变量(推荐用于生产环境)
|
||||
|
||||
## 原生部署
|
||||
在 Vercel 控制台的 Settings → Environment Variables 中添加以下环境变量:
|
||||
|
||||
请准备一台具有公网IP的服务器并将5001端口开放。
|
||||
- **环境变量名**:`DS2API_CONFIG_JSON` 或 `CONFIG_JSON`
|
||||
- **环境变量值**:配置内容的 JSON 字符串(可选:使用 base64 编码)
|
||||
|
||||
请先安装好 Python 环境并且配置好环境变量,确认 python 命令可用。
|
||||
|
||||
安装依赖
|
||||
|
||||
```shell
|
||||
git clone https://github.com/iidamie/deepseek2api.git
|
||||
cd deepseek2api
|
||||
pip install -r requirements.txt
|
||||
**JSON 字符串示例**:
|
||||
```json
|
||||
{"keys":["your-api-key-1"],"accounts":[{"email":"example@example.com","password":"your-password","token":""}]}
|
||||
```
|
||||
|
||||
配置 config.json
|
||||
|
||||
在当前目录下,修改 config.json 文件
|
||||
* keys - 你的 API 鉴权密钥
|
||||
* accounts - DeepSeek 账号列表,支持多个账号轮换,避免单账号受限
|
||||
|
||||
启动服务
|
||||
|
||||
```shell
|
||||
python app.py
|
||||
**Base64 编码示例**(Linux/Mac):
|
||||
```bash
|
||||
echo '{"keys":["your-api-key-1"],"accounts":[{"email":"example@example.com","password":"your-password","token":""}]}' | base64
|
||||
```
|
||||
|
||||
使用 nohup 启动
|
||||
添加环境变量后,在 Vercel 控制台重新部署即可。
|
||||
|
||||
```shell
|
||||
nohup uvicorn app:app --host 0.0.0.0 --port 5001 > uvicorn.log 2>&1 &
|
||||
```
|
||||
### 配置说明
|
||||
|
||||
* `keys` - 你的 API 鉴权密钥(可设置多个)
|
||||
* `accounts` - DeepSeek 账号列表,支持多个账号轮换,避免单账号受限
|
||||
* 支持使用 `email` 或 `mobile` 登录
|
||||
* `token` 字段可以留空,系统会在首次使用时自动登录并填充(注意:Vercel 环境下 token 无法回写)
|
||||
|
||||
### 注意事项
|
||||
|
||||
- 推荐使用**环境变量**方式配置,更加安全
|
||||
- 如使用配置文件方式,确保你的 GitHub 仓库是**私有仓库**
|
||||
- Vercel 免费版有请求时长限制(10秒),如遇 504 错误属正常现象
|
||||
- Vercel 环境下文件系统是只读的,token 自动更新功能可能受限
|
||||
|
||||
## 接口列表
|
||||
|
||||
|
||||
86
app.py
86
app.py
@@ -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)
|
||||
|
||||
23
config.example.json
Normal file
23
config.example.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"keys": [
|
||||
"your-api-key-1",
|
||||
"your-api-key-2"
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"email": "example1@example.com",
|
||||
"password": "your-password-1",
|
||||
"token": ""
|
||||
},
|
||||
{
|
||||
"email": "example2@example.com",
|
||||
"password": "your-password-2",
|
||||
"token": ""
|
||||
},
|
||||
{
|
||||
"mobile": "12345678901",
|
||||
"password": "your-password-3",
|
||||
"token": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
18
config.json
18
config.json
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"keys": [
|
||||
"tetr5354",
|
||||
"tetr20071102"
|
||||
],
|
||||
"accounts": [
|
||||
{
|
||||
"email": "cjackhwang@gmail.com",
|
||||
"password": "tetr5354",
|
||||
"token": ""
|
||||
},
|
||||
{
|
||||
"email": "cjackhwang@outlook.com",
|
||||
"password": "tetr5354",
|
||||
"token": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
deepseek2api:
|
||||
container_name: deepseek2api
|
||||
image: ghcr.io/iidamie/deepseek2api:latest
|
||||
restart: always
|
||||
ports:
|
||||
- "5001:5001"
|
||||
volumes:
|
||||
- "./config.json:/app/config.json"
|
||||
@@ -1,15 +0,0 @@
|
||||
@echo off
|
||||
:: 创建 conda 环境,Python 3.11,环境名为 dsapi
|
||||
echo 正在创建 conda 环境 dsapi...
|
||||
call conda create -n dsapi python=3.11 -y
|
||||
|
||||
:: 激活 conda 环境
|
||||
echo 正在激活环境 dsapi...
|
||||
call conda activate dsapi
|
||||
|
||||
:: 安装 requirements.txt 中的依赖(使用 pip)
|
||||
echo 正在安装 requirements.txt 中的依赖...
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo 环境 dsapi 已创建并安装好依赖。
|
||||
pause
|
||||
@@ -1,6 +1,6 @@
|
||||
fastapi
|
||||
uvicorn
|
||||
curl_cffi
|
||||
transformers
|
||||
wasmtime
|
||||
jinja2
|
||||
fastapi>=0.110.0,<1.0.0
|
||||
uvicorn>=0.24.0,<1.0.0
|
||||
curl_cffi>=0.7.0,<1.0.0
|
||||
transformers>=4.39.0,<5.0.0
|
||||
wasmtime>=14.0.0,<20.0.0
|
||||
jinja2>=3.1.0,<4.0.0
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
call conda activate dsapi
|
||||
python app.py
|
||||
pause
|
||||
Reference in New Issue
Block a user