import type { MiddlewareHandler } from 'hono'; import { createRouter } from '@@/create-router'; import { originMiddleware } from '@@/_middlewares'; import { isSuperAdmin } from '@@/super-admin'; import * as errors from '@@/custom-errors'; import { updateUserHandler } from './update-user'; import { listUsersHandler, updateUserRoleHandler, deleteUserHandler } from './manage-users'; import { createUserHandler } from './create-user'; import { provisionLinuxHandler } from './provision-linux-route'; import { resetUserPasswordHandler } from './reset-user-password'; import { permissionAdminRouter } from './permissions-routes'; export const usersRouter = createRouter(); usersRouter.use(originMiddleware); // Self-update. Any signed-in account may change its own name, username and avatar. usersRouter.put('/', updateUserHandler); // Everything below manages OTHER accounts and is the owner's alone. The global permission gate in // hono.ts already refuses a non-owner here — `user-admin` is `kind: 'admin'`, so it is // not grantable — but that router-level rule cannot see the one exception beside it: `PUT /` is // declared `selfService` so every account can edit its own profile. This gate is what keeps that // exception from widening to the routes below it, and it is a second lock rather than a restatement. const ownerGate: MiddlewareHandler = async (ctx, next) => { if (!(await isSuperAdmin(ctx.get('user')))) throw errors.FORBIDDEN('User management is owner-only'); return next(); }; usersRouter.get('/', ownerGate, listUsersHandler); // POST, not PUT — and worth noting they sit one line apart. `PUT /` is the selfService exception every // account may call on itself; `POST /` creates somebody else and is the owner's alone. usersRouter.post('/', ownerGate, createUserHandler); usersRouter.patch('/:id/role', ownerGate, updateUserRoleHandler); usersRouter.post('/:id/provision-linux', ownerGate, provisionLinuxHandler); // Generates a new platform password, returns it once, and signs the account out everywhere. Not the same // thing as auth/reset-password.ts, which is the token flow the person runs on themselves. usersRouter.post('/:id/password', ownerGate, resetUserPasswordHandler); usersRouter.delete('/:id', ownerGate, deleteUserHandler); // Which permissions each role holds. Owner-gated inside its own router. usersRouter.route('/', permissionAdminRouter);