import { createCookie, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, type MetaFunction, } from "@remix-run/node"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card"; import { Form, useNavigation } from "@remix-run/react"; import { Inbox, Loader, Mail } from "lucide-react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; import { LoginPageLayout } from "~/components/layout/login-page-layout"; import { Button } from "~/components/ui"; import { Fieldset } from "~/components/ui/Fieldset"; import { FormButtons } from "~/components/ui/FormButtons"; import { Header1 } from "~/components/ui/Headers"; import { Input } from "~/components/ui/input"; import { Paragraph } from "~/components/ui/Paragraph"; import { Cookie } from "@mjackson/headers"; import { authenticator } from "~/services/auth.server"; import { getUserId } from "~/services/session.server"; import { commitSession, getUserSession, } from "~/services/sessionStorage.server"; import { env } from "~/env.server"; export const meta: MetaFunction = ({ matches }) => { const parentMeta = matches .flatMap((match) => match.meta ?? []) .filter((meta) => { if ("title" in meta) return false; if ("name" in meta && meta.name === "viewport") return false; return true; }); return [ ...parentMeta, { title: `Login to C.O.R.E.` }, { name: "viewport", content: "width=device-width,initial-scale=1", }, ]; }; export async function loader({ request }: LoaderFunctionArgs): Promise { if (!env.ENABLE_EMAIL_LOGIN) { return typedjson({ emailLoginEnabled: false }); } const userId = await getUserId(request); if (userId) return redirect("/"); const session = await getUserSession(request); const error = session.get("auth:error"); let magicLinkError: string | undefined | unknown; if (error) { if ("message" in error) { magicLinkError = error.message; } else { magicLinkError = JSON.stringify(error, null, 2); } } const magicLinkSent = new Cookie(request.headers.get("cookie") ?? "").has( "core:magiclink", ); return typedjson( { emailLoginEnabled: true, magicLinkSent, magicLinkError, }, { headers: { "Set-Cookie": await commitSession(session) }, }, ); } export async function action({ request }: ActionFunctionArgs) { if (!env.ENABLE_EMAIL_LOGIN) { throw new Error("Magic link login is not enabled"); } const clonedRequest = request.clone(); const payload = Object.fromEntries(await clonedRequest.formData()); const { action } = z .object({ action: z.enum(["send", "reset"]), }) .parse(payload); if (action === "send") { const headers = await authenticator .authenticate("email-link", request) .catch((headers) => headers); throw redirect("/login/magic", { headers }); } else { const myCookie = createCookie("core:magiclink"); return redirect("/login/magic", { headers: { "Set-Cookie": await myCookie.serialize("", { maxAge: 0, path: "/", }), }, }); } } export default function LoginMagicLinkPage() { const data = useTypedLoaderData(); const navigate = useNavigation(); if (!data.emailLoginEnabled) { return ( Magic link login is not enabled. ); } const isLoading = (navigate.state === "loading" || navigate.state === "submitting") && navigate.formAction !== undefined && navigate.formData?.get("action") === "send"; return (
{data.magicLinkSent ? ( Check your magic link The magic link is printed in the container logs if you are using Docker, otherwise check your server logs.
Re-enter email } confirmButton={<>} />
) : ( Welcome Create an account or login using email
{data.magicLinkError && <>{data.magicLinkError}}
)}
); }