clash-linux commited on
Commit
9b0bfeb
·
verified ·
1 Parent(s): 68970aa

Upload 14 files

Browse files
.env.example CHANGED
@@ -4,11 +4,14 @@ NOTION_COOKIE="cookie1|cookie2"
4
  #COOKIE_FILE="./cookies.txt"
5
  # (可选)反代密码,没有的话默认密码是 default_token
6
  PROXY_AUTH_TOKEN="sk-text"
7
- # (可选)代理地址,启用tls后失效
8
  PROXY_URL=""
9
  # (可选)是否使用本地代理池,启用前确保没有设置其它代理,否则速度会减慢或者出现其它问题,推荐启用
10
  USE_NATIVE_PROXY_POOL=true
11
 
 
 
 
12
  # tls代理服务器配置(可选)建议启用
13
  # 平台选择:auto(自动检测), windows, linux, android
14
  PROXY_SERVER_PLATFORM="auto"
 
4
  #COOKIE_FILE="./cookies.txt"
5
  # (可选)反代密码,没有的话默认密码是 default_token
6
  PROXY_AUTH_TOKEN="sk-text"
7
+ # (可选)代理地址
8
  PROXY_URL=""
9
  # (可选)是否使用本地代理池,启用前确保没有设置其它代理,否则速度会减慢或者出现其它问题,推荐启用
10
  USE_NATIVE_PROXY_POOL=true
11
 
12
+ # (可选)代理国家,支持us uk jp de fr ca
13
+ PROXY_COUNTRY="us"
14
+
15
  # tls代理服务器配置(可选)建议启用
16
  # 平台选择:auto(自动检测), windows, linux, android
17
  PROXY_SERVER_PLATFORM="auto"
README.md CHANGED
@@ -1,128 +1,207 @@
1
- ---
2
- title: Notion
3
- emoji: 📝
4
- colorFrom: indigo
5
- colorTo: blue
6
- sdk: docker
7
- app_port: 7860
8
- ---
9
 
10
- # Notion API 轻量级客户端 (Hugging Face Space)
11
 
12
- 这个项目提供了一个轻量级的 Notion API 客户端,可以作为 Web 服务部署在 Hugging Face Spaces 上。它兼容 OpenAI API 格式,让你可以将 Notion AI 作为后端集成到各种应用中。
13
 
14
- ## 🚀 Hugging Face Spaces 上部署
 
 
 
15
 
16
- 1. **创建 Space**: 在 Hugging Face 上创建一个新的 Space。
17
- 2. **选择 Docker SDK**: 在 "Choose an SDK" 步骤中,选择 "Docker"。
18
- 3. **上传文件**: 将此项目的所有文件(包括 `Dockerfile`)上传到你的 Space Git 仓库中。
19
- 4. **设置 Secrets**: 这是最重要的一步。你的 Notion 凭证和其他配置需要作为 Secrets 添加到 Space 中。进入你的 Space "Settings" 页面,找到 "Repository secrets" 部分,然后点击 "New secret" 添加以下变量:
20
 
21
- * **必需**:
22
- * `NOTION_COOKIE`: 你的 Notion Cookie。你可以通过浏览器开发者工具获取。
23
- * **可选**:
24
- * `NOTION_SPACE_ID`: 你的 Notion Space ID。
25
- * `NOTION_ACTIVE_USER_HEADER`: 你的 Notion 用户 ID。
26
- * `PROXY_URL`: 如果你需要通过代理访问 Notion,请设置此项 (例如 `http://user:pass@host:port`)。
27
- * `PROXY_AUTH_TOKEN`: 如果你的代理需要单独的认证令牌,请设置此项。
28
- * `COOKIE_FILE`: 如果你使用文件管理多个 Cookie,请设置为文件名 (例如 `cookies.txt`)。请确保该文件也已上传到仓库中。
29
 
30
- **注意**: `PORT` 环境变量由 Hugging Face 自动处理,你无需设置。
31
 
32
- 5. **等待部署**: 添加完 Secrets 后,Hugging Face 会自动构建 Docker 镜像并部署你的应用。你可以在 "Logs" 标签页查看部署进度和应用日志。部署成功后,你的 API 端点即可使用。
 
 
33
 
34
- ## 📖 API 使用说明
35
 
36
- 服务启动后,你可以通过你的 Space URL 访问 API。
37
 
38
- **基础 URL**: `https://<your-space-name>.hf.space`
 
 
 
 
 
 
 
39
 
40
- ### API 端点
41
 
