a tool for shared writing and social publishing
1"use server";
2import { drizzle } from "drizzle-orm/node-postgres";
3import postgres from "postgres";
4import {
5 email_auth_tokens,
6 identities,
7 permission_token_on_homepage,
8 poll_votes_on_entity,
9} from "drizzle/schema";
10import { and, eq, isNull } from "drizzle-orm";
11import { cookies } from "next/headers";
12import { redirect } from "next/navigation";
13import { pool } from "supabase/pool";
14import { supabaseServerClient } from "supabase/serverClient";
15
16export async function loginWithEmailToken(
17 localLeaflets: { token: { id: string }; added_at: string }[],
18) {
19 const client = await pool.connect();
20 const db = drizzle(client);
21 let token_id = (await cookies()).get("auth_token")?.value;
22 let voter_token = (await cookies()).get("poll_voter_token")?.value;
23 if (!token_id) return null;
24 let result = await db.transaction(async (tx) => {
25 let [token] = await tx
26 .select()
27 .from(email_auth_tokens)
28 .where(
29 and(
30 eq(email_auth_tokens.id, token_id),
31 eq(email_auth_tokens.confirmed, true),
32 ),
33 );
34 if (!token || !token.email) return null;
35 if (token.identity) {
36 let id = token.identity;
37 if (localLeaflets.length > 0)
38 await tx
39 .insert(permission_token_on_homepage)
40 .values(
41 localLeaflets.map((l) => ({
42 identity: id,
43 token: l.token.id,
44 })),
45 )
46 .onConflictDoNothing();
47 return token;
48 }
49 let [existingIdentity] = await tx
50 .select()
51 .from(identities)
52 .where(eq(identities.email, token.email));
53
54 let identity = existingIdentity;
55 if (!existingIdentity) {
56 let identityCookie = (await cookies()).get("identity");
57 if (identityCookie) {
58 let [existingIdentityFromCookie] = await tx
59 .select()
60 .from(identities)
61 .where(
62 and(
63 eq(identities.id, identityCookie.value),
64 isNull(identities.email),
65 ),
66 );
67 if (existingIdentityFromCookie) {
68 await tx
69 .update(identities)
70 .set({ email: token.email })
71 .where(eq(identities.id, existingIdentityFromCookie.id));
72 identity = existingIdentityFromCookie;
73 }
74 } else {
75 const { data: newIdentity } = await supabaseServerClient
76 .from("identities")
77 .insert({ email: token.email })
78 .select()
79 .single();
80 identity = newIdentity!;
81 }
82 }
83
84 await tx
85 .update(email_auth_tokens)
86 .set({ identity: identity.id })
87 .where(eq(email_auth_tokens.id, token_id));
88
89 if (localLeaflets.length > 0)
90 await tx
91 .insert(permission_token_on_homepage)
92 .values(
93 localLeaflets.map((l) => ({
94 identity: identity.id,
95 token: l.token.id,
96 })),
97 )
98 .onConflictDoNothing();
99
100 return token;
101 });
102 if (result?.identity) {
103 if (result.identity !== voter_token) {
104 if (voter_token)
105 await db
106 .update(poll_votes_on_entity)
107 .set({ voter_token: result.identity })
108 .where(eq(poll_votes_on_entity.voter_token, voter_token));
109
110 (await cookies()).set("poll_voter_token", result.identity, {
111 maxAge: 60 * 60 * 24 * 365,
112 secure: process.env.NODE_ENV === "production",
113 httpOnly: true,
114 sameSite: "lax",
115 });
116 }
117 }
118 client.release();
119}