feat(platform): Sistema > Permissões (catálogo RBAC, só leitura)

GET /platform/roles novo — lista as 4 roles do sistema com as permissions
de cada uma, mais o catálogo completo de permissions. roles/permissions
não têm RLS (catálogo global do seed); só leitura, RBAC é system-defined,
sem UI de criar role customizada ainda.

Frontend: /platform/sistema/permissoes, um card por role + tabela do
catálogo completo. Testado ponta a ponta: as 4 roles reais (platform_super_admin
33 permissions, tenant_admin 30, supervisor 17, agent 2) corretas. Smoke
test nas 19 telas do tenant + 9 telas platform, todas 200.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
2026-08-29 20:41:19 -03:00
parent c95c6805fb
commit 03ec0d556b
7 changed files with 182 additions and 2 deletions

View File

@@ -0,0 +1,46 @@
import { Controller, ForbiddenException, Get, UseGuards } from "@nestjs/common";
import { getPrismaClient } from "@b2bcall/database";
import { isPlatformUser, type AccessTokenClaims } from "@b2bcall/auth";
import { JwtAuthGuard } from "../common/guards/jwt-auth.guard";
import { PermissionGuard } from "../common/guards/permission.guard";
import { RequirePermission } from "../common/decorators/require-permission.decorator";
import { CurrentUser } from "../common/decorators/current-user.decorator";
/**
* "Sistema > Permissões" (agente.md secao 142-145, 168) — só leitura por
* enquanto: RBAC é system-defined (roles/permissions vêm do seed,
* `packages/auth/src/seed.ts`), sem UI de criar role customizada ainda.
* `roles`/`permissions` não têm RLS (catálogo global do sistema).
*/
@UseGuards(JwtAuthGuard, PermissionGuard)
@Controller("platform/roles")
export class PlatformRolesController {
@RequirePermission("roles.manage")
@Get()
async list(@CurrentUser() user: AccessTokenClaims) {
if (!(await isPlatformUser(user.sub))) {
throw new ForbiddenException("So' um usuario com role de plataforma pode ver o catalogo de roles/permissoes");
}
const prisma = getPrismaClient();
const [roles, permissions] = await Promise.all([
prisma.role.findMany({
include: { rolePermissions: { include: { permission: true } } },
orderBy: { name: "asc" },
}),
prisma.permission.findMany({ orderBy: { key: "asc" } }),
]);
return {
permissions,
roles: roles.map((r) => ({
id: r.id,
key: r.key,
name: r.name,
scope: r.scope,
isSystem: r.isSystem,
permissionKeys: r.rolePermissions.map((rp) => rp.permission.key),
})),
};
}
}

View File

@@ -3,8 +3,15 @@ import { PlatformOverviewController } from "./platform-overview.controller";
import { PlatformUsersController } from "./platform-users.controller";
import { PlatformAuditController } from "./platform-audit.controller";
import { PlatformHealthController } from "./platform-health.controller";
import { PlatformRolesController } from "./platform-roles.controller";
@Module({
controllers: [PlatformOverviewController, PlatformUsersController, PlatformAuditController, PlatformHealthController],
controllers: [
PlatformOverviewController,
PlatformUsersController,
PlatformAuditController,
PlatformHealthController,
PlatformRolesController,
],
})
export class PlatformModule {}