42
- - `GET /v1/models` - 获取可用模型列表 (主要是为了兼容 OpenAI 客户端)
43
- - `POST /v1/chat/completions` - 核心的聊天完成端点
44
- - `GET /health` - 健康检查端点
45
 
46
- ### 示例: 使用 cURL 调用
47
 
48
  ```bash
49
- curl -X POST https://<your-space-name>.hf.space/v1/chat/completions \
50
- -H "Content-Type: application/json" \
51
- -d '{
52
- "model": "openai-gpt-4.1",
53
- "messages": [
54
- {"role": "user", "content": "你好,请介绍一下你自己"}
55
- ],
56
- "stream": true
57
- }'
58
  ```
59
- *请将 `<your-space-name>` 替换为你的 Space 名称。*
60
 
61
- ## 🍪 Cookie 管理
62
 
63
- 为了提高服务的稳定性,你可以提供多个 Notion Cookie。
64
 
65
- ### 通过文件管理 Cookie
 
 
66
 
67
- 1. 在项目根目录创建一个 `cookies.txt` 或 `cookies.json` 文件。
68
- 2. 将文件上传到你的 Space 仓库。
69
- 3. 在 Space Secrets 中设置 `COOKIE_FILE` 为你的文件名 (例如 `cookies.txt`)。
70
 
71
- 系统启动时会自动从该文件加载Cookie。
 
 
72
 
73
- **`cookies.txt` 示例:** (每行一个Cookie)
 
 
 
 
 
74
  ```
75
- cookie1_string_here
76
- cookie2_string_here
 
 
 
 
77
  ```
78
 
79
- **`cookies.json` 示例:**
80
- ```json
81
- {
82
- "cookies": [
83
- "cookie1_string_here",
84
- "cookie2_string_here"
85
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  }
87
  ```
88
 
89
- ### Cookie 轮询机制
90
 
91
- 系统会自动轮询使用所有有效的Cookie。当一个Cookie失效时(例如返回401错误),会自动切换到下一个有效的Cookie,确保服务不中断。
 
 
92
 
93
- ---
94
 
95
- ## 本地开发参考
 
 
 
 
96
 
97
- 以下信息用于在本地计算机上运行和开发。
98
 
99
- ### 依赖项
100
- ```bash
101
- npm install
102
- ```
 
 
 
103
 
104
- ### 环境变量
105
- 创建 `.env` 文件,设置以下环境变量:
106
  ```
107
- NOTION_COOKIE=your_notion_cookie_here
108
- NOTION_SPACE_ID=optional_space_id
109
- NOTION_ACTIVE_USER_HEADER=optional_user_id
110
- PROXY_URL=optional_proxy_url
111
- PROXY_AUTH_TOKEN=your_auth_token
112
- PORT=7860
113
  ```
114
 
115
- ### 启动服务
116
- ```bash
117
- npm start
 
118
  ```
119
 
120
- ### Cookie 管理命令行工具
121
- 项目提供了一个命令行工具来方便地管理 `cookies.json` 文件。
 
 
 
 
122
  ```bash
123
- # 运行命令行工具
124
  npm run cookie
125
 
126
- # 支持的命令
127
- help, list, add, validate, remove, save, load, exit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  ```
 
1
+ # Notion API 轻量级客户端
 
 
 
 
 
 
 
2
 
3
+ 这个项目提供了一个轻量级的 Notion API 客户端,可以在资源受限的环境(如 Termux)中运行,无需完整的浏览器环境。
4
 
5
+ ## 特点
6
 
7
+ - 使用 `node-fetch` 代替 Playwright 浏览器自动化
8
+ - 轻量级设计,适合在移动设备和资源受限环境中运行
9
+ - 支持 Notion AI 的流式响应
10
+ - 兼容 OpenAI API 格式的请求和响应
11
 
12
+ ## 安装
 
 
 
13
 
14
+ ### 依赖项
 
 
 
 
 
 
 
15
 
16
+ 确保安装以下依赖:
17
 
18
+ ```bash
19
+ npm install
20
+ ```
21
 
22
+ ### 环境变量
23
 
24
+ 创建 `.env` 文件,设置以下环境变量:
25
 
26
+ ```
27
+ NOTION_COOKIE=your_notion_cookie_here
28
+ NOTION_SPACE_ID=optional_space_id
29
+ NOTION_ACTIVE_USER_HEADER=optional_user_id
30
+ PROXY_URL=optional_proxy_url
31
+ PROXY_AUTH_TOKEN=your_auth_token
32
+ PORT=7860
33
+ ```
34
 
35
+ ## 使用方法
36
 
