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
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at bc5c0dc421fb0a09f8ed2b35d180f7eb67bd4e7e 132 lines 3.9 kB view raw
1import type { AppContext } from "./app-context.js"; 2import { roles } from "@atbb/db"; 3import { eq } from "drizzle-orm"; 4 5interface DefaultRole { 6 name: string; 7 description: string; 8 permissions: string[]; 9 priority: number; 10} 11 12const DEFAULT_ROLES: DefaultRole[] = [ 13 { 14 name: "Owner", 15 description: "Forum owner with full control", 16 permissions: ["*"], 17 priority: 0, 18 }, 19 { 20 name: "Admin", 21 description: "Can manage forum structure and users", 22 permissions: [ 23 "space.atbb.permission.manageCategories", 24 "space.atbb.permission.manageRoles", 25 "space.atbb.permission.manageMembers", 26 "space.atbb.permission.moderatePosts", 27 "space.atbb.permission.banUsers", 28 "space.atbb.permission.pinTopics", 29 "space.atbb.permission.lockTopics", 30 "space.atbb.permission.createTopics", 31 "space.atbb.permission.createPosts", 32 ], 33 priority: 10, 34 }, 35 { 36 name: "Moderator", 37 description: "Can moderate content and users", 38 permissions: [ 39 "space.atbb.permission.moderatePosts", 40 "space.atbb.permission.banUsers", 41 "space.atbb.permission.pinTopics", 42 "space.atbb.permission.lockTopics", 43 "space.atbb.permission.createTopics", 44 "space.atbb.permission.createPosts", 45 ], 46 priority: 20, 47 }, 48 { 49 name: "Member", 50 description: "Regular forum member", 51 permissions: [ 52 "space.atbb.permission.createTopics", 53 "space.atbb.permission.createPosts", 54 ], 55 priority: 30, 56 }, 57]; 58 59/** 60 * Seed default roles to Forum DID's PDS. 61 * 62 * Idempotent: Checks for existing roles by name before creating. 63 * Safe to run on every startup. 64 * 65 * @throws Error if ForumAgent is unavailable - permission system cannot function without roles 66 */ 67export async function seedDefaultRoles(ctx: AppContext): Promise<{ created: number; skipped: number }> { 68 // Check ForumAgent availability 69 if (!ctx.forumAgent) { 70 throw new Error("ForumAgent not available - permission system cannot function without roles. Check FORUM_HANDLE and FORUM_PASSWORD environment variables."); 71 } 72 73 const agent = ctx.forumAgent.getAgent(); 74 if (!agent) { 75 throw new Error("ForumAgent not authenticated - permission system cannot function without roles. Check FORUM_HANDLE and FORUM_PASSWORD are valid."); 76 } 77 78 let created = 0; 79 let skipped = 0; 80 81 for (const defaultRole of DEFAULT_ROLES) { 82 try { 83 // Check if role already exists by name 84 const [existingRole] = await ctx.db 85 .select() 86 .from(roles) 87 .where(eq(roles.name, defaultRole.name)) 88 .limit(1); 89 90 if (existingRole) { 91 ctx.logger.info(`Role "${defaultRole.name}" already exists, skipping`, { 92 operation: "seedDefaultRoles", 93 roleName: defaultRole.name, 94 }); 95 skipped++; 96 continue; 97 } 98 99 // Create role record on Forum DID's PDS 100 const response = await agent.com.atproto.repo.createRecord({ 101 repo: ctx.config.forumDid, 102 collection: "space.atbb.forum.role", 103 record: { 104 $type: "space.atbb.forum.role", 105 name: defaultRole.name, 106 description: defaultRole.description, 107 permissions: defaultRole.permissions, 108 priority: defaultRole.priority, 109 createdAt: new Date().toISOString(), 110 }, 111 }); 112 113 ctx.logger.info(`Created default role "${defaultRole.name}"`, { 114 operation: "seedDefaultRoles", 115 roleName: defaultRole.name, 116 uri: response.data.uri, 117 cid: response.data.cid, 118 }); 119 120 created++; 121 } catch (error) { 122 ctx.logger.error(`Failed to seed role "${defaultRole.name}"`, { 123 operation: "seedDefaultRoles", 124 roleName: defaultRole.name, 125 error: error instanceof Error ? error.message : String(error), 126 }); 127 // Continue seeding other roles even if one fails 128 } 129 } 130 131 return { created, skipped }; 132}