mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-03 11:20:50 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
020fccc032 | ||
|
|
c162ab3a54 | ||
|
|
85d12e15d8 | ||
|
|
ebffb49f52 | ||
|
|
316c1ffc0d | ||
|
|
b3e54e7f14 | ||
|
|
fe11bfb48f | ||
|
|
ee0d8f82d7 | ||
|
|
0bbb397df5 |
@@ -10,7 +10,7 @@ Example URL:
|
|||||||
https://remote.router-for.me/
|
https://remote.router-for.me/
|
||||||
|
|
||||||
Minimum required version: ≥ 6.0.0
|
Minimum required version: ≥ 6.0.0
|
||||||
Recommended version: ≥ 6.0.19
|
Recommended version: ≥ 6.1.3
|
||||||
|
|
||||||
Since version 6.0.19, the WebUI has been rolled into the main program. You can access it by going to `/management.html` on the external port after firing up the main project.
|
Since version 6.0.19, the WebUI has been rolled into the main program. You can access it by going to `/management.html` on the external port after firing up the main project.
|
||||||
|
|
||||||
@@ -43,7 +43,6 @@ Since version 6.0.19, the WebUI has been rolled into the main program. You can a
|
|||||||
- Download existing authentication files
|
- Download existing authentication files
|
||||||
- Delete single or all authentication files
|
- Delete single or all authentication files
|
||||||
- Display file details
|
- Display file details
|
||||||
- **Gemini Web Token**: Direct authentication using browser cookies
|
|
||||||
|
|
||||||
### Usage Statistics
|
### Usage Statistics
|
||||||
- **Real-time Analytics**: Track API usage with interactive charts
|
- **Real-time Analytics**: Track API usage with interactive charts
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ https://github.com/router-for-me/CLIProxyAPI
|
|||||||
https://remote.router-for.me/
|
https://remote.router-for.me/
|
||||||
|
|
||||||
最低可用版本 ≥ 6.0.0
|
最低可用版本 ≥ 6.0.0
|
||||||
推荐版本 ≥ 6.0.19
|
|
||||||
|
推荐版本 ≥ 6.1.3
|
||||||
|
|
||||||
自6.0.19起WebUI已经集成在主程序中 可以通过主项目开启的外部端口的`/management.html`访问
|
自6.0.19起WebUI已经集成在主程序中 可以通过主项目开启的外部端口的`/management.html`访问
|
||||||
|
|
||||||
## 功能特点
|
## 功能特点
|
||||||
@@ -40,7 +42,6 @@ https://remote.router-for.me/
|
|||||||
- 下载现有认证文件
|
- 下载现有认证文件
|
||||||
- 删除单个或所有认证文件
|
- 删除单个或所有认证文件
|
||||||
- 显示文件详细信息
|
- 显示文件详细信息
|
||||||
- **Gemini Web Token**: 使用浏览器 Cookie 直接认证
|
|
||||||
|
|
||||||
### 使用统计
|
### 使用统计
|
||||||
- **实时分析**: 通过交互式图表跟踪 API 使用情况
|
- **实时分析**: 通过交互式图表跟踪 API 使用情况
|
||||||
|
|||||||
930
app.js
930
app.js
@@ -109,6 +109,47 @@ class CLIProxyManager {
|
|||||||
this.setupThemeSwitcher();
|
this.setupThemeSwitcher();
|
||||||
// loadSettings 将在登录成功后调用
|
// loadSettings 将在登录成功后调用
|
||||||
this.updateLoginConnectionInfo();
|
this.updateLoginConnectionInfo();
|
||||||
|
// 检查主机名,如果不是 localhost 或 127.0.0.1,则隐藏 OAuth 登录框
|
||||||
|
this.checkHostAndHideOAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查主机名并隐藏 OAuth 登录框
|
||||||
|
checkHostAndHideOAuth() {
|
||||||
|
const hostname = window.location.hostname;
|
||||||
|
const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1';
|
||||||
|
|
||||||
|
if (!isLocalhost) {
|
||||||
|
// 隐藏所有 OAuth 登录卡片
|
||||||
|
const oauthCards = [
|
||||||
|
'codex-oauth-card',
|
||||||
|
'anthropic-oauth-card',
|
||||||
|
'gemini-cli-oauth-card',
|
||||||
|
'qwen-oauth-card',
|
||||||
|
'iflow-oauth-card'
|
||||||
|
];
|
||||||
|
|
||||||
|
oauthCards.forEach(cardId => {
|
||||||
|
const card = document.getElementById(cardId);
|
||||||
|
if (card) {
|
||||||
|
card.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果找不到具体的卡片 ID,尝试通过类名查找
|
||||||
|
const oauthCardElements = document.querySelectorAll('.card');
|
||||||
|
oauthCardElements.forEach(card => {
|
||||||
|
const cardText = card.textContent || '';
|
||||||
|
if (cardText.includes('Codex OAuth') ||
|
||||||
|
cardText.includes('Anthropic OAuth') ||
|
||||||
|
cardText.includes('Gemini CLI OAuth') ||
|
||||||
|
cardText.includes('Qwen OAuth') ||
|
||||||
|
cardText.includes('iFlow OAuth')) {
|
||||||
|
card.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`当前主机名: ${hostname},已隐藏 OAuth 登录框`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查登录状态
|
// 检查登录状态
|
||||||
@@ -497,12 +538,6 @@ class CLIProxyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Gemini Web Token
|
|
||||||
const geminiWebTokenBtn = document.getElementById('gemini-web-token-btn');
|
|
||||||
if (geminiWebTokenBtn) {
|
|
||||||
geminiWebTokenBtn.addEventListener('click', () => this.showGeminiWebTokenModal());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 认证文件管理
|
// 认证文件管理
|
||||||
const uploadAuthFile = document.getElementById('upload-auth-file');
|
const uploadAuthFile = document.getElementById('upload-auth-file');
|
||||||
const deleteAllAuthFiles = document.getElementById('delete-all-auth-files');
|
const deleteAllAuthFiles = document.getElementById('delete-all-auth-files');
|
||||||
@@ -518,6 +553,81 @@ class CLIProxyManager {
|
|||||||
authFileInput.addEventListener('change', (e) => this.handleFileUpload(e));
|
authFileInput.addEventListener('change', (e) => this.handleFileUpload(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Codex OAuth
|
||||||
|
const codexOauthBtn = document.getElementById('codex-oauth-btn');
|
||||||
|
const codexOpenLink = document.getElementById('codex-open-link');
|
||||||
|
const codexCopyLink = document.getElementById('codex-copy-link');
|
||||||
|
|
||||||
|
if (codexOauthBtn) {
|
||||||
|
codexOauthBtn.addEventListener('click', () => this.startCodexOAuth());
|
||||||
|
}
|
||||||
|
if (codexOpenLink) {
|
||||||
|
codexOpenLink.addEventListener('click', () => this.openCodexLink());
|
||||||
|
}
|
||||||
|
if (codexCopyLink) {
|
||||||
|
codexCopyLink.addEventListener('click', () => this.copyCodexLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anthropic OAuth
|
||||||
|
const anthropicOauthBtn = document.getElementById('anthropic-oauth-btn');
|
||||||
|
const anthropicOpenLink = document.getElementById('anthropic-open-link');
|
||||||
|
const anthropicCopyLink = document.getElementById('anthropic-copy-link');
|
||||||
|
|
||||||
|
if (anthropicOauthBtn) {
|
||||||
|
anthropicOauthBtn.addEventListener('click', () => this.startAnthropicOAuth());
|
||||||
|
}
|
||||||
|
if (anthropicOpenLink) {
|
||||||
|
anthropicOpenLink.addEventListener('click', () => this.openAnthropicLink());
|
||||||
|
}
|
||||||
|
if (anthropicCopyLink) {
|
||||||
|
anthropicCopyLink.addEventListener('click', () => this.copyAnthropicLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gemini CLI OAuth
|
||||||
|
const geminiCliOauthBtn = document.getElementById('gemini-cli-oauth-btn');
|
||||||
|
const geminiCliOpenLink = document.getElementById('gemini-cli-open-link');
|
||||||
|
const geminiCliCopyLink = document.getElementById('gemini-cli-copy-link');
|
||||||
|
|
||||||
|
if (geminiCliOauthBtn) {
|
||||||
|
geminiCliOauthBtn.addEventListener('click', () => this.startGeminiCliOAuth());
|
||||||
|
}
|
||||||
|
if (geminiCliOpenLink) {
|
||||||
|
geminiCliOpenLink.addEventListener('click', () => this.openGeminiCliLink());
|
||||||
|
}
|
||||||
|
if (geminiCliCopyLink) {
|
||||||
|
geminiCliCopyLink.addEventListener('click', () => this.copyGeminiCliLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Qwen OAuth
|
||||||
|
const qwenOauthBtn = document.getElementById('qwen-oauth-btn');
|
||||||
|
const qwenOpenLink = document.getElementById('qwen-open-link');
|
||||||
|
const qwenCopyLink = document.getElementById('qwen-copy-link');
|
||||||
|
|
||||||
|
if (qwenOauthBtn) {
|
||||||
|
qwenOauthBtn.addEventListener('click', () => this.startQwenOAuth());
|
||||||
|
}
|
||||||
|
if (qwenOpenLink) {
|
||||||
|
qwenOpenLink.addEventListener('click', () => this.openQwenLink());
|
||||||
|
}
|
||||||
|
if (qwenCopyLink) {
|
||||||
|
qwenCopyLink.addEventListener('click', () => this.copyQwenLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
// iFlow OAuth
|
||||||
|
const iflowOauthBtn = document.getElementById('iflow-oauth-btn');
|
||||||
|
const iflowOpenLink = document.getElementById('iflow-open-link');
|
||||||
|
const iflowCopyLink = document.getElementById('iflow-copy-link');
|
||||||
|
|
||||||
|
if (iflowOauthBtn) {
|
||||||
|
iflowOauthBtn.addEventListener('click', () => this.startIflowOAuth());
|
||||||
|
}
|
||||||
|
if (iflowOpenLink) {
|
||||||
|
iflowOpenLink.addEventListener('click', () => this.openIflowLink());
|
||||||
|
}
|
||||||
|
if (iflowCopyLink) {
|
||||||
|
iflowCopyLink.addEventListener('click', () => this.copyIflowLink());
|
||||||
|
}
|
||||||
|
|
||||||
// 使用统计
|
// 使用统计
|
||||||
const refreshUsageStats = document.getElementById('refresh-usage-stats');
|
const refreshUsageStats = document.getElementById('refresh-usage-stats');
|
||||||
const requestsHourBtn = document.getElementById('requests-hour-btn');
|
const requestsHourBtn = document.getElementById('requests-hour-btn');
|
||||||
@@ -2314,104 +2424,748 @@ class CLIProxyManager {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 显示 Gemini Web Token 模态框
|
// ===== Codex OAuth 相关方法 =====
|
||||||
showGeminiWebTokenModal() {
|
|
||||||
const inlineSecure1psid = document.getElementById('secure-1psid-input');
|
|
||||||
const inlineSecure1psidts = document.getElementById('secure-1psidts-input');
|
|
||||||
const inlineLabel = document.getElementById('gemini-web-label-input');
|
|
||||||
const modalBody = document.getElementById('modal-body');
|
|
||||||
modalBody.innerHTML = `
|
|
||||||
<h3>${i18n.t('auth_login.gemini_web_button')}</h3>
|
|
||||||
<div class="gemini-web-form">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="modal-secure-1psid">${i18n.t('auth_login.secure_1psid_label')}</label>
|
|
||||||
<input type="text" id="modal-secure-1psid" placeholder="${i18n.t('auth_login.secure_1psid_placeholder')}" required>
|
|
||||||
<div class="form-hint">从浏览器开发者工具 → Application → Cookies 中获取</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="modal-secure-1psidts">${i18n.t('auth_login.secure_1psidts_label')}</label>
|
|
||||||
<input type="text" id="modal-secure-1psidts" placeholder="${i18n.t('auth_login.secure_1psidts_placeholder')}" required>
|
|
||||||
<div class="form-hint">从浏览器开发者工具 → Application → Cookies 中获取</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="modal-gemini-web-label">${i18n.t('auth_login.gemini_web_label_label')}</label>
|
|
||||||
<input type="text" id="modal-gemini-web-label" placeholder="${i18n.t('auth_login.gemini_web_label_placeholder')}">
|
|
||||||
<div class="form-hint">为此认证文件设置一个标签名称(可选)</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-actions">
|
|
||||||
<button class="btn btn-secondary" onclick="manager.closeModal()">${i18n.t('common.cancel')}</button>
|
|
||||||
<button class="btn btn-primary" onclick="manager.saveGeminiWebToken()">${i18n.t('common.save')}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
this.showModal();
|
|
||||||
|
|
||||||
const modalSecure1psid = document.getElementById('modal-secure-1psid');
|
// 开始 Codex OAuth 流程
|
||||||
const modalSecure1psidts = document.getElementById('modal-secure-1psidts');
|
async startCodexOAuth() {
|
||||||
const modalLabel = document.getElementById('modal-gemini-web-label');
|
try {
|
||||||
|
const response = await this.makeRequest('/codex-auth-url?is_webui=1');
|
||||||
|
const authUrl = response.url;
|
||||||
|
const state = this.extractStateFromUrl(authUrl);
|
||||||
|
|
||||||
if (modalSecure1psid && inlineSecure1psid) {
|
// 显示授权链接
|
||||||
modalSecure1psid.value = inlineSecure1psid.value.trim();
|
const urlInput = document.getElementById('codex-oauth-url');
|
||||||
}
|
const content = document.getElementById('codex-oauth-content');
|
||||||
if (modalSecure1psidts && inlineSecure1psidts) {
|
const status = document.getElementById('codex-oauth-status');
|
||||||
modalSecure1psidts.value = inlineSecure1psidts.value.trim();
|
|
||||||
}
|
|
||||||
if (modalLabel && inlineLabel) {
|
|
||||||
modalLabel.value = inlineLabel.value.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modalSecure1psid) {
|
if (urlInput) {
|
||||||
modalSecure1psid.focus();
|
urlInput.value = authUrl;
|
||||||
|
}
|
||||||
|
if (content) {
|
||||||
|
content.style.display = 'block';
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
status.textContent = i18n.t('auth_login.codex_oauth_status_waiting');
|
||||||
|
status.style.color = 'var(--warning-text)';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始轮询认证状态
|
||||||
|
this.startCodexOAuthPolling(state);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.showNotification(`${i18n.t('auth_login.codex_oauth_start_error')} ${error.message}`, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存 Gemini Web Token
|
// 从 URL 中提取 state 参数
|
||||||
async saveGeminiWebToken() {
|
extractStateFromUrl(url) {
|
||||||
const secure1psid = document.getElementById('modal-secure-1psid').value.trim();
|
try {
|
||||||
const secure1psidts = document.getElementById('modal-secure-1psidts').value.trim();
|
const urlObj = new URL(url);
|
||||||
const label = document.getElementById('modal-gemini-web-label').value.trim();
|
return urlObj.searchParams.get('state');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to extract state from URL:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!secure1psid || !secure1psidts) {
|
// 打开 Codex 授权链接
|
||||||
this.showNotification('请填写完整的 Cookie 信息', 'error');
|
openCodexLink() {
|
||||||
|
const urlInput = document.getElementById('codex-oauth-url');
|
||||||
|
if (urlInput && urlInput.value) {
|
||||||
|
window.open(urlInput.value, '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制 Codex 授权链接
|
||||||
|
async copyCodexLink() {
|
||||||
|
const urlInput = document.getElementById('codex-oauth-url');
|
||||||
|
if (urlInput && urlInput.value) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(urlInput.value);
|
||||||
|
this.showNotification('链接已复制到剪贴板', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
// 降级方案:使用传统的复制方法
|
||||||
|
urlInput.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
this.showNotification('链接已复制到剪贴板', 'success');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始轮询 OAuth 状态
|
||||||
|
startCodexOAuthPolling(state) {
|
||||||
|
if (!state) {
|
||||||
|
this.showNotification('无法获取认证状态参数', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pollInterval = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`);
|
||||||
|
const status = response.status;
|
||||||
|
const statusElement = document.getElementById('codex-oauth-status');
|
||||||
|
|
||||||
|
if (status === 'ok') {
|
||||||
|
// 认证成功
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
// 隐藏授权链接相关内容,恢复到初始状态
|
||||||
|
this.resetCodexOAuthUI();
|
||||||
|
// 显示成功通知
|
||||||
|
this.showNotification(i18n.t('auth_login.codex_oauth_status_success'), 'success');
|
||||||
|
// 刷新认证文件列表
|
||||||
|
this.loadAuthFiles();
|
||||||
|
} else if (status === 'error') {
|
||||||
|
// 认证失败
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
const errorMessage = response.error || 'Unknown error';
|
||||||
|
// 显示错误信息
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = `${i18n.t('auth_login.codex_oauth_status_error')} ${errorMessage}`;
|
||||||
|
statusElement.style.color = 'var(--error-text)';
|
||||||
|
}
|
||||||
|
this.showNotification(`${i18n.t('auth_login.codex_oauth_status_error')} ${errorMessage}`, 'error');
|
||||||
|
// 3秒后重置UI,让用户能够重新开始
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetCodexOAuthUI();
|
||||||
|
}, 3000);
|
||||||
|
} else if (status === 'wait') {
|
||||||
|
// 继续等待
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = i18n.t('auth_login.codex_oauth_status_waiting');
|
||||||
|
statusElement.style.color = 'var(--warning-text)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
const statusElement = document.getElementById('codex-oauth-status');
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = `${i18n.t('auth_login.codex_oauth_polling_error')} ${error.message}`;
|
||||||
|
statusElement.style.color = 'var(--error-text)';
|
||||||
|
}
|
||||||
|
this.showNotification(`${i18n.t('auth_login.codex_oauth_polling_error')} ${error.message}`, 'error');
|
||||||
|
// 3秒后重置UI,让用户能够重新开始
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetCodexOAuthUI();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}, 2000); // 每2秒轮询一次
|
||||||
|
|
||||||
|
// 设置超时,5分钟后停止轮询
|
||||||
|
setTimeout(() => {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置 Codex OAuth UI 到初始状态
|
||||||
|
resetCodexOAuthUI() {
|
||||||
|
const urlInput = document.getElementById('codex-oauth-url');
|
||||||
|
const content = document.getElementById('codex-oauth-content');
|
||||||
|
const status = document.getElementById('codex-oauth-status');
|
||||||
|
|
||||||
|
// 清空并隐藏授权链接输入框
|
||||||
|
if (urlInput) {
|
||||||
|
urlInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏整个授权链接内容区域
|
||||||
|
if (content) {
|
||||||
|
content.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空状态显示
|
||||||
|
if (status) {
|
||||||
|
status.textContent = '';
|
||||||
|
status.style.color = '';
|
||||||
|
status.className = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Anthropic OAuth 相关方法 =====
|
||||||
|
|
||||||
|
// 开始 Anthropic OAuth 流程
|
||||||
|
async startAnthropicOAuth() {
|
||||||
try {
|
try {
|
||||||
const requestBody = {
|
const response = await this.makeRequest('/anthropic-auth-url?is_webui=1');
|
||||||
secure_1psid: secure1psid,
|
const authUrl = response.url;
|
||||||
secure_1psidts: secure1psidts
|
const state = this.extractStateFromUrl(authUrl);
|
||||||
};
|
|
||||||
|
|
||||||
// 如果提供了 label,则添加到请求体中
|
// 显示授权链接
|
||||||
if (label) {
|
const urlInput = document.getElementById('anthropic-oauth-url');
|
||||||
requestBody.label = label;
|
const content = document.getElementById('anthropic-oauth-content');
|
||||||
|
const status = document.getElementById('anthropic-oauth-status');
|
||||||
|
|
||||||
|
if (urlInput) {
|
||||||
|
urlInput.value = authUrl;
|
||||||
|
}
|
||||||
|
if (content) {
|
||||||
|
content.style.display = 'block';
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
status.textContent = i18n.t('auth_login.anthropic_oauth_status_waiting');
|
||||||
|
status.style.color = 'var(--warning-text)';
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this.makeRequest('/gemini-web-token', {
|
// 开始轮询认证状态
|
||||||
method: 'POST',
|
this.startAnthropicOAuthPolling(state);
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(requestBody)
|
|
||||||
});
|
|
||||||
|
|
||||||
this.closeModal();
|
|
||||||
this.loadAuthFiles(); // 刷新认证文件列表
|
|
||||||
const inlineSecure1psid = document.getElementById('secure-1psid-input');
|
|
||||||
const inlineSecure1psidts = document.getElementById('secure-1psidts-input');
|
|
||||||
const inlineLabel = document.getElementById('gemini-web-label-input');
|
|
||||||
if (inlineSecure1psid) {
|
|
||||||
inlineSecure1psid.value = secure1psid;
|
|
||||||
}
|
|
||||||
if (inlineSecure1psidts) {
|
|
||||||
inlineSecure1psidts.value = secure1psidts;
|
|
||||||
}
|
|
||||||
if (inlineLabel) {
|
|
||||||
inlineLabel.value = label;
|
|
||||||
}
|
|
||||||
this.showNotification(`${i18n.t('auth_login.gemini_web_saved')}: ${response.file}`, 'success');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.showNotification(`保存失败: ${error.message}`, 'error');
|
this.showNotification(`${i18n.t('auth_login.anthropic_oauth_start_error')} ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开 Anthropic 授权链接
|
||||||
|
openAnthropicLink() {
|
||||||
|
const urlInput = document.getElementById('anthropic-oauth-url');
|
||||||
|
if (urlInput && urlInput.value) {
|
||||||
|
window.open(urlInput.value, '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制 Anthropic 授权链接
|
||||||
|
async copyAnthropicLink() {
|
||||||
|
const urlInput = document.getElementById('anthropic-oauth-url');
|
||||||
|
if (urlInput && urlInput.value) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(urlInput.value);
|
||||||
|
this.showNotification('链接已复制到剪贴板', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
// 降级方案:使用传统的复制方法
|
||||||
|
urlInput.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
this.showNotification('链接已复制到剪贴板', 'success');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始轮询 Anthropic OAuth 状态
|
||||||
|
startAnthropicOAuthPolling(state) {
|
||||||
|
if (!state) {
|
||||||
|
this.showNotification('无法获取认证状态参数', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pollInterval = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`);
|
||||||
|
const status = response.status;
|
||||||
|
const statusElement = document.getElementById('anthropic-oauth-status');
|
||||||
|
|
||||||
|
if (status === 'ok') {
|
||||||
|
// 认证成功
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
// 隐藏授权链接相关内容,恢复到初始状态
|
||||||
|
this.resetAnthropicOAuthUI();
|
||||||
|
// 显示成功通知
|
||||||
|
this.showNotification(i18n.t('auth_login.anthropic_oauth_status_success'), 'success');
|
||||||
|
// 刷新认证文件列表
|
||||||
|
this.loadAuthFiles();
|
||||||
|
} else if (status === 'error') {
|
||||||
|
// 认证失败
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
const errorMessage = response.error || 'Unknown error';
|
||||||
|
// 显示错误信息
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = `${i18n.t('auth_login.anthropic_oauth_status_error')} ${errorMessage}`;
|
||||||
|
statusElement.style.color = 'var(--error-text)';
|
||||||
|
}
|
||||||
|
this.showNotification(`${i18n.t('auth_login.anthropic_oauth_status_error')} ${errorMessage}`, 'error');
|
||||||
|
// 3秒后重置UI,让用户能够重新开始
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetAnthropicOAuthUI();
|
||||||
|
}, 3000);
|
||||||
|
} else if (status === 'wait') {
|
||||||
|
// 继续等待
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = i18n.t('auth_login.anthropic_oauth_status_waiting');
|
||||||
|
statusElement.style.color = 'var(--warning-text)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
const statusElement = document.getElementById('anthropic-oauth-status');
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = `${i18n.t('auth_login.anthropic_oauth_polling_error')} ${error.message}`;
|
||||||
|
statusElement.style.color = 'var(--error-text)';
|
||||||
|
}
|
||||||
|
this.showNotification(`${i18n.t('auth_login.anthropic_oauth_polling_error')} ${error.message}`, 'error');
|
||||||
|
// 3秒后重置UI,让用户能够重新开始
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetAnthropicOAuthUI();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}, 2000); // 每2秒轮询一次
|
||||||
|
|
||||||
|
// 设置超时,5分钟后停止轮询
|
||||||
|
setTimeout(() => {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置 Anthropic OAuth UI 到初始状态
|
||||||
|
resetAnthropicOAuthUI() {
|
||||||
|
const urlInput = document.getElementById('anthropic-oauth-url');
|
||||||
|
const content = document.getElementById('anthropic-oauth-content');
|
||||||
|
const status = document.getElementById('anthropic-oauth-status');
|
||||||
|
|
||||||
|
// 清空并隐藏授权链接输入框
|
||||||
|
if (urlInput) {
|
||||||
|
urlInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏整个授权链接内容区域
|
||||||
|
if (content) {
|
||||||
|
content.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空状态显示
|
||||||
|
if (status) {
|
||||||
|
status.textContent = '';
|
||||||
|
status.style.color = '';
|
||||||
|
status.className = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Gemini CLI OAuth 相关方法 =====
|
||||||
|
|
||||||
|
// 开始 Gemini CLI OAuth 流程
|
||||||
|
async startGeminiCliOAuth() {
|
||||||
|
try {
|
||||||
|
// 获取项目 ID(可选)
|
||||||
|
const projectId = document.getElementById('gemini-cli-project-id').value.trim();
|
||||||
|
|
||||||
|
// 构建请求 URL
|
||||||
|
let requestUrl = '/gemini-cli-auth-url?is_webui=1';
|
||||||
|
if (projectId) {
|
||||||
|
requestUrl += `&project_id=${encodeURIComponent(projectId)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this.makeRequest(requestUrl);
|
||||||
|
const authUrl = response.url;
|
||||||
|
const state = this.extractStateFromUrl(authUrl);
|
||||||
|
|
||||||
|
// 显示授权链接
|
||||||
|
const urlInput = document.getElementById('gemini-cli-oauth-url');
|
||||||
|
const content = document.getElementById('gemini-cli-oauth-content');
|
||||||
|
const status = document.getElementById('gemini-cli-oauth-status');
|
||||||
|
|
||||||
|
if (urlInput) {
|
||||||
|
urlInput.value = authUrl;
|
||||||
|
}
|
||||||
|
if (content) {
|
||||||
|
content.style.display = 'block';
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
status.textContent = i18n.t('auth_login.gemini_cli_oauth_status_waiting');
|
||||||
|
status.style.color = 'var(--warning-text)';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始轮询认证状态
|
||||||
|
this.startGeminiCliOAuthPolling(state);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.showNotification(`${i18n.t('auth_login.gemini_cli_oauth_start_error')} ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开 Gemini CLI 授权链接
|
||||||
|
openGeminiCliLink() {
|
||||||
|
const urlInput = document.getElementById('gemini-cli-oauth-url');
|
||||||
|
if (urlInput && urlInput.value) {
|
||||||
|
window.open(urlInput.value, '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制 Gemini CLI 授权链接
|
||||||
|
async copyGeminiCliLink() {
|
||||||
|
const urlInput = document.getElementById('gemini-cli-oauth-url');
|
||||||
|
if (urlInput && urlInput.value) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(urlInput.value);
|
||||||
|
this.showNotification('链接已复制到剪贴板', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
// 降级方案:使用传统的复制方法
|
||||||
|
urlInput.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
this.showNotification('链接已复制到剪贴板', 'success');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始轮询 Gemini CLI OAuth 状态
|
||||||
|
startGeminiCliOAuthPolling(state) {
|
||||||
|
if (!state) {
|
||||||
|
this.showNotification('无法获取认证状态参数', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pollInterval = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`);
|
||||||
|
const status = response.status;
|
||||||
|
const statusElement = document.getElementById('gemini-cli-oauth-status');
|
||||||
|
|
||||||
|
if (status === 'ok') {
|
||||||
|
// 认证成功
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
// 隐藏授权链接相关内容,恢复到初始状态
|
||||||
|
this.resetGeminiCliOAuthUI();
|
||||||
|
// 显示成功通知
|
||||||
|
this.showNotification(i18n.t('auth_login.gemini_cli_oauth_status_success'), 'success');
|
||||||
|
// 刷新认证文件列表
|
||||||
|
this.loadAuthFiles();
|
||||||
|
} else if (status === 'error') {
|
||||||
|
// 认证失败
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
const errorMessage = response.error || 'Unknown error';
|
||||||
|
// 显示错误信息
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = `${i18n.t('auth_login.gemini_cli_oauth_status_error')} ${errorMessage}`;
|
||||||
|
statusElement.style.color = 'var(--error-text)';
|
||||||
|
}
|
||||||
|
this.showNotification(`${i18n.t('auth_login.gemini_cli_oauth_status_error')} ${errorMessage}`, 'error');
|
||||||
|
// 3秒后重置UI,让用户能够重新开始
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetGeminiCliOAuthUI();
|
||||||
|
}, 3000);
|
||||||
|
} else if (status === 'wait') {
|
||||||
|
// 继续等待
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = i18n.t('auth_login.gemini_cli_oauth_status_waiting');
|
||||||
|
statusElement.style.color = 'var(--warning-text)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
const statusElement = document.getElementById('gemini-cli-oauth-status');
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = `${i18n.t('auth_login.gemini_cli_oauth_polling_error')} ${error.message}`;
|
||||||
|
statusElement.style.color = 'var(--error-text)';
|
||||||
|
}
|
||||||
|
this.showNotification(`${i18n.t('auth_login.gemini_cli_oauth_polling_error')} ${error.message}`, 'error');
|
||||||
|
// 3秒后重置UI,让用户能够重新开始
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetGeminiCliOAuthUI();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}, 2000); // 每2秒轮询一次
|
||||||
|
|
||||||
|
// 设置超时,5分钟后停止轮询
|
||||||
|
setTimeout(() => {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置 Gemini CLI OAuth UI 到初始状态
|
||||||
|
resetGeminiCliOAuthUI() {
|
||||||
|
const urlInput = document.getElementById('gemini-cli-oauth-url');
|
||||||
|
const content = document.getElementById('gemini-cli-oauth-content');
|
||||||
|
const status = document.getElementById('gemini-cli-oauth-status');
|
||||||
|
|
||||||
|
// 清空并隐藏授权链接输入框
|
||||||
|
if (urlInput) {
|
||||||
|
urlInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏整个授权链接内容区域
|
||||||
|
if (content) {
|
||||||
|
content.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空状态显示
|
||||||
|
if (status) {
|
||||||
|
status.textContent = '';
|
||||||
|
status.style.color = '';
|
||||||
|
status.className = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Qwen OAuth 相关方法 =====
|
||||||
|
|
||||||
|
// 开始 Qwen OAuth 流程
|
||||||
|
async startQwenOAuth() {
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest('/qwen-auth-url?is_webui=1');
|
||||||
|
const authUrl = response.url;
|
||||||
|
const state = this.extractStateFromUrl(authUrl);
|
||||||
|
|
||||||
|
// 显示授权链接
|
||||||
|
const urlInput = document.getElementById('qwen-oauth-url');
|
||||||
|
const content = document.getElementById('qwen-oauth-content');
|
||||||
|
const status = document.getElementById('qwen-oauth-status');
|
||||||
|
|
||||||
|
if (urlInput) {
|
||||||
|
urlInput.value = authUrl;
|
||||||
|
}
|
||||||
|
if (content) {
|
||||||
|
content.style.display = 'block';
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
status.textContent = i18n.t('auth_login.qwen_oauth_status_waiting');
|
||||||
|
status.style.color = 'var(--warning-text)';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始轮询认证状态
|
||||||
|
this.startQwenOAuthPolling(response.state);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.showNotification(`${i18n.t('auth_login.qwen_oauth_start_error')} ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开 Qwen 授权链接
|
||||||
|
openQwenLink() {
|
||||||
|
const urlInput = document.getElementById('qwen-oauth-url');
|
||||||
|
if (urlInput && urlInput.value) {
|
||||||
|
window.open(urlInput.value, '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制 Qwen 授权链接
|
||||||
|
async copyQwenLink() {
|
||||||
|
const urlInput = document.getElementById('qwen-oauth-url');
|
||||||
|
if (urlInput && urlInput.value) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(urlInput.value);
|
||||||
|
this.showNotification('链接已复制到剪贴板', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
// 降级方案:使用传统的复制方法
|
||||||
|
urlInput.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
this.showNotification('链接已复制到剪贴板', 'success');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始轮询 Qwen OAuth 状态
|
||||||
|
startQwenOAuthPolling(state) {
|
||||||
|
if (!state) {
|
||||||
|
this.showNotification('无法获取认证状态参数', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pollInterval = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`);
|
||||||
|
const status = response.status;
|
||||||
|
const statusElement = document.getElementById('qwen-oauth-status');
|
||||||
|
|
||||||
|
if (status === 'ok') {
|
||||||
|
// 认证成功
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
// 隐藏授权链接相关内容,恢复到初始状态
|
||||||
|
this.resetQwenOAuthUI();
|
||||||
|
// 显示成功通知
|
||||||
|
this.showNotification(i18n.t('auth_login.qwen_oauth_status_success'), 'success');
|
||||||
|
// 刷新认证文件列表
|
||||||
|
this.loadAuthFiles();
|
||||||
|
} else if (status === 'error') {
|
||||||
|
// 认证失败
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
const errorMessage = response.error || 'Unknown error';
|
||||||
|
// 显示错误信息
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = `${i18n.t('auth_login.qwen_oauth_status_error')} ${errorMessage}`;
|
||||||
|
statusElement.style.color = 'var(--error-text)';
|
||||||
|
}
|
||||||
|
this.showNotification(`${i18n.t('auth_login.qwen_oauth_status_error')} ${errorMessage}`, 'error');
|
||||||
|
// 3秒后重置UI,让用户能够重新开始
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetQwenOAuthUI();
|
||||||
|
}, 3000);
|
||||||
|
} else if (status === 'wait') {
|
||||||
|
// 继续等待
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = i18n.t('auth_login.qwen_oauth_status_waiting');
|
||||||
|
statusElement.style.color = 'var(--warning-text)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
const statusElement = document.getElementById('qwen-oauth-status');
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = `${i18n.t('auth_login.qwen_oauth_polling_error')} ${error.message}`;
|
||||||
|
statusElement.style.color = 'var(--error-text)';
|
||||||
|
}
|
||||||
|
this.showNotification(`${i18n.t('auth_login.qwen_oauth_polling_error')} ${error.message}`, 'error');
|
||||||
|
// 3秒后重置UI,让用户能够重新开始
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetQwenOAuthUI();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}, 2000); // 每2秒轮询一次
|
||||||
|
|
||||||
|
// 设置超时,5分钟后停止轮询
|
||||||
|
setTimeout(() => {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置 Qwen OAuth UI 到初始状态
|
||||||
|
resetQwenOAuthUI() {
|
||||||
|
const urlInput = document.getElementById('qwen-oauth-url');
|
||||||
|
const content = document.getElementById('qwen-oauth-content');
|
||||||
|
const status = document.getElementById('qwen-oauth-status');
|
||||||
|
|
||||||
|
// 清空并隐藏授权链接输入框
|
||||||
|
if (urlInput) {
|
||||||
|
urlInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏整个授权链接内容区域
|
||||||
|
if (content) {
|
||||||
|
content.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空状态显示
|
||||||
|
if (status) {
|
||||||
|
status.textContent = '';
|
||||||
|
status.style.color = '';
|
||||||
|
status.className = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== iFlow OAuth 相关方法 =====
|
||||||
|
|
||||||
|
// 开始 iFlow OAuth 流程
|
||||||
|
async startIflowOAuth() {
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest('/iflow-auth-url?is_webui=1');
|
||||||
|
const authUrl = response.url;
|
||||||
|
const state = this.extractStateFromUrl(authUrl);
|
||||||
|
|
||||||
|
// 显示授权链接
|
||||||
|
const urlInput = document.getElementById('iflow-oauth-url');
|
||||||
|
const content = document.getElementById('iflow-oauth-content');
|
||||||
|
const status = document.getElementById('iflow-oauth-status');
|
||||||
|
|
||||||
|
if (urlInput) {
|
||||||
|
urlInput.value = authUrl;
|
||||||
|
}
|
||||||
|
if (content) {
|
||||||
|
content.style.display = 'block';
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
status.textContent = i18n.t('auth_login.iflow_oauth_status_waiting');
|
||||||
|
status.style.color = 'var(--warning-text)';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始轮询认证状态
|
||||||
|
this.startIflowOAuthPolling(state);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.showNotification(`${i18n.t('auth_login.iflow_oauth_start_error')} ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开 iFlow 授权链接
|
||||||
|
openIflowLink() {
|
||||||
|
const urlInput = document.getElementById('iflow-oauth-url');
|
||||||
|
if (urlInput && urlInput.value) {
|
||||||
|
window.open(urlInput.value, '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制 iFlow 授权链接
|
||||||
|
async copyIflowLink() {
|
||||||
|
const urlInput = document.getElementById('iflow-oauth-url');
|
||||||
|
if (urlInput && urlInput.value) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(urlInput.value);
|
||||||
|
this.showNotification('链接已复制到剪贴板', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
// 降级方案:使用传统的复制方法
|
||||||
|
urlInput.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
this.showNotification('链接已复制到剪贴板', 'success');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始轮询 iFlow OAuth 状态
|
||||||
|
startIflowOAuthPolling(state) {
|
||||||
|
if (!state) {
|
||||||
|
this.showNotification('无法获取认证状态参数', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pollInterval = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const response = await this.makeRequest(`/get-auth-status?state=${encodeURIComponent(state)}`);
|
||||||
|
const status = response.status;
|
||||||
|
const statusElement = document.getElementById('iflow-oauth-status');
|
||||||
|
|
||||||
|
if (status === 'ok') {
|
||||||
|
// 认证成功
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
// 隐藏授权链接相关内容,恢复到初始状态
|
||||||
|
this.resetIflowOAuthUI();
|
||||||
|
// 显示成功通知
|
||||||
|
this.showNotification(i18n.t('auth_login.iflow_oauth_status_success'), 'success');
|
||||||
|
// 刷新认证文件列表
|
||||||
|
this.loadAuthFiles();
|
||||||
|
} else if (status === 'error') {
|
||||||
|
// 认证失败
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
const errorMessage = response.error || 'Unknown error';
|
||||||
|
// 显示错误信息
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = `${i18n.t('auth_login.iflow_oauth_status_error')} ${errorMessage}`;
|
||||||
|
statusElement.style.color = 'var(--error-text)';
|
||||||
|
}
|
||||||
|
this.showNotification(`${i18n.t('auth_login.iflow_oauth_status_error')} ${errorMessage}`, 'error');
|
||||||
|
// 3秒后重置UI,让用户能够重新开始
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetIflowOAuthUI();
|
||||||
|
}, 3000);
|
||||||
|
} else if (status === 'wait') {
|
||||||
|
// 继续等待
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = i18n.t('auth_login.iflow_oauth_status_waiting');
|
||||||
|
statusElement.style.color = 'var(--warning-text)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
const statusElement = document.getElementById('iflow-oauth-status');
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.textContent = `${i18n.t('auth_login.iflow_oauth_polling_error')} ${error.message}`;
|
||||||
|
statusElement.style.color = 'var(--error-text)';
|
||||||
|
}
|
||||||
|
this.showNotification(`${i18n.t('auth_login.iflow_oauth_polling_error')} ${error.message}`, 'error');
|
||||||
|
// 3秒后重置UI,让用户能够重新开始
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetIflowOAuthUI();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}, 2000); // 每2秒轮询一次
|
||||||
|
|
||||||
|
// 设置超时,5分钟后停止轮询
|
||||||
|
setTimeout(() => {
|
||||||
|
clearInterval(pollInterval);
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置 iFlow OAuth UI 到初始状态
|
||||||
|
resetIflowOAuthUI() {
|
||||||
|
const urlInput = document.getElementById('iflow-oauth-url');
|
||||||
|
const content = document.getElementById('iflow-oauth-content');
|
||||||
|
const status = document.getElementById('iflow-oauth-status');
|
||||||
|
|
||||||
|
// 清空并隐藏授权链接输入框
|
||||||
|
if (urlInput) {
|
||||||
|
urlInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏整个授权链接内容区域
|
||||||
|
if (content) {
|
||||||
|
content.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空状态显示
|
||||||
|
if (status) {
|
||||||
|
status.textContent = '';
|
||||||
|
status.style.color = '';
|
||||||
|
status.className = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
180
i18n.js
180
i18n.js
@@ -214,17 +214,74 @@ const i18n = {
|
|||||||
'auth_files.delete_all_success': '成功删除',
|
'auth_files.delete_all_success': '成功删除',
|
||||||
'auth_files.files_count': '个文件',
|
'auth_files.files_count': '个文件',
|
||||||
|
|
||||||
// Gemini Web Token
|
|
||||||
'auth_login.gemini_web_title': 'Gemini Web Token',
|
// Codex OAuth
|
||||||
'auth_login.gemini_web_button': '保存 Gemini Web Token',
|
'auth_login.codex_oauth_title': 'Codex OAuth',
|
||||||
'auth_login.gemini_web_hint': '从浏览器开发者工具中获取 Gemini 网页版的 Cookie 值,用于直接认证访问 Gemini。',
|
'auth_login.codex_oauth_button': '开始 Codex 登录',
|
||||||
'auth_login.secure_1psid_label': '__Secure-1PSID Cookie:',
|
'auth_login.codex_oauth_hint': '通过 OAuth 流程登录 Codex 服务,自动获取并保存认证文件。',
|
||||||
'auth_login.secure_1psid_placeholder': '输入 __Secure-1PSID cookie 值',
|
'auth_login.codex_oauth_url_label': '授权链接:',
|
||||||
'auth_login.secure_1psidts_label': '__Secure-1PSIDTS Cookie:',
|
'auth_login.codex_open_link': '打开链接',
|
||||||
'auth_login.secure_1psidts_placeholder': '输入 __Secure-1PSIDTS cookie 值',
|
'auth_login.codex_copy_link': '复制链接',
|
||||||
'auth_login.gemini_web_label_label': '标签 (可选):',
|
'auth_login.codex_oauth_status_waiting': '等待认证中...',
|
||||||
'auth_login.gemini_web_label_placeholder': '输入标签名称 (可选)',
|
'auth_login.codex_oauth_status_success': '认证成功!',
|
||||||
'auth_login.gemini_web_saved': 'Gemini Web Token 保存成功',
|
'auth_login.codex_oauth_status_error': '认证失败:',
|
||||||
|
'auth_login.codex_oauth_start_error': '启动 Codex OAuth 失败:',
|
||||||
|
'auth_login.codex_oauth_polling_error': '检查认证状态失败:',
|
||||||
|
|
||||||
|
// Anthropic OAuth
|
||||||
|
'auth_login.anthropic_oauth_title': 'Anthropic OAuth',
|
||||||
|
'auth_login.anthropic_oauth_button': '开始 Anthropic 登录',
|
||||||
|
'auth_login.anthropic_oauth_hint': '通过 OAuth 流程登录 Anthropic (Claude) 服务,自动获取并保存认证文件。',
|
||||||
|
'auth_login.anthropic_oauth_url_label': '授权链接:',
|
||||||
|
'auth_login.anthropic_open_link': '打开链接',
|
||||||
|
'auth_login.anthropic_copy_link': '复制链接',
|
||||||
|
'auth_login.anthropic_oauth_status_waiting': '等待认证中...',
|
||||||
|
'auth_login.anthropic_oauth_status_success': '认证成功!',
|
||||||
|
'auth_login.anthropic_oauth_status_error': '认证失败:',
|
||||||
|
'auth_login.anthropic_oauth_start_error': '启动 Anthropic OAuth 失败:',
|
||||||
|
'auth_login.anthropic_oauth_polling_error': '检查认证状态失败:',
|
||||||
|
|
||||||
|
// Gemini CLI OAuth
|
||||||
|
'auth_login.gemini_cli_oauth_title': 'Gemini CLI OAuth',
|
||||||
|
'auth_login.gemini_cli_oauth_button': '开始 Gemini CLI 登录',
|
||||||
|
'auth_login.gemini_cli_oauth_hint': '通过 OAuth 流程登录 Google Gemini CLI 服务,自动获取并保存认证文件。',
|
||||||
|
'auth_login.gemini_cli_project_id_label': 'Google Cloud 项目 ID (可选):',
|
||||||
|
'auth_login.gemini_cli_project_id_placeholder': '输入 Google Cloud 项目 ID (可选)',
|
||||||
|
'auth_login.gemini_cli_project_id_hint': '如果指定了项目 ID,将使用该项目的认证信息。',
|
||||||
|
'auth_login.gemini_cli_oauth_url_label': '授权链接:',
|
||||||
|
'auth_login.gemini_cli_open_link': '打开链接',
|
||||||
|
'auth_login.gemini_cli_copy_link': '复制链接',
|
||||||
|
'auth_login.gemini_cli_oauth_status_waiting': '等待认证中...',
|
||||||
|
'auth_login.gemini_cli_oauth_status_success': '认证成功!',
|
||||||
|
'auth_login.gemini_cli_oauth_status_error': '认证失败:',
|
||||||
|
'auth_login.gemini_cli_oauth_start_error': '启动 Gemini CLI OAuth 失败:',
|
||||||
|
'auth_login.gemini_cli_oauth_polling_error': '检查认证状态失败:',
|
||||||
|
|
||||||
|
// Qwen OAuth
|
||||||
|
'auth_login.qwen_oauth_title': 'Qwen OAuth',
|
||||||
|
'auth_login.qwen_oauth_button': '开始 Qwen 登录',
|
||||||
|
'auth_login.qwen_oauth_hint': '通过设备授权流程登录 Qwen 服务,自动获取并保存认证文件。',
|
||||||
|
'auth_login.qwen_oauth_url_label': '授权链接:',
|
||||||
|
'auth_login.qwen_open_link': '打开链接',
|
||||||
|
'auth_login.qwen_copy_link': '复制链接',
|
||||||
|
'auth_login.qwen_oauth_status_waiting': '等待认证中...',
|
||||||
|
'auth_login.qwen_oauth_status_success': '认证成功!',
|
||||||
|
'auth_login.qwen_oauth_status_error': '认证失败:',
|
||||||
|
'auth_login.qwen_oauth_start_error': '启动 Qwen OAuth 失败:',
|
||||||
|
'auth_login.qwen_oauth_polling_error': '检查认证状态失败:',
|
||||||
|
|
||||||
|
// iFlow OAuth
|
||||||
|
'auth_login.iflow_oauth_title': 'iFlow OAuth',
|
||||||
|
'auth_login.iflow_oauth_button': '开始 iFlow 登录',
|
||||||
|
'auth_login.iflow_oauth_hint': '通过 OAuth 流程登录 iFlow 服务,自动获取并保存认证文件。',
|
||||||
|
'auth_login.iflow_oauth_url_label': '授权链接:',
|
||||||
|
'auth_login.iflow_open_link': '打开链接',
|
||||||
|
'auth_login.iflow_copy_link': '复制链接',
|
||||||
|
'auth_login.iflow_oauth_status_waiting': '等待认证中...',
|
||||||
|
'auth_login.iflow_oauth_status_success': '认证成功!',
|
||||||
|
'auth_login.iflow_oauth_status_error': '认证失败:',
|
||||||
|
'auth_login.iflow_oauth_start_error': '启动 iFlow OAuth 失败:',
|
||||||
|
'auth_login.iflow_oauth_polling_error': '检查认证状态失败:',
|
||||||
|
|
||||||
// 使用统计
|
// 使用统计
|
||||||
'usage_stats.title': '使用统计',
|
'usage_stats.title': '使用统计',
|
||||||
@@ -347,6 +404,8 @@ const i18n = {
|
|||||||
'common.required': 'Required',
|
'common.required': 'Required',
|
||||||
'common.api_key': 'Key',
|
'common.api_key': 'Key',
|
||||||
'common.base_url': 'Address',
|
'common.base_url': 'Address',
|
||||||
|
'common.proxy_url': 'Proxy',
|
||||||
|
'common.alias': 'Alias',
|
||||||
|
|
||||||
// Page titles
|
// Page titles
|
||||||
'title.main': 'CLI Proxy API Management Center',
|
'title.main': 'CLI Proxy API Management Center',
|
||||||
@@ -447,9 +506,12 @@ const i18n = {
|
|||||||
'ai_providers.codex_add_modal_key_placeholder': 'Please enter Codex API key',
|
'ai_providers.codex_add_modal_key_placeholder': 'Please enter Codex API key',
|
||||||
'ai_providers.codex_add_modal_url_label': 'Base URL (Optional):',
|
'ai_providers.codex_add_modal_url_label': 'Base URL (Optional):',
|
||||||
'ai_providers.codex_add_modal_url_placeholder': 'e.g.: https://api.example.com',
|
'ai_providers.codex_add_modal_url_placeholder': 'e.g.: https://api.example.com',
|
||||||
|
'ai_providers.codex_add_modal_proxy_label': 'Proxy URL (Optional):',
|
||||||
|
'ai_providers.codex_add_modal_proxy_placeholder': 'e.g.: socks5://proxy.example.com:1080',
|
||||||
'ai_providers.codex_edit_modal_title': 'Edit Codex API Configuration',
|
'ai_providers.codex_edit_modal_title': 'Edit Codex API Configuration',
|
||||||
'ai_providers.codex_edit_modal_key_label': 'API Key:',
|
'ai_providers.codex_edit_modal_key_label': 'API Key:',
|
||||||
'ai_providers.codex_edit_modal_url_label': 'Base URL (Optional):',
|
'ai_providers.codex_edit_modal_url_label': 'Base URL (Optional):',
|
||||||
|
'ai_providers.codex_edit_modal_proxy_label': 'Proxy URL (Optional):',
|
||||||
'ai_providers.codex_delete_confirm': 'Are you sure you want to delete this Codex configuration?',
|
'ai_providers.codex_delete_confirm': 'Are you sure you want to delete this Codex configuration?',
|
||||||
|
|
||||||
'ai_providers.claude_title': 'Claude API Configuration',
|
'ai_providers.claude_title': 'Claude API Configuration',
|
||||||
@@ -462,9 +524,12 @@ const i18n = {
|
|||||||
'ai_providers.claude_add_modal_key_placeholder': 'Please enter Claude API key',
|
'ai_providers.claude_add_modal_key_placeholder': 'Please enter Claude API key',
|
||||||
'ai_providers.claude_add_modal_url_label': 'Base URL (Optional):',
|
'ai_providers.claude_add_modal_url_label': 'Base URL (Optional):',
|
||||||
'ai_providers.claude_add_modal_url_placeholder': 'e.g.: https://api.anthropic.com',
|
'ai_providers.claude_add_modal_url_placeholder': 'e.g.: https://api.anthropic.com',
|
||||||
|
'ai_providers.claude_add_modal_proxy_label': 'Proxy URL (Optional):',
|
||||||
|
'ai_providers.claude_add_modal_proxy_placeholder': 'e.g.: socks5://proxy.example.com:1080',
|
||||||
'ai_providers.claude_edit_modal_title': 'Edit Claude API Configuration',
|
'ai_providers.claude_edit_modal_title': 'Edit Claude API Configuration',
|
||||||
'ai_providers.claude_edit_modal_key_label': 'API Key:',
|
'ai_providers.claude_edit_modal_key_label': 'API Key:',
|
||||||
'ai_providers.claude_edit_modal_url_label': 'Base URL (Optional):',
|
'ai_providers.claude_edit_modal_url_label': 'Base URL (Optional):',
|
||||||
|
'ai_providers.claude_edit_modal_proxy_label': 'Proxy URL (Optional):',
|
||||||
'ai_providers.claude_delete_confirm': 'Are you sure you want to delete this Claude configuration?',
|
'ai_providers.claude_delete_confirm': 'Are you sure you want to delete this Claude configuration?',
|
||||||
|
|
||||||
'ai_providers.openai_title': 'OpenAI Compatible Providers',
|
'ai_providers.openai_title': 'OpenAI Compatible Providers',
|
||||||
@@ -478,10 +543,19 @@ const i18n = {
|
|||||||
'ai_providers.openai_add_modal_url_placeholder': 'e.g.: https://openrouter.ai/api/v1',
|
'ai_providers.openai_add_modal_url_placeholder': 'e.g.: https://openrouter.ai/api/v1',
|
||||||
'ai_providers.openai_add_modal_keys_label': 'API Keys (one per line):',
|
'ai_providers.openai_add_modal_keys_label': 'API Keys (one per line):',
|
||||||
'ai_providers.openai_add_modal_keys_placeholder': 'sk-key1\nsk-key2',
|
'ai_providers.openai_add_modal_keys_placeholder': 'sk-key1\nsk-key2',
|
||||||
|
'ai_providers.openai_add_modal_keys_proxy_label': 'Proxy URL (one per line, optional):',
|
||||||
|
'ai_providers.openai_add_modal_keys_proxy_placeholder': 'socks5://proxy.example.com:1080\n',
|
||||||
|
'ai_providers.openai_add_modal_models_label': 'Model List (name[, alias] one per line):',
|
||||||
|
'ai_providers.openai_models_hint': 'Example: gpt-4o-mini or moonshotai/kimi-k2:free, kimi-k2',
|
||||||
|
'ai_providers.openai_model_name_placeholder': 'Model name, e.g. moonshotai/kimi-k2:free',
|
||||||
|
'ai_providers.openai_model_alias_placeholder': 'Model alias (optional)',
|
||||||
|
'ai_providers.openai_models_add_btn': 'Add Model',
|
||||||
'ai_providers.openai_edit_modal_title': 'Edit OpenAI Compatible Provider',
|
'ai_providers.openai_edit_modal_title': 'Edit OpenAI Compatible Provider',
|
||||||
'ai_providers.openai_edit_modal_name_label': 'Provider Name:',
|
'ai_providers.openai_edit_modal_name_label': 'Provider Name:',
|
||||||
'ai_providers.openai_edit_modal_url_label': 'Base URL:',
|
'ai_providers.openai_edit_modal_url_label': 'Base URL:',
|
||||||
'ai_providers.openai_edit_modal_keys_label': 'API Keys (one per line):',
|
'ai_providers.openai_edit_modal_keys_label': 'API Keys (one per line):',
|
||||||
|
'ai_providers.openai_edit_modal_keys_proxy_label': 'Proxy URL (one per line, optional):',
|
||||||
|
'ai_providers.openai_edit_modal_models_label': 'Model List (name[, alias] one per line):',
|
||||||
'ai_providers.openai_delete_confirm': 'Are you sure you want to delete this OpenAI provider?',
|
'ai_providers.openai_delete_confirm': 'Are you sure you want to delete this OpenAI provider?',
|
||||||
'ai_providers.openai_keys_count': 'Keys Count',
|
'ai_providers.openai_keys_count': 'Keys Count',
|
||||||
'ai_providers.openai_models_count': 'Models Count',
|
'ai_providers.openai_models_count': 'Models Count',
|
||||||
@@ -508,17 +582,73 @@ const i18n = {
|
|||||||
'auth_files.delete_all_success': 'Successfully deleted',
|
'auth_files.delete_all_success': 'Successfully deleted',
|
||||||
'auth_files.files_count': 'files',
|
'auth_files.files_count': 'files',
|
||||||
|
|
||||||
// Gemini Web Token
|
// Codex OAuth
|
||||||
'auth_login.gemini_web_title': 'Gemini Web Token',
|
'auth_login.codex_oauth_title': 'Codex OAuth',
|
||||||
'auth_login.gemini_web_button': 'Save Gemini Web Token',
|
'auth_login.codex_oauth_button': 'Start Codex Login',
|
||||||
'auth_login.gemini_web_hint': 'Obtain the Cookie value of the Gemini web version from the browser\'s developer tools, used for direct authentication to access Gemini.',
|
'auth_login.codex_oauth_hint': 'Login to Codex service through OAuth flow, automatically obtain and save authentication files.',
|
||||||
'auth_login.secure_1psid_label': '__Secure-1PSID Cookie:',
|
'auth_login.codex_oauth_url_label': 'Authorization URL:',
|
||||||
'auth_login.secure_1psid_placeholder': 'Enter __Secure-1PSID cookie value',
|
'auth_login.codex_open_link': 'Open Link',
|
||||||
'auth_login.secure_1psidts_label': '__Secure-1PSIDTS Cookie:',
|
'auth_login.codex_copy_link': 'Copy Link',
|
||||||
'auth_login.secure_1psidts_placeholder': 'Enter __Secure-1PSIDTS cookie value',
|
'auth_login.codex_oauth_status_waiting': 'Waiting for authentication...',
|
||||||
'auth_login.gemini_web_label_label': 'Label (Optional):',
|
'auth_login.codex_oauth_status_success': 'Authentication successful!',
|
||||||
'auth_login.gemini_web_label_placeholder': 'Enter label name (optional)',
|
'auth_login.codex_oauth_status_error': 'Authentication failed:',
|
||||||
'auth_login.gemini_web_saved': 'Gemini Web Token saved successfully',
|
'auth_login.codex_oauth_start_error': 'Failed to start Codex OAuth:',
|
||||||
|
'auth_login.codex_oauth_polling_error': 'Failed to check authentication status:',
|
||||||
|
|
||||||
|
// Anthropic OAuth
|
||||||
|
'auth_login.anthropic_oauth_title': 'Anthropic OAuth',
|
||||||
|
'auth_login.anthropic_oauth_button': 'Start Anthropic Login',
|
||||||
|
'auth_login.anthropic_oauth_hint': 'Login to Anthropic (Claude) service through OAuth flow, automatically obtain and save authentication files.',
|
||||||
|
'auth_login.anthropic_oauth_url_label': 'Authorization URL:',
|
||||||
|
'auth_login.anthropic_open_link': 'Open Link',
|
||||||
|
'auth_login.anthropic_copy_link': 'Copy Link',
|
||||||
|
'auth_login.anthropic_oauth_status_waiting': 'Waiting for authentication...',
|
||||||
|
'auth_login.anthropic_oauth_status_success': 'Authentication successful!',
|
||||||
|
'auth_login.anthropic_oauth_status_error': 'Authentication failed:',
|
||||||
|
'auth_login.anthropic_oauth_start_error': 'Failed to start Anthropic OAuth:',
|
||||||
|
'auth_login.anthropic_oauth_polling_error': 'Failed to check authentication status:',
|
||||||
|
|
||||||
|
// Gemini CLI OAuth
|
||||||
|
'auth_login.gemini_cli_oauth_title': 'Gemini CLI OAuth',
|
||||||
|
'auth_login.gemini_cli_oauth_button': 'Start Gemini CLI Login',
|
||||||
|
'auth_login.gemini_cli_oauth_hint': 'Login to Google Gemini CLI service through OAuth flow, automatically obtain and save authentication files.',
|
||||||
|
'auth_login.gemini_cli_project_id_label': 'Google Cloud Project ID (Optional):',
|
||||||
|
'auth_login.gemini_cli_project_id_placeholder': 'Enter Google Cloud Project ID (optional)',
|
||||||
|
'auth_login.gemini_cli_project_id_hint': 'If a project ID is specified, authentication information for that project will be used.',
|
||||||
|
'auth_login.gemini_cli_oauth_url_label': 'Authorization URL:',
|
||||||
|
'auth_login.gemini_cli_open_link': 'Open Link',
|
||||||
|
'auth_login.gemini_cli_copy_link': 'Copy Link',
|
||||||
|
'auth_login.gemini_cli_oauth_status_waiting': 'Waiting for authentication...',
|
||||||
|
'auth_login.gemini_cli_oauth_status_success': 'Authentication successful!',
|
||||||
|
'auth_login.gemini_cli_oauth_status_error': 'Authentication failed:',
|
||||||
|
'auth_login.gemini_cli_oauth_start_error': 'Failed to start Gemini CLI OAuth:',
|
||||||
|
'auth_login.gemini_cli_oauth_polling_error': 'Failed to check authentication status:',
|
||||||
|
|
||||||
|
// Qwen OAuth
|
||||||
|
'auth_login.qwen_oauth_title': 'Qwen OAuth',
|
||||||
|
'auth_login.qwen_oauth_button': 'Start Qwen Login',
|
||||||
|
'auth_login.qwen_oauth_hint': 'Login to Qwen service through device authorization flow, automatically obtain and save authentication files.',
|
||||||
|
'auth_login.qwen_oauth_url_label': 'Authorization URL:',
|
||||||
|
'auth_login.qwen_open_link': 'Open Link',
|
||||||
|
'auth_login.qwen_copy_link': 'Copy Link',
|
||||||
|
'auth_login.qwen_oauth_status_waiting': 'Waiting for authentication...',
|
||||||
|
'auth_login.qwen_oauth_status_success': 'Authentication successful!',
|
||||||
|
'auth_login.qwen_oauth_status_error': 'Authentication failed:',
|
||||||
|
'auth_login.qwen_oauth_start_error': 'Failed to start Qwen OAuth:',
|
||||||
|
'auth_login.qwen_oauth_polling_error': 'Failed to check authentication status:',
|
||||||
|
|
||||||
|
// iFlow OAuth
|
||||||
|
'auth_login.iflow_oauth_title': 'iFlow OAuth',
|
||||||
|
'auth_login.iflow_oauth_button': 'Start iFlow Login',
|
||||||
|
'auth_login.iflow_oauth_hint': 'Login to iFlow service through OAuth flow, automatically obtain and save authentication files.',
|
||||||
|
'auth_login.iflow_oauth_url_label': 'Authorization URL:',
|
||||||
|
'auth_login.iflow_open_link': 'Open Link',
|
||||||
|
'auth_login.iflow_copy_link': 'Copy Link',
|
||||||
|
'auth_login.iflow_oauth_status_waiting': 'Waiting for authentication...',
|
||||||
|
'auth_login.iflow_oauth_status_success': 'Authentication successful!',
|
||||||
|
'auth_login.iflow_oauth_status_error': 'Authentication failed:',
|
||||||
|
'auth_login.iflow_oauth_start_error': 'Failed to start iFlow OAuth:',
|
||||||
|
'auth_login.iflow_oauth_polling_error': 'Failed to check authentication status:',
|
||||||
|
|
||||||
// Usage Statistics
|
// Usage Statistics
|
||||||
'usage_stats.title': 'Usage Statistics',
|
'usage_stats.title': 'Usage Statistics',
|
||||||
@@ -570,6 +700,8 @@ const i18n = {
|
|||||||
'notification.claude_config_added': 'Claude configuration added successfully',
|
'notification.claude_config_added': 'Claude configuration added successfully',
|
||||||
'notification.claude_config_updated': 'Claude configuration updated successfully',
|
'notification.claude_config_updated': 'Claude configuration updated successfully',
|
||||||
'notification.claude_config_deleted': 'Claude configuration deleted successfully',
|
'notification.claude_config_deleted': 'Claude configuration deleted successfully',
|
||||||
|
'notification.field_required': 'Required fields cannot be empty',
|
||||||
|
'notification.openai_provider_required': 'Please fill in provider name and Base URL',
|
||||||
'notification.openai_provider_added': 'OpenAI provider added successfully',
|
'notification.openai_provider_added': 'OpenAI provider added successfully',
|
||||||
'notification.openai_provider_updated': 'OpenAI provider updated successfully',
|
'notification.openai_provider_updated': 'OpenAI provider updated successfully',
|
||||||
'notification.openai_provider_deleted': 'OpenAI provider deleted successfully',
|
'notification.openai_provider_deleted': 'OpenAI provider deleted successfully',
|
||||||
@@ -613,8 +745,8 @@ const i18n = {
|
|||||||
// 获取翻译文本
|
// 获取翻译文本
|
||||||
t(key, params = {}) {
|
t(key, params = {}) {
|
||||||
const translation = this.translations[this.currentLanguage]?.[key] ||
|
const translation = this.translations[this.currentLanguage]?.[key] ||
|
||||||
this.translations[this.fallbackLanguage]?.[key] ||
|
this.translations[this.fallbackLanguage]?.[key] ||
|
||||||
key;
|
key;
|
||||||
|
|
||||||
// 简单的参数替换
|
// 简单的参数替换
|
||||||
return translation.replace(/\{(\w+)\}/g, (match, param) => {
|
return translation.replace(/\{(\w+)\}/g, (match, param) => {
|
||||||
|
|||||||
1019
index.html
1019
index.html
File diff suppressed because it is too large
Load Diff
554
styles.css
554
styles.css
@@ -149,8 +149,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
0% { transform: rotate(0deg); }
|
0% {
|
||||||
100% { transform: rotate(360deg); }
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.auto-login-content h2 {
|
.auto-login-content h2 {
|
||||||
@@ -198,7 +203,8 @@
|
|||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
margin-top: 8px; /* 与标题拉开距离,避免遮挡 */
|
margin-top: 8px;
|
||||||
|
/* 与标题拉开距离,避免遮挡 */
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,12 +555,14 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-bottom: 10px; /* 在小屏幕上给控制按钮组添加下边距 */
|
margin-bottom: 10px;
|
||||||
|
/* 在小屏幕上给控制按钮组添加下边距 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 登录页面小屏幕优化 */
|
/* 登录页面小屏幕优化 */
|
||||||
.login-header-top .header-controls {
|
.login-header-top .header-controls {
|
||||||
margin: 8px auto 0 auto; /* 顶部留白,避免与标题拥挤 */
|
margin: 8px auto 0 auto;
|
||||||
|
/* 顶部留白,避免与标题拥挤 */
|
||||||
background: rgba(255, 255, 255, 0.08);
|
background: rgba(255, 255, 255, 0.08);
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -572,7 +580,8 @@
|
|||||||
.theme-btn {
|
.theme-btn {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
height: 36px; /* 在小屏幕上稍微减小高度 */
|
height: 36px;
|
||||||
|
/* 在小屏幕上稍微减小高度 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-title {
|
.login-title {
|
||||||
@@ -851,7 +860,7 @@ body {
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-navbar-actions > * {
|
.top-navbar-actions>* {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
@@ -892,7 +901,7 @@ body {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-navbar-actions > * {
|
.top-navbar-actions>* {
|
||||||
height: 34px;
|
height: 34px;
|
||||||
min-height: 34px;
|
min-height: 34px;
|
||||||
}
|
}
|
||||||
@@ -1049,6 +1058,7 @@ body {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(10px);
|
transform: translateY(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
@@ -1293,11 +1303,11 @@ textarea::placeholder {
|
|||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked + .slider {
|
input:checked+.slider {
|
||||||
background: var(--primary-color);
|
background: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked + .slider:before {
|
input:checked+.slider:before {
|
||||||
transform: translateX(20px);
|
transform: translateX(20px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1457,6 +1467,7 @@ input:checked + .slider:before {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-30px);
|
transform: translateY(-30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
@@ -1660,7 +1671,7 @@ input:checked + .slider:before {
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-navbar-actions > * {
|
.top-navbar-actions>* {
|
||||||
height: 34px;
|
height: 34px;
|
||||||
min-height: 34px;
|
min-height: 34px;
|
||||||
}
|
}
|
||||||
@@ -1803,7 +1814,9 @@ input:checked + .slider:before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
to { transform: rotate(360deg); }
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 空状态 */
|
/* 空状态 */
|
||||||
@@ -1899,53 +1912,17 @@ input:checked + .slider:before {
|
|||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Gemini Web Token 模态框样式 */
|
|
||||||
.gemini-web-form .form-group {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gemini-web-form .form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gemini-web-form .form-group input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px 16px;
|
|
||||||
border: 2px solid var(--border-primary);
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
background: var(--bg-tertiary);
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gemini-web-form .form-group input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--border-focus);
|
|
||||||
box-shadow: 0 0 0 3px var(--border-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.gemini-web-form .form-hint {
|
|
||||||
margin-top: 6px;
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 使用统计样式 */
|
/* 使用统计样式 */
|
||||||
.stats-overview {
|
.stats-overview {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -2211,3 +2188,480 @@ input:checked + .slider:before {
|
|||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Codex OAuth 样式 */
|
||||||
|
#codex-oauth-content {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-oauth-url {
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-oauth-url:focus {
|
||||||
|
border-color: var(--border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-oauth-status {
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-oauth-status.success {
|
||||||
|
background: var(--success-bg);
|
||||||
|
border-color: var(--success-border);
|
||||||
|
color: var(--success-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-oauth-status.error {
|
||||||
|
background: var(--error-bg);
|
||||||
|
border-color: var(--error-border);
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-oauth-status.warning {
|
||||||
|
background: var(--warning-bg);
|
||||||
|
border-color: var(--warning-border);
|
||||||
|
color: var(--warning-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Codex OAuth 按钮样式 */
|
||||||
|
#codex-open-link,
|
||||||
|
#codex-copy-link {
|
||||||
|
min-width: 100px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-open-link {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-open-link:hover {
|
||||||
|
background: var(--primary-hover);
|
||||||
|
border-color: var(--primary-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-copy-link {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-copy-link:hover {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-color: var(--border-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 - Codex OAuth */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
#codex-oauth-content .input-group {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-open-link,
|
||||||
|
#codex-copy-link {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#codex-oauth-url {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Anthropic OAuth 样式 */
|
||||||
|
#anthropic-oauth-content {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-oauth-url {
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-oauth-url:focus {
|
||||||
|
border-color: var(--border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-oauth-status {
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-oauth-status.success {
|
||||||
|
background: var(--success-bg);
|
||||||
|
border-color: var(--success-border);
|
||||||
|
color: var(--success-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-oauth-status.error {
|
||||||
|
background: var(--error-bg);
|
||||||
|
border-color: var(--error-border);
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-oauth-status.warning {
|
||||||
|
background: var(--warning-bg);
|
||||||
|
border-color: var(--warning-border);
|
||||||
|
color: var(--warning-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Anthropic OAuth 按钮样式 */
|
||||||
|
#anthropic-open-link,
|
||||||
|
#anthropic-copy-link {
|
||||||
|
min-width: 100px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-open-link {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-open-link:hover {
|
||||||
|
background: var(--primary-hover);
|
||||||
|
border-color: var(--primary-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-copy-link {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-copy-link:hover {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-color: var(--border-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 - Anthropic OAuth */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
#anthropic-oauth-content .input-group {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-open-link,
|
||||||
|
#anthropic-copy-link {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#anthropic-oauth-url {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gemini CLI OAuth 样式 */
|
||||||
|
#gemini-cli-oauth-content {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-oauth-url {
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-oauth-url:focus {
|
||||||
|
border-color: var(--border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-oauth-status {
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-oauth-status.success {
|
||||||
|
background: var(--success-bg);
|
||||||
|
border-color: var(--success-border);
|
||||||
|
color: var(--success-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-oauth-status.error {
|
||||||
|
background: var(--error-bg);
|
||||||
|
border-color: var(--error-border);
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-oauth-status.warning {
|
||||||
|
background: var(--warning-bg);
|
||||||
|
border-color: var(--warning-border);
|
||||||
|
color: var(--warning-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gemini CLI OAuth 按钮样式 */
|
||||||
|
#gemini-cli-open-link,
|
||||||
|
#gemini-cli-copy-link {
|
||||||
|
min-width: 100px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-open-link {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-open-link:hover {
|
||||||
|
background: var(--primary-hover);
|
||||||
|
border-color: var(--primary-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-copy-link {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-copy-link:hover {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-color: var(--border-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gemini CLI 项目 ID 输入框样式 */
|
||||||
|
#gemini-cli-project-id {
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-project-id:focus {
|
||||||
|
border-color: var(--border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 - Gemini CLI OAuth */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
#gemini-cli-oauth-content .input-group {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-open-link,
|
||||||
|
#gemini-cli-copy-link {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-oauth-url {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gemini-cli-project-id {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Qwen OAuth 样式 */
|
||||||
|
#qwen-oauth-content {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-oauth-url {
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-oauth-url:focus {
|
||||||
|
border-color: var(--border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-oauth-status {
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-oauth-status.success {
|
||||||
|
background: var(--success-bg);
|
||||||
|
border-color: var(--success-border);
|
||||||
|
color: var(--success-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-oauth-status.error {
|
||||||
|
background: var(--error-bg);
|
||||||
|
border-color: var(--error-border);
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-oauth-status.warning {
|
||||||
|
background: var(--warning-bg);
|
||||||
|
border-color: var(--warning-border);
|
||||||
|
color: var(--warning-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Qwen OAuth 按钮样式 */
|
||||||
|
#qwen-open-link,
|
||||||
|
#qwen-copy-link {
|
||||||
|
min-width: 100px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-open-link {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-open-link:hover {
|
||||||
|
background: var(--primary-hover);
|
||||||
|
border-color: var(--primary-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-copy-link {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-copy-link:hover {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-color: var(--border-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 - Qwen OAuth */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
#qwen-oauth-content .input-group {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-open-link,
|
||||||
|
#qwen-copy-link {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#qwen-oauth-url {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iFlow OAuth 样式 */
|
||||||
|
#iflow-oauth-content {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-oauth-url {
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-oauth-url:focus {
|
||||||
|
border-color: var(--border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-oauth-status {
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-oauth-status.success {
|
||||||
|
background: var(--success-bg);
|
||||||
|
border-color: var(--success-border);
|
||||||
|
color: var(--success-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-oauth-status.error {
|
||||||
|
background: var(--error-bg);
|
||||||
|
border-color: var(--error-border);
|
||||||
|
color: var(--error-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-oauth-status.warning {
|
||||||
|
background: var(--warning-bg);
|
||||||
|
border-color: var(--warning-border);
|
||||||
|
color: var(--warning-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iFlow OAuth 按钮样式 */
|
||||||
|
#iflow-open-link,
|
||||||
|
#iflow-copy-link {
|
||||||
|
min-width: 100px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-open-link {
|
||||||
|
background: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-open-link:hover {
|
||||||
|
background: var(--primary-hover);
|
||||||
|
border-color: var(--primary-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-copy-link {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-color: var(--border-primary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-copy-link:hover {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-color: var(--border-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 - iFlow OAuth */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
#iflow-oauth-content .input-group {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-open-link,
|
||||||
|
#iflow-copy-link {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#iflow-oauth-url {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user