Blame · Line-by-line history
admin.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| a4f3e24 | 1 | /** |
| 2 | * Admin middleware — blocks non-admin users. | |
| 3 | * Must be used AFTER softAuth or requireAuth. | |
| 36cc17a | 4 | * |
| 5 | * "Admin" means EITHER: | |
| 6 | * - the user is in the `site_admins` table (explicit grant), OR | |
| 7 | * - the `site_admins` table is empty and this user is the oldest | |
| 8 | * row in `users` (bootstrap rule from src/lib/admin.ts). | |
| 9 | * | |
| 10 | * The legacy `users.is_admin` column is ALSO honoured for backward | |
| 11 | * compatibility with accounts that pre-date the site_admins table. | |
| 12 | * | |
| 13 | * Until 2026-05-14 this middleware only checked `users.is_admin`, | |
| 14 | * which broke /import + every other requireAdmin-gated route for any | |
| 15 | * user who only had bootstrap-rule admin status (which is the case | |
| 16 | * after running scripts/reset-admin-password.ts). | |
| a4f3e24 | 17 | */ |
| 18 | ||
| 19 | import { createMiddleware } from "hono/factory"; | |
| 20 | import type { AuthEnv } from "./auth"; | |
| 36cc17a | 21 | import { isSiteAdmin } from "../lib/admin"; |
| a4f3e24 | 22 | |
| 23 | export const requireAdmin = createMiddleware<AuthEnv>(async (c, next) => { | |
| 24 | const user = c.get("user"); | |
| 25 | ||
| 26 | if (!user) { | |
| 27 | return c.redirect(`/login?redirect=${encodeURIComponent(c.req.path)}`); | |
| 28 | } | |
| 29 | ||
| 36cc17a | 30 | const admin = user.isAdmin === true || (await isSiteAdmin(user.id)); |
| 31 | if (!admin) { | |
| a4f3e24 | 32 | return c.html( |
| 33 | `<html><body style="background:#0d1117;color:#e6edf3;font-family:sans-serif;display:flex;justify-content:center;align-items:center;height:100vh"> | |
| 34 | <div style="text-align:center"> | |
| 35 | <h1>403</h1> | |
| 36 | <p>Admin access required.</p> | |
| 37 | <a href="/" style="color:#58a6ff">Go home</a> | |
| 38 | </div> | |
| 39 | </body></html>`, | |
| 40 | 403 | |
| 41 | ); | |
| 42 | } | |
| 43 | ||
| 44 | return next(); | |
| 45 | }); |