⚡ You're viewing a live demo of ChimerAI. Data resets daily at midnight UTC.Get the CLI →

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, viewer roles (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:

FilePurpose
app/admin/users/page.tsxSortable, filterable user table
app/api/admin/users/route.tsGET list, POST create
app/api/admin/users/[id]/route.tsGET, 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

ChimerAI Docs · Back to Demo