rl-core-api 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +54 -0
  2. package/dist/core/config/env.schema.d.ts +3 -0
  3. package/dist/core/config/env.schema.js +3 -0
  4. package/dist/core/core.module.js +3 -1
  5. package/dist/core/events/appEvent.enum.d.ts +6 -0
  6. package/dist/core/events/appEvent.enum.js +1 -0
  7. package/dist/core/health/health.controller.d.ts +4 -1
  8. package/dist/core/health/health.controller.js +9 -3
  9. package/dist/core/queue/bullConnection.module.d.ts +2 -0
  10. package/dist/core/queue/bullConnection.module.js +27 -0
  11. package/dist/core/queue/job.interface.d.ts +70 -0
  12. package/dist/core/queue/job.interface.js +2 -0
  13. package/dist/core/queue/job.producer.d.ts +15 -0
  14. package/dist/core/queue/job.producer.js +124 -0
  15. package/dist/core/queue/jobEvents.interface.d.ts +4 -0
  16. package/dist/core/queue/jobEvents.interface.js +4 -0
  17. package/dist/core/queue/jobEvents.service.d.ts +12 -0
  18. package/dist/core/queue/jobEvents.service.js +53 -0
  19. package/dist/core/queue/jobStatus.controller.d.ts +8 -0
  20. package/dist/core/queue/jobStatus.controller.js +44 -0
  21. package/dist/core/queue/jobStatus.response.d.ts +20 -0
  22. package/dist/core/queue/jobStatus.response.js +84 -0
  23. package/dist/core/queue/jobWorkerHost.d.ts +17 -0
  24. package/dist/core/queue/jobWorkerHost.js +72 -0
  25. package/dist/core/queue/queue.constants.d.ts +11 -0
  26. package/dist/core/queue/queue.constants.js +18 -0
  27. package/dist/core/queue/queue.module.d.ts +2 -0
  28. package/dist/core/queue/queue.module.js +38 -0
  29. package/dist/core/queue/queueAdmin.controller.d.ts +11 -0
  30. package/dist/core/queue/queueAdmin.controller.js +93 -0
  31. package/dist/core/queue/queueAdmin.response.d.ts +23 -0
  32. package/dist/core/queue/queueAdmin.response.js +87 -0
  33. package/dist/core/queue/queueAdmin.service.d.ts +15 -0
  34. package/dist/core/queue/queueAdmin.service.js +85 -0
  35. package/dist/core/queue/redisConnection.d.ts +2 -0
  36. package/dist/core/queue/redisConnection.js +11 -0
  37. package/dist/core/websocket/baseGateway.d.ts +1 -0
  38. package/dist/core/websocket/baseGateway.js +3 -0
  39. package/dist/features/audit/audit.module.js +1 -0
  40. package/dist/features/audit/presentation/index.d.ts +1 -0
  41. package/dist/features/audit/presentation/index.js +1 -0
  42. package/dist/features/audit/presentation/userPermissionsChanged.listener.d.ts +7 -0
  43. package/dist/features/audit/presentation/userPermissionsChanged.listener.js +43 -0
  44. package/dist/features/notifications/notifications.module.js +1 -1
  45. package/dist/features/notifications/presentation/domainEvents.listener.d.ts +2 -1
  46. package/dist/features/notifications/presentation/domainEvents.listener.js +19 -0
  47. package/dist/features/rbac/domain/rbac.catalog.js +15 -0
  48. package/dist/features/rbac/domain/rbac.service.d.ts +11 -1
  49. package/dist/features/rbac/domain/rbac.service.js +72 -2
  50. package/dist/features/rbac/presentation/commands/setUserPermissions.command.d.ts +3 -0
  51. package/dist/features/rbac/presentation/commands/setUserPermissions.command.js +24 -0
  52. package/dist/features/rbac/presentation/index.d.ts +1 -0
  53. package/dist/features/rbac/presentation/index.js +1 -0
  54. package/dist/features/rbac/presentation/rbac.controller.d.ts +5 -0
  55. package/dist/features/rbac/presentation/rbac.controller.js +57 -0
  56. package/dist/index.d.ts +11 -1
  57. package/dist/index.js +33 -2
  58. package/dist/rlCore.module.js +8 -0
  59. package/package.json +4 -1
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.UserPermissionsChangedListener = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const event_emitter_1 = require("@nestjs/event-emitter");
15
+ const appEvent_enum_1 = require("../../../core/events/appEvent.enum");
16
+ const audit_service_1 = require("../domain/audit.service");
17
+ const auditDataChange_schema_1 = require("../infra/schema/auditDataChange.schema");
18
+ const asLabel = (codes) => codes.length ? [...codes].sort().join(", ") : "—";
19
+ let UserPermissionsChangedListener = class UserPermissionsChangedListener {
20
+ constructor(audit) {
21
+ this.audit = audit;
22
+ }
23
+ async onPermissionsChanged(event) {
24
+ await this.audit.recordChange({
25
+ entity: "User",
26
+ entityId: event.userId,
27
+ action: auditDataChange_schema_1.AuditDataAction.UPDATE,
28
+ oldData: { directPermissions: asLabel(event.previousCodes) },
29
+ newData: { directPermissions: asLabel(event.codes) },
30
+ });
31
+ }
32
+ };
33
+ exports.UserPermissionsChangedListener = UserPermissionsChangedListener;
34
+ __decorate([
35
+ (0, event_emitter_1.OnEvent)(appEvent_enum_1.AppEvent.USER_PERMISSIONS_CHANGED),
36
+ __metadata("design:type", Function),
37
+ __metadata("design:paramtypes", [Object]),
38
+ __metadata("design:returntype", Promise)
39
+ ], UserPermissionsChangedListener.prototype, "onPermissionsChanged", null);
40
+ exports.UserPermissionsChangedListener = UserPermissionsChangedListener = __decorate([
41
+ (0, common_1.Injectable)(),
42
+ __metadata("design:paramtypes", [audit_service_1.AuditService])
43
+ ], UserPermissionsChangedListener);
@@ -40,6 +40,6 @@ exports.NotificationsModule = NotificationsModule = __decorate([
40
40
  notifications_publisher_1.NotificationsPublisher,
41
41
  domainEvents_listener_1.DomainEventsListener,
42
42
  ],
43
- exports: [notifications_service_1.NotificationsService, notifications_publisher_1.NotificationsPublisher],
43
+ exports: [notifications_service_1.NotificationsService, notifications_publisher_1.NotificationsPublisher, notifications_gateway_1.NotificationsGateway],
44
44
  })
