user management

This commit is contained in:
2026-03-15 14:47:58 -05:00
parent 857d34397e
commit 3197e68749
14 changed files with 1175 additions and 95 deletions

View File

@@ -1,12 +1,119 @@
import { permissions } from "@mrp/shared";
import { Router } from "express";
import { z } from "zod";
import { ok } from "../../lib/http.js";
import { fail, ok } from "../../lib/http.js";
import { requirePermissions } from "../../lib/rbac.js";
import { getAdminDiagnostics } from "./service.js";
import {
createAdminRole,
createAdminUser,
getAdminDiagnostics,
listAdminPermissions,
listAdminRoles,
listAdminUsers,
updateAdminRole,
updateAdminUser,
} from "./service.js";
export const adminRouter = Router();
const roleSchema = z.object({
name: z.string().trim().min(1).max(120),
description: z.string(),
permissionKeys: z.array(z.string().trim().min(1)),
});
const userSchema = z.object({
email: z.string().email(),
firstName: z.string().trim().min(1).max(120),
lastName: z.string().trim().min(1).max(120),
isActive: z.boolean(),
roleIds: z.array(z.string().trim().min(1)),
password: z.string().min(8).nullable(),
});
function getRouteParam(value: unknown) {
return typeof value === "string" ? value : null;
}
adminRouter.get("/diagnostics", requirePermissions([permissions.adminManage]), async (_request, response) => {
return ok(response, await getAdminDiagnostics());
});
adminRouter.get("/permissions", requirePermissions([permissions.adminManage]), async (_request, response) => {
return ok(response, await listAdminPermissions());
});
adminRouter.get("/roles", requirePermissions([permissions.adminManage]), async (_request, response) => {
return ok(response, await listAdminRoles());
});
adminRouter.post("/roles", requirePermissions([permissions.adminManage]), async (request, response) => {
const parsed = roleSchema.safeParse(request.body);
if (!parsed.success) {
return fail(response, 400, "INVALID_INPUT", "Role payload is invalid.");
}
const result = await createAdminRole(parsed.data, request.authUser?.id);
if (!result.ok) {
return fail(response, 400, "INVALID_INPUT", result.reason);
}
return ok(response, result.role, 201);
});
adminRouter.put("/roles/:roleId", requirePermissions([permissions.adminManage]), async (request, response) => {
const roleId = getRouteParam(request.params.roleId);
if (!roleId) {
return fail(response, 400, "INVALID_INPUT", "Role id is invalid.");
}
const parsed = roleSchema.safeParse(request.body);
if (!parsed.success) {
return fail(response, 400, "INVALID_INPUT", "Role payload is invalid.");
}
const result = await updateAdminRole(roleId, parsed.data, request.authUser?.id);
if (!result.ok) {
return fail(response, 400, "INVALID_INPUT", result.reason);
}
return ok(response, result.role);
});
adminRouter.get("/users", requirePermissions([permissions.adminManage]), async (_request, response) => {
return ok(response, await listAdminUsers());
});
adminRouter.post("/users", requirePermissions([permissions.adminManage]), async (request, response) => {
const parsed = userSchema.safeParse(request.body);
if (!parsed.success) {
return fail(response, 400, "INVALID_INPUT", "User payload is invalid.");
}
const result = await createAdminUser(parsed.data, request.authUser?.id);
if (!result.ok) {
return fail(response, 400, "INVALID_INPUT", result.reason);
}
return ok(response, result.user, 201);
});
adminRouter.put("/users/:userId", requirePermissions([permissions.adminManage]), async (request, response) => {
const userId = getRouteParam(request.params.userId);
if (!userId) {
return fail(response, 400, "INVALID_INPUT", "User id is invalid.");
}
const parsed = userSchema.safeParse(request.body);
if (!parsed.success) {
return fail(response, 400, "INVALID_INPUT", "User payload is invalid.");
}
const result = await updateAdminUser(userId, parsed.data, request.authUser?.id);
if (!result.ok) {
return fail(response, 400, "INVALID_INPUT", result.reason);
}
return ok(response, result.user);
});