rl-core-api 0.6.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 (75) hide show
  1. package/README.md +54 -0
  2. package/dist/core/auth/ownershipScope.util.d.ts +10 -0
  3. package/dist/core/auth/ownershipScope.util.js +14 -0
  4. package/dist/core/auth/permissionScope.util.d.ts +2 -0
  5. package/dist/core/auth/permissionScope.util.js +7 -1
  6. package/dist/core/config/env.schema.d.ts +3 -0
  7. package/dist/core/config/env.schema.js +3 -0
  8. package/dist/core/core.module.js +3 -1
  9. package/dist/core/database/migrations/1710000000000-InitialSchema.js +8 -1
  10. package/dist/core/database/migrations/index.js +0 -2
  11. package/dist/core/database/seeds/seedPermissions.d.ts +20 -1
  12. package/dist/core/database/seeds/seedPermissions.js +31 -38
  13. package/dist/core/events/appEvent.enum.d.ts +6 -0
  14. package/dist/core/events/appEvent.enum.js +1 -0
  15. package/dist/core/health/health.controller.d.ts +4 -1
  16. package/dist/core/health/health.controller.js +9 -3
  17. package/dist/core/query/applyOwnershipScope.util.d.ts +7 -0
  18. package/dist/core/query/applyOwnershipScope.util.js +16 -0
  19. package/dist/core/queue/bullConnection.module.d.ts +2 -0
  20. package/dist/core/queue/bullConnection.module.js +27 -0
  21. package/dist/core/queue/job.interface.d.ts +70 -0
  22. package/dist/core/queue/job.interface.js +2 -0
  23. package/dist/core/queue/job.producer.d.ts +15 -0
  24. package/dist/core/queue/job.producer.js +124 -0
  25. package/dist/core/queue/jobEvents.interface.d.ts +4 -0
  26. package/dist/core/queue/jobEvents.interface.js +4 -0
  27. package/dist/core/queue/jobEvents.service.d.ts +12 -0
  28. package/dist/core/queue/jobEvents.service.js +53 -0
  29. package/dist/core/queue/jobStatus.controller.d.ts +8 -0
  30. package/dist/core/queue/jobStatus.controller.js +44 -0
  31. package/dist/core/queue/jobStatus.response.d.ts +20 -0
  32. package/dist/core/queue/jobStatus.response.js +84 -0
  33. package/dist/core/queue/jobWorkerHost.d.ts +17 -0
  34. package/dist/core/queue/jobWorkerHost.js +72 -0
  35. package/dist/core/queue/queue.constants.d.ts +11 -0
  36. package/dist/core/queue/queue.constants.js +18 -0
  37. package/dist/core/queue/queue.module.d.ts +2 -0
  38. package/dist/core/queue/queue.module.js +38 -0
  39. package/dist/core/queue/queueAdmin.controller.d.ts +11 -0
  40. package/dist/core/queue/queueAdmin.controller.js +93 -0
  41. package/dist/core/queue/queueAdmin.response.d.ts +23 -0
  42. package/dist/core/queue/queueAdmin.response.js +87 -0
  43. package/dist/core/queue/queueAdmin.service.d.ts +15 -0
  44. package/dist/core/queue/queueAdmin.service.js +85 -0
  45. package/dist/core/queue/redisConnection.d.ts +2 -0
  46. package/dist/core/queue/redisConnection.js +11 -0
  47. package/dist/core/websocket/baseGateway.d.ts +1 -0
  48. package/dist/core/websocket/baseGateway.js +3 -0
  49. package/dist/features/audit/audit.module.js +1 -0
  50. package/dist/features/audit/presentation/index.d.ts +1 -0
  51. package/dist/features/audit/presentation/index.js +1 -0
  52. package/dist/features/audit/presentation/userPermissionsChanged.listener.d.ts +7 -0
  53. package/dist/features/audit/presentation/userPermissionsChanged.listener.js +43 -0
  54. package/dist/features/notifications/notifications.module.js +1 -1
  55. package/dist/features/notifications/presentation/domainEvents.listener.d.ts +2 -1
  56. package/dist/features/notifications/presentation/domainEvents.listener.js +19 -0
  57. package/dist/features/rbac/domain/rbac.catalog.js +19 -16
  58. package/dist/features/rbac/domain/rbac.service.d.ts +11 -1
  59. package/dist/features/rbac/domain/rbac.service.js +76 -3
  60. package/dist/features/rbac/domain/teamScope.service.d.ts +1 -0
  61. package/dist/features/rbac/domain/teamScope.service.js +10 -4
  62. package/dist/features/rbac/infra/schema/scope.schema.js +1 -1
  63. package/dist/features/rbac/presentation/commands/createScope.command.js +1 -1
  64. package/dist/features/rbac/presentation/commands/setUserPermissions.command.d.ts +3 -0
  65. package/dist/features/rbac/presentation/commands/setUserPermissions.command.js +24 -0
  66. package/dist/features/rbac/presentation/index.d.ts +1 -0
  67. package/dist/features/rbac/presentation/index.js +1 -0
  68. package/dist/features/rbac/presentation/rbac.controller.d.ts +5 -0
  69. package/dist/features/rbac/presentation/rbac.controller.js +57 -0
  70. package/dist/index.d.ts +17 -3
  71. package/dist/index.js +48 -2
  72. package/dist/rlCore.module.js +8 -0
  73. package/package.json +4 -1
  74. package/dist/core/database/migrations/1787270400000-GroupManager.d.ts +0 -6
  75. package/dist/core/database/migrations/1787270400000-GroupManager.js +0 -26
