a tool for shared writing and social publishing

use node-postgres and vercel's attachDBPool everywhere

+117 -121
+5 -18
actions/addLeafletToHome.ts
··· 1 "use server"; 2 3 - import { drizzle } from "drizzle-orm/postgres-js"; 4 - import { 5 - entities, 6 - identities, 7 - permission_tokens, 8 - permission_token_rights, 9 - entity_sets, 10 - facts, 11 - permission_token_on_homepage, 12 - email_auth_tokens, 13 - } from "drizzle/schema"; 14 - import { redirect } from "next/navigation"; 15 - import postgres from "postgres"; 16 - import { v7 } from "uuid"; 17 - import { sql, eq, and } from "drizzle-orm"; 18 import { cookies } from "next/headers"; 19 20 export async function addLeafletToHome(leaflet: string) { 21 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 22 let auth_token = (await cookies()).get("auth_token")?.value; 23 const db = drizzle(client); 24 await db.transaction(async (tx) => { 25 if (auth_token) { ··· 40 41 return; 42 }); 43 - 44 - client.end(); 45 return; 46 }
··· 1 "use server"; 2 3 + import { drizzle } from "drizzle-orm/node-postgres"; 4 + import { sql } from "drizzle-orm"; 5 import { cookies } from "next/headers"; 6 + import { pool } from "supabase/pool"; 7 8 export async function addLeafletToHome(leaflet: string) { 9 let auth_token = (await cookies()).get("auth_token")?.value; 10 + const client = await pool.connect(); 11 const db = drizzle(client); 12 await db.transaction(async (tx) => { 13 if (auth_token) { ··· 28 29 return; 30 }); 31 + client.release(); 32 return; 33 }
+4 -7
actions/createIdentity.ts
··· 1 - import { PostgresJsDatabase } from "drizzle-orm/postgres-js"; 2 import { 3 entities, 4 permission_tokens, 5 permission_token_rights, 6 entity_sets, 7 - facts, 8 identities, 9 } from "drizzle/schema"; 10 - import { redirect } from "next/navigation"; 11 - import postgres from "postgres"; 12 import { v7 } from "uuid"; 13 - import { sql } from "drizzle-orm"; 14 - import { cookies } from "next/headers"; 15 export async function createIdentity( 16 - db: PostgresJsDatabase, 17 data?: { email?: string; atp_did?: string }, 18 ) { 19 return db.transaction(async (tx) => {
··· 1 import { 2 entities, 3 permission_tokens, 4 permission_token_rights, 5 entity_sets, 6 identities, 7 } from "drizzle/schema"; 8 import { v7 } from "uuid"; 9 + import { PgTransaction } from "drizzle-orm/pg-core"; 10 + import { NodePgDatabase } from "drizzle-orm/node-postgres"; 11 + 12 export async function createIdentity( 13 + db: NodePgDatabase, 14 data?: { email?: string; atp_did?: string }, 15 ) { 16 return db.transaction(async (tx) => {
+4 -3
actions/createNewLeaflet.ts
··· 1 "use server"; 2 3 - import { drizzle } from "drizzle-orm/postgres-js"; 4 import { 5 entities, 6 identities, ··· 16 import { v7 } from "uuid"; 17 import { sql, eq, and } from "drizzle-orm"; 18 import { cookies } from "next/headers"; 19 20 export async function createNewLeaflet({ 21 pageType, ··· 26 redirectUser: boolean; 27 firstBlockType?: "h1" | "text"; 28 }) { 29 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 30 let auth_token = (await cookies()).get("auth_token")?.value; 31 const db = drizzle(client); 32 let { permissionToken } = await db.transaction(async (tx) => { 33 // Create a new entity set ··· 156 return { permissionToken, rights, root_entity, entity_set }; 157 }); 158 159 - client.end(); 160 if (redirectUser) redirect(`/${permissionToken.id}?focusFirstBlock`); 161 return permissionToken.id; 162 }
··· 1 "use server"; 2 3 + import { drizzle } from "drizzle-orm/node-postgres"; 4 import { 5 entities, 6 identities, ··· 16 import { v7 } from "uuid"; 17 import { sql, eq, and } from "drizzle-orm"; 18 import { cookies } from "next/headers"; 19 + import { pool } from "supabase/pool"; 20 21 export async function createNewLeaflet({ 22 pageType, ··· 27 redirectUser: boolean; 28 firstBlockType?: "h1" | "text"; 29 }) { 30 let auth_token = (await cookies()).get("auth_token")?.value; 31 + const client = await pool.connect(); 32 const db = drizzle(client); 33 let { permissionToken } = await db.transaction(async (tx) => { 34 // Create a new entity set ··· 157 return { permissionToken, rights, root_entity, entity_set }; 158 }); 159 160 + client.release(); 161 if (redirectUser) redirect(`/${permissionToken.id}?focusFirstBlock`); 162 return permissionToken.id; 163 }
+4 -5
actions/createNewLeafletFromTemplate.ts
··· 1 "use server"; 2 3 import { createServerClient } from "@supabase/ssr"; 4 - import { drizzle } from "drizzle-orm/postgres-js"; 5 - import { NextRequest } from "next/server"; 6 - import postgres from "postgres"; 7 import type { Fact } from "src/replicache"; 8 import type { Attribute } from "src/replicache/attributes"; 9 import { Database } from "supabase/database.types"; ··· 19 import { sql } from "drizzle-orm"; 20 import { redirect } from "next/navigation"; 21 import { cookies } from "next/headers"; 22 23 let supabase = createServerClient<Database>( 24 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, ··· 84 }), 85 ); 86 87 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 88 const db = drizzle(client); 89 90 let { permissionToken } = await db.transaction(async (tx) => { ··· 138 return { permissionToken, rights, entity_set }; 139 }); 140 141 - client.end(); 142 if (redirectUser) redirect(`/${permissionToken.id}`); 143 return { id: permissionToken.id, error: null } as const; 144 }
··· 1 "use server"; 2 3 import { createServerClient } from "@supabase/ssr"; 4 + import { drizzle } from "drizzle-orm/node-postgres"; 5 import type { Fact } from "src/replicache"; 6 import type { Attribute } from "src/replicache/attributes"; 7 import { Database } from "supabase/database.types"; ··· 17 import { sql } from "drizzle-orm"; 18 import { redirect } from "next/navigation"; 19 import { cookies } from "next/headers"; 20 + import { pool } from "supabase/pool"; 21 22 let supabase = createServerClient<Database>( 23 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, ··· 83 }), 84 ); 85 86 + const client = await pool.connect(); 87 const db = drizzle(client); 88 89 let { permissionToken } = await db.transaction(async (tx) => { ··· 137 return { permissionToken, rights, entity_set }; 138 }); 139 140 + client.release(); 141 if (redirectUser) redirect(`/${permissionToken.id}`); 142 return { id: permissionToken.id, error: null } as const; 143 }
+5 -9
actions/deleteLeaflet.ts
··· 1 "use server"; 2 3 - import { drizzle } from "drizzle-orm/postgres-js"; 4 import { 5 entities, 6 permission_tokens, 7 permission_token_rights, 8 } from "drizzle/schema"; 9 - import { redirect } from "next/navigation"; 10 - import postgres from "postgres"; 11 - import { v7 } from "uuid"; 12 - import { eq, sql } from "drizzle-orm"; 13 - import { cookies } from "next/headers"; 14 import { PermissionToken } from "src/replicache"; 15 - import { revalidatePath } from "next/cache"; 16 17 export async function deleteLeaflet(permission_token: PermissionToken) { 18 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 19 const db = drizzle(client); 20 await db.transaction(async (tx) => { 21 let [token] = await tx ··· 35 .delete(permission_tokens) 36 .where(eq(permission_tokens.id, permission_token.id)); 37 }); 38 - client.end(); 39 return; 40 }
··· 1 "use server"; 2 3 + import { drizzle } from "drizzle-orm/node-postgres"; 4 import { 5 entities, 6 permission_tokens, 7 permission_token_rights, 8 } from "drizzle/schema"; 9 + import { eq } from "drizzle-orm"; 10 import { PermissionToken } from "src/replicache"; 11 + import { pool } from "supabase/pool"; 12 13 export async function deleteLeaflet(permission_token: PermissionToken) { 14 + const client = await pool.connect(); 15 const db = drizzle(client); 16 await db.transaction(async (tx) => { 17 let [token] = await tx ··· 31 .delete(permission_tokens) 32 .where(eq(permission_tokens.id, permission_token.id)); 33 }); 34 + client.release(); 35 return; 36 }
+10 -9
actions/emailAuth.ts
··· 1 "use server"; 2 3 import { randomBytes } from "crypto"; 4 - import { drizzle } from "drizzle-orm/postgres-js"; 5 import postgres from "postgres"; 6 import { email_auth_tokens, identities } from "drizzle/schema"; 7 import { and, eq } from "drizzle-orm"; 8 import { cookies } from "next/headers"; 9 import { createIdentity } from "./createIdentity"; 10 import { setAuthToken } from "src/auth"; 11 12 async function sendAuthCode(email: string, code: string) { 13 if (process.env.NODE_ENV === "development") { ··· 42 43 export async function requestAuthEmailToken(emailNonNormalized: string) { 44 let email = emailNonNormalized.toLowerCase(); 45 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 46 const db = drizzle(client); 47 48 const code = randomBytes(3).toString("hex").toUpperCase(); ··· 60 61 await sendAuthCode(email, code); 62 63 - client.end(); 64 return token.id; 65 } 66 67 export async function confirmEmailAuthToken(tokenId: string, code: string) { 68 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 69 const db = drizzle(client); 70 71 const [token] = await db ··· 74 .where(eq(email_auth_tokens.id, tokenId)); 75 76 if (!token || !token.email) { 77 - client.end(); 78 return null; 79 } 80 81 if (token.confirmation_code !== code) { 82 - client.end(); 83 return null; 84 } 85 86 if (token.confirmed) { 87 - client.end(); 88 return null; 89 } 90 let authToken = (await cookies()).get("auth_token"); ··· 102 .update(identities) 103 .set({ email: token.email }) 104 .where(eq(identities.id, existingToken.identities.id)); 105 - client.end(); 106 return existingToken; 107 } 108 } ··· 135 136 await setAuthToken(confirmedToken.id); 137 138 - client.end(); 139 return confirmedToken; 140 }
··· 1 "use server"; 2 3 import { randomBytes } from "crypto"; 4 + import { drizzle } from "drizzle-orm/node-postgres"; 5 import postgres from "postgres"; 6 import { email_auth_tokens, identities } from "drizzle/schema"; 7 import { and, eq } from "drizzle-orm"; 8 import { cookies } from "next/headers"; 9 import { createIdentity } from "./createIdentity"; 10 import { setAuthToken } from "src/auth"; 11 + import { pool } from "supabase/pool"; 12 13 async function sendAuthCode(email: string, code: string) { 14 if (process.env.NODE_ENV === "development") { ··· 43 44 export async function requestAuthEmailToken(emailNonNormalized: string) { 45 let email = emailNonNormalized.toLowerCase(); 46 + const client = await pool.connect(); 47 const db = drizzle(client); 48 49 const code = randomBytes(3).toString("hex").toUpperCase(); ··· 61 62 await sendAuthCode(email, code); 63 64 + client.release(); 65 return token.id; 66 } 67 68 export async function confirmEmailAuthToken(tokenId: string, code: string) { 69 + const client = await pool.connect(); 70 const db = drizzle(client); 71 72 const [token] = await db ··· 75 .where(eq(email_auth_tokens.id, tokenId)); 76 77 if (!token || !token.email) { 78 + client.release(); 79 return null; 80 } 81 82 if (token.confirmation_code !== code) { 83 + client.release(); 84 return null; 85 } 86 87 if (token.confirmed) { 88 + client.release(); 89 return null; 90 } 91 let authToken = (await cookies()).get("auth_token"); ··· 103 .update(identities) 104 .set({ email: token.email }) 105 .where(eq(identities.id, existingToken.identities.id)); 106 + client.release(); 107 return existingToken; 108 } 109 } ··· 136 137 await setAuthToken(confirmedToken.id); 138 139 + client.release(); 140 return confirmedToken; 141 }
+5 -4
actions/get_phone_rsvp_to_event_state.ts
··· 1 "use server"; 2 3 - import { drizzle } from "drizzle-orm/postgres-js"; 4 import { and, eq } from "drizzle-orm"; 5 import postgres from "postgres"; 6 import { ··· 9 } from "drizzle/schema"; 10 import { cookies } from "next/headers"; 11 import { Database } from "supabase/database.types"; 12 13 export async function getPhoneRSVPToEventState(entityId: string) { 14 const token = (await cookies()).get("phone_auth_token"); ··· 17 return null; 18 } 19 20 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 21 const db = drizzle(client); 22 23 const [authToken] = await db ··· 26 .where(eq(phone_number_auth_tokens.id, token.value)); 27 28 if (!authToken || !authToken.confirmed) { 29 - client.end(); 30 return null; 31 } 32 ··· 40 ), 41 ); 42 43 - client.end(); 44 return rsvp; 45 }
··· 1 "use server"; 2 3 + import { drizzle } from "drizzle-orm/node-postgres"; 4 import { and, eq } from "drizzle-orm"; 5 import postgres from "postgres"; 6 import { ··· 9 } from "drizzle/schema"; 10 import { cookies } from "next/headers"; 11 import { Database } from "supabase/database.types"; 12 + import { pool } from "supabase/pool"; 13 14 export async function getPhoneRSVPToEventState(entityId: string) { 15 const token = (await cookies()).get("phone_auth_token"); ··· 18 return null; 19 } 20 21 + const client = await pool.connect(); 22 const db = drizzle(client); 23 24 const [authToken] = await db ··· 27 .where(eq(phone_number_auth_tokens.id, token.value)); 28 29 if (!authToken || !authToken.confirmed) { 30 + client.release(); 31 return null; 32 } 33 ··· 41 ), 42 ); 43 44 + client.release(); 45 return rsvp; 46 }
+4 -3
actions/login.ts
··· 1 "use server"; 2 - import { drizzle } from "drizzle-orm/postgres-js"; 3 import postgres from "postgres"; 4 import { 5 email_auth_tokens, ··· 16 import { redirect } from "next/navigation"; 17 import { v7 } from "uuid"; 18 import { createIdentity } from "./createIdentity"; 19 20 export async function loginWithEmailToken( 21 localLeaflets: { token: { id: string }; added_at: string }[], 22 ) { 23 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 24 const db = drizzle(client); 25 let token_id = (await cookies()).get("auth_token")?.value; 26 let voter_token = (await cookies()).get("poll_voter_token")?.value; ··· 115 }); 116 } 117 } 118 - client.end(); 119 }
··· 1 "use server"; 2 + import { drizzle } from "drizzle-orm/node-postgres"; 3 import postgres from "postgres"; 4 import { 5 email_auth_tokens, ··· 16 import { redirect } from "next/navigation"; 17 import { v7 } from "uuid"; 18 import { createIdentity } from "./createIdentity"; 19 + import { pool } from "supabase/pool"; 20 21 export async function loginWithEmailToken( 22 localLeaflets: { token: { id: string }; added_at: string }[], 23 ) { 24 + const client = await pool.connect(); 25 const db = drizzle(client); 26 let token_id = (await cookies()).get("auth_token")?.value; 27 let voter_token = (await cookies()).get("poll_voter_token")?.value; ··· 116 }); 117 } 118 } 119 + client.release(); 120 }
+7 -6
actions/phone_auth/confirm_phone_auth_token.ts
··· 1 "use server"; 2 3 - import { drizzle } from "drizzle-orm/postgres-js"; 4 import { and, eq } from "drizzle-orm"; 5 import postgres from "postgres"; 6 import { phone_number_auth_tokens } from "drizzle/schema"; 7 import { cookies } from "next/headers"; 8 9 export async function confirmPhoneAuthToken(tokenId: string, code: string) { 10 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 11 const db = drizzle(client); 12 13 const [token] = await db ··· 16 .where(eq(phone_number_auth_tokens.id, tokenId)); 17 18 if (!token) { 19 - client.end(); 20 throw new Error("Invalid token"); 21 } 22 23 if (token.confirmation_code !== code) { 24 - client.end(); 25 throw new Error("Invalid confirmation code"); 26 } 27 28 if (token.confirmed) { 29 - client.end(); 30 throw new Error("Token already confirmed"); 31 } 32 ··· 50 sameSite: "strict", 51 }); 52 53 - client.end(); 54 return confirmedToken; 55 }
··· 1 "use server"; 2 3 + import { drizzle } from "drizzle-orm/node-postgres"; 4 import { and, eq } from "drizzle-orm"; 5 import postgres from "postgres"; 6 import { phone_number_auth_tokens } from "drizzle/schema"; 7 import { cookies } from "next/headers"; 8 + import { pool } from "supabase/pool"; 9 10 export async function confirmPhoneAuthToken(tokenId: string, code: string) { 11 + const client = await pool.connect(); 12 const db = drizzle(client); 13 14 const [token] = await db ··· 17 .where(eq(phone_number_auth_tokens.id, tokenId)); 18 19 if (!token) { 20 + client.release(); 21 throw new Error("Invalid token"); 22 } 23 24 if (token.confirmation_code !== code) { 25 + client.release(); 26 throw new Error("Invalid confirmation code"); 27 } 28 29 if (token.confirmed) { 30 + client.release(); 31 throw new Error("Token already confirmed"); 32 } 33 ··· 51 sameSite: "strict", 52 }); 53 54 + client.release(); 55 return confirmedToken; 56 }
+4 -3
actions/phone_auth/request_phone_auth_token.ts
··· 1 "use server"; 2 3 import { randomBytes } from "crypto"; 4 - import { drizzle } from "drizzle-orm/postgres-js"; 5 import postgres from "postgres"; 6 import { phone_number_auth_tokens } from "drizzle/schema"; 7 import twilio from "twilio"; 8 9 async function sendAuthCode({ 10 country_code, ··· 46 phone_number: string; 47 country_code: string; 48 }) { 49 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 50 const db = drizzle(client); 51 52 const code = randomBytes(3).toString("hex").toUpperCase(); ··· 65 66 await sendAuthCode({ country_code, phone_number, code }); 67 68 - client.end(); 69 return token.id; 70 }
··· 1 "use server"; 2 3 import { randomBytes } from "crypto"; 4 + import { drizzle } from "drizzle-orm/node-postgres"; 5 import postgres from "postgres"; 6 import { phone_number_auth_tokens } from "drizzle/schema"; 7 import twilio from "twilio"; 8 + import { pool } from "supabase/pool"; 9 10 async function sendAuthCode({ 11 country_code, ··· 47 phone_number: string; 48 country_code: string; 49 }) { 50 + const client = await pool.connect(); 51 const db = drizzle(client); 52 53 const code = randomBytes(3).toString("hex").toUpperCase(); ··· 66 67 await sendAuthCode({ country_code, phone_number, code }); 68 69 + client.release(); 70 return token.id; 71 }
+4 -3
actions/phone_rsvp_to_event.ts
··· 1 "use server"; 2 3 - import { drizzle } from "drizzle-orm/postgres-js"; 4 import { 5 entities, 6 phone_number_auth_tokens, ··· 13 import { Database } from "supabase/database.types"; 14 import { createServerClient } from "@supabase/ssr"; 15 import { cookies } from "next/headers"; 16 17 export async function submitRSVP(args: { 18 entity: string; ··· 20 name: string; 21 plus_ones: number; 22 }) { 23 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 24 const db = drizzle(client); 25 let token = (await cookies()).get("phone_auth_token"); 26 if (!token) throw new Error("No auth token found"); ··· 58 }); 59 }); 60 61 - client.end(); 62 return { success: true }; 63 }
··· 1 "use server"; 2 3 + import { drizzle } from "drizzle-orm/node-postgres"; 4 import { 5 entities, 6 phone_number_auth_tokens, ··· 13 import { Database } from "supabase/database.types"; 14 import { createServerClient } from "@supabase/ssr"; 15 import { cookies } from "next/headers"; 16 + import { pool } from "supabase/pool"; 17 18 export async function submitRSVP(args: { 19 entity: string; ··· 21 name: string; 22 plus_ones: number; 23 }) { 24 + const client = await pool.connect(); 25 const db = drizzle(client); 26 let token = (await cookies()).get("phone_auth_token"); 27 if (!token) throw new Error("No auth token found"); ··· 59 }); 60 }); 61 62 + client.release(); 63 return { success: true }; 64 }
+4 -3
actions/removeLeafletFromHome.ts
··· 1 "use server"; 2 3 - import { drizzle } from "drizzle-orm/postgres-js"; 4 import { permission_token_on_homepage } from "drizzle/schema"; 5 import postgres from "postgres"; 6 import { v7 } from "uuid"; 7 import { sql, eq, inArray, and } from "drizzle-orm"; 8 import { cookies } from "next/headers"; 9 import { getIdentityData } from "./getIdentityData"; 10 11 export async function removeLeafletFromHome(tokens: string[]) { 12 const identity = await getIdentityData(); 13 if (!identity) return null; 14 15 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 16 const db = drizzle(client); 17 18 await db ··· 24 ), 25 ); 26 27 - client.end(); 28 return true; 29 }
··· 1 "use server"; 2 3 + import { drizzle } from "drizzle-orm/node-postgres"; 4 import { permission_token_on_homepage } from "drizzle/schema"; 5 import postgres from "postgres"; 6 import { v7 } from "uuid"; 7 import { sql, eq, inArray, and } from "drizzle-orm"; 8 import { cookies } from "next/headers"; 9 import { getIdentityData } from "./getIdentityData"; 10 + import { pool } from "supabase/pool"; 11 12 export async function removeLeafletFromHome(tokens: string[]) { 13 const identity = await getIdentityData(); 14 if (!identity) return null; 15 16 + const client = await pool.connect(); 17 const db = drizzle(client); 18 19 await db ··· 25 ), 26 ); 27 28 + client.release(); 29 return true; 30 }
+6 -11
actions/sendUpdateToRSVPS.ts
··· 1 "use server"; 2 - import { drizzle } from "drizzle-orm/postgres-js"; 3 import { eq } from "drizzle-orm"; 4 - import postgres from "postgres"; 5 import { 6 entities, 7 permission_token_rights, 8 phone_rsvps_to_entity, 9 } from "drizzle/schema"; 10 - import { createClient } from "@supabase/supabase-js"; 11 - import { Database } from "supabase/database.types"; 12 import twilio from "twilio"; 13 - 14 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 15 - let supabase = createClient<Database>( 16 - process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, 17 - process.env.SUPABASE_SERVICE_ROLE_KEY as string, 18 - ); 19 - const db = drizzle(client); 20 21 export async function sendUpdateToRSVPS( 22 token: { id: string }, ··· 34 sendto: { GOING: boolean; MAYBE: boolean; NOT_GOING: boolean }; 35 }, 36 ) { 37 let token_rights = await db 38 .select() 39 .from(permission_token_rights) ··· 44 .from(phone_rsvps_to_entity) 45 .innerJoin(entities, eq(phone_rsvps_to_entity.entity, entities.id)) 46 .where(eq(phone_rsvps_to_entity.entity, entity)); 47 48 if (!token_rights[0]?.write) return; 49 let rsvps = await RSVPS;
··· 1 "use server"; 2 + import { drizzle } from "drizzle-orm/node-postgres"; 3 import { eq } from "drizzle-orm"; 4 import { 5 entities, 6 permission_token_rights, 7 phone_rsvps_to_entity, 8 } from "drizzle/schema"; 9 import twilio from "twilio"; 10 + import { pool } from "supabase/pool"; 11 12 export async function sendUpdateToRSVPS( 13 token: { id: string }, ··· 25 sendto: { GOING: boolean; MAYBE: boolean; NOT_GOING: boolean }; 26 }, 27 ) { 28 + let dbclient = await pool.connect(); 29 + const db = drizzle(dbclient); 30 let token_rights = await db 31 .select() 32 .from(permission_token_rights) ··· 37 .from(phone_rsvps_to_entity) 38 .innerJoin(entities, eq(phone_rsvps_to_entity.entity, entities.id)) 39 .where(eq(phone_rsvps_to_entity.entity, entity)); 40 + 41 + dbclient.release(); 42 43 if (!token_rights[0]?.write) return; 44 let rsvps = await RSVPS;
+4 -3
actions/subscriptions/confirmEmailSubscription.ts
··· 2 3 import { createClient } from "@supabase/supabase-js"; 4 import { and, eq, sql } from "drizzle-orm"; 5 - import { drizzle } from "drizzle-orm/postgres-js"; 6 import { 7 email_subscriptions_to_entity, 8 facts, ··· 11 import postgres from "postgres"; 12 import type { Fact } from "src/replicache"; 13 import { Database } from "supabase/database.types"; 14 import { v7 } from "uuid"; 15 16 export async function confirmEmailSubscription( 17 subscriptionID: string, 18 code: string, 19 ) { 20 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 21 const db = drizzle(client); 22 let subscription = await db.transaction(async (tx) => { 23 let [{ email_subscriptions_to_entity: sub, permission_tokens: token }] = ··· 80 payload: { message: "poke" }, 81 }); 82 supabase.removeChannel(channel); 83 - client.end(); 84 return subscription; 85 }
··· 2 3 import { createClient } from "@supabase/supabase-js"; 4 import { and, eq, sql } from "drizzle-orm"; 5 + import { drizzle } from "drizzle-orm/node-postgres"; 6 import { 7 email_subscriptions_to_entity, 8 facts, ··· 11 import postgres from "postgres"; 12 import type { Fact } from "src/replicache"; 13 import { Database } from "supabase/database.types"; 14 + import { pool } from "supabase/pool"; 15 import { v7 } from "uuid"; 16 17 export 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 }] = ··· 81 payload: { message: "poke" }, 82 }); 83 supabase.removeChannel(channel); 84 + client.release(); 85 return subscription; 86 }
+5 -4
actions/subscriptions/deleteSubscription.ts
··· 1 "use server"; 2 3 - import { drizzle } from "drizzle-orm/postgres-js"; 4 import { email_subscriptions_to_entity, facts } from "drizzle/schema"; 5 import postgres from "postgres"; 6 import { eq, and, sql } from "drizzle-orm"; 7 import type { Fact } from "src/replicache"; 8 import { v7 } from "uuid"; 9 10 export async function deleteSubscription(subscriptionID: string) { 11 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 12 const db = drizzle(client); 13 14 try { ··· 41 .where(eq(email_subscriptions_to_entity.id, subscriptionID)); 42 }); 43 44 - client.end(); 45 return { success: true }; 46 } catch (error) { 47 console.error("Error unsubscribing:", error); 48 - client.end(); 49 return { success: false, error: "Failed to unsubscribe" }; 50 } 51 }
··· 1 "use server"; 2 3 + import { drizzle } from "drizzle-orm/node-postgres"; 4 import { email_subscriptions_to_entity, facts } from "drizzle/schema"; 5 import postgres from "postgres"; 6 import { eq, and, sql } from "drizzle-orm"; 7 import type { Fact } from "src/replicache"; 8 import { v7 } from "uuid"; 9 + import { pool } from "supabase/pool"; 10 11 export async function deleteSubscription(subscriptionID: string) { 12 + const client = await pool.connect(); 13 const db = drizzle(client); 14 15 try { ··· 42 .where(eq(email_subscriptions_to_entity.id, subscriptionID)); 43 }); 44 45 + client.release(); 46 return { success: true }; 47 } catch (error) { 48 console.error("Error unsubscribing:", error); 49 + client.release(); 50 return { success: false, error: "Failed to unsubscribe" }; 51 } 52 }
+4 -3
actions/subscriptions/sendPostToSubscribers.ts
··· 3 import { getCurrentDeploymentDomain } from "src/utils/getCurrentDeploymentDomain"; 4 import { createServerClient } from "@supabase/ssr"; 5 import { and, eq } from "drizzle-orm"; 6 - import { drizzle } from "drizzle-orm/postgres-js"; 7 import { email_subscriptions_to_entity, entities } from "drizzle/schema"; 8 import postgres from "postgres"; 9 import type { PermissionToken } from "src/replicache"; 10 import { Database } from "supabase/database.types"; 11 12 let supabase = createServerClient<Database>( 13 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, ··· 41 root: rootEntity, 42 }); 43 44 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 45 const db = drizzle(client); 46 let subscribers = await db 47 .select() ··· 95 })), 96 ), 97 }); 98 - client.end(); 99 return; 100 }
··· 3 import { getCurrentDeploymentDomain } from "src/utils/getCurrentDeploymentDomain"; 4 import { createServerClient } from "@supabase/ssr"; 5 import { and, eq } from "drizzle-orm"; 6 + import { drizzle } from "drizzle-orm/node-postgres"; 7 import { email_subscriptions_to_entity, entities } from "drizzle/schema"; 8 import postgres from "postgres"; 9 import type { PermissionToken } from "src/replicache"; 10 import { Database } from "supabase/database.types"; 11 + import { pool } from "supabase/pool"; 12 13 let supabase = createServerClient<Database>( 14 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, ··· 42 root: rootEntity, 43 }); 44 45 + const client = await pool.connect(); 46 const db = drizzle(client); 47 let subscribers = await db 48 .select() ··· 96 })), 97 ), 98 }); 99 + client.release(); 100 return; 101 }
+4 -3
actions/subscriptions/subscribeToMailboxWithEmail.ts
··· 3 import * as base64 from "base64-js"; 4 import { createServerClient } from "@supabase/ssr"; 5 import { and, eq } from "drizzle-orm"; 6 - import { drizzle } from "drizzle-orm/postgres-js"; 7 import { email_subscriptions_to_entity } from "drizzle/schema"; 8 import postgres from "postgres"; 9 import { getBlocksWithTypeLocal } from "src/hooks/queries/useBlocks"; ··· 12 import { Database } from "supabase/database.types"; 13 import * as Y from "yjs"; 14 import { YJSFragmentToString } from "components/Blocks/TextBlock/RenderYJSFragment"; 15 16 let supabase = createServerClient<Database>( 17 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, ··· 37 email: string, 38 token: PermissionToken, 39 ) { 40 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 41 const db = drizzle(client); 42 let newCode = generateCode(); 43 let subscription = await db.transaction(async (tx) => { ··· 82 `, 83 }), 84 }); 85 - client.end(); 86 return subscription; 87 } 88
··· 3 import * as base64 from "base64-js"; 4 import { createServerClient } from "@supabase/ssr"; 5 import { and, eq } from "drizzle-orm"; 6 + import { drizzle } from "drizzle-orm/node-postgres"; 7 import { email_subscriptions_to_entity } from "drizzle/schema"; 8 import postgres from "postgres"; 9 import { getBlocksWithTypeLocal } from "src/hooks/queries/useBlocks"; ··· 12 import { Database } from "supabase/database.types"; 13 import * as Y from "yjs"; 14 import { YJSFragmentToString } from "components/Blocks/TextBlock/RenderYJSFragment"; 15 + import { pool } from "supabase/pool"; 16 17 let supabase = createServerClient<Database>( 18 process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, ··· 38 email: string, 39 token: PermissionToken, 40 ) { 41 + const client = await pool.connect(); 42 const db = drizzle(client); 43 let newCode = generateCode(); 44 let subscription = await db.transaction(async (tx) => { ··· 83 `, 84 }), 85 }); 86 + client.release(); 87 return subscription; 88 } 89
+4 -5
app/api/oauth/[route]/route.ts
··· 1 import { createIdentity } from "actions/createIdentity"; 2 import { subscribeToPublication } from "app/lish/subscribeToPublication"; 3 - import { drizzle } from "drizzle-orm/postgres-js"; 4 import { cookies } from "next/headers"; 5 import { redirect } from "next/navigation"; 6 import { NextRequest, NextResponse } from "next/server"; 7 - import postgres from "postgres"; 8 import { createOauthClient } from "src/atproto-oauth"; 9 import { setAuthToken } from "src/auth"; 10 ··· 14 ActionAfterSignIn, 15 parseActionFromSearchParam, 16 } from "./afterSignInActions"; 17 18 type OauthRequestClientState = { 19 redirect: string | null; ··· 81 82 return handleAction(s.action, redirectPath); 83 } 84 - const client = postgres(process.env.DB_URL as string, { 85 - idle_timeout: 5, 86 - }); 87 const db = drizzle(client); 88 identity = await createIdentity(db, { atp_did: session.did }); 89 } 90 let { data: token } = await supabaseServerClient 91 .from("email_auth_tokens")
··· 1 import { createIdentity } from "actions/createIdentity"; 2 import { subscribeToPublication } from "app/lish/subscribeToPublication"; 3 + import { drizzle } from "drizzle-orm/node-postgres"; 4 import { cookies } from "next/headers"; 5 import { redirect } from "next/navigation"; 6 import { NextRequest, NextResponse } from "next/server"; 7 import { createOauthClient } from "src/atproto-oauth"; 8 import { setAuthToken } from "src/auth"; 9 ··· 13 ActionAfterSignIn, 14 parseActionFromSearchParam, 15 } from "./afterSignInActions"; 16 + import { pool } from "supabase/pool"; 17 18 type OauthRequestClientState = { 19 redirect: string | null; ··· 81 82 return handleAction(s.action, redirectPath); 83 } 84 + const client = await pool.connect(); 85 const db = drizzle(client); 86 identity = await createIdentity(db, { atp_did: session.did }); 87 + client.release(); 88 } 89 let { data: token } = await supabaseServerClient 90 .from("email_auth_tokens")
-2
app/api/rpc/[command]/route.ts
··· 1 - import { drizzle } from "drizzle-orm/postgres-js"; 2 import { makeRouter } from "../lib"; 3 import { push } from "./push"; 4 - import postgres from "postgres"; 5 import { createClient } from "@supabase/supabase-js"; 6 import { Database } from "supabase/database.types"; 7 import { pull } from "./pull";
··· 1 import { makeRouter } from "../lib"; 2 import { push } from "./push"; 3 import { createClient } from "@supabase/supabase-js"; 4 import { Database } from "supabase/database.types"; 5 import { pull } from "./pull";
+4 -3
app/emails/unsubscribe/route.ts
··· 1 import { NextRequest } from "next/server"; 2 - import { drizzle } from "drizzle-orm/postgres-js"; 3 import { email_subscriptions_to_entity } from "drizzle/schema"; 4 import postgres from "postgres"; 5 import { eq } from "drizzle-orm"; 6 7 export async function POST(request: NextRequest) { 8 let sub_id = request.nextUrl.searchParams.get("sub_id"); 9 if (!sub_id) return new Response(null, { status: 404 }); 10 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 11 const db = drizzle(client); 12 13 try { ··· 17 } catch (error) { 18 console.log(error); 19 } 20 - client.end(); 21 return new Response(null, { status: 200 }); 22 }
··· 1 import { NextRequest } from "next/server"; 2 + import { drizzle } from "drizzle-orm/node-postgres"; 3 import { email_subscriptions_to_entity } from "drizzle/schema"; 4 import postgres from "postgres"; 5 import { eq } from "drizzle-orm"; 6 + import { pool } from "supabase/pool"; 7 8 export async function POST(request: NextRequest) { 9 let sub_id = request.nextUrl.searchParams.get("sub_id"); 10 if (!sub_id) return new Response(null, { status: 404 }); 11 + const client = await pool.connect(); 12 const db = drizzle(client); 13 14 try { ··· 18 } catch (error) { 19 console.log(error); 20 } 21 + client.release(); 22 return new Response(null, { status: 200 }); 23 }
+4 -4
app/home/page.tsx
··· 7 } from "components/ThemeManager/ThemeProvider"; 8 import { EntitySetProvider } from "components/EntitySetProvider"; 9 import { createIdentity } from "actions/createIdentity"; 10 - import postgres from "postgres"; 11 - import { drizzle } from "drizzle-orm/postgres-js"; 12 import { IdentitySetter } from "./IdentitySetter"; 13 import { LeafletList } from "./LeafletList"; 14 import { getIdentityData } from "actions/getIdentityData"; ··· 18 import { Media } from "components/Media"; 19 import { MyPublicationList } from "./Publications"; 20 import { supabaseServerClient } from "supabase/serverClient"; 21 22 export default async function Home() { 23 let cookieStore = await cookies(); ··· 27 else identity = cookieStore.get("identity")?.value; 28 let needstosetcookie = false; 29 if (!identity) { 30 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 31 const db = drizzle(client); 32 let newIdentity = await createIdentity(db); 33 - client.end(); 34 identity = newIdentity.id; 35 needstosetcookie = true; 36 }
··· 7 } from "components/ThemeManager/ThemeProvider"; 8 import { EntitySetProvider } from "components/EntitySetProvider"; 9 import { createIdentity } from "actions/createIdentity"; 10 + import { drizzle } from "drizzle-orm/node-postgres"; 11 import { IdentitySetter } from "./IdentitySetter"; 12 import { LeafletList } from "./LeafletList"; 13 import { getIdentityData } from "actions/getIdentityData"; ··· 17 import { Media } from "components/Media"; 18 import { MyPublicationList } from "./Publications"; 19 import { supabaseServerClient } from "supabase/serverClient"; 20 + import { pool } from "supabase/pool"; 21 22 export default async function Home() { 23 let cookieStore = await cookies(); ··· 27 else identity = cookieStore.get("identity")?.value; 28 let needstosetcookie = false; 29 if (!identity) { 30 + const client = await pool.connect(); 31 const db = drizzle(client); 32 let newIdentity = await createIdentity(db); 33 + client.release(); 34 identity = newIdentity.id; 35 needstosetcookie = true; 36 }
+4 -4
appview/index.ts
··· 18 import { AtUri } from "@atproto/syntax"; 19 import { writeFile, readFile } from "fs/promises"; 20 import { createIdentity } from "actions/createIdentity"; 21 - import postgres from "postgres"; 22 - import { drizzle } from "drizzle-orm/postgres-js"; 23 import { inngest } from "app/api/inngest/client"; 24 25 const cursorFile = process.env.CURSOR_FILE || "/cursor/cursor"; 26 ··· 35 startCursor = parseInt((await readFile(cursorFile)).toString()); 36 } catch (e) {} 37 38 - const client = postgres(process.env.DB_URL!); 39 const db = drizzle(client); 40 async function handleEvent(evt: Event) { 41 if (evt.event === "identity") { ··· 257 firehose.start(); 258 const cleanup = async () => { 259 console.log("shutting down firehose..."); 260 - await client.end(); 261 await firehose.destroy(); 262 await runner.destroy(); 263 process.exit();
··· 18 import { AtUri } from "@atproto/syntax"; 19 import { writeFile, readFile } from "fs/promises"; 20 import { createIdentity } from "actions/createIdentity"; 21 + import { drizzle } from "drizzle-orm/node-postgres"; 22 import { inngest } from "app/api/inngest/client"; 23 + import { pool } from "supabase/pool"; 24 25 const cursorFile = process.env.CURSOR_FILE || "/cursor/cursor"; 26 ··· 35 startCursor = parseInt((await readFile(cursorFile)).toString()); 36 } catch (e) {} 37 38 + const client = await pool.connect(); 39 const db = drizzle(client); 40 async function handleEvent(evt: Event) { 41 if (evt.event === "identity") { ··· 257 firehose.start(); 258 const cleanup = async () => { 259 console.log("shutting down firehose..."); 260 + await client.release(); 261 await firehose.destroy(); 262 await runner.destroy(); 263 process.exit();
+4 -4
components/ShareOptions/getShareLink.ts
··· 1 "use server"; 2 3 import { eq, and } from "drizzle-orm"; 4 - import { drizzle } from "drizzle-orm/postgres-js"; 5 import { permission_token_rights, permission_tokens } from "drizzle/schema"; 6 - import postgres from "postgres"; 7 export async function getShareLink( 8 token: { id: string; entity_set: string }, 9 rootEntity: string, 10 ) { 11 - const client = postgres(process.env.DB_URL as string, { idle_timeout: 5 }); 12 const db = drizzle(client); 13 let link = await db.transaction(async (tx) => { 14 // This will likely error out when if we have multiple permission ··· 65 return newToken; 66 }); 67 68 - client.end(); 69 return link; 70 }
··· 1 "use server"; 2 3 import { eq, and } from "drizzle-orm"; 4 + import { drizzle } from "drizzle-orm/node-postgres"; 5 import { permission_token_rights, permission_tokens } from "drizzle/schema"; 6 + import { pool } from "supabase/pool"; 7 export async function getShareLink( 8 token: { id: string; entity_set: string }, 9 rootEntity: string, 10 ) { 11 + const client = await pool.connect(); 12 const db = drizzle(client); 13 let link = await db.transaction(async (tx) => { 14 // This will likely error out when if we have multiple permission ··· 65 return newToken; 66 }); 67 68 + client.release(); 69 return link; 70 }
+2 -1
package-lock.json
··· 50 "multiformats": "^13.3.2", 51 "next": "^15.5.0", 52 "pg": "^8.16.3", 53 - "postgres": "^3.4.4", 54 "prosemirror-commands": "^1.5.2", 55 "prosemirror-inputrules": "^1.4.0", 56 "prosemirror-keymap": "^1.2.2", ··· 14109 "version": "3.4.4", 14110 "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.4.tgz", 14111 "integrity": "sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==", 14112 "engines": { 14113 "node": ">=12" 14114 },
··· 50 "multiformats": "^13.3.2", 51 "next": "^15.5.0", 52 "pg": "^8.16.3", 53 "prosemirror-commands": "^1.5.2", 54 "prosemirror-inputrules": "^1.4.0", 55 "prosemirror-keymap": "^1.2.2", ··· 14108 "version": "3.4.4", 14109 "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.4.tgz", 14110 "integrity": "sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==", 14111 + "optional": true, 14112 + "peer": true, 14113 "engines": { 14114 "node": ">=12" 14115 },
-1
package.json
··· 60 "multiformats": "^13.3.2", 61 "next": "^15.5.0", 62 "pg": "^8.16.3", 63 - "postgres": "^3.4.4", 64 "prosemirror-commands": "^1.5.2", 65 "prosemirror-inputrules": "^1.4.0", 66 "prosemirror-keymap": "^1.2.2",
··· 60 "multiformats": "^13.3.2", 61 "next": "^15.5.0", 62 "pg": "^8.16.3", 63 "prosemirror-commands": "^1.5.2", 64 "prosemirror-inputrules": "^1.4.0", 65 "prosemirror-keymap": "^1.2.2",
+12
supabase/pool.ts
···
··· 1 + import { Pool } from "pg"; 2 + import { attachDatabasePool } from "@vercel/functions"; 3 + import { DbPool } from "@vercel/functions/db-connections"; 4 + 5 + export const pool = new Pool({ 6 + idleTimeoutMillis: 5000, 7 + min: 1, 8 + connectionString: process.env.DB_URL, 9 + }); 10 + 11 + // Attach the pool to ensure idle connections close before suspension 12 + attachDatabasePool(pool as DbPool);