import type { AtpAgent } from "@atproto/api"; import type { Database } from "@atbb/db"; import { boards } from "@atbb/db"; import { eq, and } from "drizzle-orm"; import { deriveSlug } from "../slug.js"; interface CreateBoardInput { name: string; description?: string; slug?: string; sortOrder?: number; categoryUri: string; // AT URI: at://did/space.atbb.forum.category/rkey categoryId: bigint; // DB FK categoryCid: string; // CID for the category strongRef } interface CreateBoardResult { created: boolean; skipped: boolean; uri?: string; cid?: string; existingName?: string; } /** * Create a space.atbb.forum.board record on the Forum DID's PDS * and insert it into the database. * Idempotent: skips if a board with the same name in the same category exists. */ export async function createBoard( db: Database, agent: AtpAgent, forumDid: string, input: CreateBoardInput ): Promise { // Check if board with this name already exists in the category const [existing] = await db .select() .from(boards) .where( and( eq(boards.did, forumDid), eq(boards.name, input.name), eq(boards.categoryUri, input.categoryUri) ) ) .limit(1); if (existing) { return { created: false, skipped: true, uri: `at://${existing.did}/space.atbb.forum.board/${existing.rkey}`, cid: existing.cid, existingName: existing.name, }; } const slug = input.slug ?? deriveSlug(input.name); const now = new Date(); const response = await agent.com.atproto.repo.createRecord({ repo: forumDid, collection: "space.atbb.forum.board", record: { $type: "space.atbb.forum.board", name: input.name, ...(input.description && { description: input.description }), slug, ...(input.sortOrder !== undefined && { sortOrder: input.sortOrder }), // categoryRef shape: { category: strongRef } category: { category: { uri: input.categoryUri, cid: input.categoryCid, }, }, createdAt: now.toISOString(), }, }); const rkey = response.data.uri.split("/").pop()!; await db.insert(boards).values({ did: forumDid, rkey, cid: response.data.cid, name: input.name, description: input.description ?? null, slug, sortOrder: input.sortOrder ?? null, categoryId: input.categoryId, categoryUri: input.categoryUri, createdAt: now, indexedAt: now, }); return { created: true, skipped: false, uri: response.data.uri, cid: response.data.cid, }; }