UNPKG

wcz-layout

Version:

1 lines 11 kB
{"version":3,"file":"utils-CnwPC4sC.mjs","names":["serverEnv","getAppSession","useSession","getSession","name","password","SESSION_SECRET","cookie","httpOnly","sameSite","secure","path","createServerFn","createServerOnlyFn","scopes","definedScopes","QueryClientParam","User","getAppSession","getSessionUser","method","handler","Promise","session","data","user","getUser","queryClient","import","meta","env","SSR","ensureQueryData","queryKey","queryFn","staleTime","Infinity","getAccessToken","scopeKey","refreshToken","Error","acquireDelegatedToken","accessToken","update","AnyFieldApi","ParsedLocation","redirect","manifest","permissions","QueryClientParam","TokenPayload","User","getUser","WISTRON_PRIMARY_COLOR","WISTRON_SECONDARY_COLOR","Platform","isAndroid","test","userAgent","isIOS","isWindows","isMacOS","navigator","RootRouteHeadOptions","rootRouteHead","options","meta","charSet","name","content","title","links","rel","sizes","href","type","requirePermission","permissionKey","location","context","user","queryClient","encodeURIComponent","reloadDocument","hasPermission","Error","FormOmittedProps","getFieldStatus","field","state","isTouched","hasError","errors","length","helperText","message","toKebabCase","str","replaceAll","toLowerCase","buildUser","payload","split","email","preferred_username","department","toUpperCase","employeeId","companyName","groups","key","some","group","includes"],"sources":["../src/lib/auth/session.ts","../src/lib/auth/user.ts","../src/lib/utils.ts"],"sourcesContent":["import { serverEnv } from \"~/env\";\r\n\r\nexport const getAppSession = async () => {\r\n // Dynamic import: `@tanstack/react-start/server` is the server entry and drags\r\n // in the SSR renderer (`react-dom/server`). Keep it out of the client graph —\r\n // this helper is only ever called server-side, and all callers already await it.\r\n // Aliased off the `use*` name so it isn't mistaken for a React hook (it's a\r\n // server session helper, not a hook).\r\n const { useSession: getSession } = await import(\"@tanstack/react-start/server\");\r\n\r\n return getSession({\r\n name: \"wcz-auth\",\r\n password: serverEnv.SESSION_SECRET,\r\n cookie: {\r\n httpOnly: true,\r\n sameSite: \"lax\",\r\n secure: true,\r\n path: \"/\",\r\n },\r\n });\r\n};\r\n","import { createServerFn, createServerOnlyFn } from \"@tanstack/react-start\";\r\nimport { scopes as definedScopes } from \"virtual:wcz-layout\";\r\nimport { QueryClientParam } from \"~/models/QueryClientParam\";\r\nimport type { User } from \"~/models/User\";\r\nimport { getAppSession } from \"./session\";\r\n\r\n/**\r\n * Reads the signed-in user from the session cookie, or null. As a server function\r\n * it runs in-process when called on the server (SSR, middleware) and as an RPC\r\n * when called from the client — so it doubles as the client `queryFn`.\r\n */\r\nexport const getSessionUser = createServerFn({ method: \"GET\" }).handler(\r\n async (): Promise<User | null> => {\r\n const session = await getAppSession();\r\n return session.data.user ?? null;\r\n },\r\n);\r\n\r\nexport const getUser = ({ queryClient }: QueryClientParam): Promise<User | null> => {\r\n if (import.meta.env.SSR) return getSessionUser();\r\n\r\n return queryClient.ensureQueryData({\r\n queryKey: [\"auth\", \"user\"],\r\n queryFn: () => getSessionUser(),\r\n staleTime: Infinity,\r\n });\r\n};\r\n\r\n/**\r\n * Server-only token acquisition: a delegated access token for the given API\r\n * scope, minted from the user's session refresh token. Entra rotates the refresh\r\n * token on each use, so the rotated token is persisted back to the session. Use\r\n * inside server functions and middleware — it is stripped from the client bundle\r\n * and throws if called there.\r\n */\r\nexport const getAccessToken = createServerOnlyFn(\r\n async (scopeKey: keyof typeof definedScopes): Promise<string> => {\r\n const session = await getAppSession();\r\n if (!session.data.refreshToken) throw new Error(\"No active session. User not signed in.\");\r\n\r\n // Dynamic import so `entra` (and its `@azure/msal-node` dependency) stays out\r\n // of the client graph — this module also exports the isomorphic `getUser`.\r\n const { acquireDelegatedToken } = await import(\"./entra\");\r\n const { accessToken, refreshToken } = await acquireDelegatedToken({\r\n refreshToken: session.data.refreshToken,\r\n scopes: definedScopes[scopeKey],\r\n });\r\n\r\n if (refreshToken !== session.data.refreshToken) await session.update({ refreshToken });\r\n return accessToken;\r\n },\r\n);\r\n","import type { AnyFieldApi } from \"@tanstack/react-form\";\nimport { ParsedLocation, redirect } from \"@tanstack/react-router\";\nimport { manifest, permissions } from \"virtual:wcz-layout\";\nimport type { QueryClientParam } from \"~/models/QueryClientParam\";\nimport type { TokenPayload } from \"~/models/TokenPayload\";\nimport type { User } from \"~/models/User\";\nimport { getUser } from \"./auth/user\";\n\nexport const WISTRON_PRIMARY_COLOR = \"#00506E\";\nexport const WISTRON_SECONDARY_COLOR = \"#64DC00\";\n\nexport class Platform {\n static get isAndroid() {\n return /android/i.test(this.userAgent);\n }\n static get isIOS() {\n return /iPad|iPhone|iPod/.test(this.userAgent);\n }\n static get isWindows() {\n return /windows/i.test(this.userAgent);\n }\n static get isMacOS() {\n return /Macintosh|MacIntel|MacPPC|Mac68K/.test(this.userAgent);\n }\n\n private static get userAgent() {\n return typeof navigator === \"undefined\" ? \"\" : navigator.userAgent;\n }\n}\n\ninterface RootRouteHeadOptions {\n manifest?: string;\n}\n\nexport const rootRouteHead = (options?: RootRouteHeadOptions) => ({\n meta: [\n { charSet: \"utf-8\" },\n { name: \"viewport\", content: \"width=device-width, initial-scale=1\" },\n { title: manifest.name },\n { name: \"og:type\", content: \"website\" },\n { name: \"og:title\", content: manifest.name },\n { name: \"og:image\", content: \"/favicon-32x32.png\" },\n ],\n links: [\n { rel: \"apple-touch-icon\", sizes: \"180x180\", href: \"/apple-touch-icon.png\" },\n { rel: \"icon\", type: \"image/png\", sizes: \"32x32\", href: \"/favicon-32x32.png\" },\n { rel: \"icon\", type: \"image/png\", sizes: \"16x16\", href: \"/favicon-16x16.png\" },\n { rel: \"manifest\", href: options?.manifest || \"/manifest.json\" },\n { rel: \"icon\", href: \"/favicon.ico\" },\n ],\n});\n\nexport const requirePermission = (permissionKey: keyof typeof permissions) => {\n return async ({ location, context }: { location: ParsedLocation; context: QueryClientParam }) => {\n const user = await getUser({ queryClient: context.queryClient });\n\n if (!user)\n throw redirect({\n href: `/auth/login?returnTo=${encodeURIComponent(location.href)}`,\n reloadDocument: true,\n });\n\n if (!hasPermission(user, permissionKey))\n throw new Error(\"You do not have permission to access this page.\");\n\n return { user };\n };\n};\n\n/* Internal Utils */\nexport type FormOmittedProps =\n | \"name\"\n | \"value\"\n | \"onChange\"\n | \"onBlur\"\n | \"error\"\n | \"helperText\"\n | \"renderInput\"\n | \"type\"\n | \"aria-label\";\n\nexport const getFieldStatus = (field: AnyFieldApi) => {\n const { meta } = field.state;\n\n const isTouched = meta.isTouched;\n const hasError = !!meta.errors.length;\n const helperText = meta.errors[0]?.message;\n\n return { isTouched, hasError, helperText };\n};\n\nexport const toKebabCase = (str: string): string => {\n return str\n .replaceAll(/([a-z])([A-Z])/g, \"$1-$2\")\n .replaceAll(/[\\s_]+/g, \"-\")\n .replaceAll(/[^a-zA-Z0-9-]/g, \"\")\n .toLowerCase()\n .replaceAll(/-+/g, \"-\")\n .replaceAll(/(^-|-$)/g, \"\");\n};\n\nexport const buildUser = (payload: TokenPayload): User => ({\n name: payload.name?.split(\"/\")[0],\n email: payload.preferred_username?.toLowerCase(),\n department: payload.department?.toUpperCase() || \"\",\n employeeId: payload.employeeId?.toUpperCase() || \"\",\n companyName: payload.companyName || \"\",\n groups: payload.groups ?? [],\n});\n\nexport const hasPermission = (user: User | null, key: keyof typeof permissions): boolean =>\n user ? permissions[key].some((group) => user.groups.includes(group)) : false;\n"],"mappings":"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"}