Files
doodle-mini/miniprogram/services/api-request.ts
T

103 lines
3.3 KiB
TypeScript

import { getApiConfig } from '../config/api';
import {
ApiRequestError,
requestApi,
type RequestOptions,
} from './api-transport';
import {
cachedServerUser,
clearSession,
ensureSession,
getAuthContext,
recoverSession,
rejectSession,
} from './auth-session';
export { ApiRequestError } from './api-transport';
const recoverable = new Set([
'AUTH_REQUIRED',
'AUTH_INVALID',
'AUTH_EXPIRED',
'AUTH_REVOKED',
]);
const blocked = new Set([
'ACCOUNT_NOT_FOUND',
'ACCOUNT_DISABLED',
'WECHAT_LOGIN_BLOCKED',
]);
/** 公开接口显式 auth=none;其余接口合并登录,最多恢复并重发一次,不跨账户补发。 */
export async function apiRequest<T>(
path: string,
options: RequestOptions & { auth?: 'required' | 'none' } = {},
): Promise<T> {
// Authorization 由会话层统一提供;公开请求不附带调用方误传的凭证。
const header = Object.fromEntries(
Object.entries(options.header || {}).filter(
([key]) => key.toLowerCase() !== 'authorization',
),
);
if (options.auth === 'none')
return requestApi(path, { ...options, header }, getApiConfig());
const context = getAuthContext();
const previousUserId = cachedServerUser()?.id;
const initial = await ensureSession(context);
if (previousUserId && initial.user.id !== previousUserId) {
clearSession(false, context);
throw new ApiRequestError(
'账户已变化,请重新操作',
401,
'ACCOUNT_CHANGED',
);
}
const send = async (accessToken: string) => {
if (getAuthContext().key !== context.key)
throw new ApiRequestError('接口环境已变化,请重新操作');
const result = await requestApi<T>(
path,
{
...options,
header: { ...header, Authorization: `Bearer ${accessToken}` },
},
context,
);
if (
getAuthContext().key !== context.key ||
cachedServerUser()?.id !== initial.user.id
)
throw new ApiRequestError('账户状态已变化,请重新操作');
return result;
};
try {
return await send(initial.accessToken);
} catch (error) {
if (!(error instanceof ApiRequestError) || error.statusCode !== 401)
throw error;
if (!recoverable.has(error.reason)) {
if (blocked.has(error.reason))
rejectSession(context, initial.accessToken, true);
throw error;
}
const renewed = await recoverSession(context, initial);
if (renewed.user.id !== initial.user.id) {
clearSession(false, context);
throw new ApiRequestError(
'账户已变化,请重新操作',
401,
'ACCOUNT_CHANGED',
);
}
try {
return await send(renewed.accessToken);
} catch (second) {
if (second instanceof ApiRequestError && second.statusCode === 401)
rejectSession(
context,
renewed.accessToken,
blocked.has(second.reason),
);
throw second;
}
}
}