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 atb-46-modlog-endpoint 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}