import { type NextRequest } from "next/server"; import { prisma } from "@/lib/prisma"; import { ok, errorResponse, requireRole, parseJson, ApiError } from "@/lib/api"; import { CreateUserSchema } from "@/lib/schemas"; import { hashPassword, hashPin } from "@/lib/password"; import { audit } from "@/lib/audit"; import { clientIp } from "@/lib/request"; export async function GET() { try { await requireRole("admin"); const users = await prisma.user.findMany({ orderBy: [{ role: "asc" }, { name: "asc" }], select: { id: true, role: true, name: true, email: true, active: true, lastLoginAt: true, createdAt: true, }, }); return ok({ users }); } catch (err) { return errorResponse(err); } } export async function POST(req: NextRequest) { try { const actor = await requireRole("admin"); const body = await parseJson(req, CreateUserSchema); if (body.role === "admin") { const existing = await prisma.user.findUnique({ where: { email: body.email } }); if (existing) throw new ApiError(409, "duplicate", "Email already in use"); const passwordHash = await hashPassword(body.password); const created = await prisma.user.create({ data: { role: "admin", name: body.name, email: body.email, passwordHash }, select: { id: true, role: true, name: true, email: true, active: true, createdAt: true }, }); await audit({ actorId: actor.id, action: "create", entity: "User", entityId: created.id, after: { role: "admin", name: created.name, email: created.email }, ipAddress: clientIp(req), }); return ok({ user: created }, { status: 201 }); } // operator: name must be unique (case-insensitive) so login tile grid is unambiguous const nameNorm = body.name.trim(); const dupe = await prisma.user.findFirst({ where: { role: "operator", name: { equals: nameNorm }, }, }); if (dupe) throw new ApiError(409, "duplicate", "Operator name already in use"); const pinHash = await hashPin(body.pin); const created = await prisma.user.create({ data: { role: "operator", name: nameNorm, pinHash }, select: { id: true, role: true, name: true, email: true, active: true, createdAt: true }, }); await audit({ actorId: actor.id, action: "create", entity: "User", entityId: created.id, after: { role: "operator", name: created.name }, ipAddress: clientIp(req), }); return ok({ user: created }, { status: 201 }); } catch (err) { return errorResponse(err); } }