Antigravity2api / src /auth /token_manager.js
link0518
重构
b88ce1b
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { log } from '../utils/logger.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const CLIENT_ID = '1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com';
const CLIENT_SECRET = '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';
class TokenManager {
constructor(filePath = path.join(__dirname,'..','..','data' ,'accounts.json')) {
this.filePath = filePath;
this.tokens = [];
this.currentIndex = 0;
this.lastLoadTime = 0;
this.loadInterval = 60000; // 1分钟内不重复加载
this.cachedData = null; // 缓存文件数据,减少磁盘读取
this.usageStats = new Map(); // Token 使用统计 { refresh_token -> { requests, lastUsed } }
this.loadTokens();
}
loadTokens() {
try {
// 避免频繁加载,1分钟内使用缓存
if (Date.now() - this.lastLoadTime < this.loadInterval && this.tokens.length > 0) {
return;
}
log.info('正在加载token...');
const data = fs.readFileSync(this.filePath, 'utf8');
const tokenArray = JSON.parse(data);
this.cachedData = tokenArray; // 缓存原始数据
this.tokens = tokenArray.filter(token => token.enable !== false);
this.currentIndex = 0;
this.lastLoadTime = Date.now();
log.info(`成功加载 ${this.tokens.length} 个可用token`);
// 触发垃圾回收(如果可用)
if (global.gc) {
global.gc();
}
} catch (error) {
log.error('加载token失败:', error.message);
this.tokens = [];
}
}
isExpired(token) {
if (!token.timestamp || !token.expires_in) return true;
const expiresAt = token.timestamp + (token.expires_in * 1000);
return Date.now() >= expiresAt - 300000;
}
async refreshToken(token) {
log.info('正在刷新token...');
const body = new URLSearchParams({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: token.refresh_token
});
const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: {
'Host': 'oauth2.googleapis.com',
'User-Agent': 'Go-http-client/1.1',
'Content-Length': body.toString().length.toString(),
'Content-Type': 'application/x-www-form-urlencoded',
'Accept-Encoding': 'gzip'
},
body: body.toString()
});
if (response.ok) {
const data = await response.json();
token.access_token = data.access_token;
token.expires_in = data.expires_in;
token.timestamp = Date.now();
this.saveToFile();
return token;
} else {
throw { statusCode: response.status, message: await response.text() };
}
}
saveToFile() {
try {
// 使用缓存数据,减少磁盘读取
let allTokens = this.cachedData;
if (!allTokens) {
const data = fs.readFileSync(this.filePath, 'utf8');
allTokens = JSON.parse(data);
}
this.tokens.forEach(memToken => {
const index = allTokens.findIndex(t => t.refresh_token === memToken.refresh_token);
if (index !== -1) allTokens[index] = memToken;
});
fs.writeFileSync(this.filePath, JSON.stringify(allTokens, null, 2), 'utf8');
this.cachedData = allTokens; // 更新缓存
} catch (error) {
log.error('保存文件失败:', error.message);
}
}
disableToken(token) {
log.warn(`禁用token`)
token.enable = false;
this.saveToFile();
this.loadTokens();
}
async getToken() {
this.loadTokens();
if (this.tokens.length === 0) return null;
for (let i = 0; i < this.tokens.length; i++) {
const token = this.tokens[this.currentIndex];
const tokenIndex = this.currentIndex;
try {
if (this.isExpired(token)) {
await this.refreshToken(token);
}
this.currentIndex = (this.currentIndex + 1) % this.tokens.length;
// 记录使用统计
this.recordUsage(token);
log.info(`🔄 轮询使用 Token #${tokenIndex} (总请求: ${this.getTokenRequests(token)})`);
return token;
} catch (error) {
if (error.statusCode === 403) {
log.warn(`Token ${this.currentIndex} 刷新失败(403),禁用并尝试下一个`);
this.disableToken(token);
} else {
log.error(`Token ${this.currentIndex} 刷新失败:`, error.message);
}
this.currentIndex = (this.currentIndex + 1) % this.tokens.length;
if (this.tokens.length === 0) return null;
}
}
return null;
}
// 记录 Token 使用
recordUsage(token) {
const key = token.refresh_token;
if (!this.usageStats.has(key)) {
this.usageStats.set(key, { requests: 0, lastUsed: null });
}
const stats = this.usageStats.get(key);
stats.requests++;
stats.lastUsed = Date.now();
}
// 获取单个 Token 的请求次数
getTokenRequests(token) {
const stats = this.usageStats.get(token.refresh_token);
return stats ? stats.requests : 0;
}
// 获取所有 Token 的使用统计
getUsageStats() {
const stats = [];
this.tokens.forEach((token, index) => {
const usage = this.usageStats.get(token.refresh_token) || { requests: 0, lastUsed: null };
stats.push({
index,
requests: usage.requests,
lastUsed: usage.lastUsed ? new Date(usage.lastUsed).toISOString() : null,
isCurrent: index === this.currentIndex
});
});
return {
totalTokens: this.tokens.length,
currentIndex: this.currentIndex,
totalRequests: Array.from(this.usageStats.values()).reduce((sum, s) => sum + s.requests, 0),
tokens: stats
};
}
disableCurrentToken(token) {
const found = this.tokens.find(t => t.access_token === token.access_token);
if (found) {
this.disableToken(found);
}
}
async handleRequestError(error, currentAccessToken) {
if (error.statusCode === 403) {
log.warn('请求遇到403错误,尝试刷新token');
const currentToken = this.tokens[this.currentIndex];
if (currentToken && currentToken.access_token === currentAccessToken) {
try {
await this.refreshToken(currentToken);
log.info('Token刷新成功,返回新token');
return currentToken;
} catch (refreshError) {
if (refreshError.statusCode === 403) {
log.warn('刷新token也遇到403,禁用并切换到下一个');
this.disableToken(currentToken);
return await this.getToken();
}
log.error('刷新token失败:', refreshError.message);
}
}
return await this.getToken();
}
return null;
}
}
const tokenManager = new TokenManager();
export default tokenManager;