zerobyte/app/routes/(dashboard)/route.tsx
Nico 4dcafa0708
feat(auth): allow skipping forced recovery key download (#900)
* feat(auth): allow skipping forced recovery key download

* refactor: move from session storage to cookie
2026-05-19 20:36:45 +02:00

68 lines
2.4 KiB
TypeScript

import { createFileRoute, redirect } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";
import { getCookie, getRequestHeaders } from "@tanstack/react-start/server";
import { Layout } from "~/client/components/layout";
import { SIDEBAR_COOKIE_NAME } from "~/client/components/ui/sidebar";
import { authMiddleware } from "~/middleware/auth";
import { auth } from "~/server/lib/auth";
import { getOrganizationContext } from "~/server/lib/functions/organization-context";
import { getServerConstants } from "~/server/lib/functions/server-constants";
import { userHasCredentialPassword } from "~/server/modules/auth/helpers";
import { authService } from "~/server/modules/auth/auth.service";
import { RECOVERY_KEY_DOWNLOAD_SKIPPED_COOKIE_NAME } from "~/lib/recovery-key-skip";
export const fetchUser = createServerFn({ method: "GET" }).handler(async () => {
const headers = getRequestHeaders();
const session = await auth.api.getSession({ headers });
const hasUsers = await authService.hasUsers();
const hasCredentialPassword = session?.user ? await userHasCredentialPassword(session.user.id) : false;
const sidebarCookie = getCookie(SIDEBAR_COOKIE_NAME);
const sidebarOpen = !sidebarCookie ? true : sidebarCookie === "true";
const hasSkippedRecoveryKeyDownload =
!!session?.user && getCookie(RECOVERY_KEY_DOWNLOAD_SKIPPED_COOKIE_NAME) === session.user.id;
return {
user: session?.user ? { ...session.user, hasCredentialPassword } : null,
hasUsers,
sidebarOpen,
hasSkippedRecoveryKeyDownload,
};
});
export const Route = createFileRoute("/(dashboard)")({
component: PathlessLayoutComponent,
errorComponent: (e) => <div>{e.error.message}</div>,
server: {
middleware: [authMiddleware],
},
loader: async ({ context }) => {
const [authContext] = await Promise.all([
fetchUser(),
context.queryClient.ensureQueryData({
queryKey: ["organization-context"],
queryFn: () => getOrganizationContext(),
}),
context.queryClient.ensureQueryData({
queryKey: ["server-constants"],
queryFn: () => getServerConstants(),
}),
]);
if (
authContext.user &&
!authContext.user.hasDownloadedResticPassword &&
!authContext.hasSkippedRecoveryKeyDownload
) {
throw redirect({ to: "/download-recovery-key" });
}
return authContext;
},
});
function PathlessLayoutComponent() {
const loaderData = Route.useLoaderData();
return <Layout loaderData={loaderData} />;
}