feat: add refresh retry/timeout and tests

This commit is contained in:
empty
2025-12-27 15:07:54 +08:00
parent 5e01993120
commit a18e45ee78
6 changed files with 331 additions and 85 deletions

106
auth.js
View File

@@ -1,10 +1,10 @@
import fs from 'fs';
import path from 'path';
import os from 'os';
import fetch from 'node-fetch';
import { logDebug, logError, logInfo } from './logger.js';
import { getNextProxyAgent } from './proxy-manager.js';
import { getAccountManager, initializeAccountManager } from './account-manager.js';
import { getRefreshConfig, requestRefreshToken } from './refresh-client.js';
// State management for API key and refresh
let currentApiKey = null;
@@ -16,6 +16,7 @@ let authFilePath = null;
let factoryApiKey = null; // From FACTORY_API_KEY environment variable
let multiAccountMode = false; // 是否启用多账号模式
let lastSelectedAccountId = null; // 记录最后选择的账号ID用于结果回调
let refreshInFlight = null; // 刷新锁,避免并发刷新
const REFRESH_URL = 'https://api.workos.com/user_management/authenticate';
const REFRESH_INTERVAL_HOURS = 6; // Refresh every 6 hours
@@ -148,64 +149,58 @@ async function refreshApiKey() {
throw new Error('No refresh token available');
}
if (!clientId) {
clientId = 'client_01HNM792M5G5G1A2THWPXKFMXB';
logDebug(`Using fixed client ID: ${clientId}`);
if (refreshInFlight) {
return refreshInFlight;
}
logInfo('Refreshing API key...');
refreshInFlight = (async () => {
if (!clientId) {
clientId = 'client_01HNM792M5G5G1A2THWPXKFMXB';
logDebug(`Using fixed client ID: ${clientId}`);
}
logInfo('Refreshing API key...');
try {
const proxyAgentInfo = getNextProxyAgent(REFRESH_URL);
const refreshConfig = getRefreshConfig();
const data = await requestRefreshToken({
refreshUrl: REFRESH_URL,
refreshToken: currentRefreshToken,
clientId,
proxyAgentInfo,
...refreshConfig
});
// Update tokens
currentApiKey = data.access_token;
currentRefreshToken = data.refresh_token;
lastRefreshTime = Date.now();
// Log user info
if (data.user) {
logInfo(`Authenticated as: ${data.user.email} (${data.user.first_name} ${data.user.last_name})`);
logInfo(`User ID: ${data.user.id}`);
logInfo(`Organization ID: ${data.organization_id}`);
}
// Save tokens to file
saveTokens(data.access_token, data.refresh_token);
logInfo(`New Refresh-Key: ${currentRefreshToken}`);
logInfo('API key refreshed successfully');
return data.access_token;
} catch (error) {
logError('Failed to refresh API key', error);
throw error;
}
})();
try {
// Create form data
const formData = new URLSearchParams();
formData.append('grant_type', 'refresh_token');
formData.append('refresh_token', currentRefreshToken);
formData.append('client_id', clientId);
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(`Failed to refresh token: ${response.status} ${errorText}`);
}
const data = await response.json();
// Update tokens
currentApiKey = data.access_token;
currentRefreshToken = data.refresh_token;
lastRefreshTime = Date.now();
// Log user info
if (data.user) {
logInfo(`Authenticated as: ${data.user.email} (${data.user.first_name} ${data.user.last_name})`);
logInfo(`User ID: ${data.user.id}`);
logInfo(`Organization ID: ${data.organization_id}`);
}
// Save tokens to file
saveTokens(data.access_token, data.refresh_token);
logInfo(`New Refresh-Key: ${currentRefreshToken}`);
logInfo('API key refreshed successfully');
return data.access_token;
} catch (error) {
logError('Failed to refresh API key', error);
throw error;
return await refreshInFlight;
} finally {
refreshInFlight = null;
}
}
@@ -383,4 +378,3 @@ export function getAuthStatus() {
multiAccountMode: false
};
}