Something went wrong. Try again.
a tool for shared writing and social publishing
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138"use server";
import { randomBytes } from "crypto";import { drizzle } from "drizzle-orm/node-postgres";
import { email_auth_tokens, identities } from "drizzle/schema";import { and, eq } from "drizzle-orm";import { cookies } from "next/headers";import { setAuthToken } from "src/auth";import { pool } from "supabase/pool";import { supabaseServerClient } from "supabase/serverClient";import { LeafletConfirmEmail } from "emails/leafletConfirmEmail";import { sendConfirmationEmail } from "src/utils/confirmationEmail";import { linkOrphanedEmailSubscribers } from "src/utils/linkOrphanedEmailSubscribers";
async function sendAuthCode(email: string, code: string) { await sendConfirmationEmail({ to: email, subject: `Your authentication code for Leaflet is ${code}`, template: ( <LeafletConfirmEmail code={code} title="Sign in to Leaflet" message="Paste this code to sign in" assetsBaseUrl={process.env.NEXT_PUBLIC_APP_URL || "https://leaflet.pub"} /> ), text: `Paste this code to login to Leaflet:\n\n${code}\n`, devLogTag: "auth code", code, });}
export async function requestAuthEmailToken(emailNonNormalized: string) { let email = emailNonNormalized.toLowerCase(); const client = await pool.connect(); const db = drizzle(client);
const code = randomBytes(3).toString("hex").toUpperCase();
const [token] = await db .insert(email_auth_tokens) .values({ email, confirmation_code: code, confirmed: false, }) .returning({ id: email_auth_tokens.id, });
await sendAuthCode(email, code);
client.release(); return token.id;}
export async function confirmEmailAuthToken(tokenId: string, code: string) { const client = await pool.connect(); const db = drizzle(client);
const [token] = await db .select() .from(email_auth_tokens) .where(eq(email_auth_tokens.id, tokenId));
if (!token || !token.email) { client.release(); return null; }
if (token.confirmation_code !== code) { client.release(); return null; }
if (token.confirmed) { client.release(); return null; } let authToken = (await cookies()).get("auth_token"); if (authToken) { let [existingToken] = await db .select() .from(email_auth_tokens) .rightJoin(identities, eq(identities.id, email_auth_tokens.identity)) .where(eq(email_auth_tokens.id, authToken.value));
if (existingToken) { if (existingToken.identities?.email) { } await db .update(identities) .set({ email: token.email }) .where(eq(identities.id, existingToken.identities.id)); client.release(); return existingToken; } }
let identityID; let [identity] = await db .select() .from(identities) .where(eq(identities.email, token.email)); if (!identity) { const { data: newIdentity } = await supabaseServerClient .from("identities") .insert({ email: token.email }) .select() .single(); identityID = newIdentity!.id; } else { identityID = identity.id; }
await linkOrphanedEmailSubscribers(identityID, token.email);
const [confirmedToken] = await db .update(email_auth_tokens) .set({ confirmed: true, identity: identityID, }) .where( and( eq(email_auth_tokens.id, tokenId), eq(email_auth_tokens.confirmation_code, code), ), ) .returning();
await setAuthToken(confirmedToken.id);
client.release(); return confirmedToken;}