feat: add refresh retry/timeout and tests
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import fetch from 'node-fetch';
|
||||
import { logInfo, logDebug, logError } from './logger.js';
|
||||
import { getNextProxyAgent } from './proxy-manager.js';
|
||||
import { getRefreshConfig, requestRefreshToken } from './refresh-client.js';
|
||||
|
||||
/**
|
||||
* Account Manager - 管理多个 OAuth 账号的选择、刷新和统计
|
||||
@@ -21,6 +21,7 @@ const CLIENT_ID = 'client_01HNM792M5G5G1A2THWPXKFMXB';
|
||||
class AccountManager {
|
||||
constructor() {
|
||||
this.accounts = [];
|
||||
this.refreshLocks = new Map(); // 刷新锁,避免同一账号并发刷新
|
||||
this.settings = {
|
||||
algorithm: 'weighted', // 'weighted' or 'simple'
|
||||
refresh_interval_hours: REFRESH_INTERVAL_HOURS,
|
||||
@@ -174,12 +175,30 @@ class AccountManager {
|
||||
const needsRefresh = !account.access_token || this._shouldRefresh(account);
|
||||
|
||||
if (needsRefresh) {
|
||||
await this._refreshToken(account);
|
||||
await this._refreshTokenWithLock(account);
|
||||
}
|
||||
|
||||
return account.access_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 带锁刷新,避免同一账号并发刷新
|
||||
*/
|
||||
async _refreshTokenWithLock(account) {
|
||||
const existing = this.refreshLocks.get(account.id);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
const refreshPromise = this._refreshToken(account)
|
||||
.finally(() => {
|
||||
this.refreshLocks.delete(account.id);
|
||||
});
|
||||
|
||||
this.refreshLocks.set(account.id, refreshPromise);
|
||||
return refreshPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否需要刷新 token
|
||||
*/
|
||||
@@ -198,32 +217,15 @@ class AccountManager {
|
||||
logInfo(`AccountManager: 刷新账号 ${account.id} 的 token...`);
|
||||
|
||||
try {
|
||||
const formData = new URLSearchParams();
|
||||
formData.append('grant_type', 'refresh_token');
|
||||
formData.append('refresh_token', account.refresh_token);
|
||||
formData.append('client_id', CLIENT_ID);
|
||||
|
||||
const proxyAgentInfo = getNextProxyAgent(REFRESH_URL);
|
||||
const fetchOptions = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: formData.toString()
|
||||
};
|
||||
|
||||
if (proxyAgentInfo?.agent) {
|
||||
fetchOptions.agent = proxyAgentInfo.agent;
|
||||
}
|
||||
|
||||
const response = await fetch(REFRESH_URL, fetchOptions);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`刷新失败: ${response.status} ${errorText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const refreshConfig = getRefreshConfig();
|
||||
const data = await requestRefreshToken({
|
||||
refreshUrl: REFRESH_URL,
|
||||
refreshToken: account.refresh_token,
|
||||
clientId: CLIENT_ID,
|
||||
proxyAgentInfo,
|
||||
...refreshConfig
|
||||
});
|
||||
|
||||
// 更新账号信息
|
||||
account.access_token = data.access_token;
|
||||
|
||||
Reference in New Issue
Block a user