45
45
  ], NotificationsModule);
@@ -1,4 +1,4 @@
1
- import { SystemCleanupFailedEvent, UserLockedEvent, UserPasswordExpiringEvent, UserPasswordResetRequestedEvent, UserRolesChangedEvent, UserTwoFactorResetEvent } from "../../../core/events/appEvent.enum";
1
+ import { SystemCleanupFailedEvent, UserLockedEvent, UserPasswordExpiringEvent, UserPasswordResetRequestedEvent, UserPermissionsChangedEvent, UserRolesChangedEvent, UserTwoFactorResetEvent } from "../../../core/events/appEvent.enum";
2
2
  import { NotificationsGateway } from "./notifications.gateway";
3
3
  import { NotificationsPublisher } from "./notifications.publisher";
4
4
  export declare class DomainEventsListener {
@@ -9,6 +9,7 @@ export declare class DomainEventsListener {
9
9
  onUserLocked(event: UserLockedEvent): Promise<void>;
10
10
  onTwoFactorReset(event: UserTwoFactorResetEvent): Promise<void>;
11
11
  onRolesChanged(event: UserRolesChangedEvent): Promise<void>;
12
+ onPermissionsChanged(event: UserPermissionsChangedEvent): Promise<void>;
12
13
  onPasswordResetRequested(event: UserPasswordResetRequestedEvent): Promise<void>;
13
14
  onPasswordExpiring(event: UserPasswordExpiringEvent): Promise<void>;
14
15
  onCleanupFailed(event: SystemCleanupFailedEvent): Promise<void>;
@@ -63,6 +63,19 @@ let DomainEventsListener = DomainEventsListener_1 = class DomainEventsListener {
63
63
  this.gateway.requestSessionRefresh(event.userId);
64
64
  });
65
65
  }