37
+ ### 启动服务
 
 
38
 
39
+ 运行轻量级服务器:
40
 
41
  ```bash
42
+ npm start
 
 
 
 
 
 
 
 
43
  ```
 
44
 
45
+ 服务器将在指定端口(默认 7860)启动。
46
 
47
+ 如果需要使用原始的基于 Playwright 的版本(不推荐在 Termux 中使用):
48
 
49
+ ```bash
50
+ npm run original
51
+ ```
52
 
53
+ ### API 端点
 
 
54
 
55
+ - `GET /v1/models` - 获取可用模型列表
56
+ - `POST /v1/chat/completions` - 聊天完成端点
57
+ - `GET /health` - 健康检查
58
 
59
+ ### Termux 中运行
60
+
61
+ 1. 安装 Termux 和 Node.js:
62
+ ```bash
63
+ pkg update
64
+ pkg install nodejs
65
  ```
66
+
67
+ 2. 克隆项目并安装依赖:
68
+ ```bash
69
+ git clone https://github.com/yourusername/notion2api-nodejs.git
70
+ cd notion2api-nodejs
71
+ npm install
72
  ```
73
 
74
+ 3. 设置环境变量并运行:
75
+ ```bash
76
+ npm start
77
+ ```
78
+
79
+ ## 作为模块集成
80
+
81
+ 你也可以将轻量级客户端作为模块导入到你自己的项目中:
82
+
83
+ ```javascript
84
+ import {
85
+ initialize,
86
+ streamNotionResponse,
87
+ buildNotionRequest,
88
+ FETCHED_IDS_SUCCESSFULLY
89
+ } from './lightweight-client.js';
90
+
91
+ // 初始化客户端
92
+ await initialize();
93
+
94
+ // 检查是否成功获取 Notion IDs
95
+ if (FETCHED_IDS_SUCCESSFULLY) {
96
+ // 构建请求
97
+ const requestData = {
98
+ notion_model: "openai-gpt-4.1",
99
+ messages: [
100
+ { role: "user", content: "你好,请介绍一下自己" }
101
+ ]
102
+ };
103
+
104
+ const notionRequestBody = buildNotionRequest(requestData);
105
+
106
+ // 获取响应流
107
+ const stream = await streamNotionResponse(notionRequestBody);
108
+
109
+ // 处理响应
110
+ stream.on('data', chunk => {
111
+ console.log(chunk.toString());
112
+ });
113
  }
114
  ```
115
 
116
+ ## 故障排除
117
 
118
+ - 如果无法获取 Notion IDs,请确保提供了有效的 NOTION_COOKIE
119
+ - 对于网络问题,可以尝试设置 PROXY_URL
120
+ - 查看日志输出以获取详细的错误信息
121
 
122
+ ## 依赖说明
123
 
124
+ - `node-fetch`: 用于发送 HTTP 请求
125
+ - `jsdom`: 提供 DOM API 的轻量级模拟
126
+ - `dotenv`: 加载环境变量
127
+ - `express`: Web 服务器框架
128
+ - `https-proxy-agent`: 支持 HTTPS 代理
129
 
130
+ ## Cookie管理功能
131
 
132
+ 本项目新增了Cookie管理功能,可以更方便地管理多个Notion Cookie,避免在.env文件中手动编辑长字符串。
133
+
134
+ ### 使用方法
135
+
136
+ #### 1. 通过文件管理Cookie
137
+
138
+ 在项目根目录创建一个`cookies.txt`文件,每行一个完整的Cookie字符串:
139
 
 
 
140
  ```
141
+ cookie1_string_here
142
+ cookie2_string_here
143
+ cookie3_string_here
 
 
 
144
  ```
145
 
146
+ 然后在`.env`文件中设置:
147
+
148
+ ```
149
+ COOKIE_FILE=cookies.txt
150
  ```
151
 
