From 394046fdb34138e18b50eacc05dfd776557d0aea Mon Sep 17 00:00:00 2001 From: Tracewebstudio Dev Date: Fri, 8 May 2026 15:34:46 +0200 Subject: [PATCH] Update admin session, gateway, jobs/leads routes, login, and add API me endpoint --- src/lib/admin-session.ts | 13 ++++++- src/lib/server/gateway.ts | 5 ++- src/routes/admin/jobs/index.tsx | 11 +++++- src/routes/admin/leads/index.tsx | 11 +++++- .../api/me/notifications/unread-count.ts | 36 +++++++++++++++++++ src/routes/login.tsx | 2 +- 6 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 src/routes/api/me/notifications/unread-count.ts diff --git a/src/lib/admin-session.ts b/src/lib/admin-session.ts index 8fcbbcb..40d51c0 100644 --- a/src/lib/admin-session.ts +++ b/src/lib/admin-session.ts @@ -4,7 +4,18 @@ const SESSION_TTL_SECONDS = 60 * 60 * 12; export function hasAdminSession(): boolean { if (typeof document === 'undefined') return false; - return document.cookie.split(';').some((entry) => entry.trim() === `${SESSION_COOKIE}=${SESSION_VALUE}`); + // Check cookie exists + const hasCookie = document.cookie.split(';').some((entry) => entry.trim() === `${SESSION_COOKIE}=${SESSION_VALUE}`); + // Also check if sessionStorage has a valid token as fallback + const hasToken = (() => { + try { + const token = sessionStorage.getItem('nxtgauge_admin_access_token'); + return Boolean(token && token.trim().length > 0); + } catch { + return false; + } + })(); + return hasCookie || hasToken; } export function setAdminSession(): void { diff --git a/src/lib/server/gateway.ts b/src/lib/server/gateway.ts index 9b0f6bc..83c1281 100644 --- a/src/lib/server/gateway.ts +++ b/src/lib/server/gateway.ts @@ -20,15 +20,18 @@ export function forwardCookies(request: Request): Record { /** * Merge auth + cookie headers from the incoming request with any extra headers provided. + * Extra headers (e.g. Content-Type: multipart/form-data for file uploads) take precedence + * over the default application/json. */ export function withAuthHeaders( request: Request, extra: Record = {}, ): Record { return { - 'Content-Type': 'application/json', ...forwardAuth(request), ...forwardCookies(request), ...extra, + // Default Content-Type only if extra didn't already provide one + ...(extra['Content-Type'] ? {} : { 'Content-Type': 'application/json' }), }; } diff --git a/src/routes/admin/jobs/index.tsx b/src/routes/admin/jobs/index.tsx index a06f50d..71e72d7 100644 --- a/src/routes/admin/jobs/index.tsx +++ b/src/routes/admin/jobs/index.tsx @@ -48,7 +48,16 @@ export default function JobsManagementPage() { const load = async () => { try { - const res = await fetch(`${API}/api/admin/companies/jobs`); + const accessToken = typeof sessionStorage !== 'undefined' + ? sessionStorage.getItem('nxtgauge_admin_access_token') || '' + : ''; + const res = await fetch('/api/admin/companies/jobs', { + headers: { + Accept: 'application/json', + ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}), + }, + credentials: 'include', + }); if (!res.ok) throw new Error('Fetch failed'); const data = await res.json(); const list = Array.isArray(data) ? data : (data.jobs || []); diff --git a/src/routes/admin/leads/index.tsx b/src/routes/admin/leads/index.tsx index c3faa0b..e21a65a 100644 --- a/src/routes/admin/leads/index.tsx +++ b/src/routes/admin/leads/index.tsx @@ -11,7 +11,16 @@ const ROLE_OPTIONS = [ async function loadLeads(): Promise { try { - const res = await fetch(`${API}/api/admin/leads`); + const accessToken = typeof sessionStorage !== 'undefined' + ? sessionStorage.getItem('nxtgauge_admin_access_token') || '' + : ''; + const res = await fetch('/api/admin/leads', { + headers: { + Accept: 'application/json', + ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}), + }, + credentials: 'include', + }); if (!res.ok) throw new Error('Failed to load'); const data = await res.json(); return Array.isArray(data) ? data : (data.leads || []); diff --git a/src/routes/api/me/notifications/unread-count.ts b/src/routes/api/me/notifications/unread-count.ts new file mode 100644 index 0000000..4530a42 --- /dev/null +++ b/src/routes/api/me/notifications/unread-count.ts @@ -0,0 +1,36 @@ +import { gatewayUrl, forwardAuth } from '~/lib/server/gateway'; + +export async function GET({ request }: { request: Request }) { + try { + const upstreamUrl = gatewayUrl('/api/me/notifications/unread-count'); + const upstreamInit: RequestInit = { + method: 'GET', + headers: { + ...forwardAuth(request), + Accept: 'application/json', + }, + credentials: 'include', + }; + + const response = await fetch(upstreamUrl, upstreamInit); + const responseHeaders = new Headers(); + response.headers.forEach((value, key) => { + if (!['server', 'transfer-encoding', 'connection'].includes(key.toLowerCase())) { + responseHeaders.set(key, value); + } + }); + responseHeaders.set('Content-Type', 'application/json'); + + const responseBody = await response.text(); + return new Response(responseBody, { + status: response.status, + statusText: response.statusText, + headers: responseHeaders, + }); + } catch (error: any) { + return new Response( + JSON.stringify({ success: false, unread_count: 0 }), + { status: 200, headers: { 'Content-Type': 'application/json' } }, + ); + } +} diff --git a/src/routes/login.tsx b/src/routes/login.tsx index 95e51c0..20c9e17 100644 --- a/src/routes/login.tsx +++ b/src/routes/login.tsx @@ -76,7 +76,7 @@ export default function LoginPage() { const body = JSON.stringify({ email: email().trim().toLowerCase(), password: password(), loginTarget: 'admin' }); const headers = { 'Content-Type': 'application/json', Accept: 'application/json', 'x-portal-target': 'admin' }; let payload: any = {}; let status = 500; let success = false; - const r = await fetch('/api/auth/login', { method: 'POST', headers, credentials: 'include', body }); + const r = await fetch('/api/admin/auth/login', { method: 'POST', headers, credentials: 'include', body }); status = r.status; payload = await r.json().catch(() => ({})); if (r.ok) { success = true; } if (!success) {