mirror of
https://github.com/hanzoai/sign.git
synced 2026-10-21 01:21:51 +00:00
A WebSocket is dialled by absolute origin and has no relative form, but
getBaseUrl() answers '' in the browser — the convention the HTTP transport
wants — so zapUrl() built `new URL('')` and threw before the socket opened.
Read the origin off the page there; keep the configured URL off-page.
isAdmin answers false for an absent user or roles column instead of
throwing, so the app shell renders while a session refresh settles.
closeZap claims the connection slot before awaiting, so a dial that races a
logout gets a fresh connection instead of an orphaned one.
ARCHITECTURE: the internal RPC mount is /zap over WebSocket, routed by
`<router>.<procedure>` in the body; /api/trpc no longer exists.
18 lines
814 B
TypeScript
18 lines
814 B
TypeScript
import type { User } from '@prisma/client';
|
|
|
|
/**
|
|
* Whether the user holds the platform admin role.
|
|
*
|
|
* `Role` is declared in the schema but no model field references it — on the
|
|
* Base SQLite store `User.roles` is a JSON `TEXT` column — so Prisma omits the
|
|
* enum from the generated client and `Role.ADMIN` reads `undefined.ADMIN`.
|
|
* The role name is the stable value; match it directly.
|
|
*
|
|
* `roles` reaches here as a decoded `string[]` (packages/prisma/json-array.ts
|
|
* decodes it at the data layer). Callers include the app shell, which renders
|
|
* before a session refresh settles, so an absent user or column answers `false`
|
|
* rather than throwing and taking the whole tree down.
|
|
*/
|
|
export const isAdmin = (user?: Pick<User, 'roles'> | null): boolean =>
|
|
user?.roles?.includes('ADMIN') ?? false;
|