You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
3.8 KiB
128 lines
3.8 KiB
'use strict';
|
|
|
|
const TOKEN_FORMAT_REGEXP = /^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/;
|
|
|
|
const getToken = (ctx) => {
|
|
const { query = {}, header = {} } = ctx;
|
|
const authHeader = header.authorization || header.Authorization;
|
|
if (query.token) return query.token;
|
|
if (header.token) return header.token;
|
|
if (authHeader && /^Bearer\s+/i.test(authHeader)) {
|
|
return authHeader.replace(/^Bearer\s+/i, '').trim();
|
|
}
|
|
return '';
|
|
};
|
|
|
|
const getVerifyTokenPaths = (ctx) => {
|
|
const configuredPath = ctx?.app?.fs?.config?.tenderUser?.aiCenter?.verifyTokenPath || '/verify-token';
|
|
const paths = [configuredPath, '/verify-token', '/auth/verify-token'];
|
|
return [...new Set(paths.map((item) => String(item || '').trim()).filter(Boolean))];
|
|
};
|
|
|
|
const getProfilePaths = (ctx) => {
|
|
const configuredPath = ctx?.app?.fs?.config?.tenderUser?.aiCenter?.profilePath || '/profile';
|
|
const paths = [configuredPath, '/profile', '/auth/profile'];
|
|
return [...new Set(paths.map((item) => String(item || '').trim()).filter(Boolean))];
|
|
};
|
|
|
|
const resolveCenterVerifyUser = (centerResponse = {}) => {
|
|
const userInfo = centerResponse?.AIUserInfo
|
|
|| centerResponse?.data?.AIUserInfo
|
|
|| centerResponse?.userInfo
|
|
|| centerResponse?.data?.userInfo
|
|
|| centerResponse?.data
|
|
|| centerResponse;
|
|
if (!userInfo || typeof userInfo !== 'object') return null;
|
|
const userId = String(
|
|
userInfo?.pepUserId
|
|
|| userInfo?.pep_user_id
|
|
|| userInfo?.pepId
|
|
|| userInfo?.pep_id
|
|
|| userInfo?.id
|
|
|| userInfo?.userId
|
|
|| '',
|
|
).trim();
|
|
if (!userId) return null;
|
|
return userInfo;
|
|
};
|
|
|
|
const verifyByAiCenter = async (ctx, token) => {
|
|
const centerRequest = ctx?.app?.fs?.centerRequest;
|
|
if (!centerRequest) return null;
|
|
|
|
const paths = getVerifyTokenPaths(ctx);
|
|
for (const path of paths) {
|
|
try {
|
|
const centerResponse = await centerRequest.get(path, {
|
|
query: { token },
|
|
});
|
|
const userInfo = resolveCenterVerifyUser(centerResponse);
|
|
if (userInfo) return userInfo;
|
|
} catch (error) {
|
|
ctx.logger.warn(`[tenderUserAuth] verify token failed on ${path}: ${error?.message || error}`);
|
|
}
|
|
}
|
|
|
|
const profilePaths = getProfilePaths(ctx);
|
|
for (const path of profilePaths) {
|
|
try {
|
|
const centerResponse = await centerRequest.get(path, {
|
|
query: { token },
|
|
});
|
|
const userInfo = resolveCenterVerifyUser(centerResponse);
|
|
if (userInfo) return userInfo;
|
|
} catch (error) {
|
|
ctx.logger.warn(`[tenderUserAuth] profile fallback failed on ${path}: ${error?.message || error}`);
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
module.exports = async (ctx, next) => {
|
|
try {
|
|
ctx.fs = ctx.fs || {};
|
|
const token = getToken(ctx);
|
|
if (!token) {
|
|
ctx.throw(401, 'Unauthorized');
|
|
return;
|
|
}
|
|
|
|
let userInfo = null;
|
|
const redis = ctx?.app?.fs?.redis;
|
|
if (redis) {
|
|
const cachedUserInfo = await redis.get(`ai_center:token:${token}:user`);
|
|
if (cachedUserInfo) {
|
|
try {
|
|
userInfo = JSON.parse(cachedUserInfo);
|
|
} catch (error) {
|
|
userInfo = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!userInfo) {
|
|
userInfo = await verifyByAiCenter(ctx, token);
|
|
if (userInfo && redis) {
|
|
await redis.setex(
|
|
`ai_center:token:${token}:user`,
|
|
24 * 3600,
|
|
JSON.stringify(userInfo),
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!userInfo) {
|
|
ctx.throw(401, 'Unauthorized');
|
|
return;
|
|
}
|
|
|
|
ctx.fs.curUser = {
|
|
token,
|
|
userInfo,
|
|
};
|
|
await next();
|
|
} catch (error) {
|
|
ctx.logger.error(error);
|
|
ctx.throw(error.status || 401, error.message || error);
|
|
}
|
|
};
|
|
|