119 lines
4.7 KiB
JavaScript
119 lines
4.7 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',
|
|
];
|
|
function buildConfig(options) {
|
|
return {
|
|
authBase: (options.authBase || process.env.AUTH_BASE || 'https://api-gw-test.yuanwei-lnc.com').replace(/\/$/, ''),
|
|
clientKey: options.clientKey || process.env.CLIENT_KEY || '',
|
|
authCacheDir: options.cacheDir || process.env.AUTH_CACHE_DIR || '/tmp/skill-auth-cache',
|
|
authMinTtlSec: options.minTtlSec ?? parseInt(process.env.AUTH_MIN_TTL_SEC || '60', 10),
|
|
};
|
|
}
|
|
function isRetryable(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((m) => body.includes(m));
|
|
}
|
|
export class SkillClient {
|
|
config;
|
|
apiBase;
|
|
dryRun;
|
|
constructor(options = {}) {
|
|
this.config = buildConfig(options);
|
|
this.apiBase = (options.apiBase || process.env.ECOM_BASE || this.config.authBase).replace(/\/$/, '');
|
|
this.dryRun = options.dryRun ?? false;
|
|
if (!this.dryRun && !this.config.clientKey) {
|
|
throw new Error('CLIENT_KEY is required. Set via env or pass clientKey option.');
|
|
}
|
|
}
|
|
/** Fetch raw session info (token + expiresIn) */
|
|
async session() {
|
|
if (this.dryRun) {
|
|
return { accessToken: '<dry-run-token>', expiresIn: 900 };
|
|
}
|
|
return this.fetchSession();
|
|
}
|
|
/** Fetch client config (metadata with hook info, provider keys, etc.) */
|
|
async clientConfig() {
|
|
if (this.dryRun) {
|
|
return { clientId: '<dry-run>', name: '<dry-run>', status: 'active', metadata: {} };
|
|
}
|
|
const result = await requestApi('GET', `${this.config.authBase}/auth/skill-credit/client-config`, undefined, undefined, { 'X-Client-Key': this.config.clientKey });
|
|
if (result.status < 200 || result.status >= 300) {
|
|
throw new Error(`Client config failed: HTTP ${result.status} — ${result.body}`);
|
|
}
|
|
return JSON.parse(result.body);
|
|
}
|
|
async get(path) {
|
|
return this.request('GET', path);
|
|
}
|
|
async post(path, body) {
|
|
return this.request('POST', path, body);
|
|
}
|
|
async put(path, body) {
|
|
return this.request('PUT', path, body);
|
|
}
|
|
async patch(path, body) {
|
|
return this.request('PATCH', path, body);
|
|
}
|
|
async delete(path, body) {
|
|
return this.request('DELETE', path, body);
|
|
}
|
|
// ---- internal ----
|
|
async request(method, path, body) {
|
|
if (this.dryRun) {
|
|
return { status: 200, body: JSON.stringify({ dryRun: true, method, path }) };
|
|
}
|
|
const url = `${this.apiBase}${path}`;
|
|
const bodyStr = body != null ? JSON.stringify(body) : undefined;
|
|
const token = await this.getToken();
|
|
const first = await requestApi(method, url, token, bodyStr);
|
|
if (!isRetryable(first)) {
|
|
return first;
|
|
}
|
|
// Token expired — refresh and retry once
|
|
const freshToken = await this.refreshToken();
|
|
return requestApi(method, url, freshToken, bodyStr);
|
|
}
|
|
async getToken() {
|
|
const cacheFile = getCacheFile(this.config.authBase, this.config.clientKey, this.config.authCacheDir);
|
|
const cached = readCachedToken(cacheFile, this.config.authMinTtlSec);
|
|
if (cached)
|
|
return cached;
|
|
const session = await this.fetchSession();
|
|
writeCache(cacheFile, session);
|
|
return session.accessToken;
|
|
}
|
|
async refreshToken() {
|
|
const cacheFile = getCacheFile(this.config.authBase, this.config.clientKey, this.config.authCacheDir);
|
|
deleteCache(cacheFile);
|
|
const session = await this.fetchSession();
|
|
writeCache(cacheFile, session);
|
|
return session.accessToken;
|
|
}
|
|
async fetchSession() {
|
|
const result = await requestApi('POST', `${this.config.authBase}/auth/skill-credit/session`, undefined, JSON.stringify({ clientKey: this.config.clientKey }));
|
|
if (result.status < 200 || result.status >= 300) {
|
|
throw new Error(`Auth session 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;
|
|
}
|
|
}
|
|
export function createSkillClient(options) {
|
|
return new SkillClient(options);
|
|
}
|
|
//# sourceMappingURL=client.js.map
|