152
+ 系统启动时会自动从该文件加载Cookie
153
+
154
+ #### 2. 使用Cookie管理工具
155
+
156
+ 项目提供了一个命令行工具来管理Cookie:
157
+
158
  ```bash
159
+ # 使用npm脚本运行
160
  npm run cookie
161
 
162
+ # 或者全局安装后运行
163
+ npm link
164
+ notion-cookie
165
+ ```
166
+
167
+ 命令行工具支持以下功能:
168
+
169
+ - `help`: 显示帮助信息
170
+ - `list`: 列出所有已加载的Cookie
171
+ - `add`: 添加新的Cookie
172
+ - `validate`: 验证所有Cookie的有效性
173
+ - `remove`: 删除指定的Cookie
174
+ - `save`: 保存Cookie到文件
175
+ - `load`: 从文件加载Cookie
176
+ - `exit`: 退出程序
177
+
178
+ ### Cookie轮询机制
179
+
180
+ 系统会自动轮询使用所有有效的Cookie,当一个Cookie返回401错误(未授权)时,会自动将其标记为无效并切换到下一个Cookie。这样可以提高系统的可靠性和可用性。
181
+
182
+ ### 文件格式支持
183
+
184
+ Cookie管理器支持两种文件格式:
185
+
186
+ 1. 文本格式(.txt):每行一个Cookie
187
+ 2. JSON格式(.json):包含Cookie数组的JSON文件
188
+
189
+ ```json
190
+ {
191
+ "cookies": [
192
+ "cookie1_string_here",
193
+ "cookie2_string_here"
194
+ ],
195
+ "updatedAt": "2023-08-01T12:00:00.000Z",
196
+ "count": 2
197
+ }
198
+ ```
199
+
200
+ 或者简单的数组:
201
+
202
+ ```json
203
+ [
204
+ "cookie1_string_here",
205
+ "cookie2_string_here"
206
+ ]
207
  ```
