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-59-theme-editor 101 lines 2.6 kB view raw
1import type { AtpAgent } from "@atproto/api"; 2import type { Database } from "@atbb/db"; 3import { categories, forums } from "@atbb/db"; 4import { eq, and } from "drizzle-orm"; 5import { deriveSlug } from "../slug.js"; 6 7interface CreateCategoryInput { 8 name: string; 9 description?: string; 10 slug?: string; 11 sortOrder?: number; 12} 13 14interface CreateCategoryResult { 15 created: boolean; 16 skipped: boolean; 17 uri?: string; 18 cid?: string; 19 existingName?: string; 20} 21 22/** 23 * Create a space.atbb.forum.category record on the Forum DID's PDS 24 * and insert it into the database. 25 * Idempotent: skips if a category with the same name already exists. 26 */ 27export async function createCategory( 28 db: Database, 29 agent: AtpAgent, 30 forumDid: string, 31 input: CreateCategoryInput 32): Promise<CreateCategoryResult> { 33 // Check if category with this name already exists 34 const [existing] = await db 35 .select() 36 .from(categories) 37 .where(and(eq(categories.did, forumDid), eq(categories.name, input.name))) 38 .limit(1); 39 40 if (existing) { 41 return { 42 created: false, 43 skipped: true, 44 uri: `at://${existing.did}/space.atbb.forum.category/${existing.rkey}`, 45 cid: existing.cid, 46 existingName: existing.name, 47 }; 48 } 49 50 // Look up forum row for FK reference (optional — null if forum not yet in DB) 51 const [forum] = await db 52 .select() 53 .from(forums) 54 .where(and(eq(forums.did, forumDid), eq(forums.rkey, "self"))) 55 .limit(1); 56 57 if (!forum?.id) { 58 console.warn( 59 "Forum record not found in DB — inserting category with forumId: null", 60 JSON.stringify({ forumDid }) 61 ); 62 } 63 64 const slug = input.slug ?? deriveSlug(input.name); 65 const now = new Date(); 66 67 const response = await agent.com.atproto.repo.createRecord({ 68 repo: forumDid, 69 collection: "space.atbb.forum.category", 70 record: { 71 $type: "space.atbb.forum.category", 72 name: input.name, 73 ...(input.description && { description: input.description }), 74 slug, 75 ...(input.sortOrder !== undefined && { sortOrder: input.sortOrder }), 76 createdAt: now.toISOString(), 77 }, 78 }); 79 80 const rkey = response.data.uri.split("/").pop()!; 81 82 await db.insert(categories).values({ 83 did: forumDid, 84 rkey, 85 cid: response.data.cid, 86 name: input.name, 87 description: input.description ?? null, 88 slug, 89 sortOrder: input.sortOrder ?? null, 90 forumId: forum?.id ?? null, 91 createdAt: now, 92 indexedAt: now, 93 }); 94 95 return { 96 created: true, 97 skipped: false, 98 uri: response.data.uri, 99 cid: response.data.cid, 100 }; 101}