export const apiKeysModule = { // 加载API密钥 async loadApiKeys() { try { const data = await this.makeRequest('/api-keys'); const apiKeysValue = data?.['api-keys'] || []; const keys = Array.isArray(apiKeysValue) ? apiKeysValue : []; this.renderApiKeys(keys); } catch (error) { console.error('加载API密钥失败:', error); } }, // 渲染API密钥列表 renderApiKeys(keys) { const container = document.getElementById('api-keys-list'); if (keys.length === 0) { container.innerHTML = `

${i18n.t('api_keys.empty_title')}

${i18n.t('api_keys.empty_desc')}

`; return; } const rows = keys.map((key, index) => { const normalizedKey = typeof key === 'string' ? key : String(key ?? ''); const maskedDisplay = this.escapeHtml(this.maskApiKey(normalizedKey)); const keyArgument = encodeURIComponent(normalizedKey); return `
#${index + 1}
${i18n.t('api_keys.item_title')}
${maskedDisplay}
`; }).join(''); container.innerHTML = `
${rows}
`; this.bindApiKeyListEvents(container); }, // 注意: escapeHtml, maskApiKey, normalizeArrayResponse // 现在由 app.js 通过工具模块提供,通过 this 访问 // 添加一行自定义请求头输入 addHeaderField(wrapperId, header = {}) { const wrapper = document.getElementById(wrapperId); if (!wrapper) return; const row = document.createElement('div'); row.className = 'header-input-row'; const keyValue = typeof header.key === 'string' ? header.key : ''; const valueValue = typeof header.value === 'string' ? header.value : ''; row.innerHTML = `
:
`; const removeBtn = row.querySelector('.header-remove-btn'); if (removeBtn) { removeBtn.addEventListener('click', () => { wrapper.removeChild(row); if (wrapper.childElementCount === 0) { this.addHeaderField(wrapperId); } }); } wrapper.appendChild(row); }, // 填充自定义请求头输入 populateHeaderFields(wrapperId, headers = null) { const wrapper = document.getElementById(wrapperId); if (!wrapper) return; wrapper.innerHTML = ''; const entries = (headers && typeof headers === 'object') ? Object.entries(headers).filter(([key, value]) => key && value !== undefined && value !== null) : []; if (!entries.length) { this.addHeaderField(wrapperId); return; } entries.forEach(([key, value]) => this.addHeaderField(wrapperId, { key, value: String(value ?? '') })); }, // 收集自定义请求头输入 collectHeaderInputs(wrapperId) { const wrapper = document.getElementById(wrapperId); if (!wrapper) return null; const rows = Array.from(wrapper.querySelectorAll('.header-input-row')); const headers = {}; rows.forEach(row => { const keyInput = row.querySelector('.header-key-input'); const valueInput = row.querySelector('.header-value-input'); const key = keyInput ? keyInput.value.trim() : ''; const value = valueInput ? valueInput.value.trim() : ''; if (key && value) { headers[key] = value; } }); return Object.keys(headers).length ? headers : null; }, addApiKeyEntryField(wrapperId, entry = {}) { const wrapper = document.getElementById(wrapperId); if (!wrapper) return; const row = document.createElement('div'); row.className = 'api-key-input-row'; const keyValue = typeof entry?.['api-key'] === 'string' ? entry['api-key'] : ''; const proxyValue = typeof entry?.['proxy-url'] === 'string' ? entry['proxy-url'] : ''; row.innerHTML = `
`; const removeBtn = row.querySelector('.api-key-remove-btn'); if (removeBtn) { removeBtn.addEventListener('click', () => { wrapper.removeChild(row); if (wrapper.childElementCount === 0) { this.addApiKeyEntryField(wrapperId); } }); } wrapper.appendChild(row); }, populateApiKeyEntryFields(wrapperId, entries = []) { const wrapper = document.getElementById(wrapperId); if (!wrapper) return; wrapper.innerHTML = ''; if (!Array.isArray(entries) || entries.length === 0) { this.addApiKeyEntryField(wrapperId); return; } entries.forEach(entry => this.addApiKeyEntryField(wrapperId, entry)); }, collectApiKeyEntryInputs(wrapperId) { const wrapper = document.getElementById(wrapperId); if (!wrapper) return []; const rows = Array.from(wrapper.querySelectorAll('.api-key-input-row')); const entries = []; rows.forEach(row => { const keyInput = row.querySelector('.api-key-value-input'); const proxyInput = row.querySelector('.api-key-proxy-input'); const key = keyInput ? keyInput.value.trim() : ''; const proxy = proxyInput ? proxyInput.value.trim() : ''; if (key) { entries.push({ 'api-key': key, 'proxy-url': proxy }); } }); return entries; }, // 规范化并写入请求头 applyHeadersToConfig(target, headers) { if (!target) { return; } if (headers && typeof headers === 'object' && Object.keys(headers).length) { target.headers = { ...headers }; } else { delete target.headers; } }, // 渲染请求头徽章 renderHeaderBadges(headers) { if (!headers || typeof headers !== 'object') { return ''; } const entries = Object.entries(headers).filter(([key, value]) => key && value !== undefined && value !== null && value !== ''); if (!entries.length) { return ''; } const badges = entries.map(([key, value]) => ` ${this.escapeHtml(key)}: ${this.escapeHtml(String(value))} `).join(''); return `
${i18n.t('common.custom_headers_label')}:
${badges}
`; }, // 构造Codex配置,保持未展示的字段 buildCodexConfig(apiKey, baseUrl, proxyUrl, original = {}, headers = null, excludedModels = null) { const result = { ...original, 'api-key': apiKey, 'base-url': baseUrl || '', 'proxy-url': proxyUrl || '' }; this.applyHeadersToConfig(result, headers); if (Array.isArray(excludedModels)) { result['excluded-models'] = excludedModels; } return result; }, // 显示添加API密钥模态框 showAddApiKeyModal() { const modal = document.getElementById('modal'); const modalBody = document.getElementById('modal-body'); modalBody.innerHTML = `

${i18n.t('api_keys.add_modal_title')}

`; modal.style.display = 'block'; }, // 添加API密钥 async addApiKey() { const newKey = document.getElementById('new-api-key').value.trim(); if (!newKey) { this.showNotification(`${i18n.t('notification.please_enter')} ${i18n.t('notification.api_key')}`, 'error'); return; } try { const data = await this.makeRequest('/api-keys'); const currentKeys = data['api-keys'] || []; currentKeys.push(newKey); await this.makeRequest('/api-keys', { method: 'PUT', body: JSON.stringify(currentKeys) }); this.clearCache('api-keys'); // 仅清除 api-keys 段缓存 this.closeModal(); this.loadApiKeys(); this.showNotification(i18n.t('notification.api_key_added'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.add_failed')}: ${error.message}`, 'error'); } }, // 编辑API密钥 editApiKey(index, currentKey) { const modal = document.getElementById('modal'); const modalBody = document.getElementById('modal-body'); modalBody.innerHTML = `

