anti_api / src /auth /token_manager.js
liuw15's picture
使用axios替换fetch,优化代码结构,添加超时和代理支持,改为从.env加载
2e9246b
raw
history blame
5.09 kB
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import axios from 'axios';
import { log } from '../utils/logger.js';
import { generateProjectId, generateSessionId } from '../utils/idGenerator.js';
import config from '../config/config.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.initialize();
}
initialize() {
try {
log.info('正在初始化token管理器...');
const data = fs.readFileSync(this.filePath, 'utf8');
let tokenArray = JSON.parse(data);
let needSave = false;
tokenArray = tokenArray.map(token => {
if (!token.projectId) {
token.projectId = generateProjectId();
needSave = true;
}
return token;
});
if (needSave) {
fs.writeFileSync(this.filePath, JSON.stringify(tokenArray, null, 2), 'utf8');
}
this.tokens = tokenArray.filter(token => token.enable !== false).map(token => ({
...token,
sessionId: generateSessionId()
}));
this.currentIndex = 0;
log.info(`成功加载 ${this.tokens.length} 个可用token`);
} 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
});
try {
const response = await axios({
method: 'POST',
url: 'https://oauth2.googleapis.com/token',
headers: {
'Host': 'oauth2.googleapis.com',
'User-Agent': 'Go-http-client/1.1',
'Content-Type': 'application/x-www-form-urlencoded',
'Accept-Encoding': 'gzip'
},
data: body.toString(),
timeout: config.timeout,
proxy: config.proxy ? (() => {
const proxyUrl = new URL(config.proxy);
return { protocol: proxyUrl.protocol.replace(':', ''), host: proxyUrl.hostname, port: parseInt(proxyUrl.port) };
})() : false
});
token.access_token = response.data.access_token;
token.expires_in = response.data.expires_in;
token.timestamp = Date.now();
this.saveToFile();
return token;
} catch (error) {
throw { statusCode: error.response?.status, message: error.response?.data || error.message };
}
}
saveToFile() {
try {
const data = fs.readFileSync(this.filePath, 'utf8');
const allTokens = JSON.parse(data);
this.tokens.forEach(memToken => {
const index = allTokens.findIndex(t => t.refresh_token === memToken.refresh_token);
if (index !== -1) {
const { sessionId, ...tokenToSave } = memToken;
allTokens[index] = tokenToSave;
}
});
fs.writeFileSync(this.filePath, JSON.stringify(allTokens, null, 2), 'utf8');
} catch (error) {
log.error('保存文件失败:', error.message);
}
}
disableToken(token) {
log.warn(`禁用token`)
token.enable = false;
this.saveToFile();
this.tokens = this.tokens.filter(t => t.refresh_token !== token.refresh_token);
this.currentIndex = this.currentIndex % Math.max(this.tokens.length, 1);
}
async getToken() {
if (this.tokens.length === 0) return null;
for (let i = 0; i < this.tokens.length; i++) {
const token = this.tokens[this.currentIndex];
try {
if (this.isExpired(token)) {
await this.refreshToken(token);
}
this.currentIndex = (this.currentIndex + 1) % this.tokens.length;
return token;
} catch (error) {
if (error.statusCode === 403 || error.statusCode === 400) {
const accountNum = this.currentIndex + 1;
log.warn(`账号 ${accountNum}: Token 已失效或错误,已自动禁用该账号`);
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;
}
disableCurrentToken(token) {
const found = this.tokens.find(t => t.access_token === token.access_token);
if (found) {
this.disableToken(found);
}
}
}
const tokenManager = new TokenManager();
export default tokenManager;