protocol-proxy 1.0.0 → 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 +268 -137
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "protocol-proxy",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "OpenAI / Anthropic 协议转换透明代理",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -1,174 +1,268 @@
1
- const express = require('express');
1
+ #!/usr/bin/env node
2
2
  const path = require('path');
3
- const cors = require('cors');
4
3
  const { exec } = require('child_process');
5
4
  const os = require('os');
6
- const configStore = require('./lib/config-store');
7
- const proxyManager = require('./lib/proxy-manager');
8
-
9
- const app = express();
10
- const PORT = process.env.ADMIN_PORT || 3000;
11
-
12
- function openBrowser(url) {
13
- const platform = os.platform();
14
- let command;
15
- if (platform === 'win32') {
16
- command = `start "" "${url}"`;
17
- } else if (platform === 'darwin') {
18
- 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
+ }
19
59
  } else {
20
- command = `xdg-open "${url}"`;
60
+ removePid();
61
+ console.log('服务未运行');
21
62
  }
22
- exec(command, (err) => {
23
- if (err) console.error('[Browser] 打开浏览器失败:', err.message);
24
- });
25
63
  }
26
64
 
27
- app.use(cors());
28
- app.use(express.json());
29
- app.use(express.static(path.join(__dirname, 'public')));
30
-
31
- // ==================== 管理 API ====================
32
-
33
- // 获取所有代理配置
34
- app.get('/api/proxies', (req, res) => {
35
- const proxies = configStore.getProxies().map(p => ({
36
- ...p,
37
- target: p.target ? {
38
- ...p.target,
39
- apiKey: p.target.apiKey ? '***' : '',
40
- } : null,
41
- running: proxyManager.isRunning(p.id),
42
- }));
43
- res.json(proxies);
44
- });
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
+ }
45
81
 
46
- // 获取单个代理配置(含敏感信息)
47
- app.get('/api/proxies/:id', (req, res) => {
48
- const proxy = configStore.getProxyById(req.params.id);
49
- if (!proxy) return res.status(404).json({ error: 'Proxy not found' });
50
- res.json(proxy);
51
- });
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
+ }
52
94
 
53
- // 创建代理
54
- app.post('/api/proxies', async (req, res) => {
55
- const { name, port, requireAuth, authToken, target } = req.body;
95
+ // ==================== 启动 ====================
56
96
 
57
- if (!name || !port || !target) {
58
- return res.status(400).json({ error: 'name, port and target are required' });
59
- }
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');
60
102
 
61
- const parsedPort = parseInt(port);
103
+ const app = express();
104
+ const PORT = process.env.ADMIN_PORT || 3000;
62
105
 
63
- // 端口冲突校验
64
- const existing = configStore.getProxies().find(p => p.port === parsedPort);
65
- if (existing) {
66
- return res.status(409).json({
67
- 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);
68
118
  });
69
119
  }
70
120
 
71
- const proxy = configStore.addProxy({
72
- name,
73
- port: parsedPort,
74
- requireAuth: !!requireAuth,
75
- authToken: authToken || null,
76
- 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);
77
138
  });
78
139
 
79
- try {
80
- await proxyManager.startProxy(proxy);
81
- res.status(201).json({ ...proxy, running: true });
82
- } catch (err) {
83
- // 启动失败,回滚已保存的配置
84
- configStore.removeProxy(proxy.id);
85
- res.status(500).json({ error: `代理启动失败: ${err.message}` });
86
- }
87
- });
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);
88
156
 
