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 88 lines 2.2 kB view raw
1import type { AtpAgent } from "@atproto/api"; 2import type { Database } from "@atbb/db"; 3import { forums } from "@atbb/db"; 4import { isProgrammingError } from "@atbb/atproto"; 5 6interface CreateForumInput { 7 name: string; 8 description?: string; 9} 10 11interface CreateForumResult { 12 created: boolean; 13 skipped: boolean; 14 uri?: string; 15 cid?: string; 16 existingName?: string; 17} 18 19/** 20 * Create the space.atbb.forum.forum/self record on the Forum DID's PDS 21 * and insert it into the database. 22 * Idempotent: skips if the record already exists on the PDS. 23 */ 24export async function createForumRecord( 25 db: Database, 26 agent: AtpAgent, 27 forumDid: string, 28 input: CreateForumInput 29): Promise<CreateForumResult> { 30 // Check if forum record already exists 31 try { 32 const existing = await agent.com.atproto.repo.getRecord({ 33 repo: forumDid, 34 collection: "space.atbb.forum.forum", 35 rkey: "self", 36 }); 37 38 return { 39 created: false, 40 skipped: true, 41 uri: existing.data.uri, 42 cid: existing.data.cid, 43 existingName: (existing.data.value as any)?.name, 44 }; 45 } catch (error) { 46 if (isProgrammingError(error)) throw error; 47 48 // Only proceed if the record was not found. 49 // XRPC "RecordNotFound" errors have an `error` property on the thrown object. 50 const isNotFound = 51 error instanceof Error && 52 "status" in error && 53 (error as any).error === "RecordNotFound"; 54 55 if (!isNotFound) { 56 throw error; 57 } 58 } 59 60 const response = await agent.com.atproto.repo.createRecord({ 61 repo: forumDid, 62 collection: "space.atbb.forum.forum", 63 rkey: "self", 64 record: { 65 $type: "space.atbb.forum.forum", 66 name: input.name, 67 ...(input.description && { description: input.description }), 68 createdAt: new Date().toISOString(), 69 }, 70 }); 71 72 // Insert forum record into DB so downstream steps can reference it 73 await db.insert(forums).values({ 74 did: forumDid, 75 rkey: "self", 76 cid: response.data.cid, 77 name: input.name, 78 description: input.description ?? null, 79 indexedAt: new Date(), 80 }); 81 82 return { 83 created: true, 84 skipped: false, 85 uri: response.data.uri, 86 cid: response.data.cid, 87 }; 88}