WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto
at main 81 lines 2.4 kB view raw
1import type { Database } from "@atbb/db"; 2import { users, memberships } from "@atbb/db"; 3import { eq, and } from "drizzle-orm"; 4import type { SeededRole } from "./seed-roles.js"; 5 6interface AssignOwnerResult { 7 assigned: boolean; 8 skipped: boolean; 9 roleUri?: string; 10} 11 12/** 13 * Assign the Owner role to a user via direct DB insert. 14 * 15 * The CLI cannot write to the user's PDS (no user credentials), so 16 * this inserts the membership directly into the database as a bootstrap 17 * shortcut. When the user logs in via OAuth, the normal membership 18 * flow will create the PDS record on their repo. 19 * 20 * Idempotent: skips if the user already has a membership with the 21 * Owner role URI. 22 */ 23export async function assignOwnerRole( 24 db: Database, 25 forumDid: string, 26 ownerDid: string, 27 ownerHandle: string | undefined, 28 seededRoles: SeededRole[] 29): Promise<AssignOwnerResult> { 30 // Find the Owner role from seeded roles 31 const ownerRole = seededRoles.find((r) => r.name === "Owner"); 32 33 if (!ownerRole) { 34 throw new Error( 35 "Owner role not found in seeded roles. Run role seeding first." 36 ); 37 } 38 39 const forumUri = `at://${forumDid}/space.atbb.forum.forum/self`; 40 41 // Check if user already has a membership with this role 42 const [existingMembership] = await db 43 .select() 44 .from(memberships) 45 .where(and(eq(memberships.did, ownerDid), eq(memberships.roleUri, ownerRole.uri))) 46 .limit(1); 47 48 if (existingMembership) { 49 return { assigned: false, skipped: true, roleUri: ownerRole.uri }; 50 } 51 52 // Ensure user exists in the users table (FK constraint) 53 await db 54 .insert(users) 55 .values({ 56 did: ownerDid, 57 handle: ownerHandle ?? null, 58 indexedAt: new Date(), 59 }) 60 .onConflictDoNothing(); 61 62 // Insert membership directly into DB. 63 // rkey and cid use "bootstrap" sentinel — there is no PDS record backing 64 // this membership yet. When the user first logs in via OAuth, 65 // createMembershipForUser detects cid==="bootstrap" and upgrades the row 66 // by writing a real PDS record and updating rkey/cid. 67 const now = new Date(); 68 await db.insert(memberships).values({ 69 did: ownerDid, 70 rkey: "bootstrap", 71 cid: "bootstrap", 72 forumUri, 73 roleUri: ownerRole.uri, 74 role: "Owner", 75 joinedAt: now, 76 createdAt: now, 77 indexedAt: now, 78 }); 79 80 return { assigned: true, skipped: false, roleUri: ownerRole.uri }; 81}