Skip to content

Commit 6a7d005

Browse files
committed
Update version
1 parent 531e6f8 commit 6a7d005

File tree

8 files changed

+262
-134
lines changed

8 files changed

+262
-134
lines changed

public/admin/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ <h3 class="text-lg font-medium text-blue-800" data-i18n="running_all_tests">正
200200
<button type="button" id="run-all-test-btn" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 run-test-btn" data-i18n="run_all_test">
201201
运行所有测试
202202
</button>
203+
<button type="button" id="ignore-all-errors-btn" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-yellow-600 hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500" data-i18n="ignore_all_errors">
204+
忽略所有报错
205+
</button>
203206
<button type="button" id="clean-error-keys-btn" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500" data-i18n="clean_error_keys">
204207
清理报错密钥
205208
</button>

public/admin/script.js

Lines changed: 152 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ document.addEventListener('DOMContentLoaded', () => {
5959
const moonIcon = document.getElementById('moon-icon');
6060
// Run All Test Elements
6161
const runAllTestBtn = document.getElementById('run-all-test-btn');
62+
const ignoreAllErrorsBtn = document.getElementById('ignore-all-errors-btn');
6263
const cleanErrorKeysBtn = document.getElementById('clean-error-keys-btn');
6364
const geminiKeysActionsDiv = document.getElementById('gemini-keys-actions');
6465
const testProgressArea = document.getElementById('test-progress-area');
@@ -411,10 +412,12 @@ async function renderGeminiKeys(keys) {
411412
// Check if there are any error keys
412413
const hasErrorKeys = keys.some(key => key.errorStatus === 400 || key.errorStatus === 401 || key.errorStatus === 403);
413414

414-
// Show/hide clean error keys button based on error keys existence
415+
// Show/hide error-related buttons based on error keys existence
415416
if (hasErrorKeys) {
417+
ignoreAllErrorsBtn.classList.remove('hidden');
416418
cleanErrorKeysBtn.classList.remove('hidden');
417419
} else {
420+
ignoreAllErrorsBtn.classList.add('hidden');
418421
cleanErrorKeysBtn.classList.add('hidden');
419422
}
420423

@@ -860,95 +863,8 @@ async function renderGeminiKeys(keys) {
860863
});
861864
});
862865

