Spaces:
Running
Running
github-actions[bot]
Sync from GitHub Viciy2023/Qwen2API-A@ae093476e9bc5b0a599620b5925df3a20057038e
f120063 | const axios = require('axios') | |
| const { sha256Encrypt, JwtDecode } = require('./tools') | |
| const { logger } = require('./logger') | |
| const { getProxyAgent, getChatBaseUrl, applyProxyToAxiosConfig } = require('./proxy-helper') | |
| /** | |
| * 令牌管理器 | |
| * 负责令牌的获取、验证、刷新等操作 | |
| */ | |
| class TokenManager { | |
| constructor() { | |
| this.defaultHeaders = { | |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0' | |
| } | |
| } | |
| /** | |
| * 获取登录端点 | |
| * @returns {string} 登录端点URL | |
| */ | |
| get loginEndpoint() { | |
| return `${getChatBaseUrl()}/api/v1/auths/signin` | |
| } | |
| /** | |
| * 用户登录获取令牌 | |
| * @param {string} email - 邮箱 | |
| * @param {string} password - 密码 | |
| * @returns {Promise<string|null>} 令牌或null | |
| */ | |
| async login(email, password) { | |
| try { | |
| const proxyAgent = getProxyAgent() | |
| const requestConfig = { | |
| headers: this.defaultHeaders, | |
| timeout: 10000 // 10秒超时 | |
| } | |
| // 添加代理配置 | |
| if (proxyAgent) { | |
| requestConfig.httpsAgent = proxyAgent | |
| requestConfig.proxy = false | |
| } | |
| const response = await axios.post(this.loginEndpoint, { | |
| email: email, | |
| password: sha256Encrypt(password) | |
| }, requestConfig) | |
| if (response.data && response.data.token) { | |
| logger.success(`${email} 登录成功:${response.data.token}`, 'AUTH') | |
| return response.data.token | |
| } else { | |
| logger.error(`${email} 登录响应缺少令牌`, 'AUTH') | |
| return null | |
| } | |
| } catch (error) { | |
| if (error.response) { | |
| logger.error(`${email} 登录失败 (${error.response.status})`, 'AUTH', '', error) | |
| } else if (error.request) { | |
| logger.error(`${email} 登录失败: 网络请求超时或无响应`, 'AUTH') | |
| } else { | |
| logger.error(`${email} 登录失败`, 'AUTH', '', error) | |
| } | |
| return null | |
| } | |
| } | |
| /** | |
| * 验证令牌是否有效 | |
| * @param {string} token - JWT令牌 | |
| * @returns {Object|null} 解码后的令牌信息或null | |
| */ | |
| validateToken(token) { | |
| try { | |
| if (!token) return null | |
| const decoded = JwtDecode(token) | |
| if (!decoded || !decoded.exp) { | |
| return null | |
| } | |
| const now = Math.floor(Date.now() / 1000) | |
| if (decoded.exp <= now) { | |
| return null // 令牌已过期 | |
| } | |
| return decoded | |
| } catch (error) { | |
| logger.error('令牌验证失败', 'TOKEN', '', error) | |
| return null | |
| } | |
| } | |
| /** | |
| * 检查令牌是否即将过期 | |
| * @param {string} token - JWT令牌 | |
| * @param {number} thresholdHours - 过期阈值(小时) | |
| * @returns {boolean} 是否即将过期 | |
| */ | |
| isTokenExpiringSoon(token, thresholdHours = 6) { | |
| const decoded = this.validateToken(token) | |
| if (!decoded) return true // 无效令牌视为即将过期 | |
| const now = Math.floor(Date.now() / 1000) | |
| const thresholdSeconds = thresholdHours * 60 * 60 | |
| return decoded.exp - now < thresholdSeconds | |
| } | |
| /** | |
| * 获取令牌剩余有效时间(小时) | |
| * @param {string} token - JWT令牌 | |
| * @returns {number} 剩余小时数,-1表示无效令牌 | |
| */ | |
| getTokenRemainingHours(token) { | |
| const decoded = this.validateToken(token) | |
| if (!decoded) return -1 | |
| const now = Math.floor(Date.now() / 1000) | |
| const remainingSeconds = decoded.exp - now | |
| return Math.max(0, Math.round(remainingSeconds / 3600)) | |
| } | |
| /** | |
| * 刷新单个账户的令牌 | |
| * @param {Object} account - 账户对象 {email, password, token, expires} | |
| * @returns {Promise<Object|null>} 更新后的账户对象或null | |
| */ | |
| async refreshToken(account) { | |
| try { | |
| const newToken = await this.login(account.email, account.password) | |
| if (!newToken) { | |
| return null | |
| } | |
| const decoded = this.validateToken(newToken) | |
| if (!decoded) { | |
| logger.error(`刷新后的令牌无效: ${account.email}`, 'TOKEN') | |
| return null | |
| } | |
| const updatedAccount = { | |
| ...account, | |
| token: newToken, | |
| expires: decoded.exp | |
| } | |
| const remainingHours = this.getTokenRemainingHours(newToken) | |
| logger.success(`令牌刷新成功: ${account.email} (有效期: ${remainingHours}小时)`, 'TOKEN') | |
| return updatedAccount | |
| } catch (error) { | |
| logger.error(`刷新令牌失败 (${account.email})`, 'TOKEN', '', error) | |
| return null | |
| } | |
| } | |
| /** | |
| * 批量刷新即将过期的令牌 | |
| * @param {Array} accounts - 账户列表 | |
| * @param {number} thresholdHours - 过期阈值(小时) | |
| * @param {Function} onEachRefresh - 每次刷新成功后的回调函数 (updatedAccount, index, total) => void | |
| * @returns {Promise<Object>} 刷新结果 {refreshed: Array, failed: Array} | |
| */ | |
| async batchRefreshTokens(accounts, thresholdHours = 24, onEachRefresh = null) { | |
| const needsRefresh = accounts.filter(account => | |
| this.isTokenExpiringSoon(account.token, thresholdHours) | |
| ) | |
| if (needsRefresh.length === 0) { | |
| logger.info('没有需要刷新的令牌', 'TOKEN') | |
| return { refreshed: [], failed: [] } | |
| } | |
| logger.info(`发现 ${needsRefresh.length} 个令牌需要刷新`, 'TOKEN') | |
| const refreshed = [] | |
| const failed = [] | |
| for (let i = 0; i < needsRefresh.length; i++) { | |
| const account = needsRefresh[i] | |
| const updatedAccount = await this.refreshToken(account) | |
| if (updatedAccount) { | |
| refreshed.push(updatedAccount) | |
| // 如果提供了回调函数,立即调用 | |
| if (onEachRefresh && typeof onEachRefresh === 'function') { | |
| try { | |
| await onEachRefresh(updatedAccount, i + 1, needsRefresh.length) | |
| } catch (error) { | |
| logger.error(`刷新回调函数执行失败 (${account.email})`, 'TOKEN', '', error) | |
| } | |
| } | |
| } else { | |
| failed.push(account) | |
| } | |
| // 添加延迟避免请求过于频繁 | |
| await this._delay(1000) | |
| } | |
| logger.success(`令牌刷新完成: 成功 ${refreshed.length} 个,失败 ${failed.length} 个`, 'TOKEN') | |
| return { refreshed, failed } | |
| } | |
| /** | |
| * 获取健康的令牌统计信息 | |
| * @param {Array} accounts - 账户列表 | |
| * @returns {Object} 统计信息 | |
| */ | |
| getTokenHealthStats(accounts) { | |
| const stats = { | |
| total: accounts.length, | |
| valid: 0, | |
| expired: 0, | |
| expiringSoon: 0, | |
| invalid: 0 | |
| } | |
| accounts.forEach(account => { | |
| if (!account.token) { | |
| stats.invalid++ | |
| return | |
| } | |
| const decoded = this.validateToken(account.token) | |
| if (!decoded) { | |
| stats.invalid++ | |
| return | |
| } | |
| const now = Math.floor(Date.now() / 1000) | |
| if (decoded.exp <= now) { | |
| stats.expired++ | |
| } else if (this.isTokenExpiringSoon(account.token, 6)) { | |
| stats.expiringSoon++ | |
| } else { | |
| stats.valid++ | |
| } | |
| }) | |
| return stats | |
| } | |
| /** | |
| * 延迟函数 | |
| * @param {number} ms - 延迟毫秒数 | |
| * @private | |
| */ | |
| async _delay(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)) | |
| } | |
| } | |
| module.exports = TokenManager | |