File size: 5,092 Bytes
10feaec 2e9246b 4cb1301 28d241c 2e9246b 10feaec 4cb1301 10feaec 28d241c 10feaec 28d241c 10feaec 28d241c 10feaec 28d241c 10feaec 28d241c 10feaec 2e9246b 10feaec 2e9246b 10feaec 2e9246b 10feaec 28d241c 10feaec 30e53c1 10feaec 28d241c 10feaec 30e53c1 10feaec 28d241c 10feaec 30e53c1 10feaec | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | 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;
|