863-
// Add test button click event (no changes needed here)
864-
document.querySelectorAll('.test-gemini-key').forEach(btn => {
865-
btn.addEventListener('click', (e) => {
866-
const keyId = e.target.dataset.id;
867-
const testSection = document.querySelector(`.test-model-section[data-key-id="${keyId}"]`);
868-
869-
// Toggle display status
870-
if (testSection.classList.contains('hidden')) {
871-
// Hide all other test areas
872-
document.querySelectorAll('.test-model-section').forEach(section => {
873-
section.classList.add('hidden');
874-
section.querySelector('.test-result')?.classList.add('hidden');
875-
});
876-
877-
// Show current test area
878-
testSection.classList.remove('hidden');
879-
} else {
880-
testSection.classList.add('hidden');
881-
}
882-
});
883-
});
884-
885-
// Add run test button click event(修正:测试报错只显示在测试区域)
886-
document.querySelectorAll('.run-test-btn').forEach(btn => {
887-
btn.addEventListener('click', async (e) => {
888-
const testSection = e.target.closest('.test-model-section');
889-
const keyId = testSection.dataset.keyId;
890-
const modelId = testSection.querySelector('.model-select').value;
891-
const resultDiv = testSection.querySelector('.test-result');
892-
const resultPre = resultDiv.querySelector('pre');
893-
894-
if (!modelId) {
895-
showError(t('please_select_model'));
896-
return;
897-
}
898-
899-
// Show result area and set "Loading" text
900-
resultDiv.classList.remove('hidden');
901-
resultPre.textContent = t('testing');
902-
903-
// Send test request directly to handle both success and error responses
904-
let result = null;
905-
try {
906-
// Use direct fetch instead of apiFetch to get raw response
907-
const response = await fetch('/api/admin/test-gemini-key', {
908-
method: 'POST',
909-
headers: {
910-
'Content-Type': 'application/json',
911-
},
912-
credentials: 'include',
913-
body: JSON.stringify({ keyId, modelId })
914-
});
915-
916-
// Parse response regardless of status code
917-
const contentType = response.headers.get("content-type");
918-
if (contentType && contentType.indexOf("application/json") !== -1) {
919-
result = await response.json();
920-
} else {
921-
const textContent = await response.text();
922-
result = {
923-
success: false,
924-
status: response.status,
925-
content: textContent || 'No response content'
926-
};
927-
}
928-
929-
if (result) {
930-
const formattedContent = typeof result.content === 'object'
931-
? JSON.stringify(result.content, null, 2)
932-
: result.content;
933-
934-
if (result.success) {
935-
resultPre.textContent = `${t('test_passed')}\n${t('status')}: ${result.status}\n\n${t('response')}:\n${formattedContent}`;
936-
resultPre.className = 'text-xs bg-green-50 text-green-800 p-2 rounded overflow-x-auto';
937-
} else {
938-
resultPre.textContent = `${t('test_failed')}\n${t('status')}: ${result.status}\n\n${t('response')}:\n${formattedContent}`;
939-
resultPre.className = 'text-xs bg-red-50 text-red-800 p-2 rounded overflow-x-auto';
940-
}
941-
} else {
942-
resultPre.textContent = t('test_failed_no_response');
943-
resultPre.className = 'text-xs bg-red-50 text-red-800 p-2 rounded overflow-x-auto';
944-
}
945-
} catch (error) {
946-
// 只在测试区域显示网络错误
947-
resultPre.textContent = t('test_failed_network', error.message || t('unknown_error'));
948-
resultPre.className = 'text-xs bg-red-50 text-red-800 p-2 rounded overflow-x-auto';
949-
}
950-
});
951-
});
866+
// Note: Event listeners for .test-gemini-key and .run-test-btn are now handled
867+
// by global event delegation to prevent duplicate listeners and DOM reference issues
952868
}
953869

954870
function renderWorkerKeys(keys) {
@@ -1499,8 +1415,117 @@ async function renderGeminiKeys(keys) {
14991415
}
15001416
});
15011417

