WFDASH-WEBAPP/src/lib/server/browser-session.ts
SJCoetz 69175e493d feat(orders): show resolved sales rep name on order list and sprint
The order list and order sprint pages were rendering the rep column
with a raw PocketBase record id, which the operator could not
recognize. The data layer now resolves the rep through the user's
name:

- New `resolveRepDisplayName` helper (`src/lib/order-rep.ts`) takes
  the customer record's `expand.rep`, the `customer.rep` relation
  id, the legacy `orders.rep` text snapshot, and an admin-resolved
  user map, and returns a name (or empty when only an unresolved PB
  id is present).
- The orders service now fetches the user list alongside the order
  page/counts query and resolves the rep on the way out, so the
  client receives a `repName` per row. New endpoints
  `/api/v1/orders/list` and `/api/v1/orders/[id]/review` expose
  the read model; the page and sprint calls go through them.
- The list card and the sprint table + mobile card render the
  resolved name. An opaque PB id is never displayed.

Migrates `/orders` off `pb-helpers.ts` per the data-layer
discipline, and adds a shared browser-session helper for
`Authorization: Bearer <PB token>` validation in route handlers
that serve the dashboard UI (vs. the agent API).

Co-authored-by: Orca <help@stably.ai>
2026-08-04 08:50:54 +02:00

37 lines
1.1 KiB
TypeScript

import type { NextRequest } from "next/server";
/**
* Validates a browser-forwarded PocketBase auth token against PocketBase.
*
* Dashboard auth lives in localStorage, so Route Handlers cannot read it from
* cookies. Browser callers send the current PB token in `Authorization`; this
* helper asks PB to refresh/validate it instead of trusting or decoding the
* token locally.
*/
export async function assertBrowserSession(req: NextRequest): Promise<void> {
const authorization = req.headers.get("authorization") || "";
const token = authorization.startsWith("Bearer ")
? authorization.slice(7)
: authorization;
if (!token) {
throw Object.assign(new Error("Not authenticated."), { status: 401 });
}
const pbUrl =
process.env.NEXT_PUBLIC_POCKETBASE_URL || "http://127.0.0.1:8090";
const response = await fetch(
`${pbUrl.replace(/\/$/, "")}/api/collections/users/auth-refresh`,
{
method: "POST",
headers: { Authorization: token },
signal: AbortSignal.timeout(5_000),
},
);
if (!response.ok) {
throw Object.assign(new Error("Invalid or expired session."), {
status: 401,
});
}
}