g1t/apps/web/app/routes/verify.tsx

83 lines3,096 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Email verification, password reset, and Git for AI scale positioning1import { CircleCheck, MailWarning } from "lucide-react";
2import { redirect } from "react-router";
3
4import type { Route } from "./+types/verify";
Social cards for every page: og.g1t.sh5import { page } from "../lib/meta";
Email verification, password reset, and Git for AI scale positioning6import { ButtonLink } from "../components/ui";
7import { identity } from "../lib/services.server";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look8import { assertSameOrigin, getViewer, requireUser } from "../lib/session.server";
Email verification, password reset, and Git for AI scale positioning9
Social cards for every page: og.g1t.sh10export function meta(args: Route.MetaArgs) {
11 return page(args, { title: "Confirm your email · g1t" });
Email verification, password reset, and Git for AI scale positioning12}
13
14/** Follows the link from the confirmation email. */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look15export async function loader({ request, context }: Route.LoaderArgs) {
Email verification, password reset, and Git for AI scale positioning16 const token = new URL(request.url).searchParams.get("token") ?? "";
17 const result = await identity.verifyEmail(token);
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look18 // An address added to an account already in use goes back to settings;
19 // a new account's first goes on to make a workspace.
20 const settled = (getViewer(context)?.workspaces ?? []).length > 0;
Email verification, password reset, and Git for AI scale positioning21 return result.ok
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look22 ? { ok: true as const, username: result.value.username, added: settled }
Email verification, password reset, and Git for AI scale positioning23 : { ok: false as const, message: result.error.message };
24}
25
26/** "Resend" from the banner shown to unconfirmed accounts. */
27export async function action({ request, context }: Route.ActionArgs) {
28 assertSameOrigin(request);
29 await identity.resendVerification(requireUser(context, request));
30 throw redirect("/?sent=1");
31}
32
33export default function Verify({ loaderData }: Route.ComponentProps) {
34 return (
35 <main className="mx-auto max-w-md px-4 py-32 text-center">
36 {loaderData.ok ? (
37 <>
38 <CircleCheck size={40} className="mx-auto text-accent" />
39 <h1 className="mt-6 text-2xl font-semibold tracking-tight">
40 Email confirmed
41 </h1>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look42 {loaderData.added ? (
43 <>
44 <p className="mt-2 text-muted">
45 The address is confirmed on {loaderData.username}. You can make it
46 your primary address in your settings.
47 </p>
48 <div className="mt-8">
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas49 <ButtonLink to="/settings/emails">Back to settings</ButtonLink>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look50 </div>
51 </>
52 ) : (
53 <>
54 <p className="mt-2 text-muted">
55 Your account {loaderData.username} is ready. Next, create the
56 workspace your repositories will live in.
57 </p>
58 <div className="mt-8">
59 <ButtonLink to="/">Continue</ButtonLink>
60 </div>
61 </>
62 )}
Email verification, password reset, and Git for AI scale positioning63 </>
64 ) : (
65 <>
66 <MailWarning size={40} className="mx-auto text-warn" />
67 <h1 className="mt-6 text-2xl font-semibold tracking-tight">
68 That link did not work
69 </h1>
70 <p className="mt-2 text-muted">{loaderData.message}</p>
71 <p className="mt-2 text-sm text-muted">
72 Sign in and use the banner at the top to send a new one.
73 </p>
74 <div className="mt-8">
75 <ButtonLink to="/login" variant="quiet">
76 Sign in
77 </ButtonLink>
78 </div>
79 </>
80 )}
81 </main>
82 );
83}