AIClient-2-API/static/app/auth.js
hex2077 85d7b50cb1 fix: 修复Qwen API配额错误处理和Gemini初始化顺序问题
- 修复Qwen API的配额错误识别和速率限制,避免因配额耗尽导致服务中断
- 修正Gemini API服务初始化顺序,确保OAuth2客户端在HTTP代理配置后创建
- 优化提供商数据脱敏逻辑,防止保存时覆盖真实的敏感信息
- 增强前端错误处理,支持国际化错误消息的翻译和显示
- 移除Antigravity中冗余的思考签名修复代码,简化历史记录处理
- 修复服务管理器初始化逻辑,确保提供商池状态正确更新
- 统一日志下载文件名格式,改进文件下载错误处理
- 更新翻译文件,添加缺失的通用错误消息国际化支持
2026-04-05 17:50:11 +08:00

369 lines
No EOL
9.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 认证模块 - 处理token管理和API调用封装
import { t } from './i18n.js';
/**
* 认证管理类
*/
class AuthManager {
constructor() {
this.tokenKey = 'authToken';
this.expiryKey = 'authTokenExpiry';
this.baseURL = window.location.origin;
}
/**
* 获取存储的token
*/
getToken() {
return localStorage.getItem(this.tokenKey);
}
/**
* 获取token过期时间
*/
getTokenExpiry() {
const expiry = localStorage.getItem(this.expiryKey);
return expiry ? parseInt(expiry) : null;
}
/**
* 检查token是否有效
*/
isTokenValid() {
const token = this.getToken();
const expiry = this.getTokenExpiry();
if (!token) return false;
// 如果设置了过期时间,检查是否过期
if (expiry && Date.now() > expiry) {
this.clearToken();
return false;
}
return true;
}
/**
* 保存token到本地存储
*/
saveToken(token, rememberMe = false) {
localStorage.setItem(this.tokenKey, token);
if (rememberMe) {
const expiryTime = Date.now() + (7 * 24 * 60 * 60 * 1000); // 7天
localStorage.setItem(this.expiryKey, expiryTime.toString());
}
}
/**
* 清除token
*/
clearToken() {
localStorage.removeItem(this.tokenKey);
localStorage.removeItem(this.expiryKey);
}
/**
* 登出
*/
async logout() {
this.clearToken();
window.location.href = '/login.html';
}
}
/**
* API调用封装类
*/
class ApiClient {
constructor() {
this.authManager = new AuthManager();
this.baseURL = window.location.origin;
}
/**
* 获取带认证的请求头
*/
getAuthHeaders() {
const token = this.authManager.getToken();
return token ? {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
} : {
'Content-Type': 'application/json'
};
}
/**
* 处理401错误重定向到登录页
*/
handleUnauthorized() {
this.authManager.clearToken();
window.location.href = '/login.html';
}
/**
* 通用API请求方法
*/
async request(endpoint, options = {}) {
const url = `${this.baseURL}/api${endpoint}`;
const headers = {
...this.getAuthHeaders(),
...options.headers
};
const config = {
...options,
headers
};
try {
const response = await fetch(url, config);
// 如果是401错误重定向到登录页
if (response.status === 401) {
this.handleUnauthorized();
throw new Error(t('common.unauthorized'));
}
const contentType = response.headers.get('content-type');
let data;
if (contentType && contentType.includes('application/json')) {
data = await response.json();
} else {
data = await response.text();
}
// 如果响应状态码不是 2xx抛出错误
if (!response.ok) {
let errorMessage;
if (data && typeof data === 'object') {
// 优先使用错误代码进行翻译
const code = (data.error && data.error.messageCode) || data.messageCode;
if (code) {
const translated = t(code);
if (translated !== code) {
errorMessage = translated;
}
}
// 如果没有翻译,使用原始错误消息
if (!errorMessage) {
errorMessage = (data.error && data.error.message) || data.message;
}
}
if (!errorMessage) {
errorMessage = `${t('common.requestFailed')} (${t('common.status')}: ${response.status})`;
}
throw new Error(errorMessage);
}
return data;
} catch (error) {
if (error.message === t('common.unauthorized')) {
// 已经在handleUnauthorized中处理了重定向
throw error;
}
console.error('API请求错误:', error);
throw error;
}
}
/**
* GET请求
*/
async get(endpoint, params = {}) {
const queryString = new URLSearchParams(params).toString();
const url = queryString ? `${endpoint}?${queryString}` : endpoint;
return this.request(url, { method: 'GET' });
}
/**
* POST请求
*/
async post(endpoint, data = {}) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
/**
* PUT请求
*/
async put(endpoint, data = {}) {
return this.request(endpoint, {
method: 'PUT',
body: JSON.stringify(data)
});
}
/**
* DELETE请求
*/
async delete(endpoint) {
return this.request(endpoint, { method: 'DELETE' });
}
/**
* POST请求支持FormData上传
*/
async upload(endpoint, formData) {
const url = `${this.baseURL}/api${endpoint}`;
// 获取认证token
const token = this.authManager.getToken();
const headers = {};
// 如果有token添加Authorization头部
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
// 对于FormData请求不添加Content-Type头部让浏览器自动设置
const config = {
method: 'POST',
headers,
body: formData
};
try {
const response = await fetch(url, config);
// 如果是401错误重定向到登录页
if (response.status === 401) {
this.handleUnauthorized();
throw new Error(t('common.unauthorized'));
}
const contentType = response.headers.get('content-type');
let data;
if (contentType && contentType.includes('application/json')) {
data = await response.json();
} else {
data = await response.text();
}
// 如果响应状态码不是 2xx抛出错误
if (!response.ok) {
const errorMessage = (data && typeof data === 'object' && data.error && data.error.message)
|| (data && typeof data === 'object' && data.message)
|| `${t('common.uploadFailed')} (${t('common.status')}: ${response.status})`;
throw new Error(errorMessage);
}
return data;
} catch (error) {
if (error.message === t('common.unauthorized')) {
// 已经在handleUnauthorized中处理了重定向
throw error;
}
console.error('API请求错误:', error);
throw error;
}
}
}
/**
* 初始化认证检查
*/
async function initAuth() {
const authManager = new AuthManager();
// 检查是否已经有有效的token
if (authManager.isTokenValid()) {
// 验证token是否仍然有效发送一个测试请求
try {
const apiClient = new ApiClient();
await apiClient.get('/health');
return true;
} catch (error) {
// Token无效清除并重定向到登录页
authManager.clearToken();
window.location.href = '/login.html';
return false;
}
} else {
// 没有有效token重定向到登录页
window.location.href = '/login.html';
return false;
}
}
/**
* 登出函数
*/
async function logout() {
const authManager = new AuthManager();
await authManager.logout();
}
/**
* 登录函数(供登录页面使用)
*/
async function login(password, rememberMe = false) {
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
password,
rememberMe
})
});
const data = await response.json();
if (data.success) {
// 保存token
const authManager = new AuthManager();
authManager.saveToken(data.token, rememberMe);
return { success: true };
} else {
return { success: false, message: data.message };
}
} catch (error) {
console.error('登录错误:', error);
return { success: false, message: '登录失败,请检查网络连接' };
}
}
// 创建单例实例
const authManager = new AuthManager();
const apiClient = new ApiClient();
/**
* 获取带认证的请求头(便捷函数)
* @returns {Object} 包含认证信息的请求头
*/
function getAuthHeaders() {
return apiClient.getAuthHeaders();
}
// 导出实例到 window兼容旧代码
window.authManager = authManager;
window.apiClient = apiClient;
window.initAuth = initAuth;
window.logout = logout;
window.login = login;
// 导出认证管理器类和API客户端类供其他模块使用
window.AuthManager = AuthManager;
window.ApiClient = ApiClient;
// ES6 模块导出
export {
AuthManager,
ApiClient,
authManager,
apiClient,
initAuth,
logout,
login,
getAuthHeaders
};
console.log('认证模块已加载');