import { redirect, type ActionFunctionArgs } from "@remix-run/server-runtime"; import { z } from "zod"; import { prisma } from "~/db.server"; import { commitSession as commitMessageSession, getSession as getMessageSession, setErrorMessage, setSuccessMessage, } from "~/models/message.server"; import { requireUserId } from "~/services/session.server"; import { commitAuthenticatedSession, getAllowedSessionOptions, getEffectiveSessionDuration, isAllowedSessionDuration, } from "~/services/sessionDuration.server"; import { getUserSession } from "~/services/sessionStorage.server"; const FormSchema = z.object({ sessionDuration: z.coerce.number().int().positive(), }); const REDIRECT_PATH = "/account/security"; async function redirectWithError(request: Request, message: string) { const messageSession = await getMessageSession(request.headers.get("cookie")); setErrorMessage(messageSession, message); return redirect(REDIRECT_PATH, { headers: { "Set-Cookie": await commitMessageSession(messageSession) }, }); } export async function action({ request }: ActionFunctionArgs) { const userId = await requireUserId(request); const formData = await request.formData(); const parsed = FormSchema.safeParse(Object.fromEntries(formData)); if (!parsed.success) { return redirectWithError(request, "Invalid session duration value."); } const { sessionDuration } = parsed.data; if (!isAllowedSessionDuration(sessionDuration)) { return redirectWithError(request, "Invalid session duration value."); } const { orgCapSeconds, durationSeconds } = await getEffectiveSessionDuration(userId); const allowed = getAllowedSessionOptions(orgCapSeconds, durationSeconds); if (!allowed.some((o) => o.value === sessionDuration)) { return redirectWithError( request, "Your organization's policy does not allow that session duration." ); } await prisma.user.update({ where: { id: userId }, data: { sessionDuration }, }); // Re-issue the cookie with the new maxAge and reset issuedAt so the user // gets a fresh window matching their new selection right away. This also // restamps `User.nextSessionEnd` against the new effective duration. const authSession = await getUserSession(request); const authCookie = await commitAuthenticatedSession(authSession, userId); const messageSession = await getMessageSession(request.headers.get("cookie")); setSuccessMessage(messageSession, "Session duration updated."); const messageCookie = await commitMessageSession(messageSession); const headers = new Headers(); headers.append("Set-Cookie", authCookie); headers.append("Set-Cookie", messageCookie); return redirect(REDIRECT_PATH, { headers }); }