pr_01m47d15m3e54sn21z27rpy5n9/apps/web/app/lib/emails.server.ts
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.
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 1 | import type { AccountEmails, Reauth, Result, SecurityEvent, User } from "@g1t/contracts"; |
| 2 | ||
| 3 | import { pendingFields } from "./emails"; | |
| 4 | import { accounts } from "./services.server"; | |
| 5 | import { clientOf, sessionTokenOf } from "./session.server"; | |
| 6 | ||
| 7 | /** A change that waits on the person proving it is them. */ | |
| 8 | export type PendingChange = { | |
| 9 | intent: string; | |
| 10 | /** The form's other fields, sent again with the password. */ | |
| 11 | fields: Record<string, string>; | |
| 12 | message: string; | |
| 13 | }; | |
| 14 | ||
| 15 | /** What the Emails section's forms answer. */ | |
| 16 | export type EmailActionData = { | |
| 17 | emailError?: string; | |
| 18 | emailNotice?: string; | |
| 19 | reauth?: PendingChange; | |
| 20 | } | null; | |
| 21 | ||
| 22 | /** The intents the Emails section posts. */ | |
| 23 | export const EMAIL_INTENTS = ["add-email", "remove-email", "resend-email", "primary-email", "backup-email", "email-privacy"] as const; | |
| 24 | ||
| 25 | /** A person's addresses and security log for their settings. */ | |
| 26 | export async function loadEmails(user: User): Promise<{ emails: AccountEmails | null; log: SecurityEvent[] }> { | |
| 27 | const [emails, log] = await Promise.all([ | |
| 28 | accounts.listEmails(user).catch(() => null), | |
| 29 | accounts.securityLog(user).catch(() => null), | |
| 30 | ]); | |
| 31 | return { | |
| 32 | emails: emails?.ok ? emails.value : null, | |
| 33 | log: log?.ok ? log.value : [], | |
| 34 | }; | |
| 35 | } | |
| 36 | ||
| 37 | /** The proof a change carries: this session, and the password if it was just typed. */ | |
| 38 | function proof(request: Request, form: FormData): Reauth { | |
| 39 | const password = String(form.get("password") ?? ""); | |
| 40 | return { sessionToken: sessionTokenOf(request), password: password || null, client: clientOf(request) }; | |
| 41 | } | |
| 42 | ||
| 43 | function answer(result: Result<unknown>, form: FormData, notice?: string): EmailActionData { | |
| 44 | if (result.ok) return notice ? { emailNotice: notice } : null; | |
| 45 | if (result.error.code === "reauth_required") { | |
| 46 | return { reauth: { intent: String(form.get("intent")), fields: pendingFields(form), message: result.error.message } }; | |
| 47 | } | |
| 48 | return { emailError: result.error.message }; | |
| 49 | } | |
| 50 | ||
| 51 | /** Handles the Emails section's intents; undefined for any other intent. */ | |
| 52 | export async function emailAction(user: User, form: FormData, request: Request): Promise<EmailActionData | undefined> { | |
| 53 | const email = String(form.get("email") ?? "").trim(); | |
| 54 | switch (form.get("intent")) { | |
| 55 | case "add-email": | |
| 56 | return answer( | |
| 57 | await accounts.addEmail(user, email, proof(request, form)), | |
| 58 | form, | |
| 59 | `A confirmation link is on its way to ${email}.`, | |
| 60 | ); | |
| 61 | case "remove-email": | |
| 62 | return answer(await accounts.removeEmail(user, email, proof(request, form)), form, `Removed ${email}.`); | |
| 63 | case "resend-email": | |
| 64 | return answer(await accounts.resendEmailVerification(user, email), form, `Sent the link to ${email} again.`); | |
| 65 | case "primary-email": | |
| 66 | return answer( | |
| 67 | await accounts.updateEmailSettings(user, { primary: email }, proof(request, form)), | |
| 68 | form, | |
| 69 | `${email} is now your primary address.`, | |
| 70 | ); | |
| 71 | case "backup-email": | |
| 72 | return answer( | |
| 73 | await accounts.updateEmailSettings(user, { backup: String(form.get("backup") ?? "") }, proof(request, form)), | |
| 74 | form, | |
| 75 | "Saved where security notices go.", | |
| 76 | ); | |
| 77 | case "email-privacy": | |
| 78 | return answer( | |
| 79 | await accounts.updateEmailSettings( | |
| 80 | user, | |
| 81 | { privateEmail: form.get("private") === "on", blockPrivatePushes: form.get("block") === "on" }, | |
| 82 | proof(request, form), | |
| 83 | ), | |
| 84 | form, | |
| 85 | "Saved.", | |
| 86 | ); | |
| 87 | } | |
| 88 | return undefined; | |
| 89 | } |