From 69e825d0e2f3686cfe18fef31718dd3e4e8fd84c Mon Sep 17 00:00:00 2001 From: jiaguangqi <52811251+jiaguangqi@users.noreply.github.com> Date: Mon, 10 Aug 2026 22:29:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8F=91=E5=B8=83=20API=20Hub=20v2.1.0?= =?UTF-8?q?=20=E6=B5=81=E5=BC=8F=E4=B8=8E=E6=99=BA=E8=83=BD=E4=BD=93?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ARCHITECTURE.md | 4 +- CHANGELOG.md | 34 ++++++ README.md | 1 + README_zh.md | 1 + database.sql | 24 +---- index.html | 55 ++++++---- package.json | 11 ++ server.js | 271 ++++++++++++++++++++++++++++++++++++++++-------- 8 files changed, 311 insertions(+), 90 deletions(-) create mode 100644 CHANGELOG.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0739efb..dc66259 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -99,7 +99,7 @@ API 请求 ```bash # .env 文件 -JWT_SECRET=api-hub-v2-jiaguangqi-jwt-secret-2025 +JWT_SECRET=<使用 openssl rand -hex 32 生成的独立密钥> ``` ⚠️ **重要**: JWT_SECRET 必须固定,否则服务器重启后所有 token 失效 @@ -305,7 +305,7 @@ npm install # 3. 配置环境变量 cat > .env << 'EOF' -JWT_SECRET=your-fixed-secret-key +JWT_SECRET=<使用 openssl rand -hex 32 生成的独立密钥> EOF # 4. 启动后端服务 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..eafba3c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog + +本项目的重要变更均记录在此文件中。 + +## 2.1.0 - 2026-08-10 + +### 新增 + +- 支持 OpenAI 兼容的 SSE 流式对话转发。 +- 完整透传 `tools`、`tool_choice`、`response_format`、采样参数和 token 限制等智能体参数。 +- 客户端中断流式请求时,同步取消上游请求。 + +### 优化 + +- 新增渠道时强制校验渠道名称、上游地址和 API Key。 +- 新增或修改模型时,校验关联渠道处于启用状态且已配置 API Key。 +- 渠道 Key 读取兼容 `key`、`keys` 和 `secret_key` 字段。 +- 模型弹窗打开时重置上次选中的渠道,避免错误绑定。 + +### 修复 + +- 修复 `stream: true` 时将 SSE 当作 JSON 解析而返回 HTTP 500 的问题。 +- 修复上游非 2xx 状态被错误包装为 HTTP 200 的问题。 +- 移除模型测试时随机尝试无关渠道的回退逻辑。 + +### 安全 + +- 清理 `database.sql` 中的运行数据与凭据,仅保留数据库结构。 +- 将架构文档中的固定 JWT 密钥替换为安全生成指引。 + +### 部署 + +- 版本号设置为 `2.1.0`。 +- 本版本不需要数据库迁移,不新增环境变量。 diff --git a/README.md b/README.md index 7c34948..6721be3 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ English | [简体中文](./README_zh.md) - ✅ **API Key Management** - Personal API keys for users - ✅ **Model Playground** - Online testing experience center - ✅ **OpenAI-compatible API** - Drop-in replacement for OpenAI API +- ✅ **Streaming & Agent Tools** - SSE streaming plus `tools` / `tool_choice` passthrough - ✅ **Dynamic IP Configuration** - Auto-detect server IP during deployment ## 🏗️ Architecture diff --git a/README_zh.md b/README_zh.md index 0dc9fe5..73fc059 100644 --- a/README_zh.md +++ b/README_zh.md @@ -12,6 +12,7 @@ - ✅ **API Key 管理** - 为用户提供个人 API Key - ✅ **模型广场** - 在线体验中心,可直接测试模型 - ✅ **OpenAI 兼容接口** - 完美兼容 OpenAI API 格式 +- ✅ **流式与智能体工具** - 支持 SSE 流式输出及 `tools` / `tool_choice` 透传 - ✅ **动态 IP 配置** - 部署时自动检测服务器 IP ## 🏗️ 技术架构 diff --git a/database.sql b/database.sql index a5b44e8..2dc6fac 100644 --- a/database.sql +++ b/database.sql @@ -1,5 +1,6 @@ PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; +-- Schema-only bootstrap: runtime data, user records and credentials are intentionally excluded. CREATE TABLE inf_users ( id TEXT PRIMARY KEY, account TEXT UNIQUE NOT NULL, @@ -18,8 +19,6 @@ CREATE TABLE inf_users ( invite_code TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP , balance REAL DEFAULT 0); -INSERT INTO inf_users VALUES('d2dd171f-bccc-4eba-af92-ed8f7a1ba06d','admin','系统管理员',NULL,'$2b$10$oCtxdm3WEUY4DAELIhtmaOCSGVav79Xy7Z.6AJnDvUbrte5ARjoCK','inf_admin_default',NULL,NULL,'SUCCESS','admin','ENABLED','default',0,0,NULL,'2026-02-11 07:03:53',0.0); -INSERT INTO inf_users VALUES('ba4f6618-45f5-4b12-b003-02c29e37ffa3','test1','test1','test1@qq.com','$2b$10$q6tBzOKSVWuTMezfmxdgHuNteCzcDqB/gYp9.y24oCQNu0SLpc85q','inf_f6242314-8875-4778-b37a-25d1b1a6299a',NULL,NULL,'SUCCESS','user','ENABLED','default',10000,0,NULL,'2026-02-11 08:57:16',0.0); CREATE TABLE inf_channels ( id TEXT PRIMARY KEY, name TEXT NOT NULL, @@ -44,7 +43,6 @@ CREATE TABLE inf_channels ( auto_ban INTEGER DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -INSERT INTO inf_channels VALUES('86a4fd64-e77a-437e-989f-1774bdc5ad7d','云雾',1,'sk-5x6EJaCNn5ROlCvnPoPVQ39iXSchWHQKCmdMFc0YqCVyOIFs','sk-5x6EJaCNn5ROlCvnPoPVQ39iXSchWHQKCmdMFc0YqCVyOIFs','OPENAI_COMPATIBLE','https://yunwu.ai/v1','sk-5x6EJaCNn5ROlCvnPoPVQ39iXSchWHQKCmdMFc0YqCVyOIFs','SYNC',1,10,100,1802,0.0,0,'云雾AI API 提供国内外知名模型',NULL,'default',NULL,1770799105,1,'2026-02-11 07:05:00'); CREATE TABLE inf_abilities ( id INTEGER PRIMARY KEY AUTOINCREMENT, channel_id TEXT NOT NULL, @@ -54,7 +52,6 @@ CREATE TABLE inf_abilities ( weight INTEGER DEFAULT 1, UNIQUE(channel_id, model) ); -INSERT INTO inf_abilities VALUES(5,'86a4fd64-e77a-437e-989f-1774bdc5ad7d','云雾AI API 提供国内外知名模型',1,0,1); CREATE TABLE inf_models ( id TEXT PRIMARY KEY, model_id TEXT UNIQUE NOT NULL, @@ -67,9 +64,6 @@ CREATE TABLE inf_models ( enabled_status TEXT DEFAULT 'ENABLED', created_at DATETIME DEFAULT CURRENT_TIMESTAMP , tags TEXT); -INSERT INTO inf_models VALUES('b3385c11-25df-467e-a699-43f7c9959da2','gemini-3-flash-preview','gemini-3-flash-preview','云雾','TEXT',NULL,0.0,0.0,'ENABLED','2026-02-11 08:32:57','文本,多模态,语音'); -INSERT INTO inf_models VALUES('01301ea0-d025-41d0-8f11-2f99ef0d6fcc','claude-sonnet-4-20250514','claude-sonnet-4-20250514','云雾','TEXT',NULL,0.0,0.0,'ENABLED','2026-02-11 08:32:57','文本,文生图'); -INSERT INTO inf_models VALUES('8e32d917-cc45-4be3-93a0-bb3fb4fa4a32','gpt-4o-mini-2024-07-18','gpt-4o-mini-2024-07-18','云雾','TEXT',NULL,0.0,0.0,'ENABLED','2026-02-11 08:32:57','文本,多模态,语音'); CREATE TABLE inf_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, channel_id TEXT, @@ -96,7 +90,6 @@ CREATE TABLE inf_settings ( value TEXT NOT NULL, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -INSERT INTO inf_settings VALUES('smtp','{"smtp_account":"hpcadmin@163.com","smtp_password":"YAnESUx2YTvCdJnX","smtp_server":"smtp.163.com","smtp_port":"465","pop3_server":"pop.163.com","imap_server":"imap.163.com","smtp_from_name":"AI_API_HUB"}','2026-02-11 10:45:51'); CREATE TABLE inf_payment_settings ( id INTEGER PRIMARY KEY CHECK (id = 1), yipay_url TEXT, @@ -121,7 +114,6 @@ CREATE TABLE inf_topup_orders ( created_at DATETIME DEFAULT CURRENT_TIMESTAMP, paid_at DATETIME ); -INSERT INTO inf_topup_orders VALUES('e0864e2f-68ec-4559-9dd9-afd4c276394c','d2dd171f-bccc-4eba-af92-ed8f7a1ba06d',50.0,50.0,'alipay','PENDING','TOPUP1770901834099HA3X6S','2026-02-12 13:10:34',NULL); CREATE TABLE inf_site_settings ( id INTEGER PRIMARY KEY CHECK (id = 1), site_name TEXT DEFAULT 'AI API Hub', @@ -133,14 +125,8 @@ CREATE TABLE inf_site_settings ( email_verify INTEGER DEFAULT 0, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -INSERT INTO inf_site_settings VALUES(1,'OGAI API Hub','大模型 API 管理平台','','','© 2024 AI API Hub. All rights reserved.',1,0,'2026-02-11 12:22:43'); CREATE TABLE user_api_keys (id TEXT PRIMARY KEY, user_id TEXT NOT NULL, name TEXT NOT NULL, api_key TEXT UNIQUE NOT NULL, api_key_masked TEXT NOT NULL, status TEXT DEFAULT "ENABLED", usage_count INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_used_at DATETIME, quota INTEGER DEFAULT 100, used_quota INTEGER DEFAULT 0, expires_at DATETIME, allowed_models TEXT DEFAULT "all"); -INSERT INTO user_api_keys VALUES('139f0ac2-3184-4673-ab2f-00f092fb6207','d2dd171f-bccc-4eba-af92-ed8f7a1ba06d','test1','sk-8f5e061c4f2c0124a17ebb3d88ba67afcad4bf1079a0f599','sk-8f5e061******f599','ENABLED',0,'2026-02-12 06:36:43',NULL,100,0,'2027-02-12T06:36:43.335Z','all'); CREATE TABLE api_key_usage_logs (id INTEGER PRIMARY KEY AUTOINCREMENT, api_key_id INTEGER, api_key_masked TEXT, user_id INTEGER, model_id TEXT, input_tokens INTEGER, output_tokens INTEGER, total_tokens INTEGER, cost REAL, request_type TEXT, note TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP); -INSERT INTO api_key_usage_logs VALUES(1,1,'sk-8f5e061******f599',1,'gpt-4o-mini-2024-07-18',1500,800,2300,0.050000000000000002775,NULL,'正常调用','2026-02-12 08:35:21'); -INSERT INTO api_key_usage_logs VALUES(2,1,'sk-8f5e061******f599',1,'claude-sonnet-4-20250514',2500,1200,3700,0.080000000000000001665,NULL,'代码生成','2026-02-12 08:35:21'); -INSERT INTO api_key_usage_logs VALUES(3,1,'sk-8f5e061******f599',1,'gemini-3-flash-preview',2000,500,2500,0.029999999999999998889,NULL,'数据分析','2026-02-12 08:35:21'); -INSERT INTO api_key_usage_logs VALUES(4,1,'sk-8f5e061******f599',1,'gpt-4o-mini-2024-07-18',500,300,800,0.020000000000000000416,NULL,'简单查询','2026-02-12 08:35:21'); CREATE TABLE user_bills ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, @@ -154,14 +140,6 @@ CREATE TABLE user_bills ( created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES inf_users(id) ); -INSERT INTO user_bills VALUES(1,'1','consumption','USAGE1',-0.050000000000000002775,0.0,0.0,'API调用 gpt-4o-mini-2024-07-18','1','2026-02-12 08:35:21'); -INSERT INTO user_bills VALUES(2,'1','consumption','USAGE2',-0.080000000000000001665,0.0,0.0,'API调用 claude-sonnet-4-20250514','2','2026-02-12 08:35:21'); -INSERT INTO user_bills VALUES(3,'1','consumption','USAGE3',-0.029999999999999998889,0.0,0.0,'API调用 gemini-3-flash-preview','3','2026-02-12 08:35:21'); -INSERT INTO user_bills VALUES(4,'1','consumption','USAGE4',-0.020000000000000000416,0.0,0.0,'API调用 gpt-4o-mini-2024-07-18','4','2026-02-12 08:35:21'); -DELETE FROM sqlite_sequence; -INSERT INTO sqlite_sequence VALUES('inf_abilities',5); -INSERT INTO sqlite_sequence VALUES('api_key_usage_logs',4); -INSERT INTO sqlite_sequence VALUES('user_bills',4); CREATE INDEX idx_usage_user ON api_key_usage_logs(user_id); CREATE INDEX idx_bills_user ON user_bills(user_id); CREATE INDEX idx_bills_type ON user_bills(bill_type); diff --git a/index.html b/index.html index 41104da..1bad0c3 100644 --- a/index.html +++ b/index.html @@ -942,12 +942,7 @@