import { z } from "zod"; import { apiBaseUrl } from "@/lib"; import { UserMeError } from "@/lib/auth-errors"; import { PERMISSION_KEY, type RolePermissionAttributes } from "@/types/users"; const currentUserDocumentSchema = z.object({ data: z.object({ type: z.literal("users"), id: z.string().min(1), attributes: z.object({ name: z.string(), email: z.string(), company_name: z.string().optional(), date_joined: z.string().optional(), }), }), included: z.array( z.object({ type: z.literal("roles"), id: z.string().min(1), attributes: z.record(z.string(), z.unknown()), }), ), }); export interface CurrentUser { name: string; email: string; company?: string; dateJoined?: string; permissions: RolePermissionAttributes; manageRegistry: true | false | undefined; } const toPermissions = ( roles: readonly Record[], ): RolePermissionAttributes => Object.fromEntries( Object.values(PERMISSION_KEY).map((key) => [ key, roles.some((attributes) => attributes[key] === true), ]), ) as RolePermissionAttributes; export async function fetchCurrentUser( accessToken: string, options: { signal?: AbortSignal } = {}, ): Promise { if (!accessToken.trim()) throw new Error("Current user token is required"); let response: Response; try { response = await fetch(`${apiBaseUrl}/users/me?include=roles`, { method: "GET", cache: "no-store", signal: options.signal, headers: { Accept: "application/vnd.api+json", Authorization: `Bearer ${accessToken}`, }, }); } catch { throw new UserMeError("Unable to load user"); } if (!response.ok) { const message = response.status === 401 ? "Invalid or expired token" : response.status === 403 ? "Access denied" : response.status === 404 ? "User not found" : "Unable to load user"; throw new UserMeError(message, response.status); } const parsed = currentUserDocumentSchema.safeParse( await response.json().catch(() => undefined), ); if (!parsed.success) throw new Error("Malformed current user response"); const roles = parsed.data.included.map((role) => role.attributes); if (roles.length === 0) { throw new Error("Missing current user role"); } const permissions = toPermissions(roles); return { name: parsed.data.data.attributes.name, email: parsed.data.data.attributes.email, company: parsed.data.data.attributes.company_name, dateJoined: parsed.data.data.attributes.date_joined, permissions, manageRegistry: permissions.manage_registry ? true : roles.every((attributes) => attributes.manage_registry === false) ? false : undefined, }; }