a tool for shared writing and social publishing
at debug/datetime 86 lines 2.5 kB view raw
1"use server"; 2 3import { createClient } from "@supabase/supabase-js"; 4import { and, eq, sql } from "drizzle-orm"; 5import { drizzle } from "drizzle-orm/node-postgres"; 6import { 7 email_subscriptions_to_entity, 8 facts, 9 permission_tokens, 10} from "drizzle/schema"; 11import postgres from "postgres"; 12import type { Fact } from "src/replicache"; 13import { Database } from "supabase/database.types"; 14import { pool } from "supabase/pool"; 15import { v7 } from "uuid"; 16 17export async function confirmEmailSubscription( 18 subscriptionID: string, 19 code: string, 20) { 21 const client = await pool.connect(); 22 const db = drizzle(client); 23 let subscription = await db.transaction(async (tx) => { 24 let [{ email_subscriptions_to_entity: sub, permission_tokens: token }] = 25 await db 26 .select() 27 .from(email_subscriptions_to_entity) 28 .innerJoin( 29 permission_tokens, 30 eq(permission_tokens.id, email_subscriptions_to_entity.token), 31 ) 32 .where(and(eq(email_subscriptions_to_entity.id, subscriptionID))); 33 if (sub.confirmed) return { subscription: sub, token }; 34 if (code !== sub.confirmation_code) return null; 35 let [fact] = (await db 36 .select() 37 .from(facts) 38 .where( 39 and( 40 eq(facts.entity, sub.entity), 41 42 eq(facts.attribute, "mailbox/subscriber-count"), 43 ), 44 )) as Fact<"mailbox/subscriber-count">[]; 45 if (!fact) { 46 await db.insert(facts).values({ 47 id: v7(), 48 entity: sub.entity, 49 data: sql`${{ type: "number", value: 1 }}::jsonb`, 50 attribute: "mailbox/subscriber-count", 51 }); 52 } else { 53 await db 54 .update(facts) 55 .set({ 56 data: sql`${{ type: "number", value: fact.data.value + 1 }}::jsonb`, 57 }) 58 .where(eq(facts.id, fact.id)); 59 } 60 let [subscription] = await db 61 .update(email_subscriptions_to_entity) 62 .set({ 63 confirmed: true, 64 }) 65 .where(eq(email_subscriptions_to_entity.id, sub.id)) 66 .returning(); 67 68 return { subscription, token }; 69 }); 70 71 let supabase = createClient<Database>( 72 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, 73 process.env.SUPABASE_SERVICE_ROLE_KEY as string, 74 ); 75 let channel = supabase.channel( 76 `rootEntity:${subscription?.token.root_entity}`, 77 ); 78 await channel.send({ 79 type: "broadcast", 80 event: "poke", 81 payload: { message: "poke" }, 82 }); 83 supabase.removeChannel(channel); 84 client.release(); 85 return subscription; 86}