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