a tool for shared writing and social publishing

handle migrating users

+46 -4
+31 -4
app/api/inngest/functions/migrate_user_to_standard.ts
··· 44 44 }; 45 45 46 46 // Step 1: Verify OAuth session is valid 47 - await step.run("verify-oauth-session", async () => { 47 + const oauthValid = await step.run("verify-oauth-session", async () => { 48 48 const result = await restoreOAuthSession(did); 49 49 if (!result.ok) { 50 - throw new Error( 51 - `Failed to restore OAuth session: ${result.error.message}`, 52 - ); 50 + // Mark identity as needing migration so we can retry later 51 + await supabaseServerClient 52 + .from("identities") 53 + .update({ 54 + metadata: { needsStandardSiteMigration: true }, 55 + }) 56 + .eq("atp_did", did); 57 + 58 + return { success: false, error: result.error.message }; 53 59 } 54 60 return { success: true }; 55 61 }); 62 + 63 + if (!oauthValid.success) { 64 + return { 65 + success: false, 66 + error: `Failed to restore OAuth session`, 67 + stats, 68 + publicationUriMap: {}, 69 + documentUriMap: {}, 70 + userSubscriptionUriMap: {}, 71 + }; 72 + } 56 73 57 74 // Step 2: Get user's pub.leaflet.publication records 58 75 const oldPublications = await step.run( ··· 471 488 // 2. External references (e.g., from other AT Proto apps) to old URIs continue to work 472 489 // 3. The normalization layer handles both schemas transparently for reads 473 490 // Old records are also kept on the user's PDS so existing AT-URI references remain valid. 491 + 492 + // Clear the migration flag on success 493 + if (stats.errors.length === 0) { 494 + await step.run("clear-migration-flag", async () => { 495 + await supabaseServerClient 496 + .from("identities") 497 + .update({ metadata: null }) 498 + .eq("atp_did", did); 499 + }); 500 + } 474 501 475 502 return { 476 503 success: stats.errors.length === 0,
+11
app/api/oauth/[route]/route.ts
··· 11 11 ActionAfterSignIn, 12 12 parseActionFromSearchParam, 13 13 } from "./afterSignInActions"; 14 + import { inngest } from "app/api/inngest/client"; 14 15 15 16 type OauthRequestClientState = { 16 17 redirect: string | null; ··· 84 85 .single(); 85 86 identity = data; 86 87 } 88 + 89 + // Trigger migration if identity needs it 90 + const metadata = identity?.metadata as Record<string, unknown> | null; 91 + if (metadata?.needsStandardSiteMigration) { 92 + await inngest.send({ 93 + name: "user/migrate-to-standard", 94 + data: { did: session.did }, 95 + }); 96 + } 97 + 87 98 let { data: token } = await supabaseServerClient 88 99 .from("email_auth_tokens") 89 100 .insert({
+1
drizzle/schema.ts
··· 140 140 email: text("email"), 141 141 atp_did: text("atp_did"), 142 142 interface_state: jsonb("interface_state"), 143 + metadata: jsonb("metadata"), 143 144 }, 144 145 (table) => { 145 146 return {
+3
supabase/database.types.ts
··· 551 551 home_page: string 552 552 id: string 553 553 interface_state: Json | null 554 + metadata: Json | null 554 555 } 555 556 Insert: { 556 557 atp_did?: string | null ··· 559 560 home_page?: string 560 561 id?: string 561 562 interface_state?: Json | null 563 + metadata?: Json | null 562 564 } 563 565 Update: { 564 566 atp_did?: string | null ··· 567 569 home_page?: string 568 570 id?: string 569 571 interface_state?: Json | null 572 + metadata?: Json | null 570 573 } 571 574 Relationships: [ 572 575 {