Apariencia
Permisos
Un plugin declara sus permisos en permissionGroups del ZModule. Al cargar el plugin se registran en el catálogo (registerPluginPermissions, api/src/lib/permissions.ts), salen en GET /permissions y en la pantalla de roles.
ts
permissionGroups: [
{
group: "notas",
label: trKey("Notas"),
permissions: [
{ key: "notas.view", label: trKey("Leer las notas internas") },
{ key: "notas.edit", label: trKey("Crear y borrar notas internas") },
],
},
],Formato
- Clave:
<grupo>.<acción>(notas.view,notas.edit). El grupo suele coincidir con la clave del plugin; si no coincide (el pluginhappyHourusa el grupooffers) no hay nada más que hacer: el grupo se declara en el plugin. labeles un texto en español contrKey.- Convención de acciones del catálogo:
view,edit, y las específicas del módulo (record,approve,run…).
Quién los tiene
- El rol de sistema
adminlos tiene todos, implícitamente. - El resto de roles los concede la migración SQL del plugin con
INSERT INTO role_permissions … WHERE NOT EXISTS: es la única vía y tiene que ser idempotente. Ejemplo en la guía. - Un permiso nuevo en una versión posterior se concede en una migración
002_….sql.
En las rutas
ts
import { requirePermission } from "@host/http/auth";
import { perm } from "./tables";
app.get("/notas", { preHandler: requirePermission(perm("notas.view")) }, handler);El tipo Permission del host no conoce los permisos del plugin (se registran al cargar). Por eso cada plugin define un único cast en tables.ts:
ts
export const perm = (p: "notas.view" | "notas.edit"): Permission => p as Permission;Úsalo también en notify({ permission }). requireAnyPermission(...) admite varios.
En el panel
session.can("notas.view") y meta.permission: ["notas.view"] en la ruta. Las pantallas respetan ambos; la API siempre vuelve a comprobar.
Qué no cubre
Desinstalar un plugin no retira los permisos de los roles ni los ajustes: son datos del núcleo. «Desinstalar y borrar datos» solo borra las tablas que el plugin crea. Ver Ciclo de vida.