src/ProxyPool.js CHANGED
@@ -21,6 +21,7 @@ class ProxyPool {
21
  * @param {boolean} options.useCache - 是否使用缓存,默认true
22
  * @param {number} options.cacheExpiry - 缓存过期时间(毫秒),默认3600000 (1小时)
23
  * @param {string} options.logLevel - 日志级别,可选值:'debug', 'info', 'warn', 'error', 'none',默认'info'
 
24
  */
25
  constructor(options = {}) {
26
  // 配置参数
@@ -38,6 +39,7 @@ class ProxyPool {
38
  this.useCache = options.useCache !== undefined ? options.useCache : true;
39
  this.cacheExpiry = options.cacheExpiry || 3600000; // 默认1小时
40
  this.logLevel = options.logLevel || 'info'; // 默认日志级别为info
 
41
 
42
  // 内部状态
43
  this.availableProxies = [];
@@ -46,6 +48,13 @@ class ProxyPool {
46
  this.isRefilling = false;
47
  this.checkTimer = null;
48
  this.proxyCache = new Map(); // 缓存验证过的代理
 
 
 
 
 
 
 
49
 
50
  // 日志级别权重
51
  this.logLevels = {
@@ -61,6 +70,14 @@ class ProxyPool {
61
  this.removeProxy = this.removeProxy.bind(this);
62
  this.checkAndRefill = this.checkAndRefill.bind(this);
63
  }
 
 
 
 
 
 
 
 
64
 
65
  /**
66
  * 根据设置的日志级别输出日志
@@ -79,6 +96,62 @@ class ProxyPool {
79
  }
80
  }
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  /**
83
  * 初始化代理池
84
  * @returns {Promise<void>}
@@ -87,12 +160,21 @@ class ProxyPool {
87
  if (this.isInitialized) return;
88
 
89
  this.log('info', `初始化代理池,目标数量: ${this.targetCount}`);
 
 
 
 
 
 
90
  await this.refillProxies();
91
 
92
  // 设置定时检查
93
  this.checkTimer = setInterval(this.checkAndRefill, this.checkInterval);
94
 
95
  this.isInitialized = true;
 
 
 
96
  this.log('info', `代理池初始化完成,当前可用代理数量: ${this.availableProxies.length}`);
97
  }
98
 
@@ -127,6 +209,11 @@ class ProxyPool {
127
  this.isRefilling = true;
128
  this.log('info', `开始补充代理,当前数量: ${this.availableProxies.length},目标数量: ${this.targetCount}`);
129
 
 
 
 
 
 
130
  let attempts = 0;
131
 
132
  try {
@@ -136,6 +223,10 @@ class ProxyPool {
136
  // 优先检查缓存中的代理
137
  if (this.useCache && this.proxyCache.size > 0) {
138
  await this.tryUsingCachedProxies(neededProxies);
 
 
 
 
139
  }
140
 
141
  // 如果缓存中的代理不足,继续获取新代理
@@ -165,12 +256,19 @@ class ProxyPool {
165
  continue;
166
  }
167
 
 
 
 
168
  // 测试代理
169
  const results = await this.testProxiesConcurrently(newProxies);
170
 
171
  // 添加可用代理
172
  this.addValidProxies(results);
173
 
 
 
 
 
174
  // 如果已经获取到足够的代理,提前结束
175
  if (this.availableProxies.length >= this.targetCount) {
176
  break;
@@ -184,8 +282,7 @@ class ProxyPool {
184
  } catch (error) {
185
  this.log('error', '补充代理过程中出错:', error);
186
  } finally {
187
- this.isRefilling = false;
188
-
189
  if (this.availableProxies.length >= this.targetCount) {
190
  this.log('info', `代理补充完成,当前可用代理: ${this.availableProxies.length}/${this.targetCount}`);
191
  } else {
@@ -331,7 +428,7 @@ class ProxyPool {
331
  const requestCount = count || this.batchSize;
332
  // 限制请求数量最大为10
333
  const actualCount = Math.min(requestCount, 10);
334
- const url = `https://proxy.doudouzi.me/random/us?number=${actualCount}&protocol=${this.proxyProtocol}&type=json`;
335
  this.log('debug', `正在获取代理,URL: ${url}`);
336
 
337
  const response = await axios.get(url, {
@@ -387,6 +484,16 @@ class ProxyPool {
387
  // 分批处理代理
388
  for (let i = 0; i < proxies.length; i += concurrentRequests) {
389
  const batch = proxies.slice(i, i + concurrentRequests);
 
 
 
 
 
 
 
 
 
 
390
  const promises = batch.map(proxy => {
391
  // 检查缓存中是否有近期验证过的结果
392
  if (this.useCache && this.proxyCache.has(proxy)) {
@@ -414,6 +521,8 @@ class ProxyPool {
414
  }
415
  }
416
 
 
 
417
  return results;
418
  }
419
 
@@ -590,7 +699,8 @@ async function example() {
590
  useCache: true, // 启用缓存
591
  maxRefillAttempts: 15, // 减少最大尝试次数
592
  retryDelay: 1000, // 减少重试延迟
593
- logLevel: 'info' // 设置日志级别
 
594
  });
595
 
596
  // 初始化代理池
@@ -626,5 +736,7 @@ if (typeof require !== 'undefined' && require.main === module) {
626
  // 导出 ProxyPool 类和实例
627
  export default ProxyPool;
628
  export const proxyPool = new ProxyPool({
629
- logLevel: 'info' // 默认导出的实例使用info级别的日志
 
630
  });
 
 
21
  * @param {boolean} options.useCache - 是否使用缓存,默认true
22
  * @param {number} options.cacheExpiry - 缓存过期时间(毫秒),默认3600000 (1小时)
23
  * @param {string} options.logLevel - 日志级别,可选值:'debug', 'info', 'warn', 'error', 'none',默认'info'
24
+ * @param {boolean} options.showProgressBar - 是否显示进度条,默认false
25
  */
26
  constructor(options = {}) {
27
  // 配置参数
 
39
  this.useCache = options.useCache !== undefined ? options.useCache : true;
40
  this.cacheExpiry = options.cacheExpiry || 3600000; // 默认1小时
41
  this.logLevel = options.logLevel || 'info'; // 默认日志级别为info
42
+ this.showProgressBar = options.showProgressBar !== undefined ? options.showProgressBar : false; // 是否显示进度条
43
 
44
  // 内部状态
45
  this.availableProxies = [];
 
48
  this.isRefilling = false;
49
  this.checkTimer = null;
50
  this.proxyCache = new Map(); // 缓存验证过的代理
51
+ this.proxyCountry = 'us';
52
+
53
+ // 进度条状态
54
+ this.progressTotal = 0;
55
+ this.progressCurrent = 0;
56
+ this.progressBarLength = 30; // 进度条长度
57
+ this.lastProgressString = ''; // 上次显示的进度字符串
58
 
59
  // 日志级别权重
60
  this.logLevels = {
 
70
  this.removeProxy = this.removeProxy.bind(this);
71
  this.checkAndRefill = this.checkAndRefill.bind(this);
72
  }
73
+
74
+ /**
75
+ * 设置代理国家
76
+ * @param {string} country - 代理国家 支持us uk jp de fr ca
77
+ */
78
+ setCountry(country) {
79
+ this.proxyCountry = country;
80
+ }
81
 
82
  /**
83
  * 根据设置的日志级别输出日志
 
96
  }
97
  }
98
 
99
+ /**
100
+ * 显示进度条
101
+ * @param {string} message - 进度条前的消息
102
+ * @param {number} current - 当前进度
103
+ * @param {number} total - 总进度
104
+ * @param {boolean} [complete=false] - 是否完成
105
+ */
106
+ showProgress(message, current, total) {
107
+ if (!this.showProgressBar) return;
108
+
109
+ // 确保进度不超过总数
110
+ current = Math.min(current, total);
111
+
112
+ // 计算进度百分比
113
+ const percent = total > 0 ? Math.floor((current / total) * 100) : 0;
114
+
115
+ // 创建终端风格的箭头进度条
116
+ let bar = '';
117
+ let ProcessBarLength = 15;
118
+ let CurrentProcessBarLength = Math.floor(ProcessBarLength*(percent/100));
119
+ let ProcessBarRemainLength = ProcessBarLength-CurrentProcessBarLength;
120
+ let ProcessBar = '-'.repeat(CurrentProcessBarLength)+'>';
121
+ let ProcessBarRemain = '·'.repeat(ProcessBarRemainLength);
122
+ if (percent === 100) {
123
+ // 完成状态显示完整箭头
124
+ bar = '['+'-'.repeat(ProcessBarLength)+'>'+']';
125
+ } else {
126
+ bar = '['+ProcessBar+ProcessBarRemain+']';
127
+ }
128
+
129
+ // 构建进度字符串,添加百分比
130
+ const progressString = `${message} ${bar} ${current}/${total} (${percent}%)`;
131
+
132
+ // 如果进度字符串与上次相同,则不重复显示
133
+ if (progressString === this.lastProgressString) {
134
+ return;
135
+ }
136
+
137
+ // 清除上一行
138
+ if (this.lastProgressString) {
139
+ process.stdout.clearLine(0);
140
+ process.stdout.cursorTo(0);
141
+ }
142
+
143
+ // 显示进度条
144
+ process.stdout.write(progressString);
145
+
146
+ // 如果完成,换行
147
+ if (percent === 100) {
148
+ process.stdout.write('\n');
149
+ }
150
+
151
+ // 保存当前进度字符串
152
+ this.lastProgressString = progressString;
153
+ }
154
+
155
  /**
156
  * 初始化代理池
157
  * @returns {Promise<void>}
 
160
  if (this.isInitialized) return;
161
 
162
  this.log('info', `初始化代理池,目标数量: ${this.targetCount}`);
163
+
164
+ // 重置进度条状态
165
+ this.progressTotal = this.targetCount;
166
+ this.progressCurrent = 0;
167
+ //this.showProgress('初始化代理池进度', this.progressCurrent, this.progressTotal);
168
+
169
  await this.refillProxies();
170
 
171
  // 设置定时检查
172
  this.checkTimer = setInterval(this.checkAndRefill, this.checkInterval);
173
 
174
  this.isInitialized = true;
175
+
176
+ // 完成进度条
177
+ this.showProgress('补充代理进度', this.availableProxies.length, this.progressTotal);
178
  this.log('info', `代理池初始化完成,当前可用代理数量: ${this.availableProxies.length}`);
179
  }
180
 
 
209
  this.isRefilling = true;
210
  this.log('info', `开始补充代理,当前数量: ${this.availableProxies.length},目标数量: ${this.targetCount}`);
211
 
212
+ // 设置进度条状态
213
+ this.progressTotal = this.targetCount;
214
+ this.progressCurrent = this.availableProxies.length;
215
+ this.showProgress('补充代理进度', this.progressCurrent, this.progressTotal);
216
+
217
  let attempts = 0;
218
 
219
  try {
 
223
  // 优先检查缓存中的代理
224
  if (this.useCache && this.proxyCache.size > 0) {
225
  await this.tryUsingCachedProxies(neededProxies);
226
+
227
+ // 更新进度条
228
+ this.progressCurrent = this.availableProxies.length;
229
+ this.showProgress('补充代理进度', this.progressCurrent, this.progressTotal);
230
  }
231
 
232
  // 如果缓存中的代理不足,继续获取新代理
 
256
  continue;
257
  }
258
 
259
+ // 保存当前进度,测试代理过程中会临时更新进度条
260
+ const previousProgress = this.progressCurrent;
261
+
262
  // 测试代理
263
  const results = await this.testProxiesConcurrently(newProxies);
264
 
265
  // 添加可用代理
266
  this.addValidProxies(results);
267
 
268
+ // 更新实际进度
269
+ this.progressCurrent = this.availableProxies.length;
270
+ this.showProgress('补充代理进度', this.progressCurrent, this.progressTotal);
271
+
272
  // 如果已经获取到足够的代理,提前结束
273
  if (this.availableProxies.length >= this.targetCount) {
274
  break;
 
282
  } catch (error) {
283
  this.log('error', '补充代理过程中出错:', error);
284
  } finally {
285
+ this.isRefilling = false;
 
286
  if (this.availableProxies.length >= this.targetCount) {
287
  this.log('info', `代理补充完成,当前可用代理: ${this.availableProxies.length}/${this.targetCount}`);
288
  } else {
 
428
  const requestCount = count || this.batchSize;
429
  // 限制请求数量最大为10
430
  const actualCount = Math.min(requestCount, 10);
431
+ const url = `https://proxy.doudouzi.me/random/${this.proxyCountry}?number=${actualCount}&protocol=${this.proxyProtocol}&type=json`;
432
  this.log('debug', `正在获取代理,URL: ${url}`);
433
 
434
  const response = await axios.get(url, {
 
484
  // 分批处理代理
485
  for (let i = 0; i < proxies.length; i += concurrentRequests) {
486
  const batch = proxies.slice(i, i + concurrentRequests);
487
+
488
+ // 不再显示测试代理的单独进度条,而是更新补充代理的总体进度
489
+ if (this.showProgressBar) {
490
+ // 计算当前测试进度对应的总体进度
491
+ const testedPercent = i / proxies.length;
492
+ const progressStep = Math.min(1, testedPercent) * 0.2; // 测试部分占总体进度的20%
493
+ const currentProgress = this.progressCurrent + progressStep * (this.targetCount - this.progressCurrent);
494
+ this.showProgress('补充代理进度', Math.floor(currentProgress), this.progressTotal);
495
+ }
496
+
497
  const promises = batch.map(proxy => {
498
  // 检查缓存中是否有近期验证过的结果
499
  if (this.useCache && this.proxyCache.has(proxy)) {
 
521
  }
522
  }
523
 
524
+ // 不再显示测试完成的进度条
525
+
526
  return results;
527
  }
528
 
 
699
  useCache: true, // 启用缓存
700
  maxRefillAttempts: 15, // 减少最大尝试次数
701
  retryDelay: 1000, // 减少重试延迟
702
+ logLevel: 'info', // 设置日志级别
703
+ showProgressBar: true // 启用进度条
704
  });
705
 
706
  // 初始化代理池
 
736
  // 导出 ProxyPool 类和实例
737
  export default ProxyPool;
738
  export const proxyPool = new ProxyPool({
739
+ logLevel: 'info', // 默认导出的实例使用info级别的日志
740
+ showProgressBar: true // 启用进度条
741
  });
742
+
src/ProxyServer.js CHANGED
@@ -26,13 +26,7 @@ class ProxyServer {
26
  this.proxyProcess = null;
27
  this.platform = process.env.PROXY_SERVER_PLATFORM || 'auto';
28
  this.port = process.env.PROXY_SERVER_PORT || 10655;
29
-
30
- // 检查是否在Linux环境下,并且没有指定日志路径
31
- const defaultLogPath = os.platform() === 'linux' && !process.env.PROXY_SERVER_LOG_PATH
32
- ? '/tmp/proxy_server.log'
33
- : './proxy_server.log';
34
-
35
- this.logPath = process.env.PROXY_SERVER_LOG_PATH || defaultLogPath;
36
  this.enabled = process.env.ENABLE_PROXY_SERVER === 'true';
37
  this.proxyAuthToken = process.env.PROXY_AUTH_TOKEN || 'default_token';
38
  this.logStream = null;
@@ -103,11 +97,9 @@ class ProxyServer {
103
  // 确保可执行文件有执行权限(在Linux/Android上)
104
  if (this.detectPlatform() !== 'windows') {
105
  try {
106
- // 在Docker等只读文件系统中,这可能会失败,但我们在Dockerfile中已经设置了权限
107
- // fs.chmodSync(proxyServerPath, 0o755);
108
  } catch (err) {
109
- // 忽略这个错误,因为可能是在一个只读的环境中
110
- // logger.warning(`无法设置执行权限: ${err.message}`);
111
  }
112
  }
113
 
 
26
  this.proxyProcess = null;
27
  this.platform = process.env.PROXY_SERVER_PLATFORM || 'auto';
28
  this.port = process.env.PROXY_SERVER_PORT || 10655;
29
+ this.logPath = process.env.PROXY_SERVER_LOG_PATH || './proxy_server.log';
 
 
 
 
 
 
30
  this.enabled = process.env.ENABLE_PROXY_SERVER === 'true';
31
  this.proxyAuthToken = process.env.PROXY_AUTH_TOKEN || 'default_token';
32
  this.logStream = null;
 
97
  // 确保可执行文件有执行权限(在Linux/Android上)
98
  if (this.detectPlatform() !== 'windows') {
99
  try {
100
+ fs.chmodSync(proxyServerPath, 0o755);
 
101
  } catch (err) {
102
+ logger.warning(`无法设置执行权限: ${err.message}`);
 
103
  }
104
  }
105
 
src/lightweight-client-express.js CHANGED
@@ -137,7 +137,9 @@ app.get('/v1/models', authenticate, (req, res) => {
137
  { id: "openai-gpt-4.1" },
138
  { id: "anthropic-opus-4" },
139
  { id: "anthropic-sonnet-4" },
140
- { id: "anthropic-sonnet-3.x-stable" }
 
 
141
  ]
142
  };
143
 
@@ -319,7 +321,7 @@ app.get('/cookies/status', authenticate, (req, res) => {
319
  const PORT = process.env.PORT || 7860;
320
 
321
  // 设置代理池日志级别为warn,减少详细日志输出
322
- proxyPool.logLevel = 'warn';
323
 
324
  // 初始化并启动服务器
325
  initialize().then(() => {
 
137
  { id: "openai-gpt-4.1" },
138
  { id: "anthropic-opus-4" },
139
  { id: "anthropic-sonnet-4" },
140
+ { id: "anthropic-sonnet-3.x-stable" },
141
+ { id: "google-gemini-2.5-pro"}, //vertex-gemini-2.5-pro
142
+ { id: "google-gemini-2.5-flash"}, //vertex-gemini-2.5-flash
143
  ]
144
  };
145
 
 
321
  const PORT = process.env.PORT || 7860;
322
 
323
  // 设置代理池日志级别为warn,减少详细日志输出
324
+ proxyPool.logLevel = 'error';
325
 
326
  // 初始化并启动服务器
327
  initialize().then(() => {
src/lightweight-client.js CHANGED
@@ -100,7 +100,22 @@ function buildNotionRequest(requestData) {
100
  value: new NotionTranscriptConfigValue({
101
  })
102
  }));
103
- }else{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  transcript.push(new NotionTranscriptItem({
105
  type: "config",
106
  value: new NotionTranscriptConfigValue({
@@ -840,8 +855,19 @@ async function initialize() {
840
  if (process.env.USE_NATIVE_PROXY_POOL === 'true') {
841
  logger.info(`正在初始化本地代理池...`);
842
  // 设置代理池的日志级别为warn,减少详细日志输出
843
- proxyPool.logLevel = 'warn';
 
 
 
 
 
 
 
 
 
844
  await proxyPool.initialize();
 
 
845
  }
846
 
847
  INITIALIZED_SUCCESSFULLY = true;
 
100
  value: new NotionTranscriptConfigValue({
101
  })
102
  }));
103
+ } else if(requestData.model === 'google-gemini-2.5-pro'){
104
+ transcript.push(new NotionTranscriptItem({
105
+ type: "config",
106
+ value: new NotionTranscriptConfigValue({
107
+ model: 'vertex-gemini-2.5-pro'
108
+ })
109
+ }));
110
+ } else if (requestData.model === 'google-gemini-2.5-flash'){
111
+ transcript.push(new NotionTranscriptItem({
112
+ type: "config",
113
+ value: new NotionTranscriptConfigValue({
114
+ model: 'vertex-gemini-2.5-flash'
115
+ })
116
+ }));
117
+ }
118
+ else{
119
  transcript.push(new NotionTranscriptItem({
120
  type: "config",
121
  value: new NotionTranscriptConfigValue({
 
855
  if (process.env.USE_NATIVE_PROXY_POOL === 'true') {
856
  logger.info(`正在初始化本地代理池...`);
857
  // 设置代理池的日志级别为warn,减少详细日志输出
858
+ proxyPool.logLevel = 'error';
859
+ // 启用进度条显示
860
+ proxyPool.showProgressBar = true;
861
+
862
+ if (['us', 'uk', 'jp', 'de', 'fr', 'ca'].includes(process.env.PROXY_COUNTRY)) {
863
+ proxyPool.setCountry(process.env.PROXY_COUNTRY);
864
+ } else {
865
+ logger.warning(`未设置正确PROXY_COUNTRY,使用默认代理国家: us`);
866
+ proxyPool.setCountry('us');
867
+ }
868
  await proxyPool.initialize();
869
+ await new Promise(resolve => setTimeout(resolve, 1000));
870
+ logger.success(`代理池初始化完成,当前代理国家: ${proxyPool.proxyCountry}`);
871
  }
872
 
873
  INITIALIZED_SUCCESSFULLY = true;