User Management
ChimerAI includes a full User Management system with role assignment, invitations, pagination, and workspace membership — pre-integrated with Prisma and NextAuth.
What you get
- User list — Search, filter, paginate all users
- Role management — Assign
admin,member,viewerroles (RBAC-ready) - Invite users — Send email invitations with secure token links
- Suspend / unsuspend — Temporarily disable accounts
- Delete accounts — With GDPR-compliant anonymisation
- Audit trail — All admin actions logged
Quick setup
# Users table only (admin CRUD)
chimerai add users-table
# Full admin panel (users + roles + audit log + settings)
chimerai add admin-dashboard
Files installed by users-table:
| File | Purpose |
|---|---|
app/admin/users/page.tsx | Sortable, filterable user table |
app/api/admin/users/route.ts | GET list, POST create |
app/api/admin/users/[id]/route.ts | GET, PATCH role/suspend, DELETE |
admin-dashboard installs the full panel including roles, settings, and audit log. See User Management Guide for the complete file list.
Fetching users
// app/api/users/route.ts
import { db } from '@/lib/db';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
export async function GET(req: Request) {
const session = await getServerSession(authOptions);
if (session?.user?.role !== 'admin') {
return new Response('Forbidden', { status: 403 });
}
const { searchParams } = new URL(req.url);
const page = Number(searchParams.get('page') ?? 1);
const q = searchParams.get('q') ?? '';
const pageSize = 20;
const [users, total] = await Promise.all([
db.user.findMany({
where: { name: { contains: q } },
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: { createdAt: 'desc' },
select: { id: true, name: true, email: true, role: true, createdAt: true },
}),
db.user.count({ where: { name: { contains: q } } }),
]);
return Response.json({ users, total, page, pageSize });
}
Updating a user
// app/api/users/[id]/route.ts
export async function PATCH(req: Request, { params }: { params: { id: string } }) {
const session = await getServerSession(authOptions);
if (session?.user?.role !== 'admin') return new Response('Forbidden', { status: 403 });
const { role, suspended } = await req.json();
const user = await db.user.update({
where: { id: params.id },
data: { role, suspended },
});
return Response.json(user);
}
Inviting a user
// app/api/users/invite/route.ts
import { randomBytes } from 'crypto';
import { sendEmail } from '@/lib/email';
export async function POST(req: Request) {
const { email, role } = await req.json();
const token = randomBytes(32).toString('hex');
await db.invite.create({
data: { email, role, token, expiresAt: new Date(Date.now() + 7 * 86400_000) },
});
await sendEmail({
to: email,
subject: 'You have been invited',
html: `<a href="${process.env.NEXTAUTH_URL}/invite?token=${token}">Accept invitation</a>`,
});
return Response.json({ ok: true });
}
RBAC — Roles & Permissions
Roles and permissions are stored in two Prisma models:
model Role {
id String @id @default(cuid())
name String @unique // e.g. "admin", "editor", "viewer"
permissions String @default("[]") // JSON array: ["users:read", "users:write"]
}
model UserRole {
userId String
roleId String
@@id([userId, roleId])
}
To check permissions in server code:
const roles = await prisma.userRole.findMany({
where: { userId: session.user.id },
include: { role: true },
});
const permissions = roles.flatMap((r) => JSON.parse(r.role.permissions));
if (!permissions.includes('users:write')) return new Response('Forbidden', { status: 403 });
Further reading
- User Management Guide — complete CRUD API spec and admin-dashboard file list
- RBAC Guide
- GDPR Compliance