protocol-proxy 2.0.2 → 2.0.3

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 (3) hide show
  1. package/package.json +1 -1
  2. package/public/app.js +57 -10
  3. package/server.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "protocol-proxy",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "OpenAI / Anthropic 协议转换透明代理",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/app.js CHANGED
@@ -47,9 +47,12 @@ function initProviderDropdown() {
47
47
  e.stopPropagation();
48
48
  dropdown.classList.toggle('open');
49
49
  if (dropdown.classList.contains('open')) {
50
+ editingProviderId = null;
50
51
  addNameInput.value = '';
51
52
  addUrlInput.value = '';
52
53
  addKeyInput.value = '';
54
+ addUrlInput.disabled = false;
55
+ addBtn.textContent = '添加';
53
56
  renderProviderOptions();
54
57
  addNameInput.focus();
55
58
  }
@@ -70,22 +73,39 @@ function initProviderDropdown() {
70
73
  return;
71
74
  }
72
75
  try {
73
- const res = await fetch('/api/providers', {
74
- method: 'POST',
75
- headers: { 'Content-Type': 'application/json' },
76
- body: JSON.stringify({ name, url, apiKey }),
77
- });
76
+ let res;
77
+ if (editingProviderId) {
78
+ // 更新模式
79
+ res = await fetch(`/api/providers/${editingProviderId}`, {
80
+ method: 'PUT',
81
+ headers: { 'Content-Type': 'application/json' },
82
+ body: JSON.stringify({ name, apiKey }),
83
+ });
84
+ } else {
85
+ // 新增模式
86
+ res = await fetch('/api/providers', {
87
+ method: 'POST',
88
+ headers: { 'Content-Type': 'application/json' },
89
+ body: JSON.stringify({ name, url, apiKey }),
90
+ });
91
+ }
78
92
  if (!res.ok) {
79
93
  const err = await res.json();
80
- showToast(err.error || '添加失败', true);
94
+ showToast(err.error || '操作失败', true);
81
95
  return;
82
96
  }
83
97
  const provider = await res.json();
98
+ editingProviderId = null;
99
+ addUrlInput.disabled = false;
100
+ addBtn.textContent = '添加';
101
+ addNameInput.value = '';
102
+ addUrlInput.value = '';
103
+ addKeyInput.value = '';
84
104
  await loadProviders();
85
105
  selectProvider(provider.id);
86
- dropdown.classList.remove('open');
106
+ renderProviderOptions();
87
107
  } catch (err) {
88
- showToast('添加失败: ' + err.message, true);
108
+ showToast('操作失败: ' + err.message, true);
89
109
  }
90
110
  });
91
111
 
@@ -98,6 +118,8 @@ function initProviderDropdown() {
98
118
  });
99
119
  }
100
120
 
121
+ let editingProviderId = null;
122
+
101
123
  function renderProviderOptions() {
102
124
  const container = document.getElementById('provider-dropdown-options');
103
125
  const currentId = document.getElementById('provider-id').value;
@@ -106,7 +128,10 @@ function renderProviderOptions() {
106
128
  <div class="model-option${p.id === currentId ? ' selected' : ''}" data-id="${escapeHtml(p.id)}">
107
129
  <span class="model-option-name">${escapeHtml(p.name)}</span>
108
130
  ${p.name !== p.url ? `<span style="color:#64748b;font-size:12px;margin-left:4px">${escapeHtml(p.url)}</span>` : ''}
109
- <button type="button" class="model-option-delete" data-delete-id="${escapeHtml(p.id)}" title="删除此供应商">&times;</button>
131
+ <span style="margin-left:auto;display:flex;gap:4px">
132
+ <button type="button" class="model-option-delete" data-edit-id="${escapeHtml(p.id)}" title="编辑此供应商" style="color:#60a5fa">&times;</button>
133
+ <button type="button" class="model-option-delete" data-delete-id="${escapeHtml(p.id)}" title="删除此供应商">&times;</button>
134
+ </span>
110
135
  </div>
111
136
  `).join('');
112
137
 
@@ -122,7 +147,29 @@ function renderProviderOptions() {
122
147
  });
123
148
  });
124
149
 
125
- container.querySelectorAll('.model-option-delete').forEach(btn => {
150
+ // 编辑供应商
151
+ container.querySelectorAll('[data-edit-id]').forEach(btn => {
152
+ btn.addEventListener('click', async (e) => {
153
+ e.stopPropagation();
154
+ const id = btn.dataset.editId;
155
+ try {
156
+ const res = await fetch(`/api/providers/${id}`);
157
+ if (!res.ok) throw new Error('加载失败');
158
+ const p = await res.json();
159
+ editingProviderId = id;
160
+ document.getElementById('provider-add-name').value = p.name;
161
+ document.getElementById('provider-add-url').value = p.url;
162
+ document.getElementById('provider-add-key').value = p.apiKey || '';
163
+ document.getElementById('provider-add-url').disabled = true;
164
+ document.getElementById('provider-add-btn').textContent = '更新';
165
+ } catch (err) {
166
+ showToast('加载供应商失败: ' + err.message, true);
167
+ }
168
+ });
169
+ });
170
+
171
+ // 删除供应商
172
+ container.querySelectorAll('[data-delete-id]').forEach(btn => {
126
173
  btn.addEventListener('click', async (e) => {
127
174
  e.stopPropagation();
128
175
  const id = btn.dataset.deleteId;
package/server.js CHANGED
@@ -198,7 +198,7 @@ async function init() {
198
198
  if (req.body.name !== undefined) updates.name = req.body.name;
199
199
  if (req.body.url !== undefined) updates.url = req.body.url;
200
200
  if (req.body.protocol !== undefined) updates.protocol = req.body.protocol;
201
- if (req.body.apiKey !== undefined) updates.apiKey = req.body.apiKey;
201
+ if (req.body.apiKey !== undefined && req.body.apiKey !== '') updates.apiKey = req.body.apiKey;
202
202
  if (req.body.models !== undefined) updates.models = req.body.models;
203
203
 
204
204
  const updated = configStore.updateProvider(req.params.id, updates);