'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); } };