89
- // 更新代理
90
- app.put('/api/proxies/:id', async (req, res) => {
91
- const { name, port, requireAuth, authToken, target } = req.body;
92
- const existing = configStore.getProxyById(req.params.id);
93
- if (!existing) return res.status(404).json({ error: 'Proxy not found' });
94
-
95
- const updates = {};
96
- if (name !== undefined) updates.name = name;
97
- if (port !== undefined) updates.port = parseInt(port);
98
- if (requireAuth !== undefined) updates.requireAuth = !!requireAuth;
99
- if (authToken !== undefined) updates.authToken = authToken || null;
100
- if (target !== undefined) updates.target = target;
101
-
102
- // 端口变更时校验冲突
103
- const needRestart = updates.port !== undefined && updates.port !== existing.port;
104
- if (needRestart) {
105
- const conflict = configStore.getProxies().find(p => p.id !== req.params.id && p.port === updates.port);
106
- if (conflict) {
157
+ // 端口冲突校验
158
+ const existing = configStore.getProxies().find(p => p.port === parsedPort);
159
+ if (existing) {
107
160
  return res.status(409).json({
108
- error: `端口 ${updates.port} 已被代理「${conflict.name}」占用,请更换端口`,
161
+ error: `端口 ${parsedPort} 已被代理「${existing.name}」占用,请更换端口`,
109
162
  });
110
163
  }
111
- }
112
164
 
113
- 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
+ });
114
172
 
115
- if (needRestart) {
116
173
  try {
117
- await proxyManager.restartProxy(updated);
174
+ await proxyManager.startProxy(proxy);
175
+ res.status(201).json({ ...proxy, running: true });
118
176
  } catch (err) {
119
- return res.status(500).json({ error: `代理重启失败: ${err.message}` });
177
+ // 启动失败,回滚已保存的配置
178
+ configStore.removeProxy(proxy.id);
179
+ res.status(500).json({ error: `代理启动失败: ${err.message}` });
120
180
  }
121
- } else {
122
- proxyManager.updateProxyConfig(updated);
123
- }
181
+ });
124
182
 
125
- res.json({ ...updated, running: proxyManager.isRunning(updated.id) });
126
- });
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' });
127
188
 
128
- // 删除代理
129
- app.delete('/api/proxies/:id', async (req, res) => {
130
- const existing = configStore.getProxyById(req.params.id);
131
- 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;
132
195
 
133
- await proxyManager.stopProxy(req.params.id);
134
- configStore.removeProxy(req.params.id);
135
- res.json({ success: true });
136
- });
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
+ }
137
206
 
138
- // 启动/停止代理
139
- app.post('/api/proxies/:id/start', async (req, res) => {
140
- const proxy = configStore.getProxyById(req.params.id);
141
- if (!proxy) return res.status(404).json({ error: 'Proxy not found' });
207
+ const updated = configStore.updateProxy(req.params.id, updates);
142
208
 
143
- try {
144
- await proxyManager.startProxy(proxy);
145
- res.json({ success: true, running: true });
146
- } catch (err) {
147
- res.status(500).json({ error: 'Failed to start proxy', message: err.message });
148
- }
149
- });
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
+ }
150
218
 
151
- app.post('/api/proxies/:id/stop', async (req, res) => {
152
- await proxyManager.stopProxy(req.params.id);
153
- res.json({ success: true, running: false });
154
- });
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' });
155
226
 
156
- // 获取运行状态
157
- app.get('/api/status', (req, res) => {
158
- res.json({
159
- running: proxyManager.getRunningPorts(),
160
- total: configStore.getProxies().length,
227
+ await proxyManager.stopProxy(req.params.id);
228
+ configStore.removeProxy(req.params.id);
229
+ res.json({ success: true });
161
230
  });
162
- });
163
231
 
164
- // 前端首页
165
- app.get('/', (req, res) => {
166
- res.sendFile(path.join(__dirname, 'public', 'index.html'));
167
- });
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' });
168
236
 
169
- // ==================== 启动 ====================
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();
170
265
 
171
- async function init() {
172
266
  // 启动所有已配置的代理
173
267
  const proxies = configStore.getProxies();
174
268
  for (const proxy of proxies) {
@@ -190,13 +284,50 @@ async function init() {
190
284
  // 优雅关闭
191
285
  process.on('SIGINT', async () => {
192
286
  console.log('\nShutting down...');
193
- await proxyManager.stopAll();
287
+ removePid();
288
+ try {
289
+ const proxyManager = require('./lib/proxy-manager');
290
+ await proxyManager.stopAll();
291
+ } catch {}
194
292
  process.exit(0);
195
293
  });
196
294
 
197
295
  process.on('SIGTERM', async () => {
198
- await proxyManager.stopAll();
296
+ removePid();
297
+ try {
298
+ const proxyManager = require('./lib/proxy-manager');
299
+ await proxyManager.stopAll();
300
+ } catch {}
199
301
  process.exit(0);
200
302
  });
201
303
 
202
- 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
+ }