1502-
// Delete Gemini Key (no changes needed)
1418+
// Global event delegation for Gemini key actions
15031419
document.addEventListener('click', async (e) => {
1420+
// Handle test gemini key button clicks
1421+
if (e.target.classList.contains('test-gemini-key')) {
1422+
const keyId = e.target.dataset.id;
1423+
const testSection = document.querySelector(`.test-model-section[data-key-id="${keyId}"]`);
1424+
1425+
// Check if testSection exists (防止DOM重新渲染后元素不存在的错误)
1426+
if (!testSection) {
1427+
console.warn('Test section not found for keyId:', keyId);
1428+
return;
1429+
}
1430+
1431+
// Toggle display status
1432+
if (testSection.classList.contains('hidden')) {
1433+
// Hide all other test areas
1434+
document.querySelectorAll('.test-model-section').forEach(section => {
1435+
section.classList.add('hidden');
1436+
section.querySelector('.test-result')?.classList.add('hidden');
1437+
});
1438+
1439+
// Show current test area
1440+
testSection.classList.remove('hidden');
1441+
} else {
1442+
testSection.classList.add('hidden');
1443+
}
1444+
return;
1445+
}
1446+
1447+
// Handle run test button clicks
1448+
if (e.target.classList.contains('run-test-btn') && !e.target.id) { // Exclude the main "run all test" button
1449+
const testSection = e.target.closest('.test-model-section');
1450+
1451+
// Check if testSection exists (防止DOM重新渲染后元素不存在的错误)
1452+
if (!testSection) {
1453+
console.warn('Test section not found, possibly due to DOM re-rendering');
1454+
return;
1455+
}
1456+
1457+
const keyId = testSection.dataset.keyId;
1458+
const modelSelect = testSection.querySelector('.model-select');
1459+
const resultDiv = testSection.querySelector('.test-result');
1460+
const resultPre = resultDiv?.querySelector('pre');
1461+
1462+
// Additional safety checks
1463+
if (!keyId || !modelSelect || !resultDiv || !resultPre) {
1464+
console.warn('Required elements not found in test section');
1465+
return;
1466+
}
1467+
1468+
const modelId = modelSelect.value;
1469+
1470+
if (!modelId) {
1471+
showError(t('please_select_model'));
1472+
return;
1473+
}
1474+
1475+
// Show result area and set "Loading" text
1476+
resultDiv.classList.remove('hidden');
1477+
resultPre.textContent = t('testing');
1478+
1479+
// Send test request directly to handle both success and error responses
1480+
let result = null;
1481+
try {
1482+
// Use direct fetch instead of apiFetch to get raw response
1483+
const response = await fetch('/api/admin/test-gemini-key', {
1484+
method: 'POST',
1485+
headers: {
1486+
'Content-Type': 'application/json',
1487+
},
1488+
credentials: 'include',
1489+
body: JSON.stringify({ keyId, modelId })
1490+
});
1491+
1492+
// Parse response regardless of status code
1493+
const contentType = response.headers.get("content-type");
1494+
if (contentType && contentType.indexOf("application/json") !== -1) {
1495+
result = await response.json();
1496+
} else {
1497+
const textContent = await response.text();
1498+
result = {
1499+
success: false,
1500+
status: response.status,
1501+
content: textContent || 'No response content'
1502+
};
1503+
}
1504+
1505+
if (result) {
1506+
const formattedContent = typeof result.content === 'object'
1507+
? JSON.stringify(result.content, null, 2)
1508+
: result.content;
1509+
1510+
if (result.success) {
1511+
resultPre.textContent = `${t('test_passed')}\n${t('status')}: ${result.status}\n\n${t('response')}:\n${formattedContent}`;
1512+
resultPre.className = 'text-xs bg-green-50 text-green-800 p-2 rounded overflow-x-auto';
1513+
} else {
1514+
resultPre.textContent = `${t('test_failed')}\n${t('status')}: ${result.status}\n\n${t('response')}:\n${formattedContent}`;
1515+
resultPre.className = 'text-xs bg-red-50 text-red-800 p-2 rounded overflow-x-auto';
1516+
}
1517+
} else {
1518+
resultPre.textContent = t('test_failed_no_response');
1519+
resultPre.className = 'text-xs bg-red-50 text-red-800 p-2 rounded overflow-x-auto';
1520+
}
1521+
} catch (error) {
1522+
// 只在测试区域显示网络错误
1523+
resultPre.textContent = t('test_failed_network', error.message || t('unknown_error'));
1524+
resultPre.className = 'text-xs bg-red-50 text-red-800 p-2 rounded overflow-x-auto';
1525+
}
1526+
return;
1527+
}
1528+
15041529
if (e.target.classList.contains('delete-gemini-key')) {
15051530
const keyId = e.target.dataset.id;
15061531
if (confirm(t('delete_confirm_gemini', keyId))) {
@@ -1822,6 +1847,42 @@ async function renderGeminiKeys(keys) {
18221847
cancelAllTestBtn.disabled = true;
18231848
});
18241849

1850+
// Ignore All Errors Logic
1851+
ignoreAllErrorsBtn.addEventListener('click', async () => {
1852+
// Prevent concurrent operations
1853+
if (operationInProgress) {
1854+
showError(t('operation_in_progress') || 'Another operation is in progress. Please wait.');
1855+
return;
1856+
}
1857+
1858+
if (!confirm(t('ignore_all_errors_confirm'))) {
1859+
return;
1860+
}
1861+
1862+
try {
1863+
operationInProgress = true; // Lock operations
1864+
showLoading();
1865+
const result = await apiFetch('/clear-all-errors', {
1866+
method: 'POST',
1867+
});
1868+
1869+
if (result && result.success) {
1870+
if (result.clearedCount === 0) {
1871+
showSuccess(t('no_error_keys_found'));
1872+
} else {
1873+
showSuccess(t('error_keys_ignored', result.clearedCount));
1874+
}
1875+
await loadGeminiKeys(); // Reload the keys list
1876+
}
1877+
} catch (error) {
1878+
console.error('Error ignoring error keys:', error);
1879+
showError(t('failed_to_ignore_error_keys', error.message));
1880+
} finally {
1881+
operationInProgress = false; // Release lock
1882+
hideLoading();
1883+
}
1884+
});
1885+
18251886
// Clean Error Keys Logic
18261887
cleanErrorKeysBtn.addEventListener('click', async () => {
18271888
// Prevent concurrent operations

public/admin/version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.2.0
1+
1.3.0

public/i18n.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class I18n {
4242
'gemini_key_batch_help': '支持批量添加:使用逗号分隔多个密钥,或每行一个密钥。',
4343
'add_gemini_key': '添加 Gemini 密钥',
4444
'run_all_test': '运行所有测试',
45+
'ignore_all_errors': '忽略所有报错',
4546
'clean_error_keys': '清理报错密钥',
4647
'loading_keys': '加载密钥中...',
4748

@@ -68,9 +69,12 @@ class I18n {
6869
'invalid_json': '无效的 JSON 配置',
6970
'vertex_config_overwrite_confirm': '检测到已存在 Vertex 配置,是否要覆盖当前配置?',
7071
'clean_error_keys_confirm': '确定要删除所有带错误标记的 Gemini 密钥吗?此操作不可撤销。',
72+
'ignore_all_errors_confirm': '确定要清除所有带错误标记的 Gemini 密钥的错误状态吗?密钥将保留但错误标记会被移除。',
7173
'no_error_keys_found': '没有找到带错误标记的密钥。',
7274
'error_keys_cleaned': '成功清理了 {0} 个报错密钥。',
75+
'error_keys_ignored': '成功忽略了 {0} 个报错密钥的错误状态。',
7376
'failed_to_clean_error_keys': '清理报错密钥失败:{0}',
77+
'failed_to_ignore_error_keys': '忽略报错密钥失败:{0}',
7478

7579
// Worker API Keys 部分
7680
'add_new_worker_key': '添加新的 Worker 密钥',
@@ -213,12 +217,16 @@ class I18n {
213217
'gemini_key_batch_help': 'Batch addition supported: Use commas to separate multiple keys, or one key per line.',
214218
'add_gemini_key': 'Add Gemini Key',
215219
'run_all_test': 'Run All Test',
220+
'ignore_all_errors': 'Ignore All Errors',
216221
'clean_error_keys': 'Clean Error Keys',
217222
'loading_keys': 'Loading keys...',
218223
'clean_error_keys_confirm': 'Are you sure you want to delete all Gemini keys with error status? This action cannot be undone.',
224+
'ignore_all_errors_confirm': 'Are you sure you want to clear error status for all Gemini keys with errors? Keys will be kept but error marks will be removed.',
219225
'no_error_keys_found': 'No keys with error status found.',
220226
'error_keys_cleaned': 'Successfully cleaned {0} error keys.',
227+
'error_keys_ignored': 'Successfully ignored error status for {0} keys.',
221228
'failed_to_clean_error_keys': 'Failed to clean error keys: {0}',
229+
'failed_to_ignore_error_keys': 'Failed to ignore error keys: {0}',
222230

223231
// Vertex AI Configuration
224232
'current_vertex_config': 'Current Vertex Configuration',

0 commit comments

Comments
 (0)