${i18n.t('api_keys.edit_modal_title')}

`; modal.style.display = 'block'; }, // 更新API密钥 async updateApiKey(index) { const newKey = document.getElementById('edit-api-key').value.trim(); if (!newKey) { this.showNotification(`${i18n.t('notification.please_enter')} ${i18n.t('notification.api_key')}`, 'error'); return; } try { await this.makeRequest('/api-keys', { method: 'PATCH', body: JSON.stringify({ index, value: newKey }) }); this.clearCache('api-keys'); // 仅清除 api-keys 段缓存 this.closeModal(); this.loadApiKeys(); this.showNotification(i18n.t('notification.api_key_updated'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.update_failed')}: ${error.message}`, 'error'); } }, // 删除API密钥 async deleteApiKey(index) { if (!confirm(i18n.t('api_keys.delete_confirm'))) return; try { await this.makeRequest(`/api-keys?index=${index}`, { method: 'DELETE' }); this.clearCache('api-keys'); // 仅清除 api-keys 段缓存 this.loadApiKeys(); this.showNotification(i18n.t('notification.api_key_deleted'), 'success'); } catch (error) { this.showNotification(`${i18n.t('notification.delete_failed')}: ${error.message}`, 'error'); } }, bindApiKeyListEvents(container = null) { if (this.apiKeyListEventsBound) { return; } const listContainer = container || document.getElementById('api-keys-list'); if (!listContainer) return; listContainer.addEventListener('click', (event) => { const button = event.target.closest('[data-action][data-index]'); if (!button || !listContainer.contains(button)) return; const action = button.dataset.action; const index = Number(button.dataset.index); if (!Number.isFinite(index)) return; switch (action) { case 'edit-api-key': { const rawKey = button.dataset.key || ''; let decodedKey = ''; try { decodedKey = decodeURIComponent(rawKey); } catch (e) { decodedKey = rawKey; } this.editApiKey(index, decodedKey); break; } case 'delete-api-key': this.deleteApiKey(index); break; default: break; } }); this.apiKeyListEventsBound = true; } };