66
+ async onPermissionsChanged(event) {
67
+ await this.safePublish(appEvent_enum_1.AppEvent.USER_PERMISSIONS_CHANGED, async () => {
68
+ await this.publisher.publishAndPush({
69
+ title: "Seus acessos foram ajustados",
70
+ body: event.codes.length
71
+ ? "Um administrador ajustou permissões específicas da sua conta. Parte do sistema pode aparecer diferente para você."
72
+ : "Um administrador removeu as permissões específicas da sua conta. Seu acesso volta a ser o dos seus papéis.",
73
+ type: notificationType_enum_1.NotificationType.INFO,
74
+ targets: [{ type: scopeType_enum_1.ScopeType.USER, key: event.userId }],
75
+ });
76
+ this.gateway.requestSessionRefresh(event.userId);
77
+ });
78
+ }
66
79
  async onPasswordResetRequested(event) {
67
80
  await this.safePublish(appEvent_enum_1.AppEvent.USER_PASSWORD_RESET_REQUESTED, () => this.publisher.publishAndPush({
68
81
  title: "Redefinição de senha solicitada",
@@ -118,6 +131,12 @@ __decorate([
118
131
  __metadata("design:paramtypes", [Object]),
119
132
  __metadata("design:returntype", Promise)
120
133
  ], DomainEventsListener.prototype, "onRolesChanged", null);
134
+ __decorate([
135
+ (0, event_emitter_1.OnEvent)(appEvent_enum_1.AppEvent.USER_PERMISSIONS_CHANGED),
136
+ __metadata("design:type", Function),
137
+ __metadata("design:paramtypes", [Object]),
138
+ __metadata("design:returntype", Promise)
139
+ ], DomainEventsListener.prototype, "onPermissionsChanged", null);
121
140
  __decorate([
122
141
  (0, event_emitter_1.OnEvent)(appEvent_enum_1.AppEvent.USER_PASSWORD_RESET_REQUESTED),
123
142
  __metadata("design:type", Function),
@@ -11,6 +11,7 @@ exports.RESOURCES = [
11
11
  { name: "resources", description: "Controle de acesso — áreas" },
12
12
  { name: "actions", description: "Controle de acesso — ações" },
13
13
  { name: "scopes", description: "Controle de acesso — alcances" },
14
+ { name: "queues", description: "Fila de processamento em segundo plano" },
14
15
  ];
15
16
  exports.ACTIONS = [
16
17
  { name: "create", description: "Criar" },
@@ -18,6 +19,8 @@ exports.ACTIONS = [
18
19
  { name: "update", description: "Atualizar" },
19
20
  { name: "delete", description: "Remover" },
20
21
  { name: "promote", description: "Alterar papel/promover" },
22
+ { name: "grant", description: "Conceder permissão avulsa, fora de papel" },
23
+ { name: "manage", description: "Operar (reprocessar, remover)" },
21
24
  { name: "deactivate", description: "Ativar/desativar" },
22
25
  { name: "reset-password", description: "Resetar senha de usuário" },
23
26
  { name: "invite", description: "Enviar link de primeiro acesso" },
@@ -51,6 +54,10 @@ exports.PERMISSIONS = [
51
54
  code: "users:deactivate:any",
52
55
  description: "Ativar/desativar qualquer usuário",
53
56
  },
57
+ {
58
+ code: "users:grant:any",
59
+ description: "Conceder permissão diretamente a um usuário, fora de papel — restrito a administradores",
60
+ },
54
61
  {
55
62
  code: "users:reset-password:any",
56
63
  description: "Resetar senha de qualquer usuário",
@@ -138,6 +145,14 @@ exports.PERMISSIONS = [
138
145
  description: "Atualizar scopes do catálogo RBAC",
139
146
  },
140
147
  { code: "scopes:delete:any", description: "Remover scopes do catálogo RBAC" },
148
+ {
149
+ code: "queues:read:any",
150
+ description: "Ver a fila de processamento e os jobs de qualquer usuário",
151
+ },
152
+ {
153
+ code: "queues:manage:any",
154
+ description: "Reprocessar e remover jobs da fila",
155
+ },
141
156
  ];
142
157
  exports.ROLES = [
143
158
  {
@@ -1,3 +1,4 @@
1
+ import { EventEmitter2 } from "@nestjs/event-emitter";
1
2
  import { ActionRepository } from "../infra/repositories/action.repository";
2
3
  import { GroupRepository } from "../infra/repositories/group.repository";
3
4
  import { PermissionRepository } from "../infra/repositories/permission.repository";
@@ -31,7 +32,8 @@ export declare class RbacService {
31
32
  private readonly roleRepo;
32
33
  private readonly groupRepo;
33
34
  private readonly userRepo;
34
- constructor(resourceRepo: ResourceRepository, actionRepo: ActionRepository, scopeRepo: ScopeRepository, permissionRepo: PermissionRepository, roleRepo: RoleRepository, groupRepo: GroupRepository, userRepo: UserRepository);
35
+ private readonly events;
36
+ constructor(resourceRepo: ResourceRepository, actionRepo: ActionRepository, scopeRepo: ScopeRepository, permissionRepo: PermissionRepository, roleRepo: RoleRepository, groupRepo: GroupRepository, userRepo: UserRepository, events: EventEmitter2);
35
37
  getUserPermissionCodes(userId: string): Promise<string[]>;
36
38
  getUserRoleNames(userId: string): Promise<string[]>;
37
39
  listResources(): Promise<Resource[]>;
@@ -58,6 +60,14 @@ export declare class RbacService {
58
60
  deleteRole(id: string): Promise<void>;
59
61
  addPermissionToRole(roleId: string, permissionId: string, actorPermissions?: string[]): Promise<Role>;
60
62
  removePermissionFromRole(roleId: string, permissionId: string): Promise<Role>;
63
+ listUserDirectPermissions(userId: string): Promise<Permission[]>;
64
+ setUserDirectPermissions(userId: string, permissionIds: string[], actorPermissions?: string[]): Promise<Permission[]>;
65
+ grantDirectPermission(userId: string, permissionId: string, actorPermissions?: string[]): Promise<Permission[]>;
66
+ revokeDirectPermission(userId: string, permissionId: string): Promise<Permission[]>;
67
+ private findUserWithDirectPermissions;
68
+ private replaceDirectPermissions;
69
+ private sameCodes;
70
+ private sortedByCode;
61
71
  listGroups(): Promise<Group[]>;
62
72
  listManagedGroups(managerId: string): Promise<Group[]>;
63
73
  getGroup(id: string): Promise<Group>;
@@ -11,7 +11,9 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.RbacService = void 0;
13
13
  const common_1 = require("@nestjs/common");
14
+ const event_emitter_1 = require("@nestjs/event-emitter");
14
15
  const typeorm_1 = require("typeorm");
16
+ const appEvent_enum_1 = require("../../../core/events/appEvent.enum");
15
17
  const action_repository_1 = require("../infra/repositories/action.repository");
16
18
  const group_repository_1 = require("../infra/repositories/group.repository");
17
19
  const permission_repository_1 = require("../infra/repositories/permission.repository");
@@ -20,7 +22,7 @@ const role_repository_1 = require("../infra/repositories/role.repository");
20
22
  const scope_repository_1 = require("../infra/repositories/scope.repository");
21
23
  const user_repository_1 = require("../../users/infra/repositories/user.repository");
22
24
  let RbacService = class RbacService {
23
- constructor(resourceRepo, actionRepo, scopeRepo, permissionRepo, roleRepo, groupRepo, userRepo) {
25
+ constructor(resourceRepo, actionRepo, scopeRepo, permissionRepo, roleRepo, groupRepo, userRepo, events) {
24
26
  this.resourceRepo = resourceRepo;
25
27
  this.actionRepo = actionRepo;
26
28
  this.scopeRepo = scopeRepo;
@@ -28,6 +30,7 @@ let RbacService = class RbacService {
28
30
  this.roleRepo = roleRepo;
29
31
  this.groupRepo = groupRepo;
30
32
  this.userRepo = userRepo;
33
+ this.events = events;
31
34
  }
32
35
  async getUserPermissionCodes(userId) {
33
36
  const user = await this.userRepo.findOne({
@@ -345,6 +348,72 @@ let RbacService = class RbacService {
345
348
  await this.roleRepo.save(role);
346
349
  return this.getRole(roleId);
347
350
  }
351
+ async listUserDirectPermissions(userId) {
352
+ const user = await this.findUserWithDirectPermissions(userId);
353
+ return this.sortedByCode(user.directPermissions ?? []);
354
+ }
355
+ async setUserDirectPermissions(userId, permissionIds, actorPermissions) {
356
+ const user = await this.findUserWithDirectPermissions(userId);
357
+ const permissions = permissionIds.length
358
+ ? await this.permissionRepo.findBy({ id: (0, typeorm_1.In)(permissionIds) })
359
+ : [];
360
+ if (permissions.length !== permissionIds.length) {
361
+ throw new common_1.NotFoundException("Alguma das permissões informadas não existe");
362
+ }
363
+ this.assertGrantableByActor(permissions, actorPermissions);
364
+ return this.replaceDirectPermissions(user, permissions);
365
+ }
366
+ async grantDirectPermission(userId, permissionId, actorPermissions) {
367
+ const user = await this.findUserWithDirectPermissions(userId);
368
+ const permission = await this.permissionRepo.findOneBy({
369
+ id: permissionId,
370
+ });
371
+ if (!permission) {
372
+ throw new common_1.NotFoundException("Permissão não encontrada");
373
+ }
374
+ this.assertGrantableByActor([permission], actorPermissions);
375
+ const current = user.directPermissions ?? [];
376
+ if (current.some((p) => p.id === permissionId)) {
377
+ return this.sortedByCode(current);
378
+ }
379
+ return this.replaceDirectPermissions(user, [...current, permission]);
380
+ }
381
+ async revokeDirectPermission(userId, permissionId) {
382
+ const user = await this.findUserWithDirectPermissions(userId);
383
+ const current = user.directPermissions ?? [];
384
+ return this.replaceDirectPermissions(user, current.filter((p) => p.id !== permissionId));
385
+ }
386
+ async findUserWithDirectPermissions(userId) {
387
+ const user = await this.userRepo.findOne({
388
+ where: { id: userId },
389
+ relations: { directPermissions: true },
390
+ });
391
+ if (!user) {
392
+ throw new common_1.NotFoundException("Usuário não encontrado");
393
+ }
394
+ return user;
395
+ }
396
+ async replaceDirectPermissions(user, permissions) {
397
+ const previousCodes = (user.directPermissions ?? []).map((p) => p.code);
398
+ const codes = permissions.map((p) => p.code);
399
+ user.directPermissions = permissions;
400
+ await this.userRepo.save(user);
401
+ if (!this.sameCodes(previousCodes, codes)) {
402
+ this.events.emit(appEvent_enum_1.AppEvent.USER_PERMISSIONS_CHANGED, {
403
+ userId: user.id,
404
+ codes,
405
+ previousCodes,
406
+ });
407
+ }
408
+ return this.sortedByCode(permissions);
409
+ }
410
+ sameCodes(a, b) {
411
+ const before = new Set(a);
412
+ return a.length === b.length && b.every((code) => before.has(code));
413
+ }
414
+ sortedByCode(permissions) {
415
+ return [...permissions].sort((a, b) => a.code.localeCompare(b.code));
416
+ }
348
417
  listGroups() {
349
418
  return this.groupRepo.find({
350
419
  relations: { roles: true },
@@ -449,5 +518,6 @@ exports.RbacService = RbacService = __decorate([
449
518
  permission_repository_1.PermissionRepository,
450
519
  role_repository_1.RoleRepository,
451
520
  group_repository_1.GroupRepository,
452
- user_repository_1.UserRepository])
521
+ user_repository_1.UserRepository,
522
+ event_emitter_1.EventEmitter2])
453
523
  ], RbacService);
@@ -0,0 +1,3 @@
1
+ export declare class SetUserPermissionsCommand {
2
+ permissionIds: string[];
3
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SetUserPermissionsCommand = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ const class_validator_1 = require("class-validator");
15
+ class SetUserPermissionsCommand {
16
+ }
17
+ exports.SetUserPermissionsCommand = SetUserPermissionsCommand;
18
+ __decorate([
19
+ (0, swagger_1.ApiProperty)({ type: [String], format: "uuid" }),
20
+ (0, class_validator_1.IsArray)(),
21
+ (0, class_validator_1.ArrayUnique)(),
22
+ (0, class_validator_1.IsUUID)("all", { each: true }),
23
+ __metadata("design:type", Array)
24
+ ], SetUserPermissionsCommand.prototype, "permissionIds", void 0);
@@ -4,6 +4,7 @@ export * from "./commands/createPermission.command";
4
4
  export * from "./commands/createResource.command";
5
5
  export * from "./commands/createRole.command";
6
6
  export * from "./commands/createScope.command";
7
+ export * from "./commands/setUserPermissions.command";
7
8
  export * from "./commands/updateAction.command";
8
9
  export * from "./commands/updateGroup.command";
9
10
  export * from "./commands/updatePermission.command";
@@ -20,6 +20,7 @@ __exportStar(require("./commands/createPermission.command"), exports);
20
20
  __exportStar(require("./commands/createResource.command"), exports);
21
21
  __exportStar(require("./commands/createRole.command"), exports);
22
22
  __exportStar(require("./commands/createScope.command"), exports);
23
+ __exportStar(require("./commands/setUserPermissions.command"), exports);
23
24
  __exportStar(require("./commands/updateAction.command"), exports);
24
25
  __exportStar(require("./commands/updateGroup.command"), exports);
25
26
  __exportStar(require("./commands/updatePermission.command"), exports);
@@ -12,6 +12,7 @@ import { CreatePermissionCommand } from "./commands/createPermission.command";
12
12
  import { CreateResourceCommand } from "./commands/createResource.command";
13
13
  import { CreateRoleCommand } from "./commands/createRole.command";
14
14
  import { CreateScopeCommand } from "./commands/createScope.command";
15
+ import { SetUserPermissionsCommand } from "./commands/setUserPermissions.command";
15
16
  import { UpdateActionCommand } from "./commands/updateAction.command";
16
17
  import { UpdateGroupCommand } from "./commands/updateGroup.command";
17
18
  import { UpdatePermissionCommand } from "./commands/updatePermission.command";
@@ -52,4 +53,8 @@ export declare class RbacController {
52
53
  deleteRole(id: string): Promise<void>;
53
54
  addPermissionToRole(id: string, permissionId: string, actor: AuthenticatedUser): Promise<Role>;
54
55
  removePermissionFromRole(id: string, permissionId: string): Promise<Role>;
56
+ userPermissions(userId: string): Promise<Permission[]>;
57
+ setUserPermissions(userId: string, dto: SetUserPermissionsCommand, actor: AuthenticatedUser): Promise<Permission[]>;
58
+ grantUserPermission(userId: string, permissionId: string, actor: AuthenticatedUser): Promise<Permission[]>;
59
+ revokeUserPermission(userId: string, permissionId: string): Promise<Permission[]>;
55
60
  }
@@ -26,6 +26,7 @@ const createPermission_command_1 = require("./commands/createPermission.command"
26
26
  const createResource_command_1 = require("./commands/createResource.command");
27
27
  const createRole_command_1 = require("./commands/createRole.command");
28
28
  const createScope_command_1 = require("./commands/createScope.command");
29
+ const setUserPermissions_command_1 = require("./commands/setUserPermissions.command");
29
30
  const updateAction_command_1 = require("./commands/updateAction.command");
30
31
  const updateGroup_command_1 = require("./commands/updateGroup.command");
31
32
  const updatePermission_command_1 = require("./commands/updatePermission.command");
@@ -130,6 +131,18 @@ let RbacController = class RbacController {
130
131
  removePermissionFromRole(id, permissionId) {
131
132
  return this.rbac.removePermissionFromRole(id, permissionId);
132
133
  }
134
+ userPermissions(userId) {
135
+ return this.rbac.listUserDirectPermissions(userId);
136
+ }
137
+ setUserPermissions(userId, dto, actor) {
138
+ return this.rbac.setUserDirectPermissions(userId, dto.permissionIds, actor.permissions);
139
+ }
140
+ grantUserPermission(userId, permissionId, actor) {
141
+ return this.rbac.grantDirectPermission(userId, permissionId, actor.permissions);
142
+ }
143
+ revokeUserPermission(userId, permissionId) {
144
+ return this.rbac.revokeDirectPermission(userId, permissionId);
145
+ }
133
146
  };
134
147
  exports.RbacController = RbacController;
135
148
  __decorate([
@@ -420,6 +433,50 @@ __decorate([
420
433
  __metadata("design:paramtypes", [String, String]),
421
434
  __metadata("design:returntype", Promise)
422
435
  ], RbacController.prototype, "removePermissionFromRole", null);
436
+ __decorate([
437
+ (0, common_1.Get)("users/:userId/permissions"),
438
+ (0, requirePermission_decorator_1.RequirePermission)("users:read:any"),
439
+ (0, swagger_1.ApiOkResponse)({ type: rbac_response_1.PermissionResponse, isArray: true }),
440
+ __param(0, (0, common_1.Param)("userId", common_1.ParseUUIDPipe)),
441
+ __metadata("design:type", Function),
442
+ __metadata("design:paramtypes", [String]),
443
+ __metadata("design:returntype", Promise)
444
+ ], RbacController.prototype, "userPermissions", null);
445
+ __decorate([
446
+ (0, auditable_decorator_1.Auditable)("Permissões avulsas do usuário redefinidas"),
447
+ (0, common_1.Put)("users/:userId/permissions"),
448
+ (0, requirePermission_decorator_1.RequirePermission)("users:grant:any"),
449
+ (0, swagger_1.ApiOkResponse)({ type: rbac_response_1.PermissionResponse, isArray: true }),
450
+ __param(0, (0, common_1.Param)("userId", common_1.ParseUUIDPipe)),
451
+ __param(1, (0, common_1.Body)()),
452
+ __param(2, (0, currentUser_decorator_1.CurrentUser)()),
453
+ __metadata("design:type", Function),
454
+ __metadata("design:paramtypes", [String, setUserPermissions_command_1.SetUserPermissionsCommand, Object]),
455
+ __metadata("design:returntype", Promise)
456
+ ], RbacController.prototype, "setUserPermissions", null);
457
+ __decorate([
458
+ (0, auditable_decorator_1.Auditable)("Permissão avulsa concedida ao usuário"),
459
+ (0, common_1.Post)("users/:userId/permissions/:permissionId"),
460
+ (0, requirePermission_decorator_1.RequirePermission)("users:grant:any"),
461
+ (0, swagger_1.ApiOkResponse)({ type: rbac_response_1.PermissionResponse, isArray: true }),
462
+ __param(0, (0, common_1.Param)("userId", common_1.ParseUUIDPipe)),
463
+ __param(1, (0, common_1.Param)("permissionId", common_1.ParseUUIDPipe)),
464
+ __param(2, (0, currentUser_decorator_1.CurrentUser)()),
465
+ __metadata("design:type", Function),
466
+ __metadata("design:paramtypes", [String, String, Object]),
467
+ __metadata("design:returntype", Promise)
468
+ ], RbacController.prototype, "grantUserPermission", null);
469
+ __decorate([
470
+ (0, auditable_decorator_1.Auditable)("Permissão avulsa revogada do usuário"),
471
+ (0, common_1.Delete)("users/:userId/permissions/:permissionId"),
472
+ (0, requirePermission_decorator_1.RequirePermission)("users:grant:any"),
473
+ (0, swagger_1.ApiOkResponse)({ type: rbac_response_1.PermissionResponse, isArray: true }),
474
+ __param(0, (0, common_1.Param)("userId", common_1.ParseUUIDPipe)),
475
+ __param(1, (0, common_1.Param)("permissionId", common_1.ParseUUIDPipe)),
476
+ __metadata("design:type", Function),
477
+ __metadata("design:paramtypes", [String, String]),
478
+ __metadata("design:returntype", Promise)
479
+ ], RbacController.prototype, "revokeUserPermission", null);
423
480
  exports.RbacController = RbacController = __decorate([
424
481
  (0, swagger_1.ApiTags)("RBAC"),
425
482
  (0, swagger_1.ApiBearerAuth)(),
package/dist/index.d.ts CHANGED
@@ -56,12 +56,22 @@ export { Scope } from "./features/rbac/infra/schema/scope.schema";
56
56
  export { PasswordHistory } from "./features/users/infra/schema/passwordHistory.schema";
57
57
  export { User } from "./features/users/infra/schema/user.schema";
58
58
  export { auditContext, AuditContextStore } from "./core/context/auditContext";
59
- export { AppEvent, HttpErrorEvent, SystemCleanupFailedEvent, UserLockedEvent, UserPasswordExpiringEvent, UserPasswordResetRequestedEvent, UserRolesChangedEvent, UserTwoFactorResetEvent, } from "./core/events/appEvent.enum";
59
+ export { AppEvent, HttpErrorEvent, SystemCleanupFailedEvent, UserLockedEvent, UserPasswordExpiringEvent, UserPasswordResetRequestedEvent, UserPermissionsChangedEvent, UserRolesChangedEvent, UserTwoFactorResetEvent, } from "./core/events/appEvent.enum";
60
60
  export { AppErrorCode } from "./core/filters/appErrorCode.enum";
61
61
  export { codedBadRequest, codedTooManyRequests, codedUnauthorized, } from "./core/utils/codedError.util";
62
62
  export { ErrorType } from "./features/audit/domain/enums/errorType.enum";
63
63
  export { RequestOutcome } from "./features/audit/domain/enums/requestOutcome.enum";
64
64
  export { Auditable } from "./features/audit/presentation/decorators/auditable.decorator";
65
+ export { BullConnectionModule } from "./core/queue/bullConnection.module";
66
+ export { EnqueueOptions, JobCompleted, JobContext, JobEnvelope, JobFailed, JobProgress, JobRef, JobState, JobStatus, QueueCounts, QueueJobSummary, } from "./core/queue/job.interface";
67
+ export { JobProducer } from "./core/queue/job.producer";
68
+ export { JOB_EVENTS_PUBLISHER, JobEventsPublisher, } from "./core/queue/jobEvents.interface";
69
+ export { JobEventsService } from "./core/queue/jobEvents.service";
70
+ export { JobErrorResponse, JobProgressResponse, JobStatusResponse, } from "./core/queue/jobStatus.response";
71
+ export { JobWorkerHost, toSafeError } from "./core/queue/jobWorkerHost";
72
+ export { COMPLETED_RETENTION_SECONDS, FAILED_RETENTION_SECONDS, isRedisConfigured, JOB_ATTEMPTS, JOB_BACKOFF_DELAY_MS, JOB_COMPLETED_EVENT, JOB_CONCURRENCY, JOB_FAILED_EVENT, JOB_PROGRESS_EVENT, JOBS_QUEUE, queuePrefix, } from "./core/queue/queue.constants";
73
+ export { QueueModule } from "./core/queue/queue.module";
74
+ export { buildRedisOptions } from "./core/queue/redisConnection";
65
75
  export { BaseGateway, SocketData } from "./core/websocket/baseGateway";
66
76
  export { NOTIFICATIONS_NAMESPACE, roleRoom, SESSION_REFRESH_EVENT, userRoom, } from "./core/websocket/socket.constants";
67
77
  export { SOCKET_EVENT_VERSION, SocketEvent, wrapSocketEvent, } from "./core/websocket/socketEvent.envelope";
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveOrder = exports.PaginationQuery = exports.Paginated = exports.parseFilter = exports.paginateWithFilter = exports.resolveCatalogOrder = exports.toFilterSchema = exports.FilterOptionResponse = exports.FilterFieldResponse = exports.FilterOperator = exports.FilterCombinator = exports.FilterFieldType = exports.FILTER_MAX_NODES = exports.FILTER_MAX_LENGTH = exports.FILTER_MAX_IN_ITEMS = exports.FILTER_MAX_DEPTH = exports.DEFAULT_OPERATORS = exports.applyOwnershipScope = exports.applyFilter = exports.SCOPE_TEAM = exports.SCOPE_OWN = exports.SCOPE_ANY = exports.anyOrTeam = exports.anyOrOwn = exports.resolveOwnershipScope = exports.PermissionsGuard = exports.JwtAuthGuard = exports.CsrfGuard = exports.AppThrottlerGuard = exports.RequirePermission = exports.RequireAnyPermission = exports.PERMISSIONS_KEY = exports.ANY_PERMISSIONS_KEY = exports.Public = exports.IS_PUBLIC_KEY = exports.CurrentUser = exports.setAuthCookies = exports.REFRESH_COOKIE = exports.clearAuthCookies = exports.ACCESS_COOKIE = exports.EnvService = exports.validateEnv = exports.envSchema = exports.ConfigModule = exports.RlCoreModule = exports.coreEntities = exports.coreMigrations = exports.createCoreDataSource = exports.CoreModule = exports.bootstrapCore = void 0;
4
- exports.encryptSecret = exports.decryptSecret = exports.MailerService = exports.createAppLogger = exports.WS_USER_RESOLVER = exports.WsAuthService = exports.wrapSocketEvent = exports.SOCKET_EVENT_VERSION = exports.userRoom = exports.SESSION_REFRESH_EVENT = exports.roleRoom = exports.NOTIFICATIONS_NAMESPACE = exports.BaseGateway = exports.Auditable = exports.RequestOutcome = exports.ErrorType = exports.codedUnauthorized = exports.codedTooManyRequests = exports.codedBadRequest = exports.AppErrorCode = exports.AppEvent = exports.auditContext = exports.User = exports.PasswordHistory = exports.Scope = exports.Role = exports.Resource = exports.Permission = exports.Group = exports.Action = exports.NotificationsPublisher = exports.NotificationsModule = exports.NotificationScope = exports.NotificationRead = exports.Notification = exports.NotificationsService = exports.ScopeType = exports.NotificationType = exports.SCOPES = exports.ROLES = exports.RESOURCES = exports.PERMISSIONS = exports.ACTIONS = exports.AuthToken = exports.RequestLog = exports.AuditDataChange = exports.seedPermissions = exports.seedAdmin = exports.BaseEntity = exports.PaginationMetaResponse = void 0;
5
- exports.normalizeIp = exports.sha256 = exports.randomToken = exports.numericCode = exports.backupCode = void 0;
4
+ exports.JOB_ATTEMPTS = exports.isRedisConfigured = exports.FAILED_RETENTION_SECONDS = exports.COMPLETED_RETENTION_SECONDS = exports.toSafeError = exports.JobWorkerHost = exports.JobStatusResponse = exports.JobProgressResponse = exports.JobErrorResponse = exports.JobEventsService = exports.JOB_EVENTS_PUBLISHER = exports.JobProducer = exports.BullConnectionModule = exports.Auditable = exports.RequestOutcome = exports.ErrorType = exports.codedUnauthorized = exports.codedTooManyRequests = exports.codedBadRequest = exports.AppErrorCode = exports.AppEvent = exports.auditContext = exports.User = exports.PasswordHistory = exports.Scope = exports.Role = exports.Resource = exports.Permission = exports.Group = exports.Action = exports.NotificationsPublisher = exports.NotificationsModule = exports.NotificationScope = exports.NotificationRead = exports.Notification = exports.NotificationsService = exports.ScopeType = exports.NotificationType = exports.SCOPES = exports.ROLES = exports.RESOURCES = exports.PERMISSIONS = exports.ACTIONS = exports.AuthToken = exports.RequestLog = exports.AuditDataChange = exports.seedPermissions = exports.seedAdmin = exports.BaseEntity = exports.PaginationMetaResponse = void 0;
5
+ exports.normalizeIp = exports.sha256 = exports.randomToken = exports.numericCode = exports.backupCode = exports.encryptSecret = exports.decryptSecret = exports.MailerService = exports.createAppLogger = exports.WS_USER_RESOLVER = exports.WsAuthService = exports.wrapSocketEvent = exports.SOCKET_EVENT_VERSION = exports.userRoom = exports.SESSION_REFRESH_EVENT = exports.roleRoom = exports.NOTIFICATIONS_NAMESPACE = exports.BaseGateway = exports.buildRedisOptions = exports.QueueModule = exports.queuePrefix = exports.JOBS_QUEUE = exports.JOB_PROGRESS_EVENT = exports.JOB_FAILED_EVENT = exports.JOB_CONCURRENCY = exports.JOB_COMPLETED_EVENT = exports.JOB_BACKOFF_DELAY_MS = void 0;
6
6
  var bootstrapCore_util_1 = require("./core/bootstrap/bootstrapCore.util");
7
7
  Object.defineProperty(exports, "bootstrapCore", { enumerable: true, get: function () { return bootstrapCore_util_1.bootstrapCore; } });
8
8
  var core_module_1 = require("./core/core.module");
@@ -150,6 +150,37 @@ var requestOutcome_enum_1 = require("./features/audit/domain/enums/requestOutcom
150
150
  Object.defineProperty(exports, "RequestOutcome", { enumerable: true, get: function () { return requestOutcome_enum_1.RequestOutcome; } });
151
151
  var auditable_decorator_1 = require("./features/audit/presentation/decorators/auditable.decorator");
152
152
  Object.defineProperty(exports, "Auditable", { enumerable: true, get: function () { return auditable_decorator_1.Auditable; } });
153
+ var bullConnection_module_1 = require("./core/queue/bullConnection.module");
154
+ Object.defineProperty(exports, "BullConnectionModule", { enumerable: true, get: function () { return bullConnection_module_1.BullConnectionModule; } });
155
+ var job_producer_1 = require("./core/queue/job.producer");
156
+ Object.defineProperty(exports, "JobProducer", { enumerable: true, get: function () { return job_producer_1.JobProducer; } });
157
+ var jobEvents_interface_1 = require("./core/queue/jobEvents.interface");
158
+ Object.defineProperty(exports, "JOB_EVENTS_PUBLISHER", { enumerable: true, get: function () { return jobEvents_interface_1.JOB_EVENTS_PUBLISHER; } });
159
+ var jobEvents_service_1 = require("./core/queue/jobEvents.service");
160
+ Object.defineProperty(exports, "JobEventsService", { enumerable: true, get: function () { return jobEvents_service_1.JobEventsService; } });
161
+ var jobStatus_response_1 = require("./core/queue/jobStatus.response");
162
+ Object.defineProperty(exports, "JobErrorResponse", { enumerable: true, get: function () { return jobStatus_response_1.JobErrorResponse; } });
163
+ Object.defineProperty(exports, "JobProgressResponse", { enumerable: true, get: function () { return jobStatus_response_1.JobProgressResponse; } });
164
+ Object.defineProperty(exports, "JobStatusResponse", { enumerable: true, get: function () { return jobStatus_response_1.JobStatusResponse; } });
165
+ var jobWorkerHost_1 = require("./core/queue/jobWorkerHost");
166
+ Object.defineProperty(exports, "JobWorkerHost", { enumerable: true, get: function () { return jobWorkerHost_1.JobWorkerHost; } });
167
+ Object.defineProperty(exports, "toSafeError", { enumerable: true, get: function () { return jobWorkerHost_1.toSafeError; } });
168
+ var queue_constants_1 = require("./core/queue/queue.constants");
169
+ Object.defineProperty(exports, "COMPLETED_RETENTION_SECONDS", { enumerable: true, get: function () { return queue_constants_1.COMPLETED_RETENTION_SECONDS; } });
170
+ Object.defineProperty(exports, "FAILED_RETENTION_SECONDS", { enumerable: true, get: function () { return queue_constants_1.FAILED_RETENTION_SECONDS; } });
171
+ Object.defineProperty(exports, "isRedisConfigured", { enumerable: true, get: function () { return queue_constants_1.isRedisConfigured; } });
172
+ Object.defineProperty(exports, "JOB_ATTEMPTS", { enumerable: true, get: function () { return queue_constants_1.JOB_ATTEMPTS; } });
173
+ Object.defineProperty(exports, "JOB_BACKOFF_DELAY_MS", { enumerable: true, get: function () { return queue_constants_1.JOB_BACKOFF_DELAY_MS; } });
174
+ Object.defineProperty(exports, "JOB_COMPLETED_EVENT", { enumerable: true, get: function () { return queue_constants_1.JOB_COMPLETED_EVENT; } });
175
+ Object.defineProperty(exports, "JOB_CONCURRENCY", { enumerable: true, get: function () { return queue_constants_1.JOB_CONCURRENCY; } });
176
+ Object.defineProperty(exports, "JOB_FAILED_EVENT", { enumerable: true, get: function () { return queue_constants_1.JOB_FAILED_EVENT; } });
177
+ Object.defineProperty(exports, "JOB_PROGRESS_EVENT", { enumerable: true, get: function () { return queue_constants_1.JOB_PROGRESS_EVENT; } });
178
+ Object.defineProperty(exports, "JOBS_QUEUE", { enumerable: true, get: function () { return queue_constants_1.JOBS_QUEUE; } });
179
+ Object.defineProperty(exports, "queuePrefix", { enumerable: true, get: function () { return queue_constants_1.queuePrefix; } });
180
+ var queue_module_1 = require("./core/queue/queue.module");
181
+ Object.defineProperty(exports, "QueueModule", { enumerable: true, get: function () { return queue_module_1.QueueModule; } });
182
+ var redisConnection_1 = require("./core/queue/redisConnection");
183
+ Object.defineProperty(exports, "buildRedisOptions", { enumerable: true, get: function () { return redisConnection_1.buildRedisOptions; } });
153
184
  var baseGateway_1 = require("./core/websocket/baseGateway");
154
185
  Object.defineProperty(exports, "BaseGateway", { enumerable: true, get: function () { return baseGateway_1.BaseGateway; } });
155
186
  var socket_constants_1 = require("./core/websocket/socket.constants");
@@ -10,11 +10,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.RlCoreModule = void 0;
11
11
  const common_1 = require("@nestjs/common");
12
12
  const core_module_1 = require("./core/core.module");
13
+ const jobEvents_interface_1 = require("./core/queue/jobEvents.interface");
13
14
  const coreEntities_1 = require("./coreEntities");
14
15
  const audit_module_1 = require("./features/audit/audit.module");
15
16
  const auth_module_1 = require("./features/auth/auth.module");
16
17
  const maintenance_module_1 = require("./features/maintenance/maintenance.module");
17
18
  const notifications_module_1 = require("./features/notifications/notifications.module");
19
+ const notifications_gateway_1 = require("./features/notifications/presentation/notifications.gateway");
18
20
  const rbac_module_1 = require("./features/rbac/rbac.module");
19
21
  const users_module_1 = require("./features/users/users.module");
20
22
  let RlCoreModule = RlCoreModule_1 = class RlCoreModule {
@@ -33,6 +35,12 @@ let RlCoreModule = RlCoreModule_1 = class RlCoreModule {
33
35
  maintenance_module_1.MaintenanceModule,
34
36
  notifications_module_1.NotificationsModule,
35
37
  ],
38
+ providers: [
39
+ {
40
+ provide: jobEvents_interface_1.JOB_EVENTS_PUBLISHER,
41
+ useExisting: notifications_gateway_1.NotificationsGateway,
42
+ },
43
+ ],
36
44
  exports: [notifications_module_1.NotificationsModule],
37
45
  };
38
46
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rl-core-api",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Core NestJS: autenticação com 2FA, RBAC, auditoria, notificações e listagens com filtro dinâmico",
5
5
  "author": "Rodrigo Liberti",
6
6
  "license": "MIT",
@@ -69,6 +69,7 @@
69
69
  "zod": "^4.4.3"
70
70
  },
71
71
  "dependencies": {
72
+ "@nestjs/bullmq": "^11.0.5",
72
73
  "@nestjs/config": "^4.0.4",
73
74
  "@nestjs/event-emitter": "^3.1.0",
74
75
  "@nestjs/jwt": "^11.0.2",
@@ -78,9 +79,11 @@
78
79
  "@nestjs/throttler": "^6.5.0",
79
80
  "@nestjs/websockets": "^11.2.1",
80
81
  "argon2": "^0.45.1",
82
+ "bullmq": "^5.81.4",
81
83
  "cookie-parser": "^1.4.7",
82
84
  "dotenv": "^17.4.2",
83
85
  "helmet": "^8.3.0",
86
+ "ioredis": "^5.11.1",
84
87
  "mysql2": "^3.23.2",
85
88
  "nest-winston": "^1.10.2",
86
89
  "nodemailer": "^9.0.5",