@@ -0,0 +1,124 @@
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
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ var JobProducer_1;
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.JobProducer = void 0;
20
+ const bullmq_1 = require("@nestjs/bullmq");
21
+ const common_1 = require("@nestjs/common");
22
+ const bullmq_2 = require("bullmq");
23
+ const ioredis_1 = __importDefault(require("ioredis"));
24
+ const queue_constants_1 = require("./queue.constants");
25
+ const redisConnection_1 = require("./redisConnection");
26
+ const PROBE_TIMEOUT_MS = 5000;
27
+ const HEALTH_TIMEOUT_MS = 1500;
28
+ const isStoredProgress = (value) => typeof value === "object" && value !== null && "percent" in value;
29
+ let JobProducer = JobProducer_1 = class JobProducer {
30
+ constructor(queue) {
31
+ this.queue = queue;
32
+ this.logger = new common_1.Logger(JobProducer_1.name);
33
+ }
34
+ async onApplicationBootstrap() {
35
+ if (!(0, queue_constants_1.isRedisConfigured)()) {
36
+ this.logger.log("Fila desligada: REDIS_HOST não declarado");
37
+ return;
38
+ }
39
+ await this.assertRedisReachable();
40
+ }
41
+ isAvailable() {
42
+ return Boolean(this.queue);
43
+ }
44
+ async enqueue(name, payload, options) {
45
+ const queue = this.requireQueue();
46
+ const envelope = {
47
+ ownerId: options.ownerId,
48
+ payload,
49
+ };
50
+ const job = await queue.add(name, envelope, {
51
+ jobId: options.jobId,
52
+ attempts: options.attempts ?? queue_constants_1.JOB_ATTEMPTS,
53
+ backoff: { type: "exponential", delay: queue_constants_1.JOB_BACKOFF_DELAY_MS },
54
+ removeOnComplete: { age: queue_constants_1.COMPLETED_RETENTION_SECONDS, count: 1000 },
55
+ removeOnFail: { age: queue_constants_1.FAILED_RETENTION_SECONDS },
56
+ });
57
+ return { jobId: String(job.id), name };
58
+ }
59
+ async status(jobId, ownerId) {
60
+ const queue = this.requireQueue();
61
+ const job = await queue.getJob(jobId);
62
+ if (!job || job.data?.ownerId !== ownerId) {
63
+ throw new common_1.NotFoundException("Job não encontrado");
64
+ }
65
+ const state = (await job.getState());
66
+ return {
67
+ id: String(job.id),
68
+ name: job.name,
69
+ state,
70
+ progress: isStoredProgress(job.progress) ? job.progress : null,
71
+ summary: job.returnvalue ?? null,
72
+ error: job.failedReason ? { code: null, message: job.failedReason } : null,
73
+ createdAt: new Date(job.timestamp).toISOString(),
74
+ finishedAt: job.finishedOn ? new Date(job.finishedOn).toISOString() : null,
75
+ };
76
+ }
77
+ async redisStatus() {
78
+ if (!(0, queue_constants_1.isRedisConfigured)()) {
79
+ return "off";
80
+ }
81
+ if (!this.queue) {
82
+ return "down";
83
+ }
84
+ const timeout = new Promise((resolve) => setTimeout(() => resolve("down"), HEALTH_TIMEOUT_MS).unref());
85
+ return Promise.race([
86
+ this.queue
87
+ .getJobCounts("waiting")
88
+ .then(() => "up")
89
+ .catch(() => "down"),
90
+ timeout,
91
+ ]);
92
+ }
93
+ requireQueue() {
94
+ if (!this.queue) {
95
+ throw new common_1.ServiceUnavailableException("Processamento em segundo plano indisponível neste ambiente");
96
+ }
97
+ return this.queue;
98
+ }
99
+ async assertRedisReachable() {
100
+ const probe = new ioredis_1.default({
101
+ ...(0, redisConnection_1.buildRedisOptions)(),
102
+ lazyConnect: true,
103
+ maxRetriesPerRequest: 1,
104
+ retryStrategy: () => null,
105
+ connectTimeout: PROBE_TIMEOUT_MS,
106
+ });
107
+ const failure = await probe
108
+ .connect()
109
+ .then(() => probe.ping())
110
+ .then(() => null)
111
+ .catch((err) => err);
112
+ probe.disconnect();
113
+ if (failure) {
114
+ throw new Error(`REDIS_HOST está declarado mas o Redis não respondeu: ${failure.message}`);
115
+ }
116
+ }
117
+ };
118
+ exports.JobProducer = JobProducer;
119
+ exports.JobProducer = JobProducer = JobProducer_1 = __decorate([
120
+ (0, common_1.Injectable)(),
121
+ __param(0, (0, common_1.Optional)()),
122
+ __param(0, (0, bullmq_1.InjectQueue)(queue_constants_1.JOBS_QUEUE)),
123
+ __metadata("design:paramtypes", [bullmq_2.Queue])
124
+ ], JobProducer);
@@ -0,0 +1,4 @@
1
+ export interface JobEventsPublisher {
2
+ emitToUser<T>(userId: string, event: string, data: T): void;
3
+ }
4
+ export declare const JOB_EVENTS_PUBLISHER: unique symbol;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JOB_EVENTS_PUBLISHER = void 0;
4
+ exports.JOB_EVENTS_PUBLISHER = Symbol("JOB_EVENTS_PUBLISHER");
@@ -0,0 +1,12 @@
1
+ import { ModuleRef } from "@nestjs/core";
2
+ export declare class JobEventsService {
3
+ private readonly moduleRef;
4
+ private readonly logger;
5
+ private publisher;
6
+ constructor(moduleRef: ModuleRef);
7
+ progress(ownerId: string, data: unknown): void;
8
+ completed(ownerId: string, data: unknown): void;
9
+ failed(ownerId: string, data: unknown): void;
10
+ private emit;
11
+ private resolve;
12
+ }
@@ -0,0 +1,53 @@
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
+ var JobEventsService_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.JobEventsService = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const core_1 = require("@nestjs/core");
16
+ const jobEvents_interface_1 = require("./jobEvents.interface");
17
+ const queue_constants_1 = require("./queue.constants");
18
+ let JobEventsService = JobEventsService_1 = class JobEventsService {
19
+ constructor(moduleRef) {
20
+ this.moduleRef = moduleRef;
21
+ this.logger = new common_1.Logger(JobEventsService_1.name);
22
+ }
23
+ progress(ownerId, data) {
24
+ this.emit(ownerId, queue_constants_1.JOB_PROGRESS_EVENT, data);
25
+ }
26
+ completed(ownerId, data) {
27
+ this.emit(ownerId, queue_constants_1.JOB_COMPLETED_EVENT, data);
28
+ }
29
+ failed(ownerId, data) {
30
+ this.emit(ownerId, queue_constants_1.JOB_FAILED_EVENT, data);
31
+ }
32
+ emit(ownerId, event, data) {
33
+ this.resolve()?.emitToUser(ownerId, event, data);
34
+ }
35
+ resolve() {
36
+ if (this.publisher !== undefined) {
37
+ return this.publisher;
38
+ }
39
+ try {
40
+ this.publisher = this.moduleRef.get(jobEvents_interface_1.JOB_EVENTS_PUBLISHER, { strict: false });
41
+ }
42
+ catch {
43
+ this.logger.warn("Fila sem canal de tempo real: o andamento só sai por GET /jobs/:id");
44
+ this.publisher = null;
45
+ }
46
+ return this.publisher;
47
+ }
48
+ };
49
+ exports.JobEventsService = JobEventsService;
50
+ exports.JobEventsService = JobEventsService = JobEventsService_1 = __decorate([
51
+ (0, common_1.Injectable)(),
52
+ __metadata("design:paramtypes", [core_1.ModuleRef])
53
+ ], JobEventsService);
@@ -0,0 +1,8 @@
1
+ import { AuthenticatedUser } from "../auth/authenticatedUser.interface";
2
+ import { JobStatus } from "./job.interface";
3
+ import { JobProducer } from "./job.producer";
4
+ export declare class JobStatusController {
5
+ private readonly jobs;
6
+ constructor(jobs: JobProducer);
7
+ status(id: string, actor: AuthenticatedUser): Promise<JobStatus>;
8
+ }
@@ -0,0 +1,44 @@
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
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.JobStatusController = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const swagger_1 = require("@nestjs/swagger");
18
+ const currentUser_decorator_1 = require("../auth/decorators/currentUser.decorator");
19
+ const job_producer_1 = require("./job.producer");
20
+ const jobStatus_response_1 = require("./jobStatus.response");
21
+ let JobStatusController = class JobStatusController {
22
+ constructor(jobs) {
23
+ this.jobs = jobs;
24
+ }
25
+ status(id, actor) {
26
+ return this.jobs.status(id, actor.id);
27
+ }
28
+ };
29
+ exports.JobStatusController = JobStatusController;
30
+ __decorate([
31
+ (0, common_1.Get)(":id"),
32
+ (0, swagger_1.ApiOkResponse)({ type: jobStatus_response_1.JobStatusResponse }),
33
+ __param(0, (0, common_1.Param)("id")),
34
+ __param(1, (0, currentUser_decorator_1.CurrentUser)()),
35
+ __metadata("design:type", Function),
36
+ __metadata("design:paramtypes", [String, Object]),
37
+ __metadata("design:returntype", Promise)
38
+ ], JobStatusController.prototype, "status", null);
39
+ exports.JobStatusController = JobStatusController = __decorate([
40
+ (0, swagger_1.ApiTags)("Jobs"),
41
+ (0, swagger_1.ApiBearerAuth)(),
42
+ (0, common_1.Controller)({ path: "jobs", version: "1" }),
43
+ __metadata("design:paramtypes", [job_producer_1.JobProducer])
44
+ ], JobStatusController);
@@ -0,0 +1,20 @@
1
+ export declare class JobProgressResponse {
2
+ processed: number;
3
+ total: number;
4
+ percent: number;
5
+ message?: string;
6
+ }
7
+ export declare class JobErrorResponse {
8
+ code: string | null;
9
+ message: string;
10
+ }
11
+ export declare class JobStatusResponse {
12
+ id: string;
13
+ name: string;
14
+ state: string;
15
+ progress: JobProgressResponse | null;
16
+ summary: unknown;
17
+ error: JobErrorResponse | null;
18
+ createdAt: string;
19
+ finishedAt: string | null;
20
+ }
@@ -0,0 +1,84 @@
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.JobStatusResponse = exports.JobErrorResponse = exports.JobProgressResponse = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ class JobProgressResponse {
15
+ }
16
+ exports.JobProgressResponse = JobProgressResponse;
17
+ __decorate([
18
+ (0, swagger_1.ApiProperty)({ example: 120 }),
19
+ __metadata("design:type", Number)
20
+ ], JobProgressResponse.prototype, "processed", void 0);
21
+ __decorate([
22
+ (0, swagger_1.ApiProperty)({ example: 175 }),
23
+ __metadata("design:type", Number)
24
+ ], JobProgressResponse.prototype, "total", void 0);
25
+ __decorate([
26
+ (0, swagger_1.ApiProperty)({ example: 69, description: "Inteiro de 0 a 100" }),
27
+ __metadata("design:type", Number)
28
+ ], JobProgressResponse.prototype, "percent", void 0);
29
+ __decorate([
30
+ (0, swagger_1.ApiProperty)({ required: false, example: "Lendo a planilha" }),
31
+ __metadata("design:type", String)
32
+ ], JobProgressResponse.prototype, "message", void 0);
33
+ class JobErrorResponse {
34
+ }
35
+ exports.JobErrorResponse = JobErrorResponse;
36
+ __decorate([
37
+ (0, swagger_1.ApiProperty)({ type: String, nullable: true, example: "INVALID_FILTER" }),
38
+ __metadata("design:type", Object)
39
+ ], JobErrorResponse.prototype, "code", void 0);
40
+ __decorate([
41
+ (0, swagger_1.ApiProperty)({ example: "Falha ao processar. Tente novamente" }),
42
+ __metadata("design:type", String)
43
+ ], JobErrorResponse.prototype, "message", void 0);
44
+ class JobStatusResponse {
45
+ }
46
+ exports.JobStatusResponse = JobStatusResponse;
47
+ __decorate([
48
+ (0, swagger_1.ApiProperty)({ example: "42" }),
49
+ __metadata("design:type", String)
50
+ ], JobStatusResponse.prototype, "id", void 0);
51
+ __decorate([
52
+ (0, swagger_1.ApiProperty)({ example: "funkos:import" }),
53
+ __metadata("design:type", String)
54
+ ], JobStatusResponse.prototype, "name", void 0);
55
+ __decorate([
56
+ (0, swagger_1.ApiProperty)({
57
+ example: "active",
58
+ enum: ["waiting", "active", "completed", "failed", "delayed", "unknown"],
59
+ }),
60
+ __metadata("design:type", String)
61
+ ], JobStatusResponse.prototype, "state", void 0);
62
+ __decorate([
63
+ (0, swagger_1.ApiProperty)({ type: JobProgressResponse, nullable: true }),
64
+ __metadata("design:type", Object)
65
+ ], JobStatusResponse.prototype, "progress", void 0);
66
+ __decorate([
67
+ (0, swagger_1.ApiProperty)({
68
+ nullable: true,
69
+ description: "Relatório que o processador devolveu, no formato da feature",
70
+ }),
71
+ __metadata("design:type", Object)
72
+ ], JobStatusResponse.prototype, "summary", void 0);
73
+ __decorate([
74
+ (0, swagger_1.ApiProperty)({ type: JobErrorResponse, nullable: true }),
75
+ __metadata("design:type", Object)
76
+ ], JobStatusResponse.prototype, "error", void 0);
77
+ __decorate([
78
+ (0, swagger_1.ApiProperty)({ format: "date-time" }),
79
+ __metadata("design:type", String)
80
+ ], JobStatusResponse.prototype, "createdAt", void 0);
81
+ __decorate([
82
+ (0, swagger_1.ApiProperty)({ type: String, format: "date-time", nullable: true }),
83
+ __metadata("design:type", Object)
84
+ ], JobStatusResponse.prototype, "finishedAt", void 0);
@@ -0,0 +1,17 @@
1
+ import { WorkerHost } from "@nestjs/bullmq";
2
+ import { Logger, OnApplicationBootstrap } from "@nestjs/common";
3
+ import { Job } from "bullmq";
4
+ import { JobContext, JobEnvelope } from "./job.interface";
5
+ import { JobEventsService } from "./jobEvents.service";
6
+ export declare abstract class JobWorkerHost<TPayload = unknown, TSummary = unknown> extends WorkerHost implements OnApplicationBootstrap {
7
+ protected readonly jobEvents: JobEventsService;
8
+ protected readonly logger: Logger;
9
+ constructor(jobEvents: JobEventsService);
10
+ onApplicationBootstrap(): void;
11
+ abstract handle(job: JobContext<TPayload>): Promise<TSummary>;
12
+ process(job: Job<JobEnvelope<TPayload>>): Promise<TSummary>;
13
+ }
14
+ export declare const toSafeError: (err: Error) => {
15
+ errorCode: string | null;
16
+ message: string;
17
+ };
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toSafeError = exports.JobWorkerHost = void 0;
4
+ const bullmq_1 = require("@nestjs/bullmq");
5
+ const common_1 = require("@nestjs/common");
6
+ class JobWorkerHost extends bullmq_1.WorkerHost {
7
+ constructor(jobEvents) {
8
+ super();
9
+ this.jobEvents = jobEvents;
10
+ this.logger = new common_1.Logger(this.constructor.name);
11
+ }
12
+ onApplicationBootstrap() {
13
+ if (!this.worker.isRunning()) {
14
+ void this.worker.run();
15
+ }
16
+ }
17
+ async process(job) {
18
+ const jobId = String(job.id);
19
+ const { ownerId, payload } = job.data;
20
+ try {
21
+ const summary = await this.handle({
22
+ jobId,
23
+ ownerId,
24
+ payload,
25
+ report: async (processed, total, message) => {
26
+ const percent = total > 0 ? Math.min(100, Math.round((processed / total) * 100)) : 0;
27
+ await job.updateProgress({ processed, total, percent, message });
28
+ this.jobEvents.progress(ownerId, {
29
+ jobId,
30
+ name: job.name,
31
+ processed,
32
+ total,
33
+ percent,
34
+ message,
35
+ });
36
+ },
37
+ });
38
+ this.jobEvents.completed(ownerId, {
39
+ jobId,
40
+ name: job.name,
41
+ summary,
42
+ });
43
+ return summary;
44
+ }
45
+ catch (err) {
46
+ this.logger.error(`Job ${job.name} #${jobId} falhou: ${err.message}`, err.stack);
47
+ if (job.attemptsMade + 1 >= (job.opts.attempts ?? 1)) {
48
+ this.jobEvents.failed(ownerId, {
49
+ jobId,
50
+ name: job.name,
51
+ ...(0, exports.toSafeError)(err),
52
+ });
53
+ }
54
+ throw err;
55
+ }
56
+ }
57
+ }
58
+ exports.JobWorkerHost = JobWorkerHost;
59
+ const toSafeError = (err) => {
60
+ if (err instanceof common_1.HttpException) {
61
+ const response = err.getResponse();
62
+ const code = typeof response === "object" && response !== null
63
+ ? (response.errorCode ?? null)
64
+ : null;
65
+ return { errorCode: code, message: err.message };
66
+ }
67
+ return {
68
+ errorCode: null,
69
+ message: "Falha ao processar. Tente novamente ou avise o suporte",
70
+ };
71
+ };
72
+ exports.toSafeError = toSafeError;
@@ -0,0 +1,11 @@
1
+ export declare const JOBS_QUEUE = "jobs";
2
+ export declare const JOB_PROGRESS_EVENT = "job:progress";
3
+ export declare const JOB_COMPLETED_EVENT = "job:completed";
4
+ export declare const JOB_FAILED_EVENT = "job:failed";
5
+ export declare const JOB_CONCURRENCY = 5;
6
+ export declare const JOB_ATTEMPTS = 3;
7
+ export declare const JOB_BACKOFF_DELAY_MS = 30000;
8
+ export declare const COMPLETED_RETENTION_SECONDS = 86400;
9
+ export declare const FAILED_RETENTION_SECONDS = 14400;
10
+ export declare const isRedisConfigured: () => boolean;
11
+ export declare const queuePrefix: () => string;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queuePrefix = exports.isRedisConfigured = exports.FAILED_RETENTION_SECONDS = exports.COMPLETED_RETENTION_SECONDS = exports.JOB_BACKOFF_DELAY_MS = exports.JOB_ATTEMPTS = exports.JOB_CONCURRENCY = exports.JOB_FAILED_EVENT = exports.JOB_COMPLETED_EVENT = exports.JOB_PROGRESS_EVENT = exports.JOBS_QUEUE = void 0;
4
+ const dotenv_1 = require("dotenv");
5
+ (0, dotenv_1.config)();
6
+ exports.JOBS_QUEUE = "jobs";
7
+ exports.JOB_PROGRESS_EVENT = "job:progress";
8
+ exports.JOB_COMPLETED_EVENT = "job:completed";
9
+ exports.JOB_FAILED_EVENT = "job:failed";
10
+ exports.JOB_CONCURRENCY = 5;
11
+ exports.JOB_ATTEMPTS = 3;
12
+ exports.JOB_BACKOFF_DELAY_MS = 30_000;
13
+ exports.COMPLETED_RETENTION_SECONDS = 86_400;
14
+ exports.FAILED_RETENTION_SECONDS = 14_400;
15
+ const isRedisConfigured = () => Boolean(process.env.REDIS_HOST);
16
+ exports.isRedisConfigured = isRedisConfigured;
17
+ const queuePrefix = () => process.env.REDIS_PREFIX ?? "core";
18
+ exports.queuePrefix = queuePrefix;
@@ -0,0 +1,2 @@
1
+ export declare class QueueModule {
2
+ }
@@ -0,0 +1,38 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.QueueModule = void 0;
10
+ const bullmq_1 = require("@nestjs/bullmq");
11
+ const common_1 = require("@nestjs/common");
12
+ const bullConnection_module_1 = require("./bullConnection.module");
13
+ const job_producer_1 = require("./job.producer");
14
+ const jobEvents_service_1 = require("./jobEvents.service");
15
+ const jobStatus_controller_1 = require("./jobStatus.controller");
16
+ const queue_constants_1 = require("./queue.constants");
17
+ const queueAdmin_controller_1 = require("./queueAdmin.controller");
18
+ const queueAdmin_service_1 = require("./queueAdmin.service");
19
+ const logger = new common_1.Logger("QueueModule");
20
+ const buildImports = () => {
21
+ if (!(0, queue_constants_1.isRedisConfigured)()) {
22
+ logger.warn("REDIS_HOST não configurado — a fila não será registrada e o enfileiramento fica indisponível");
23
+ return [];
24
+ }
25
+ return [bullConnection_module_1.BullConnectionModule, bullmq_1.BullModule.registerQueue({ name: queue_constants_1.JOBS_QUEUE })];
26
+ };
27
+ let QueueModule = class QueueModule {
28
+ };
29
+ exports.QueueModule = QueueModule;
30
+ exports.QueueModule = QueueModule = __decorate([
31
+ (0, common_1.Global)(),
32
+ (0, common_1.Module)({
33
+ imports: buildImports(),
34
+ controllers: [jobStatus_controller_1.JobStatusController, queueAdmin_controller_1.QueueAdminController],
35
+ providers: [job_producer_1.JobProducer, jobEvents_service_1.JobEventsService, queueAdmin_service_1.QueueAdminService],
36
+ exports: [job_producer_1.JobProducer, jobEvents_service_1.JobEventsService],
37
+ })
38
+ ], QueueModule);
@@ -0,0 +1,11 @@
1
+ import { QueueCounts, QueueJobSummary } from "./job.interface";
2
+ import { QueueAdminService } from "./queueAdmin.service";
3
+ import { Paginated } from "../utils/pagination.util";
4
+ export declare class QueueAdminController {
5
+ private readonly queues;
6
+ constructor(queues: QueueAdminService);
7
+ counts(): Promise<QueueCounts>;
8
+ jobs(state: string, page?: string, limit?: string): Promise<Paginated<QueueJobSummary>>;
9
+ retry(id: string): Promise<void>;
10
+ remove(id: string): Promise<void>;
11
+ }
@@ -0,0 +1,93 @@
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
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.QueueAdminController = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const swagger_1 = require("@nestjs/swagger");
18
+ const requirePermission_decorator_1 = require("../auth/decorators/requirePermission.decorator");
19
+ const permissions_guard_1 = require("../auth/guards/permissions.guard");
20
+ const queueAdmin_response_1 = require("./queueAdmin.response");
21
+ const queueAdmin_service_1 = require("./queueAdmin.service");
22
+ const DEFAULT_LIMIT = 20;
23
+ const MAX_LIMIT = 100;
24
+ let QueueAdminController = class QueueAdminController {
25
+ constructor(queues) {
26
+ this.queues = queues;
27
+ }
28
+ counts() {
29
+ return this.queues.counts();
30
+ }
31
+ jobs(state, page, limit) {
32
+ return this.queues.list(toState(state), toPositive(page, 1), Math.min(toPositive(limit, DEFAULT_LIMIT), MAX_LIMIT));
33
+ }
34
+ retry(id) {
35
+ return this.queues.retry(id);
36
+ }
37
+ remove(id) {
38
+ return this.queues.remove(id);
39
+ }
40
+ };
41
+ exports.QueueAdminController = QueueAdminController;
42
+ __decorate([
43
+ (0, common_1.Get)("counts"),
44
+ (0, requirePermission_decorator_1.RequirePermission)("queues:read:any"),
45
+ (0, swagger_1.ApiOkResponse)({ type: queueAdmin_response_1.QueueCountsResponse }),
46
+ __metadata("design:type", Function),
47
+ __metadata("design:paramtypes", []),
48
+ __metadata("design:returntype", Promise)
49
+ ], QueueAdminController.prototype, "counts", null);
50
+ __decorate([
51
+ (0, common_1.Get)("jobs"),
52
+ (0, requirePermission_decorator_1.RequirePermission)("queues:read:any"),
53
+ (0, swagger_1.ApiQuery)({ name: "state", enum: queueAdmin_service_1.LISTABLE_STATES }),
54
+ (0, swagger_1.ApiOkResponse)({ type: queueAdmin_response_1.PaginatedQueueJobsResponse }),
55
+ __param(0, (0, common_1.Query)("state")),
56
+ __param(1, (0, common_1.Query)("page")),
57
+ __param(2, (0, common_1.Query)("limit")),
58
+ __metadata("design:type", Function),
59
+ __metadata("design:paramtypes", [String, String, String]),
60
+ __metadata("design:returntype", Promise)
61
+ ], QueueAdminController.prototype, "jobs", null);
62
+ __decorate([
63
+ (0, common_1.Post)("jobs/:id/retry"),
64
+ (0, common_1.HttpCode)(204),
65
+ (0, requirePermission_decorator_1.RequirePermission)("queues:manage:any"),
66
+ __param(0, (0, common_1.Param)("id")),
67
+ __metadata("design:type", Function),
68
+ __metadata("design:paramtypes", [String]),
69
+ __metadata("design:returntype", Promise)
70
+ ], QueueAdminController.prototype, "retry", null);
71
+ __decorate([
72
+ (0, common_1.Delete)("jobs/:id"),
73
+ (0, common_1.HttpCode)(204),
74
+ (0, requirePermission_decorator_1.RequirePermission)("queues:manage:any"),
75
+ __param(0, (0, common_1.Param)("id")),
76
+ __metadata("design:type", Function),
77
+ __metadata("design:paramtypes", [String]),
78
+ __metadata("design:returntype", Promise)
79
+ ], QueueAdminController.prototype, "remove", null);
80
+ exports.QueueAdminController = QueueAdminController = __decorate([
81
+ (0, swagger_1.ApiTags)("Queues"),
82
+ (0, swagger_1.ApiBearerAuth)(),
83
+ (0, common_1.UseGuards)(permissions_guard_1.PermissionsGuard),
84
+ (0, common_1.Controller)({ path: "queues", version: "1" }),
85
+ __metadata("design:paramtypes", [queueAdmin_service_1.QueueAdminService])
86
+ ], QueueAdminController);
87
+ const toState = (value) => queueAdmin_service_1.LISTABLE_STATES.includes(value)
88
+ ? value
89
+ : "failed";
90
+ const toPositive = (value, fallback) => {
91
+ const parsed = Number(value);
92
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
93
+ };
@@ -0,0 +1,23 @@
1
+ import { PaginationMetaResponse } from "../utils/paginationMeta.response";
2
+ export declare class QueueCountsResponse {
3
+ waiting: number;
4
+ active: number;
5
+ completed: number;
6
+ failed: number;
7
+ delayed: number;
8
+ }
9
+ export declare class QueueJobResponse {
10
+ id: string;
11
+ name: string;
12
+ state: string;
13
+ ownerId: string | null;
14
+ attemptsMade: number;
15
+ percent: number | null;
16
+ failedReason: string | null;
17
+ createdAt: string;
18
+ finishedAt: string | null;
19
+ }
20
+ export declare class PaginatedQueueJobsResponse {
21
+ items: QueueJobResponse[];
22
+ meta: PaginationMetaResponse;
23
+ }