import type { AtpAgent } from "@atproto/api"; import type { Database } from "@atbb/db"; import { forums } from "@atbb/db"; import { isProgrammingError } from "@atbb/atproto"; interface CreateForumInput { name: string; description?: string; } interface CreateForumResult { created: boolean; skipped: boolean; uri?: string; cid?: string; existingName?: string; } /** * Create the space.atbb.forum.forum/self record on the Forum DID's PDS * and insert it into the database. * Idempotent: skips if the record already exists on the PDS. */ export async function createForumRecord( db: Database, agent: AtpAgent, forumDid: string, input: CreateForumInput ): Promise { // Check if forum record already exists try { const existing = await agent.com.atproto.repo.getRecord({ repo: forumDid, collection: "space.atbb.forum.forum", rkey: "self", }); return { created: false, skipped: true, uri: existing.data.uri, cid: existing.data.cid, existingName: (existing.data.value as any)?.name, }; } catch (error) { if (isProgrammingError(error)) throw error; // Only proceed if the record was not found. // XRPC "RecordNotFound" errors have an `error` property on the thrown object. const isNotFound = error instanceof Error && "status" in error && (error as any).error === "RecordNotFound"; if (!isNotFound) { throw error; } } const response = await agent.com.atproto.repo.createRecord({ repo: forumDid, collection: "space.atbb.forum.forum", rkey: "self", record: { $type: "space.atbb.forum.forum", name: input.name, ...(input.description && { description: input.description }), createdAt: new Date().toISOString(), }, }); // Insert forum record into DB so downstream steps can reference it await db.insert(forums).values({ did: forumDid, rkey: "self", cid: response.data.cid, name: input.name, description: input.description ?? null, indexedAt: new Date(), }); return { created: true, skipped: false, uri: response.data.uri, cid: response.data.cid, }; }