protocol-proxy 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +267 -137
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "protocol-proxy",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "OpenAI / Anthropic 协议转换透明代理",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -1,175 +1,268 @@
1
1
  #!/usr/bin/env node
2
- const express = require('express');
3
2
  const path = require('path');
4
- const cors = require('cors');
5
3
  const { exec } = require('child_process');
6
4
  const os = require('os');
7
- const configStore = require('./lib/config-store');
8
- const proxyManager = require('./lib/proxy-manager');
9
-
10
- const app = express();
11
- const PORT = process.env.ADMIN_PORT || 3000;
12
-
13
- function openBrowser(url) {
14
- const platform = os.platform();
15
- let command;
16
- if (platform === 'win32') {
17
- command = `start "" "${url}"`;
18
- } else if (platform === 'darwin') {
19
- command = `open "${url}"`;
5
+ const fs = require('fs');
6
+
7
+ // ==================== CLI ====================
8
+
9
+ const PID_FILE = path.join(os.tmpdir(), 'protocol-proxy.pid');
10
+ const pkg = require('./package.json');
11
+
12
+ function writePid() {
13
+ try { fs.writeFileSync(PID_FILE, String(process.pid)); } catch {}
14
+ }
15
+
16
+ function readPid() {
17
+ try { return parseInt(fs.readFileSync(PID_FILE, 'utf8').trim()); } catch { return null; }
18
+ }
19
+
20
+ function removePid() {
21
+ try { fs.unlinkSync(PID_FILE); } catch {}
22
+ }
23
+
24
+ function isProcessAlive(pid) {
25
+ try { process.kill(pid, 0); return true; } catch { return false; }
26
+ }
27
+
28
+ function showHelp() {
29
+ console.log(`
30
+ protocol-proxy - OpenAI / Anthropic 协议转换透明代理
31
+
32
+ 用法:
33
+ protocol-proxy 启动服务
34
+ protocol-proxy start 启动服务
35
+ protocol-proxy stop 停止服务
36
+ protocol-proxy status 查看运行状态
37
+ protocol-proxy help 显示帮助信息
38
+ protocol-proxy -v, --version 显示版本号
39
+ protocol-proxy update 更新到最新版本
40
+ `);
41
+ }
42
+
43
+ function showVersion() {
44
+ console.log(pkg.version);
45
+ }
46
+
47
+ function showStatus() {
48
+ const pid = readPid();
49
+ if (pid && isProcessAlive(pid)) {
50
+ console.log(`服务正在运行 (PID: ${pid})`);
51
+ const configStore = require('./lib/config-store');
52
+ const proxies = configStore.getProxies();
53
+ if (proxies.length > 0) {
54
+ console.log(`\n已配置的代理 (${proxies.length} 个):`);
55
+ for (const p of proxies) {
56
+ console.log(` - ${p.name}: 端口 ${p.port} → ${p.target?.providerUrl || '未设置'}`);
57
+ }
58
+ }
20
59
  } else {
21
- command = `xdg-open "${url}"`;
60
+ removePid();
61
+ console.log('服务未运行');
22
62
  }
23
- exec(command, (err) => {
24
- if (err) console.error('[Browser] 打开浏览器失败:', err.message);
25
- });
26
63
  }
27
64
 
28
- app.use(cors());
29
- app.use(express.json());
30
- app.use(express.static(path.join(__dirname, 'public')));
31
-
32
- // ==================== 管理 API ====================
33
-
34
- // 获取所有代理配置
35
- app.get('/api/proxies', (req, res) => {
36
- const proxies = configStore.getProxies().map(p => ({
37
- ...p,
38
- target: p.target ? {
39
- ...p.target,
40
- apiKey: p.target.apiKey ? '***' : '',
41
- } : null,
42
- running: proxyManager.isRunning(p.id),
43
- }));
44
- res.json(proxies);
45
- });
65
+ function stopService() {
66
+ const pid = readPid();
67
+ if (!pid || !isProcessAlive(pid)) {
68
+ removePid();
69
+ console.log('服务未运行');
70
+ return;
71
+ }
72
+ try {
73
+ process.kill(pid, 'SIGTERM');
74
+ removePid();
75
+ console.log(`服务已停止 (PID: ${pid})`);
76
+ } catch (err) {
77
+ console.error('停止服务失败:', err.message);
78
+ removePid();
79
+ }
80
+ }
46
81
 
47
- // 获取单个代理配置(含敏感信息)
48
- app.get('/api/proxies/:id', (req, res) => {
49
- const proxy = configStore.getProxyById(req.params.id);
50
- if (!proxy) return res.status(404).json({ error: 'Proxy not found' });
51
- res.json(proxy);
52
- });
82
+ function updateService() {
83
+ console.log('正在更新 protocol-proxy...');
84
+ exec('npm install -g protocol-proxy@latest', (err, stdout, stderr) => {
85
+ if (err) {
86
+ console.error('更新失败:', err.message);
87
+ process.exit(1);
88
+ }
89
+ if (stdout) console.log(stdout);
90
+ if (stderr) console.error(stderr);
91
+ console.log('更新完成');
92
+ });
93
+ }
53
94
 
54
- // 创建代理
55
- app.post('/api/proxies', async (req, res) => {
56
- const { name, port, requireAuth, authToken, target } = req.body;
95
+ // ==================== 启动 ====================
57
96
 
58
- if (!name || !port || !target) {
59
- return res.status(400).json({ error: 'name, port and target are required' });
60
- }
97
+ async function init() {
98
+ const express = require('express');
99
+ const cors = require('cors');
100
+ const configStore = require('./lib/config-store');
101
+ const proxyManager = require('./lib/proxy-manager');
61
102
 
62
- const parsedPort = parseInt(port);
103
+ const app = express();
104
+ const PORT = process.env.ADMIN_PORT || 3000;
63
105
 
64
- // 端口冲突校验
65
- const existing = configStore.getProxies().find(p => p.port === parsedPort);
66
- if (existing) {
67
- return res.status(409).json({
68
- error: `端口 ${parsedPort} 已被代理「${existing.name}」占用,请更换端口`,
106
+ function openBrowser(url) {
107
+ const platform = os.platform();
108
+ let command;
109
+ if (platform === 'win32') {
110
+ command = `start "" "${url}"`;
111
+ } else if (platform === 'darwin') {
112
+ command = `open "${url}"`;
113
+ } else {
114
+ command = `xdg-open "${url}"`;
115
+ }
116
+ exec(command, (err) => {
117
+ if (err) console.error('[Browser] 打开浏览器失败:', err.message);
69
118
  });
70
119
  }
71
120
 
72
- const proxy = configStore.addProxy({
73
- name,
74
- port: parsedPort,
75
- requireAuth: !!requireAuth,
76
- authToken: authToken || null,
77
- target,
121
+ app.use(cors());
122
+ app.use(express.json());
123
+ app.use(express.static(path.join(__dirname, 'public')));
124
+
125
+ // ==================== 管理 API ====================
126
+
127
+ // 获取所有代理配置
128
+ app.get('/api/proxies', (req, res) => {
129
+ const proxies = configStore.getProxies().map(p => ({
130
+ ...p,
131
+ target: p.target ? {
132
+ ...p.target,
133
+ apiKey: p.target.apiKey ? '***' : '',
134
+ } : null,
135
+ running: proxyManager.isRunning(p.id),
136
+ }));
137
+ res.json(proxies);
78
138
  });
79
139
 
80
- try {
81
- await proxyManager.startProxy(proxy);
82
- res.status(201).json({ ...proxy, running: true });
83
- } catch (err) {
84
- // 启动失败,回滚已保存的配置
85
- configStore.removeProxy(proxy.id);
86
- res.status(500).json({ error: `代理启动失败: ${err.message}` });
87
- }
88
- });
140
+ // 获取单个代理配置(含敏感信息)
141
+ app.get('/api/proxies/:id', (req, res) => {
142
+ const proxy = configStore.getProxyById(req.params.id);
143
+ if (!proxy) return res.status(404).json({ error: 'Proxy not found' });
144
+ res.json(proxy);
145
+ });
146
+
147
+ // 创建代理
148
+ app.post('/api/proxies', async (req, res) => {
149
+ const { name, port, requireAuth, authToken, target } = req.body;
150
+
151
+ if (!name || !port || !target) {
152
+ return res.status(400).json({ error: 'name, port and target are required' });
153
+ }
154
+
155
+ const parsedPort = parseInt(port);
89
156
 
90
- // 更新代理
91
- app.put('/api/proxies/:id', async (req, res) => {
92
- const { name, port, requireAuth, authToken, target } = req.body;
93
- const existing = configStore.getProxyById(req.params.id);
94
- if (!existing) return res.status(404).json({ error: 'Proxy not found' });
95
-
96
- const updates = {};
97
- if (name !== undefined) updates.name = name;
98
- if (port !== undefined) updates.port = parseInt(port);
99
- if (requireAuth !== undefined) updates.requireAuth = !!requireAuth;
100
- if (authToken !== undefined) updates.authToken = authToken || null;
101
- if (target !== undefined) updates.target = target;
102
-
103
- // 端口变更时校验冲突
104
- const needRestart = updates.port !== undefined && updates.port !== existing.port;
105
- if (needRestart) {
106
- const conflict = configStore.getProxies().find(p => p.id !== req.params.id && p.port === updates.port);
107
- if (conflict) {
157
+ // 端口冲突校验
158
+ const existing = configStore.getProxies().find(p => p.port === parsedPort);
159
+ if (existing) {
108
160
  return res.status(409).json({
109
- error: `端口 ${updates.port} 已被代理「${conflict.name}」占用,请更换端口`,
161
+ error: `端口 ${parsedPort} 已被代理「${existing.name}」占用,请更换端口`,
110
162
  });
111
163
  }
112
- }
113
164
 
114
- const updated = configStore.updateProxy(req.params.id, updates);
165
+ const proxy = configStore.addProxy({
166
+ name,
167
+ port: parsedPort,
168
+ requireAuth: !!requireAuth,
169
+ authToken: authToken || null,
170
+ target,
171
+ });
115
172
 
116
- if (needRestart) {
117
173
  try {
118
- await proxyManager.restartProxy(updated);
174
+ await proxyManager.startProxy(proxy);
175
+ res.status(201).json({ ...proxy, running: true });
119
176
  } catch (err) {
120
- return res.status(500).json({ error: `代理重启失败: ${err.message}` });
177
+ // 启动失败,回滚已保存的配置
178
+ configStore.removeProxy(proxy.id);
179
+ res.status(500).json({ error: `代理启动失败: ${err.message}` });
121
180
  }
122
- } else {
123
- proxyManager.updateProxyConfig(updated);
124
- }
181
+ });
125
182
 
126
- res.json({ ...updated, running: proxyManager.isRunning(updated.id) });
127
- });
183
+ // 更新代理
184
+ app.put('/api/proxies/:id', async (req, res) => {
185
+ const { name, port, requireAuth, authToken, target } = req.body;
186
+ const existing = configStore.getProxyById(req.params.id);
187
+ if (!existing) return res.status(404).json({ error: 'Proxy not found' });
128
188
 
129
- // 删除代理
130
- app.delete('/api/proxies/:id', async (req, res) => {
131
- const existing = configStore.getProxyById(req.params.id);
132
- if (!existing) return res.status(404).json({ error: 'Proxy not found' });
189
+ const updates = {};
190
+ if (name !== undefined) updates.name = name;
191
+ if (port !== undefined) updates.port = parseInt(port);
192
+ if (requireAuth !== undefined) updates.requireAuth = !!requireAuth;
193
+ if (authToken !== undefined) updates.authToken = authToken || null;
194
+ if (target !== undefined) updates.target = target;
133
195
 
134
- await proxyManager.stopProxy(req.params.id);
135
- configStore.removeProxy(req.params.id);
136
- res.json({ success: true });
137
- });
196
+ // 端口变更时校验冲突
197
+ const needRestart = updates.port !== undefined && updates.port !== existing.port;
198
+ if (needRestart) {
199
+ const conflict = configStore.getProxies().find(p => p.id !== req.params.id && p.port === updates.port);
200
+ if (conflict) {
201
+ return res.status(409).json({
202
+ error: `端口 ${updates.port} 已被代理「${conflict.name}」占用,请更换端口`,
203
+ });
204
+ }
205
+ }
138
206
 
139
- // 启动/停止代理
140
- app.post('/api/proxies/:id/start', async (req, res) => {
141
- const proxy = configStore.getProxyById(req.params.id);
142
- if (!proxy) return res.status(404).json({ error: 'Proxy not found' });
207
+ const updated = configStore.updateProxy(req.params.id, updates);
143
208
 
144
- try {
145
- await proxyManager.startProxy(proxy);
146
- res.json({ success: true, running: true });
147
- } catch (err) {
148
- res.status(500).json({ error: 'Failed to start proxy', message: err.message });
149
- }
150
- });
209
+ if (needRestart) {
210
+ try {
211
+ await proxyManager.restartProxy(updated);
212
+ } catch (err) {
213
+ return res.status(500).json({ error: `代理重启失败: ${err.message}` });
214
+ }
215
+ } else {
216
+ proxyManager.updateProxyConfig(updated);
217
+ }
151
218
 
152
- app.post('/api/proxies/:id/stop', async (req, res) => {
153
- await proxyManager.stopProxy(req.params.id);
154
- res.json({ success: true, running: false });
155
- });
219
+ res.json({ ...updated, running: proxyManager.isRunning(updated.id) });
220
+ });
221
+
222
+ // 删除代理
223
+ app.delete('/api/proxies/:id', async (req, res) => {
224
+ const existing = configStore.getProxyById(req.params.id);
225
+ if (!existing) return res.status(404).json({ error: 'Proxy not found' });
156
226
 
157
- // 获取运行状态
158
- app.get('/api/status', (req, res) => {
159
- res.json({
160
- running: proxyManager.getRunningPorts(),
161
- total: configStore.getProxies().length,
227
+ await proxyManager.stopProxy(req.params.id);
228
+ configStore.removeProxy(req.params.id);
229
+ res.json({ success: true });
162
230
  });
163
- });
164
231
 
165
- // 前端首页
166
- app.get('/', (req, res) => {
167
- res.sendFile(path.join(__dirname, 'public', 'index.html'));
168
- });
232
+ // 启动/停止代理
233
+ app.post('/api/proxies/:id/start', async (req, res) => {
234
+ const proxy = configStore.getProxyById(req.params.id);
235
+ if (!proxy) return res.status(404).json({ error: 'Proxy not found' });
169
236
 
170
- // ==================== 启动 ====================
237
+ try {
238
+ await proxyManager.startProxy(proxy);
239
+ res.json({ success: true, running: true });
240
+ } catch (err) {
241
+ res.status(500).json({ error: 'Failed to start proxy', message: err.message });
242
+ }
243
+ });
244
+
245
+ app.post('/api/proxies/:id/stop', async (req, res) => {
246
+ await proxyManager.stopProxy(req.params.id);
247
+ res.json({ success: true, running: false });
248
+ });
249
+
250
+ // 获取运行状态
251
+ app.get('/api/status', (req, res) => {
252
+ res.json({
253
+ running: proxyManager.getRunningPorts(),
254
+ total: configStore.getProxies().length,
255
+ });
256
+ });
257
+
258
+ // 前端首页
259
+ app.get('/', (req, res) => {
260
+ res.sendFile(path.join(__dirname, 'public', 'index.html'));
261
+ });
262
+
263
+ // 启动
264
+ writePid();
171
265
 
172
- async function init() {
173
266
  // 启动所有已配置的代理
174
267
  const proxies = configStore.getProxies();
175
268
  for (const proxy of proxies) {
@@ -191,13 +284,50 @@ async function init() {
191
284
  // 优雅关闭
192
285
  process.on('SIGINT', async () => {
193
286
  console.log('\nShutting down...');
194
- await proxyManager.stopAll();
287
+ removePid();
288
+ try {
289
+ const proxyManager = require('./lib/proxy-manager');
290
+ await proxyManager.stopAll();
291
+ } catch {}
195
292
  process.exit(0);
196
293
  });
197
294
 
198
295
  process.on('SIGTERM', async () => {
199
- await proxyManager.stopAll();
296
+ removePid();
297
+ try {
298
+ const proxyManager = require('./lib/proxy-manager');
299
+ await proxyManager.stopAll();
300
+ } catch {}
200
301
  process.exit(0);
201
302
  });
202
303
 
203
- init();
304
+ // ==================== CLI Dispatch ====================
305
+
306
+ const cmd = process.argv[2];
307
+
308
+ switch (cmd) {
309
+ case 'help':
310
+ showHelp();
311
+ break;
312
+ case '-v':
313
+ case '--version':
314
+ showVersion();
315
+ break;
316
+ case 'update':
317
+ updateService();
318
+ break;
319
+ case 'stop':
320
+ stopService();
321
+ break;
322
+ case 'status':
323
+ showStatus();
324
+ break;
325
+ case 'start':
326
+ case undefined:
327
+ init();
328
+ break;
329
+ default:
330
+ console.error(`未知命令: ${cmd}`);
331
+ showHelp();
332
+ process.exit(1);
333
+ }