106 lines
3.6 KiB
JavaScript
106 lines
3.6 KiB
JavaScript
|
|
import { requestApi } from './http.js';
|
||
|
|
import { getCacheFile, readCachedToken, writeCache, deleteCache } from './cache.js';
|
||
|
|
const SESSION_RETRYABLE_STATUS = new Set([401, 403]);
|
||
|
|
const SESSION_RETRYABLE_BODY_MARKERS = [
|
||
|
|
'session not found or expired',
|
||
|
|
'invalid or expired token',
|
||
|
|
'unauthorized',
|
||
|
|
'client key expired',
|
||
|
|
'client key revoked',
|
||
|
|
];
|
||
|
|
/**
|
||
|
|
* Create environment configuration from process.env
|
||
|
|
*/
|
||
|
|
export function createEnvConfig() {
|
||
|
|
return {
|
||
|
|
authBase: (process.env.AUTH_BASE || 'https://api-gw-test.yuanwei-lnc.com').replace(/\/$/, ''),
|
||
|
|
clientKey: process.env.CLIENT_KEY || '',
|
||
|
|
authCacheDir: process.env.AUTH_CACHE_DIR || '/tmp/skill-auth-cache',
|
||
|
|
authMinTtlSec: parseInt(process.env.AUTH_MIN_TTL_SEC || '60', 10),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Fetch session JSON from auth endpoint
|
||
|
|
*/
|
||
|
|
export async function fetchSessionJson(dryRun, config) {
|
||
|
|
if (dryRun) {
|
||
|
|
return {
|
||
|
|
accessToken: '<dry-run-token>',
|
||
|
|
hookUrl: '<dry-run-hook-url>',
|
||
|
|
hookToken: '<dry-run-hook-token>',
|
||
|
|
expiresIn: 900,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
if (!config.clientKey) {
|
||
|
|
throw new Error('CLIENT_KEY is required');
|
||
|
|
}
|
||
|
|
const payload = JSON.stringify({ clientKey: config.clientKey });
|
||
|
|
const result = await requestApi('POST', `${config.authBase}/auth/skill-credit/session`, undefined, payload);
|
||
|
|
if (result.status < 200 || result.status >= 300) {
|
||
|
|
throw new Error(`Auth session request failed: HTTP ${result.status} - ${result.body}`);
|
||
|
|
}
|
||
|
|
const session = JSON.parse(result.body);
|
||
|
|
if (!session.accessToken) {
|
||
|
|
throw new Error(`Missing accessToken in session response: ${result.body}`);
|
||
|
|
}
|
||
|
|
return session;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Get access token with caching
|
||
|
|
*/
|
||
|
|
export async function getAccessToken(dryRun, config) {
|
||
|
|
if (dryRun) {
|
||
|
|
return '<dry-run-token>';
|
||
|
|
}
|
||
|
|
if (!config.clientKey) {
|
||
|
|
throw new Error('CLIENT_KEY is required');
|
||
|
|
}
|
||
|
|
const cacheFile = getCacheFile(config.authBase, config.clientKey, config.authCacheDir);
|
||
|
|
const cachedToken = readCachedToken(cacheFile, config.authMinTtlSec);
|
||
|
|
if (cachedToken) {
|
||
|
|
return cachedToken;
|
||
|
|
}
|
||
|
|
const session = await fetchSessionJson(dryRun, config);
|
||
|
|
writeCache(cacheFile, session);
|
||
|
|
return session.accessToken;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Refresh access token (bypass cache)
|
||
|
|
*/
|
||
|
|
export async function refreshAccessToken(dryRun, config) {
|
||
|
|
if (dryRun) {
|
||
|
|
return '<dry-run-token>';
|
||
|
|
}
|
||
|
|
if (!config.clientKey) {
|
||
|
|
throw new Error('CLIENT_KEY is required');
|
||
|
|
}
|
||
|
|
const cacheFile = getCacheFile(config.authBase, config.clientKey, config.authCacheDir);
|
||
|
|
deleteCache(cacheFile);
|
||
|
|
return getAccessToken(dryRun, config);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Check whether response likely indicates expired/invalid runtime session.
|
||
|
|
*/
|
||
|
|
export function isRetryableSessionError(response) {
|
||
|
|
if (!SESSION_RETRYABLE_STATUS.has(response.status)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
const body = (response.body || '').toLowerCase();
|
||
|
|
if (!body) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return SESSION_RETRYABLE_BODY_MARKERS.some((marker) => body.includes(marker));
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Make API request with automatic runtime token refresh and one retry.
|
||
|
|
*/
|
||
|
|
export async function requestApiWithAutoRefresh(method, url, dryRun, config, body, accessToken) {
|
||
|
|
const token = accessToken || await getAccessToken(dryRun, config);
|
||
|
|
const first = await requestApi(method, url, token, body);
|
||
|
|
if (!isRetryableSessionError(first)) {
|
||
|
|
return first;
|
||
|
|
}
|
||
|
|
const freshToken = await refreshAccessToken(dryRun, config);
|
||
|
|
return requestApi(method, url, freshToken, body);
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=auth.js.map
|