a tool for shared writing and social publishing

create new identities via supabase

+200 -110
-51
actions/createIdentity.ts
··· 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 - import { Json } from "supabase/database.types"; 12 - 13 - export async function createIdentity( 14 - db: NodePgDatabase, 15 - data?: { email?: string; atp_did?: string }, 16 - ) { 17 - return db.transaction(async (tx) => { 18 - // Create a new entity set 19 - let [entity_set] = await tx.insert(entity_sets).values({}).returning(); 20 - // Create a root-entity 21 - let [entity] = await tx 22 - .insert(entities) 23 - // And add it to that permission set 24 - .values({ set: entity_set.id, id: v7() }) 25 - .returning(); 26 - //Create a new permission token 27 - let [permissionToken] = await tx 28 - .insert(permission_tokens) 29 - .values({ root_entity: entity.id }) 30 - .returning(); 31 - //and give it all the permission on that entity set 32 - let [rights] = await tx 33 - .insert(permission_token_rights) 34 - .values({ 35 - token: permissionToken.id, 36 - entity_set: entity_set.id, 37 - read: true, 38 - write: true, 39 - create_token: true, 40 - change_entity_set: true, 41 - }) 42 - .returning(); 43 - let [identity] = await tx 44 - .insert(identities) 45 - .values({ home_page: permissionToken.id, ...data }) 46 - .returning(); 47 - return identity as Omit<typeof identity, "interface_state"> & { 48 - interface_state: Json; 49 - }; 50 - }); 51 - }
+7 -3
actions/emailAuth.ts
··· 6 6 import { email_auth_tokens, identities } from "drizzle/schema"; 7 7 import { and, eq } from "drizzle-orm"; 8 8 import { cookies } from "next/headers"; 9 - import { createIdentity } from "./createIdentity"; 10 9 import { setAuthToken } from "src/auth"; 11 10 import { pool } from "supabase/pool"; 11 + import { supabaseServerClient } from "supabase/serverClient"; 12 12 13 13 async function sendAuthCode(email: string, code: string) { 14 14 if (process.env.NODE_ENV === "development") { ··· 114 114 .from(identities) 115 115 .where(eq(identities.email, token.email)); 116 116 if (!identity) { 117 - let newIdentity = await createIdentity(db, { email: token.email }); 118 - identityID = newIdentity.id; 117 + const { data: newIdentity } = await supabaseServerClient 118 + .from("identities") 119 + .insert({ email: token.email }) 120 + .select() 121 + .single(); 122 + identityID = newIdentity!.id; 119 123 } else { 120 124 identityID = identity.id; 121 125 }
+7 -8
actions/login.ts
··· 4 4 import { 5 5 email_auth_tokens, 6 6 identities, 7 - entity_sets, 8 - entities, 9 - permission_tokens, 10 - permission_token_rights, 11 7 permission_token_on_homepage, 12 8 poll_votes_on_entity, 13 9 } from "drizzle/schema"; 14 10 import { and, eq, isNull } from "drizzle-orm"; 15 11 import { cookies } from "next/headers"; 16 12 import { redirect } from "next/navigation"; 17 - import { v7 } from "uuid"; 18 - import { createIdentity } from "./createIdentity"; 19 13 import { pool } from "supabase/pool"; 14 + import { supabaseServerClient } from "supabase/serverClient"; 20 15 21 16 export async function loginWithEmailToken( 22 17 localLeaflets: { token: { id: string }; added_at: string }[], ··· 77 72 identity = existingIdentityFromCookie; 78 73 } 79 74 } else { 80 - // Create a new identity 81 - identity = await createIdentity(tx, { email: token.email }); 75 + const { data: newIdentity } = await supabaseServerClient 76 + .from("identities") 77 + .insert({ email: token.email }) 78 + .select() 79 + .single(); 80 + identity = newIdentity!; 82 81 } 83 82 } 84 83
+5 -7
app/api/inngest/functions/index_follows.ts
··· 1 1 import { supabaseServerClient } from "supabase/serverClient"; 2 2 import { AtpAgent, AtUri } from "@atproto/api"; 3 - import { createIdentity } from "actions/createIdentity"; 4 - import { drizzle } from "drizzle-orm/node-postgres"; 5 3 import { inngest } from "../client"; 6 - import { pool } from "supabase/pool"; 7 4 8 5 export const index_follows = inngest.createFunction( 9 6 { ··· 58 55 .eq("atp_did", event.data.did) 59 56 .single(); 60 57 if (!exists) { 61 - const client = await pool.connect(); 62 - let db = drizzle(client); 63 - let identity = await createIdentity(db, { atp_did: event.data.did }); 64 - client.release(); 58 + const { data: identity } = await supabaseServerClient 59 + .from("identities") 60 + .insert({ atp_did: event.data.did }) 61 + .select() 62 + .single(); 65 63 return identity; 66 64 } 67 65 }),
+7 -8
app/api/oauth/[route]/route.ts
··· 1 - import { createIdentity } from "actions/createIdentity"; 2 1 import { subscribeToPublication } from "app/lish/subscribeToPublication"; 3 - import { drizzle } from "drizzle-orm/node-postgres"; 4 2 import { cookies } from "next/headers"; 5 3 import { redirect } from "next/navigation"; 6 4 import { NextRequest, NextResponse } from "next/server"; ··· 13 11 ActionAfterSignIn, 14 12 parseActionFromSearchParam, 15 13 } from "./afterSignInActions"; 16 - import { pool } from "supabase/pool"; 17 14 18 15 type OauthRequestClientState = { 19 16 redirect: string | null; ··· 80 77 81 78 return handleAction(s.action, redirectPath); 82 79 } 83 - const client = await pool.connect(); 84 - const db = drizzle(client); 85 - identity = await createIdentity(db, { atp_did: session.did }); 86 - client.release(); 80 + const { data } = await supabaseServerClient 81 + .from("identities") 82 + .insert({ atp_did: session.did }) 83 + .select() 84 + .single(); 85 + identity = data; 87 86 } 88 87 let { data: token } = await supabaseServerClient 89 88 .from("email_auth_tokens") 90 89 .insert({ 91 - identity: identity.id, 90 + identity: identity!.id, 92 91 confirmed: true, 93 92 confirmation_code: "", 94 93 })
+8 -32
appview/index.ts
··· 20 20 } from "@atproto/api"; 21 21 import { AtUri } from "@atproto/syntax"; 22 22 import { writeFile, readFile } from "fs/promises"; 23 - import { createIdentity } from "actions/createIdentity"; 24 - import { drizzle } from "drizzle-orm/node-postgres"; 25 23 import { inngest } from "app/api/inngest/client"; 26 - import { Client } from "pg"; 27 24 28 25 const cursorFile = process.env.CURSOR_FILE || "/cursor/cursor"; 29 26 ··· 135 132 if (evt.event === "create" || evt.event === "update") { 136 133 let record = PubLeafletPublication.validateRecord(evt.record); 137 134 if (!record.success) return; 138 - let { error } = await supabase.from("publications").upsert({ 135 + await supabase 136 + .from("identities") 137 + .upsert({ atp_did: evt.did }, { onConflict: "atp_did" }); 138 + await supabase.from("publications").upsert({ 139 139 uri: evt.uri.toString(), 140 140 identity_did: evt.did, 141 141 name: record.value.name, 142 142 record: record.value as Json, 143 143 }); 144 - 145 - if (error && error.code === "23503") { 146 - console.log("creating identity"); 147 - let client = new Client({ connectionString: process.env.DB_URL }); 148 - let db = drizzle(client); 149 - await createIdentity(db, { atp_did: evt.did }); 150 - client.end(); 151 - await supabase.from("publications").upsert({ 152 - uri: evt.uri.toString(), 153 - identity_did: evt.did, 154 - name: record.value.name, 155 - record: record.value as Json, 156 - }); 157 - } 158 144 } 159 145 if (evt.event === "delete") { 160 146 await supabase ··· 222 208 if (evt.event === "create" || evt.event === "update") { 223 209 let record = PubLeafletGraphSubscription.validateRecord(evt.record); 224 210 if (!record.success) return; 225 - let { error } = await supabase.from("publication_subscriptions").upsert({ 211 + await supabase 212 + .from("identities") 213 + .upsert({ atp_did: evt.did }, { onConflict: "atp_did" }); 214 + await supabase.from("publication_subscriptions").upsert({ 226 215 uri: evt.uri.toString(), 227 216 identity: evt.did, 228 217 publication: record.value.publication, 229 218 record: record.value as Json, 230 219 }); 231 - if (error && error.code === "23503") { 232 - console.log("creating identity"); 233 - let client = new Client({ connectionString: process.env.DB_URL }); 234 - let db = drizzle(client); 235 - await createIdentity(db, { atp_did: evt.did }); 236 - client.end(); 237 - await supabase.from("publication_subscriptions").upsert({ 238 - uri: evt.uri.toString(), 239 - identity: evt.did, 240 - publication: record.value.publication, 241 - record: record.value as Json, 242 - }); 243 - } 244 220 } 245 221 if (evt.event === "delete") { 246 222 await supabase
+5 -1
supabase/database.types.ts
··· 556 556 atp_did?: string | null 557 557 created_at?: string 558 558 email?: string | null 559 - home_page: string 559 + home_page?: string 560 560 id?: string 561 561 interface_state?: Json | null 562 562 } ··· 1118 1118 [_ in never]: never 1119 1119 } 1120 1120 Functions: { 1121 + create_identity_homepage: { 1122 + Args: Record<PropertyKey, never> 1123 + Returns: string 1124 + } 1121 1125 get_facts: { 1122 1126 Args: { 1123 1127 root: string
+161
supabase/migrations/20260106190000_add_site_standard_tables.sql
··· 1 + -- site_standard_publications table (modeled off publications) 2 + create table "public"."site_standard_publications" ( 3 + "uri" text not null, 4 + "data" jsonb not null, 5 + "indexed_at" timestamp with time zone not null default now(), 6 + "identity_did" text not null 7 + ); 8 + alter table "public"."site_standard_publications" enable row level security; 9 + 10 + -- site_standard_documents table (modeled off documents) 11 + create table "public"."site_standard_documents" ( 12 + "uri" text not null, 13 + "data" jsonb not null, 14 + "indexed_at" timestamp with time zone not null default now(), 15 + "identity_did" text not null 16 + ); 17 + alter table "public"."site_standard_documents" enable row level security; 18 + 19 + -- site_standard_documents_in_publications relation table (modeled off documents_in_publications) 20 + create table "public"."site_standard_documents_in_publications" ( 21 + "publication" text not null, 22 + "document" text not null, 23 + "indexed_at" timestamp with time zone not null default now() 24 + ); 25 + alter table "public"."site_standard_documents_in_publications" enable row level security; 26 + 27 + -- Primary key indexes 28 + CREATE UNIQUE INDEX site_standard_publications_pkey ON public.site_standard_publications USING btree (uri); 29 + CREATE UNIQUE INDEX site_standard_documents_pkey ON public.site_standard_documents USING btree (uri); 30 + CREATE UNIQUE INDEX site_standard_documents_in_publications_pkey ON public.site_standard_documents_in_publications USING btree (publication, document); 31 + 32 + -- Add primary key constraints 33 + alter table "public"."site_standard_publications" add constraint "site_standard_publications_pkey" PRIMARY KEY using index "site_standard_publications_pkey"; 34 + alter table "public"."site_standard_documents" add constraint "site_standard_documents_pkey" PRIMARY KEY using index "site_standard_documents_pkey"; 35 + alter table "public"."site_standard_documents_in_publications" add constraint "site_standard_documents_in_publications_pkey" PRIMARY KEY using index "site_standard_documents_in_publications_pkey"; 36 + 37 + -- Foreign key constraints for identity relations 38 + alter table "public"."site_standard_publications" add constraint "site_standard_publications_identity_did_fkey" FOREIGN KEY (identity_did) REFERENCES identities(atp_did) ON DELETE CASCADE not valid; 39 + alter table "public"."site_standard_publications" validate constraint "site_standard_publications_identity_did_fkey"; 40 + alter table "public"."site_standard_documents" add constraint "site_standard_documents_identity_did_fkey" FOREIGN KEY (identity_did) REFERENCES identities(atp_did) ON DELETE CASCADE not valid; 41 + alter table "public"."site_standard_documents" validate constraint "site_standard_documents_identity_did_fkey"; 42 + 43 + -- Foreign key constraints for relation table 44 + alter table "public"."site_standard_documents_in_publications" add constraint "site_standard_documents_in_publications_document_fkey" FOREIGN KEY (document) REFERENCES site_standard_documents(uri) ON DELETE CASCADE not valid; 45 + alter table "public"."site_standard_documents_in_publications" validate constraint "site_standard_documents_in_publications_document_fkey"; 46 + alter table "public"."site_standard_documents_in_publications" add constraint "site_standard_documents_in_publications_publication_fkey" FOREIGN KEY (publication) REFERENCES site_standard_publications(uri) ON DELETE CASCADE not valid; 47 + alter table "public"."site_standard_documents_in_publications" validate constraint "site_standard_documents_in_publications_publication_fkey"; 48 + 49 + -- Grants for site_standard_publications 50 + grant delete on table "public"."site_standard_publications" to "anon"; 51 + grant insert on table "public"."site_standard_publications" to "anon"; 52 + grant references on table "public"."site_standard_publications" to "anon"; 53 + grant select on table "public"."site_standard_publications" to "anon"; 54 + grant trigger on table "public"."site_standard_publications" to "anon"; 55 + grant truncate on table "public"."site_standard_publications" to "anon"; 56 + grant update on table "public"."site_standard_publications" to "anon"; 57 + grant delete on table "public"."site_standard_publications" to "authenticated"; 58 + grant insert on table "public"."site_standard_publications" to "authenticated"; 59 + grant references on table "public"."site_standard_publications" to "authenticated"; 60 + grant select on table "public"."site_standard_publications" to "authenticated"; 61 + grant trigger on table "public"."site_standard_publications" to "authenticated"; 62 + grant truncate on table "public"."site_standard_publications" to "authenticated"; 63 + grant update on table "public"."site_standard_publications" to "authenticated"; 64 + grant delete on table "public"."site_standard_publications" to "service_role"; 65 + grant insert on table "public"."site_standard_publications" to "service_role"; 66 + grant references on table "public"."site_standard_publications" to "service_role"; 67 + grant select on table "public"."site_standard_publications" to "service_role"; 68 + grant trigger on table "public"."site_standard_publications" to "service_role"; 69 + grant truncate on table "public"."site_standard_publications" to "service_role"; 70 + grant update on table "public"."site_standard_publications" to "service_role"; 71 + 72 + -- Grants for site_standard_documents 73 + grant delete on table "public"."site_standard_documents" to "anon"; 74 + grant insert on table "public"."site_standard_documents" to "anon"; 75 + grant references on table "public"."site_standard_documents" to "anon"; 76 + grant select on table "public"."site_standard_documents" to "anon"; 77 + grant trigger on table "public"."site_standard_documents" to "anon"; 78 + grant truncate on table "public"."site_standard_documents" to "anon"; 79 + grant update on table "public"."site_standard_documents" to "anon"; 80 + grant delete on table "public"."site_standard_documents" to "authenticated"; 81 + grant insert on table "public"."site_standard_documents" to "authenticated"; 82 + grant references on table "public"."site_standard_documents" to "authenticated"; 83 + grant select on table "public"."site_standard_documents" to "authenticated"; 84 + grant trigger on table "public"."site_standard_documents" to "authenticated"; 85 + grant truncate on table "public"."site_standard_documents" to "authenticated"; 86 + grant update on table "public"."site_standard_documents" to "authenticated"; 87 + grant delete on table "public"."site_standard_documents" to "service_role"; 88 + grant insert on table "public"."site_standard_documents" to "service_role"; 89 + grant references on table "public"."site_standard_documents" to "service_role"; 90 + grant select on table "public"."site_standard_documents" to "service_role"; 91 + grant trigger on table "public"."site_standard_documents" to "service_role"; 92 + grant truncate on table "public"."site_standard_documents" to "service_role"; 93 + grant update on table "public"."site_standard_documents" to "service_role"; 94 + 95 + -- Grants for site_standard_documents_in_publications 96 + grant delete on table "public"."site_standard_documents_in_publications" to "anon"; 97 + grant insert on table "public"."site_standard_documents_in_publications" to "anon"; 98 + grant references on table "public"."site_standard_documents_in_publications" to "anon"; 99 + grant select on table "public"."site_standard_documents_in_publications" to "anon"; 100 + grant trigger on table "public"."site_standard_documents_in_publications" to "anon"; 101 + grant truncate on table "public"."site_standard_documents_in_publications" to "anon"; 102 + grant update on table "public"."site_standard_documents_in_publications" to "anon"; 103 + grant delete on table "public"."site_standard_documents_in_publications" to "authenticated"; 104 + grant insert on table "public"."site_standard_documents_in_publications" to "authenticated"; 105 + grant references on table "public"."site_standard_documents_in_publications" to "authenticated"; 106 + grant select on table "public"."site_standard_documents_in_publications" to "authenticated"; 107 + grant trigger on table "public"."site_standard_documents_in_publications" to "authenticated"; 108 + grant truncate on table "public"."site_standard_documents_in_publications" to "authenticated"; 109 + grant update on table "public"."site_standard_documents_in_publications" to "authenticated"; 110 + grant delete on table "public"."site_standard_documents_in_publications" to "service_role"; 111 + grant insert on table "public"."site_standard_documents_in_publications" to "service_role"; 112 + grant references on table "public"."site_standard_documents_in_publications" to "service_role"; 113 + grant select on table "public"."site_standard_documents_in_publications" to "service_role"; 114 + grant trigger on table "public"."site_standard_documents_in_publications" to "service_role"; 115 + grant truncate on table "public"."site_standard_documents_in_publications" to "service_role"; 116 + grant update on table "public"."site_standard_documents_in_publications" to "service_role"; 117 + 118 + -- site_standard_subscriptions table (modeled off publication_subscriptions) 119 + create table "public"."site_standard_subscriptions" ( 120 + "publication" text not null, 121 + "identity" text not null, 122 + "created_at" timestamp with time zone not null default now(), 123 + "record" jsonb not null, 124 + "uri" text not null 125 + ); 126 + alter table "public"."site_standard_subscriptions" enable row level security; 127 + 128 + -- Primary key and unique indexes 129 + CREATE UNIQUE INDEX site_standard_subscriptions_pkey ON public.site_standard_subscriptions USING btree (publication, identity); 130 + CREATE UNIQUE INDEX site_standard_subscriptions_uri_key ON public.site_standard_subscriptions USING btree (uri); 131 + 132 + -- Add constraints 133 + alter table "public"."site_standard_subscriptions" add constraint "site_standard_subscriptions_pkey" PRIMARY KEY using index "site_standard_subscriptions_pkey"; 134 + alter table "public"."site_standard_subscriptions" add constraint "site_standard_subscriptions_uri_key" UNIQUE using index "site_standard_subscriptions_uri_key"; 135 + alter table "public"."site_standard_subscriptions" add constraint "site_standard_subscriptions_publication_fkey" FOREIGN KEY (publication) REFERENCES site_standard_publications(uri) ON DELETE CASCADE not valid; 136 + alter table "public"."site_standard_subscriptions" validate constraint "site_standard_subscriptions_publication_fkey"; 137 + alter table "public"."site_standard_subscriptions" add constraint "site_standard_subscriptions_identity_fkey" FOREIGN KEY (identity) REFERENCES identities(atp_did) ON DELETE CASCADE not valid; 138 + alter table "public"."site_standard_subscriptions" validate constraint "site_standard_subscriptions_identity_fkey"; 139 + 140 + -- Grants for site_standard_subscriptions 141 + grant delete on table "public"."site_standard_subscriptions" to "anon"; 142 + grant insert on table "public"."site_standard_subscriptions" to "anon"; 143 + grant references on table "public"."site_standard_subscriptions" to "anon"; 144 + grant select on table "public"."site_standard_subscriptions" to "anon"; 145 + grant trigger on table "public"."site_standard_subscriptions" to "anon"; 146 + grant truncate on table "public"."site_standard_subscriptions" to "anon"; 147 + grant update on table "public"."site_standard_subscriptions" to "anon"; 148 + grant delete on table "public"."site_standard_subscriptions" to "authenticated"; 149 + grant insert on table "public"."site_standard_subscriptions" to "authenticated"; 150 + grant references on table "public"."site_standard_subscriptions" to "authenticated"; 151 + grant select on table "public"."site_standard_subscriptions" to "authenticated"; 152 + grant trigger on table "public"."site_standard_subscriptions" to "authenticated"; 153 + grant truncate on table "public"."site_standard_subscriptions" to "authenticated"; 154 + grant update on table "public"."site_standard_subscriptions" to "authenticated"; 155 + grant delete on table "public"."site_standard_subscriptions" to "service_role"; 156 + grant insert on table "public"."site_standard_subscriptions" to "service_role"; 157 + grant references on table "public"."site_standard_subscriptions" to "service_role"; 158 + grant select on table "public"."site_standard_subscriptions" to "service_role"; 159 + grant trigger on table "public"."site_standard_subscriptions" to "service_role"; 160 + grant truncate on table "public"."site_standard_subscriptions" to "service_role"; 161 + grant update on table "public"."site_standard_subscriptions" to "service_role";