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 root/atb-56-theme-caching-layer 152 lines 4.0 kB view raw
1import type { AtpAgent } from "@atproto/api"; 2import type { Database } from "@atbb/db"; 3import { roles, rolePermissions } from "@atbb/db"; 4import { eq } from "drizzle-orm"; 5 6interface DefaultRole { 7 name: string; 8 description: string; 9 permissions: string[]; 10 priority: number; 11} 12 13export const DEFAULT_ROLES: DefaultRole[] = [ 14 { 15 name: "Owner", 16 description: "Forum owner with full control", 17 permissions: ["*"], 18 priority: 0, 19 }, 20 { 21 name: "Admin", 22 description: "Can manage forum structure and users", 23 permissions: [ 24 "space.atbb.permission.manageCategories", 25 "space.atbb.permission.manageRoles", 26 "space.atbb.permission.manageMembers", 27 "space.atbb.permission.moderatePosts", 28 "space.atbb.permission.banUsers", 29 "space.atbb.permission.pinTopics", 30 "space.atbb.permission.lockTopics", 31 "space.atbb.permission.createTopics", 32 "space.atbb.permission.createPosts", 33 ], 34 priority: 10, 35 }, 36 { 37 name: "Moderator", 38 description: "Can moderate content and users", 39 permissions: [ 40 "space.atbb.permission.moderatePosts", 41 "space.atbb.permission.banUsers", 42 "space.atbb.permission.pinTopics", 43 "space.atbb.permission.lockTopics", 44 "space.atbb.permission.createTopics", 45 "space.atbb.permission.createPosts", 46 ], 47 priority: 20, 48 }, 49 { 50 name: "Member", 51 description: "Regular forum member", 52 permissions: [ 53 "space.atbb.permission.createTopics", 54 "space.atbb.permission.createPosts", 55 ], 56 priority: 30, 57 }, 58]; 59 60export interface SeededRole { 61 name: string; 62 uri: string; 63 cid: string; 64} 65 66interface SeedRolesResult { 67 created: number; 68 skipped: number; 69 roles: SeededRole[]; 70} 71 72/** 73 * Seed default roles to Forum DID's PDS and database. 74 * Idempotent: checks for existing roles by name before creating. 75 * Returns role data (URI + CID) for downstream steps. 76 */ 77export async function seedDefaultRoles( 78 db: Database, 79 agent: AtpAgent, 80 forumDid: string 81): Promise<SeedRolesResult> { 82 let created = 0; 83 let skipped = 0; 84 const seededRoles: SeededRole[] = []; 85 86 for (const defaultRole of DEFAULT_ROLES) { 87 // Check if role already exists by name 88 const [existingRole] = await db 89 .select() 90 .from(roles) 91 .where(eq(roles.name, defaultRole.name)) 92 .limit(1); 93 94 if (existingRole) { 95 skipped++; 96 seededRoles.push({ 97 name: existingRole.name, 98 uri: `at://${existingRole.did}/space.atbb.forum.role/${existingRole.rkey}`, 99 cid: existingRole.cid, 100 }); 101 continue; 102 } 103 104 // Create role record on Forum DID's PDS 105 const response = await agent.com.atproto.repo.createRecord({ 106 repo: forumDid, 107 collection: "space.atbb.forum.role", 108 record: { 109 $type: "space.atbb.forum.role", 110 name: defaultRole.name, 111 description: defaultRole.description, 112 permissions: defaultRole.permissions, 113 priority: defaultRole.priority, 114 createdAt: new Date().toISOString(), 115 }, 116 }); 117 118 // Extract rkey from the returned URI (at://did/collection/rkey) 119 const rkey = response.data.uri.split("/").pop()!; 120 121 // Insert into database so downstream steps can query it 122 const [insertedRole] = await db.insert(roles).values({ 123 did: forumDid, 124 rkey, 125 cid: response.data.cid, 126 name: defaultRole.name, 127 description: defaultRole.description, 128 priority: defaultRole.priority, 129 createdAt: new Date(), 130 indexedAt: new Date(), 131 }).returning({ id: roles.id }); 132 133 if (defaultRole.permissions.length > 0) { 134 await db.insert(rolePermissions).values( 135 defaultRole.permissions.map((permission) => ({ 136 roleId: insertedRole.id, 137 permission, 138 })) 139 ); 140 } 141 142 seededRoles.push({ 143 name: defaultRole.name, 144 uri: response.data.uri, 145 cid: response.data.cid, 146 }); 147 148 created++; 149 } 150 151 return { created, skipped, roles: seededRoles }; 152}