166 lines
No EOL
4.8 KiB
TypeScript
166 lines
No EOL
4.8 KiB
TypeScript
/**
|
|
* Maps quinn.my BFF paths to quinn.api paths for direct browser → :3030 cutover.
|
|
* SSO cookie auth is validated by quinn.api (ssoRequired on /my/*, /admin/*, /engine/tour-optimizer).
|
|
*
|
|
* Returns null when the path must stay on my-backend (:3024) — local_db modules only.
|
|
*/
|
|
|
|
/** 1:1 /api/<segment> → /my/<segment> */
|
|
export const QUINN_API_MY_PREFIXES = [
|
|
'bookings',
|
|
'claude-accounts',
|
|
'credentials',
|
|
'financials',
|
|
'flight-monitor',
|
|
'flights',
|
|
'hotel-observations',
|
|
'hotel-stays',
|
|
'inspiration',
|
|
'journal',
|
|
'notifications',
|
|
'outreach',
|
|
'pending-income',
|
|
'planner',
|
|
'price-watches',
|
|
'projects',
|
|
'prospector',
|
|
'prospects',
|
|
'reminders',
|
|
'tour-hotels',
|
|
'tour-legs',
|
|
'tour-stops',
|
|
] as const;
|
|
|
|
/** BFF first segment → quinn.api /my/<target> */
|
|
export const QUINN_API_SEGMENT_MAP: Record<string, string> = {
|
|
events: 'demand-events',
|
|
'hotel-rooms': 'hotel-rooms',
|
|
people: 'contacts',
|
|
};
|
|
|
|
const MY_PREFIX_SET = new Set<string>(QUINN_API_MY_PREFIXES);
|
|
|
|
const CLIENT_ADMIN_SUBPATHS = new Set([
|
|
'reputation-score',
|
|
'reputation-events',
|
|
'screening',
|
|
'qualification',
|
|
]);
|
|
|
|
function rewriteClientsPath(pathname: string, method: string): string | null {
|
|
if (!pathname.startsWith('/api/clients')) return null;
|
|
|
|
const rest = pathname.slice('/api/clients'.length);
|
|
if (!rest || rest === '/') return '/my/clients';
|
|
|
|
const parts = rest.split('/').filter(Boolean);
|
|
const id = parts[0];
|
|
const sub = parts[1];
|
|
const subId = parts[2];
|
|
|
|
if (id && /^\d+$/.test(id) && sub && CLIENT_ADMIN_SUBPATHS.has(sub)) {
|
|
if (sub === 'reputation-score') return `/admin/clients/${id}/reputation-score`;
|
|
if (sub === 'qualification') return `/admin/clients/${id}/qualification`;
|
|
if (sub === 'reputation-events') {
|
|
if (subId && /^\d+$/.test(subId)) return `/admin/reputation-events/${subId}`;
|
|
return `/admin/reputation-events?clientId=${id}`;
|
|
}
|
|
if (sub === 'screening') {
|
|
if (method === 'POST') return '/admin/screening/check';
|
|
return `/admin/screening?clientId=${id}`;
|
|
}
|
|
}
|
|
|
|
return `/my/clients${rest}`;
|
|
}
|
|
|
|
function rewriteCalendarPath(pathname: string): string | null {
|
|
if (pathname.startsWith('/api/calendars')) {
|
|
return `/my/calendars${pathname.slice('/api/calendars'.length)}`;
|
|
}
|
|
|
|
if (pathname === '/api/calendar/sync') return '/my/calendar/sync';
|
|
|
|
if (pathname.startsWith('/api/calendar/client-bookings/')) {
|
|
const id = pathname.split('/')[4];
|
|
if (id) return `/my/client-bookings/${id}`;
|
|
}
|
|
if (pathname === '/api/calendar/client-bookings') return '/my/calendar/client-bookings';
|
|
|
|
if (pathname.startsWith('/api/calendar/events')) {
|
|
const rest = pathname.slice('/api/calendar/events'.length);
|
|
return `/my/calendar-events${rest}`;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Rewrite BFF path (+ optional method for screening POST) to quinn.api path.
|
|
*/
|
|
export function rewriteBffPathToQuinnApi(pathname: string, method = 'GET'): string | null {
|
|
if (pathname === '/api/waitlist') return '/admin/waitlist-subscriptions';
|
|
if (pathname === '/api/tour-optimizer') return '/engine/tour-optimizer';
|
|
if (pathname === '/public/bookings') return '/public/bookings';
|
|
|
|
const clients = rewriteClientsPath(pathname, method.toUpperCase());
|
|
if (clients) return clients;
|
|
|
|
const calendar = rewriteCalendarPath(pathname);
|
|
if (calendar) return calendar;
|
|
|
|
if (pathname.startsWith('/api/city-visits')) {
|
|
return `/my/tour-schedule-visits${pathname.slice('/api/city-visits'.length)}`;
|
|
}
|
|
|
|
if (pathname.startsWith('/api/messages')) {
|
|
return `/my/messages${pathname.slice('/api/messages'.length)}`;
|
|
}
|
|
|
|
if (!pathname.startsWith('/api/')) return null;
|
|
|
|
const segment = pathname.split('/')[2] ?? '';
|
|
if (!segment) return null;
|
|
|
|
const mapped = QUINN_API_SEGMENT_MAP[segment];
|
|
if (mapped) {
|
|
const rest = pathname.slice(`/api/${segment}`.length);
|
|
return `/my/${mapped}${rest}`;
|
|
}
|
|
|
|
if (MY_PREFIX_SET.has(segment)) {
|
|
const rest = pathname.slice(`/api/${segment}`.length);
|
|
return `/my/${segment}${rest}`;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/** Append query params required by quinn.api when the BFF used to inject them. */
|
|
export function augmentQuinnApiSearch(pathname: string, search: string): string {
|
|
const raw = search.startsWith('?') ? search.slice(1) : search;
|
|
const params = new URLSearchParams(raw);
|
|
|
|
if (pathname.startsWith('/api/calendar/events') && !params.has('provider')) {
|
|
params.set('provider', 'icloud');
|
|
}
|
|
|
|
const qs = params.toString();
|
|
return qs ? `?${qs}` : '';
|
|
}
|
|
|
|
/** Longest-first prefixes for vite / nginx location ordering. */
|
|
export function quinnApiProxyPrefixes(): string[] {
|
|
const all = [
|
|
...QUINN_API_MY_PREFIXES,
|
|
...Object.keys(QUINN_API_SEGMENT_MAP),
|
|
'waitlist',
|
|
'clients',
|
|
'calendars',
|
|
'calendar',
|
|
'city-visits',
|
|
'messages',
|
|
'tour-optimizer',
|
|
];
|
|
return [...new Set(all)].sort((a, b) => b.length - a.length);
|
|
} |