From d742da9401176a372304794fc56f2adf700e812f Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Tue, 6 Jan 2026 16:48:55 -0500 Subject: [PATCH] create new identities via supabase --- actions/createIdentity.ts | 51 ------ actions/emailAuth.ts | 10 +- actions/login.ts | 15 +- app/api/inngest/functions/index_follows.ts | 12 +- app/api/oauth/[route]/route.ts | 15 +- appview/index.ts | 40 +---- supabase/database.types.ts | 6 +- ...0260106190000_add_site_standard_tables.sql | 161 ++++++++++++++++++ 8 files changed, 200 insertions(+), 110 deletions(-) delete mode 100644 actions/createIdentity.ts create mode 100644 supabase/migrations/20260106190000_add_site_standard_tables.sql diff --git a/actions/createIdentity.ts b/actions/createIdentity.ts deleted file mode 100644 index 5e11835a..00000000 --- a/actions/createIdentity.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { - entities, - permission_tokens, - permission_token_rights, - entity_sets, - identities, -} from "drizzle/schema"; -import { v7 } from "uuid"; -import { PgTransaction } from "drizzle-orm/pg-core"; -import { NodePgDatabase } from "drizzle-orm/node-postgres"; -import { Json } from "supabase/database.types"; - -export async function createIdentity( - db: NodePgDatabase, - data?: { email?: string; atp_did?: string }, -) { - return db.transaction(async (tx) => { - // Create a new entity set - let [entity_set] = await tx.insert(entity_sets).values({}).returning(); - // Create a root-entity - let [entity] = await tx - .insert(entities) - // And add it to that permission set - .values({ set: entity_set.id, id: v7() }) - .returning(); - //Create a new permission token - let [permissionToken] = await tx - .insert(permission_tokens) - .values({ root_entity: entity.id }) - .returning(); - //and give it all the permission on that entity set - let [rights] = await tx - .insert(permission_token_rights) - .values({ - token: permissionToken.id, - entity_set: entity_set.id, - read: true, - write: true, - create_token: true, - change_entity_set: true, - }) - .returning(); - let [identity] = await tx - .insert(identities) - .values({ home_page: permissionToken.id, ...data }) - .returning(); - return identity as Omit & { - interface_state: Json; - }; - }); -} diff --git a/actions/emailAuth.ts b/actions/emailAuth.ts index da26b75f..8fc2a21e 100644 --- a/actions/emailAuth.ts +++ b/actions/emailAuth.ts @@ -6,9 +6,9 @@ import postgres from "postgres"; import { email_auth_tokens, identities } from "drizzle/schema"; import { and, eq } from "drizzle-orm"; import { cookies } from "next/headers"; -import { createIdentity } from "./createIdentity"; import { setAuthToken } from "src/auth"; import { pool } from "supabase/pool"; +import { supabaseServerClient } from "supabase/serverClient"; async function sendAuthCode(email: string, code: string) { if (process.env.NODE_ENV === "development") { @@ -114,8 +114,12 @@ export async function confirmEmailAuthToken(tokenId: string, code: string) { .from(identities) .where(eq(identities.email, token.email)); if (!identity) { - let newIdentity = await createIdentity(db, { email: token.email }); - identityID = newIdentity.id; + const { data: newIdentity } = await supabaseServerClient + .from("identities") + .insert({ email: token.email }) + .select() + .single(); + identityID = newIdentity!.id; } else { identityID = identity.id; } diff --git a/actions/login.ts b/actions/login.ts index b19a3a91..120696b2 100644 --- a/actions/login.ts +++ b/actions/login.ts @@ -4,19 +4,14 @@ import postgres from "postgres"; import { email_auth_tokens, identities, - entity_sets, - entities, - permission_tokens, - permission_token_rights, permission_token_on_homepage, poll_votes_on_entity, } from "drizzle/schema"; import { and, eq, isNull } from "drizzle-orm"; import { cookies } from "next/headers"; import { redirect } from "next/navigation"; -import { v7 } from "uuid"; -import { createIdentity } from "./createIdentity"; import { pool } from "supabase/pool"; +import { supabaseServerClient } from "supabase/serverClient"; export async function loginWithEmailToken( localLeaflets: { token: { id: string }; added_at: string }[], @@ -77,8 +72,12 @@ export async function loginWithEmailToken( identity = existingIdentityFromCookie; } } else { - // Create a new identity - identity = await createIdentity(tx, { email: token.email }); + const { data: newIdentity } = await supabaseServerClient + .from("identities") + .insert({ email: token.email }) + .select() + .single(); + identity = newIdentity!; } } diff --git a/app/api/inngest/functions/index_follows.ts b/app/api/inngest/functions/index_follows.ts index dcba1dc3..310f42eb 100644 --- a/app/api/inngest/functions/index_follows.ts +++ b/app/api/inngest/functions/index_follows.ts @@ -1,9 +1,6 @@ import { supabaseServerClient } from "supabase/serverClient"; import { AtpAgent, AtUri } from "@atproto/api"; -import { createIdentity } from "actions/createIdentity"; -import { drizzle } from "drizzle-orm/node-postgres"; import { inngest } from "../client"; -import { pool } from "supabase/pool"; export const index_follows = inngest.createFunction( { @@ -58,10 +55,11 @@ export const index_follows = inngest.createFunction( .eq("atp_did", event.data.did) .single(); if (!exists) { - const client = await pool.connect(); - let db = drizzle(client); - let identity = await createIdentity(db, { atp_did: event.data.did }); - client.release(); + const { data: identity } = await supabaseServerClient + .from("identities") + .insert({ atp_did: event.data.did }) + .select() + .single(); return identity; } }), diff --git a/app/api/oauth/[route]/route.ts b/app/api/oauth/[route]/route.ts index b904069d..1cd296e4 100644 --- a/app/api/oauth/[route]/route.ts +++ b/app/api/oauth/[route]/route.ts @@ -1,6 +1,4 @@ -import { createIdentity } from "actions/createIdentity"; import { subscribeToPublication } from "app/lish/subscribeToPublication"; -import { drizzle } from "drizzle-orm/node-postgres"; import { cookies } from "next/headers"; import { redirect } from "next/navigation"; import { NextRequest, NextResponse } from "next/server"; @@ -13,7 +11,6 @@ import { ActionAfterSignIn, parseActionFromSearchParam, } from "./afterSignInActions"; -import { pool } from "supabase/pool"; type OauthRequestClientState = { redirect: string | null; @@ -80,15 +77,17 @@ export async function GET( return handleAction(s.action, redirectPath); } - const client = await pool.connect(); - const db = drizzle(client); - identity = await createIdentity(db, { atp_did: session.did }); - client.release(); + const { data } = await supabaseServerClient + .from("identities") + .insert({ atp_did: session.did }) + .select() + .single(); + identity = data; } let { data: token } = await supabaseServerClient .from("email_auth_tokens") .insert({ - identity: identity.id, + identity: identity!.id, confirmed: true, confirmation_code: "", }) diff --git a/appview/index.ts b/appview/index.ts index 269f1309..25c38037 100644 --- a/appview/index.ts +++ b/appview/index.ts @@ -20,10 +20,7 @@ import { } from "@atproto/api"; import { AtUri } from "@atproto/syntax"; import { writeFile, readFile } from "fs/promises"; -import { createIdentity } from "actions/createIdentity"; -import { drizzle } from "drizzle-orm/node-postgres"; import { inngest } from "app/api/inngest/client"; -import { Client } from "pg"; const cursorFile = process.env.CURSOR_FILE || "/cursor/cursor"; @@ -135,26 +132,15 @@ async function handleEvent(evt: Event) { if (evt.event === "create" || evt.event === "update") { let record = PubLeafletPublication.validateRecord(evt.record); if (!record.success) return; - let { error } = await supabase.from("publications").upsert({ + await supabase + .from("identities") + .upsert({ atp_did: evt.did }, { onConflict: "atp_did" }); + await supabase.from("publications").upsert({ uri: evt.uri.toString(), identity_did: evt.did, name: record.value.name, record: record.value as Json, }); - - if (error && error.code === "23503") { - console.log("creating identity"); - let client = new Client({ connectionString: process.env.DB_URL }); - let db = drizzle(client); - await createIdentity(db, { atp_did: evt.did }); - client.end(); - await supabase.from("publications").upsert({ - uri: evt.uri.toString(), - identity_did: evt.did, - name: record.value.name, - record: record.value as Json, - }); - } } if (evt.event === "delete") { await supabase @@ -222,25 +208,15 @@ async function handleEvent(evt: Event) { if (evt.event === "create" || evt.event === "update") { let record = PubLeafletGraphSubscription.validateRecord(evt.record); if (!record.success) return; - let { error } = await supabase.from("publication_subscriptions").upsert({ + await supabase + .from("identities") + .upsert({ atp_did: evt.did }, { onConflict: "atp_did" }); + await supabase.from("publication_subscriptions").upsert({ uri: evt.uri.toString(), identity: evt.did, publication: record.value.publication, record: record.value as Json, }); - if (error && error.code === "23503") { - console.log("creating identity"); - let client = new Client({ connectionString: process.env.DB_URL }); - let db = drizzle(client); - await createIdentity(db, { atp_did: evt.did }); - client.end(); - await supabase.from("publication_subscriptions").upsert({ - uri: evt.uri.toString(), - identity: evt.did, - publication: record.value.publication, - record: record.value as Json, - }); - } } if (evt.event === "delete") { await supabase diff --git a/supabase/database.types.ts b/supabase/database.types.ts index d7772b5f..e9197185 100644 --- a/supabase/database.types.ts +++ b/supabase/database.types.ts @@ -556,7 +556,7 @@ export type Database = { atp_did?: string | null created_at?: string email?: string | null - home_page: string + home_page?: string id?: string interface_state?: Json | null } @@ -1118,6 +1118,10 @@ export type Database = { [_ in never]: never } Functions: { + create_identity_homepage: { + Args: Record + Returns: string + } get_facts: { Args: { root: string diff --git a/supabase/migrations/20260106190000_add_site_standard_tables.sql b/supabase/migrations/20260106190000_add_site_standard_tables.sql new file mode 100644 index 00000000..e13668e3 --- /dev/null +++ b/supabase/migrations/20260106190000_add_site_standard_tables.sql @@ -0,0 +1,161 @@ +-- site_standard_publications table (modeled off publications) +create table "public"."site_standard_publications" ( + "uri" text not null, + "data" jsonb not null, + "indexed_at" timestamp with time zone not null default now(), + "identity_did" text not null +); +alter table "public"."site_standard_publications" enable row level security; + +-- site_standard_documents table (modeled off documents) +create table "public"."site_standard_documents" ( + "uri" text not null, + "data" jsonb not null, + "indexed_at" timestamp with time zone not null default now(), + "identity_did" text not null +); +alter table "public"."site_standard_documents" enable row level security; + +-- site_standard_documents_in_publications relation table (modeled off documents_in_publications) +create table "public"."site_standard_documents_in_publications" ( + "publication" text not null, + "document" text not null, + "indexed_at" timestamp with time zone not null default now() +); +alter table "public"."site_standard_documents_in_publications" enable row level security; + +-- Primary key indexes +CREATE UNIQUE INDEX site_standard_publications_pkey ON public.site_standard_publications USING btree (uri); +CREATE UNIQUE INDEX site_standard_documents_pkey ON public.site_standard_documents USING btree (uri); +CREATE UNIQUE INDEX site_standard_documents_in_publications_pkey ON public.site_standard_documents_in_publications USING btree (publication, document); + +-- Add primary key constraints +alter table "public"."site_standard_publications" add constraint "site_standard_publications_pkey" PRIMARY KEY using index "site_standard_publications_pkey"; +alter table "public"."site_standard_documents" add constraint "site_standard_documents_pkey" PRIMARY KEY using index "site_standard_documents_pkey"; +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"; + +-- Foreign key constraints for identity relations +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; +alter table "public"."site_standard_publications" validate constraint "site_standard_publications_identity_did_fkey"; +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; +alter table "public"."site_standard_documents" validate constraint "site_standard_documents_identity_did_fkey"; + +-- Foreign key constraints for relation table +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; +alter table "public"."site_standard_documents_in_publications" validate constraint "site_standard_documents_in_publications_document_fkey"; +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; +alter table "public"."site_standard_documents_in_publications" validate constraint "site_standard_documents_in_publications_publication_fkey"; + +-- Grants for site_standard_publications +grant delete on table "public"."site_standard_publications" to "anon"; +grant insert on table "public"."site_standard_publications" to "anon"; +grant references on table "public"."site_standard_publications" to "anon"; +grant select on table "public"."site_standard_publications" to "anon"; +grant trigger on table "public"."site_standard_publications" to "anon"; +grant truncate on table "public"."site_standard_publications" to "anon"; +grant update on table "public"."site_standard_publications" to "anon"; +grant delete on table "public"."site_standard_publications" to "authenticated"; +grant insert on table "public"."site_standard_publications" to "authenticated"; +grant references on table "public"."site_standard_publications" to "authenticated"; +grant select on table "public"."site_standard_publications" to "authenticated"; +grant trigger on table "public"."site_standard_publications" to "authenticated"; +grant truncate on table "public"."site_standard_publications" to "authenticated"; +grant update on table "public"."site_standard_publications" to "authenticated"; +grant delete on table "public"."site_standard_publications" to "service_role"; +grant insert on table "public"."site_standard_publications" to "service_role"; +grant references on table "public"."site_standard_publications" to "service_role"; +grant select on table "public"."site_standard_publications" to "service_role"; +grant trigger on table "public"."site_standard_publications" to "service_role"; +grant truncate on table "public"."site_standard_publications" to "service_role"; +grant update on table "public"."site_standard_publications" to "service_role"; + +-- Grants for site_standard_documents +grant delete on table "public"."site_standard_documents" to "anon"; +grant insert on table "public"."site_standard_documents" to "anon"; +grant references on table "public"."site_standard_documents" to "anon"; +grant select on table "public"."site_standard_documents" to "anon"; +grant trigger on table "public"."site_standard_documents" to "anon"; +grant truncate on table "public"."site_standard_documents" to "anon"; +grant update on table "public"."site_standard_documents" to "anon"; +grant delete on table "public"."site_standard_documents" to "authenticated"; +grant insert on table "public"."site_standard_documents" to "authenticated"; +grant references on table "public"."site_standard_documents" to "authenticated"; +grant select on table "public"."site_standard_documents" to "authenticated"; +grant trigger on table "public"."site_standard_documents" to "authenticated"; +grant truncate on table "public"."site_standard_documents" to "authenticated"; +grant update on table "public"."site_standard_documents" to "authenticated"; +grant delete on table "public"."site_standard_documents" to "service_role"; +grant insert on table "public"."site_standard_documents" to "service_role"; +grant references on table "public"."site_standard_documents" to "service_role"; +grant select on table "public"."site_standard_documents" to "service_role"; +grant trigger on table "public"."site_standard_documents" to "service_role"; +grant truncate on table "public"."site_standard_documents" to "service_role"; +grant update on table "public"."site_standard_documents" to "service_role"; + +-- Grants for site_standard_documents_in_publications +grant delete on table "public"."site_standard_documents_in_publications" to "anon"; +grant insert on table "public"."site_standard_documents_in_publications" to "anon"; +grant references on table "public"."site_standard_documents_in_publications" to "anon"; +grant select on table "public"."site_standard_documents_in_publications" to "anon"; +grant trigger on table "public"."site_standard_documents_in_publications" to "anon"; +grant truncate on table "public"."site_standard_documents_in_publications" to "anon"; +grant update on table "public"."site_standard_documents_in_publications" to "anon"; +grant delete on table "public"."site_standard_documents_in_publications" to "authenticated"; +grant insert on table "public"."site_standard_documents_in_publications" to "authenticated"; +grant references on table "public"."site_standard_documents_in_publications" to "authenticated"; +grant select on table "public"."site_standard_documents_in_publications" to "authenticated"; +grant trigger on table "public"."site_standard_documents_in_publications" to "authenticated"; +grant truncate on table "public"."site_standard_documents_in_publications" to "authenticated"; +grant update on table "public"."site_standard_documents_in_publications" to "authenticated"; +grant delete on table "public"."site_standard_documents_in_publications" to "service_role"; +grant insert on table "public"."site_standard_documents_in_publications" to "service_role"; +grant references on table "public"."site_standard_documents_in_publications" to "service_role"; +grant select on table "public"."site_standard_documents_in_publications" to "service_role"; +grant trigger on table "public"."site_standard_documents_in_publications" to "service_role"; +grant truncate on table "public"."site_standard_documents_in_publications" to "service_role"; +grant update on table "public"."site_standard_documents_in_publications" to "service_role"; + +-- site_standard_subscriptions table (modeled off publication_subscriptions) +create table "public"."site_standard_subscriptions" ( + "publication" text not null, + "identity" text not null, + "created_at" timestamp with time zone not null default now(), + "record" jsonb not null, + "uri" text not null +); +alter table "public"."site_standard_subscriptions" enable row level security; + +-- Primary key and unique indexes +CREATE UNIQUE INDEX site_standard_subscriptions_pkey ON public.site_standard_subscriptions USING btree (publication, identity); +CREATE UNIQUE INDEX site_standard_subscriptions_uri_key ON public.site_standard_subscriptions USING btree (uri); + +-- Add constraints +alter table "public"."site_standard_subscriptions" add constraint "site_standard_subscriptions_pkey" PRIMARY KEY using index "site_standard_subscriptions_pkey"; +alter table "public"."site_standard_subscriptions" add constraint "site_standard_subscriptions_uri_key" UNIQUE using index "site_standard_subscriptions_uri_key"; +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; +alter table "public"."site_standard_subscriptions" validate constraint "site_standard_subscriptions_publication_fkey"; +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; +alter table "public"."site_standard_subscriptions" validate constraint "site_standard_subscriptions_identity_fkey"; + +-- Grants for site_standard_subscriptions +grant delete on table "public"."site_standard_subscriptions" to "anon"; +grant insert on table "public"."site_standard_subscriptions" to "anon"; +grant references on table "public"."site_standard_subscriptions" to "anon"; +grant select on table "public"."site_standard_subscriptions" to "anon"; +grant trigger on table "public"."site_standard_subscriptions" to "anon"; +grant truncate on table "public"."site_standard_subscriptions" to "anon"; +grant update on table "public"."site_standard_subscriptions" to "anon"; +grant delete on table "public"."site_standard_subscriptions" to "authenticated"; +grant insert on table "public"."site_standard_subscriptions" to "authenticated"; +grant references on table "public"."site_standard_subscriptions" to "authenticated"; +grant select on table "public"."site_standard_subscriptions" to "authenticated"; +grant trigger on table "public"."site_standard_subscriptions" to "authenticated"; +grant truncate on table "public"."site_standard_subscriptions" to "authenticated"; +grant update on table "public"."site_standard_subscriptions" to "authenticated"; +grant delete on table "public"."site_standard_subscriptions" to "service_role"; +grant insert on table "public"."site_standard_subscriptions" to "service_role"; +grant references on table "public"."site_standard_subscriptions" to "service_role"; +grant select on table "public"."site_standard_subscriptions" to "service_role"; +grant trigger on table "public"."site_standard_subscriptions" to "service_role"; +grant truncate on table "public"."site_standard_subscriptions" to "service_role"; +grant update on table "public"."site_standard_subscriptions" to "service_role"; -- 2.51.2