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.

feat(web): add GET /admin landing page with permission-gated nav cards (ATB-42)

Malpercio 47c6c9bf b2fa966c

+95
+93
apps/web/src/routes/admin.tsx
··· 1 + import { Hono } from "hono"; 2 + import { BaseLayout } from "../layouts/base.js"; 3 + import { PageHeader, Card } from "../components/index.js"; 4 + import { getSessionWithPermissions, hasAnyAdminPermission } from "../lib/session.js"; 5 + 6 + // ─── Permission helpers ─────────────────────────────────────────────────── 7 + 8 + function canManageMembers(auth: { authenticated: boolean; permissions: Set<string> }): boolean { 9 + return ( 10 + auth.authenticated && 11 + (auth.permissions.has("space.atbb.permission.manageMembers") || 12 + auth.permissions.has("*")) 13 + ); 14 + } 15 + 16 + function canManageCategories(auth: { authenticated: boolean; permissions: Set<string> }): boolean { 17 + return ( 18 + auth.authenticated && 19 + (auth.permissions.has("space.atbb.permission.manageCategories") || 20 + auth.permissions.has("*")) 21 + ); 22 + } 23 + 24 + function canViewModLog(auth: { authenticated: boolean; permissions: Set<string> }): boolean { 25 + return ( 26 + auth.authenticated && 27 + (auth.permissions.has("space.atbb.permission.moderatePosts") || 28 + auth.permissions.has("space.atbb.permission.banUsers") || 29 + auth.permissions.has("space.atbb.permission.lockTopics") || 30 + auth.permissions.has("*")) 31 + ); 32 + } 33 + 34 + // ─── Route ──────────────────────────────────────────────────────────────── 35 + 36 + export function createAdminRoutes(appviewUrl: string) { 37 + return new Hono().get("/admin", async (c) => { 38 + const auth = await getSessionWithPermissions(appviewUrl, c.req.header("cookie")); 39 + 40 + if (!auth.authenticated) { 41 + return c.redirect("/login"); 42 + } 43 + 44 + if (!hasAnyAdminPermission(auth)) { 45 + return c.html( 46 + <BaseLayout title="Access Denied — atBB Forum" auth={auth}> 47 + <PageHeader title="Access Denied" /> 48 + <p>You don&apos;t have permission to access the admin panel.</p> 49 + </BaseLayout>, 50 + 403 51 + ); 52 + } 53 + 54 + const showMembers = canManageMembers(auth); 55 + const showStructure = canManageCategories(auth); 56 + const showModLog = canViewModLog(auth); 57 + 58 + return c.html( 59 + <BaseLayout title="Admin Panel — atBB Forum" auth={auth}> 60 + <PageHeader title="Admin Panel" /> 61 + <div class="admin-nav-grid"> 62 + {showMembers && ( 63 + <a href="/admin/members" class="admin-nav-card"> 64 + <Card> 65 + <p class="admin-nav-card__icon" aria-hidden="true">👥</p> 66 + <p class="admin-nav-card__title">Members</p> 67 + <p class="admin-nav-card__description">View and assign member roles</p> 68 + </Card> 69 + </a> 70 + )} 71 + {showStructure && ( 72 + <a href="/admin/structure" class="admin-nav-card"> 73 + <Card> 74 + <p class="admin-nav-card__icon" aria-hidden="true">📁</p> 75 + <p class="admin-nav-card__title">Structure</p> 76 + <p class="admin-nav-card__description">Manage categories and boards</p> 77 + </Card> 78 + </a> 79 + )} 80 + {showModLog && ( 81 + <a href="/admin/modlog" class="admin-nav-card"> 82 + <Card> 83 + <p class="admin-nav-card__icon" aria-hidden="true">📋</p> 84 + <p class="admin-nav-card__title">Mod Log</p> 85 + <p class="admin-nav-card__description">Audit trail of moderation actions</p> 86 + </Card> 87 + </a> 88 + )} 89 + </div> 90 + </BaseLayout> 91 + ); 92 + }); 93 + }
+2
apps/web/src/routes/index.ts
··· 7 7 import { createNewTopicRoutes } from "./new-topic.js"; 8 8 import { createAuthRoutes } from "./auth.js"; 9 9 import { createModActionRoute } from "./mod.js"; 10 + import { createAdminRoutes } from "./admin.js"; 10 11 import { createNotFoundRoute } from "./not-found.js"; 11 12 12 13 const config = loadConfig(); ··· 19 20 .route("/", createNewTopicRoutes(config.appviewUrl)) 20 21 .route("/", createAuthRoutes(config.appviewUrl)) 21 22 .route("/", createModActionRoute(config.appviewUrl)) 23 + .route("/", createAdminRoutes(config.appviewUrl)) 22 24 .route("/", createNotFoundRoute(config.appviewUrl));