实现完整的用量查询功能,包括: 1. 新增用量查询页面和导航入口 2. 为各API服务适配器添加用量查询接口 3. 实现用量数据缓存机制 4. 添加响应式用量数据展示界面 5. 支持自动刷新和手动刷新用量数据 6. 显示模型配额、剩余用量和重置时间等信息
329 lines
No EOL
8.1 KiB
JavaScript
329 lines
No EOL
8.1 KiB
JavaScript
// 认证模块 - 处理token管理和API调用封装
|
||
/**
|
||
* 认证管理类
|
||
*/
|
||
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('未授权访问');
|
||
}
|
||
|
||
const contentType = response.headers.get('content-type');
|
||
if (contentType && contentType.includes('application/json')) {
|
||
return await response.json();
|
||
} else {
|
||
return await response.text();
|
||
}
|
||
} catch (error) {
|
||
if (error.message === '未授权访问') {
|
||
// 已经在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('未授权访问');
|
||
}
|
||
|
||
const contentType = response.headers.get('content-type');
|
||
if (contentType && contentType.includes('application/json')) {
|
||
return await response.json();
|
||
} else {
|
||
return await response.text();
|
||
}
|
||
} catch (error) {
|
||
if (error.message === '未授权访问') {
|
||
// 已经在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('认证模块已加载'); |