rl-core-api 0.14.4 → 0.15.1

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 (58) hide show
  1. package/dist/core/bootstrap/bootstrapCore.util.d.ts +1 -0
  2. package/dist/core/bootstrap/bootstrapCore.util.js +5 -0
  3. package/dist/core/bootstrap/bootstrapWorker.util.d.ts +6 -0
  4. package/dist/core/bootstrap/bootstrapWorker.util.js +27 -0
  5. package/dist/core/config/env.schema.d.ts +7 -0
  6. package/dist/core/config/env.schema.js +4 -0
  7. package/dist/core/export/export.controller.d.ts +14 -0
  8. package/dist/core/export/export.controller.js +86 -0
  9. package/dist/core/export/export.module.d.ts +2 -0
  10. package/dist/core/export/export.module.js +24 -0
  11. package/dist/core/export/export.processor.d.ts +16 -0
  12. package/dist/core/export/export.processor.js +62 -0
  13. package/dist/core/export/export.service.d.ts +13 -0
  14. package/dist/core/export/export.service.js +54 -0
  15. package/dist/core/export/exportAccepted.response.d.ts +4 -0
  16. package/dist/core/export/exportAccepted.response.js +24 -0
  17. package/dist/core/export/exportApi.module.d.ts +2 -0
  18. package/dist/core/export/exportApi.module.js +25 -0
  19. package/dist/core/export/exportCleanup.service.d.ts +9 -0
  20. package/dist/core/export/exportCleanup.service.js +49 -0
  21. package/dist/core/export/exportColumn.interface.d.ts +11 -0
  22. package/dist/core/export/exportColumn.interface.js +9 -0
  23. package/dist/core/export/exportEngine.service.d.ts +15 -0
  24. package/dist/core/export/exportEngine.service.js +151 -0
  25. package/dist/core/export/exportHandler.interface.d.ts +32 -0
  26. package/dist/core/export/exportHandler.interface.js +7 -0
  27. package/dist/core/export/exportHandler.registry.d.ts +13 -0
  28. package/dist/core/export/exportHandler.registry.js +58 -0
  29. package/dist/core/export/exportStorage.service.d.ts +13 -0
  30. package/dist/core/export/exportStorage.service.js +87 -0
  31. package/dist/core/export/exportWorker.module.d.ts +2 -0
  32. package/dist/core/export/exportWorker.module.js +22 -0
  33. package/dist/core/export/requestExport.command.d.ts +6 -0
  34. package/dist/core/export/requestExport.command.js +51 -0
  35. package/dist/core/query/paginateWithFilter.util.d.ts +1 -0
  36. package/dist/core/query/paginateWithFilter.util.js +3 -2
  37. package/dist/core/queue/job.interface.d.ts +3 -0
  38. package/dist/core/queue/job.producer.d.ts +7 -5
  39. package/dist/core/queue/job.producer.js +40 -15
  40. package/dist/core/queue/jobWorkerHost.js +6 -1
  41. package/dist/core/queue/queue.constants.d.ts +6 -0
  42. package/dist/core/queue/queue.constants.js +7 -1
  43. package/dist/core/queue/queue.module.js +4 -1
  44. package/dist/core/queue/queueAdmin.controller.d.ts +4 -4
  45. package/dist/core/queue/queueAdmin.controller.js +22 -12
  46. package/dist/core/queue/queueAdmin.response.d.ts +1 -0
  47. package/dist/core/queue/queueAdmin.response.js +8 -0
  48. package/dist/core/queue/queueAdmin.service.d.ts +5 -4
  49. package/dist/core/queue/queueAdmin.service.js +12 -10
  50. package/dist/core/queue/redisJobEvents.bridge.d.ts +10 -0
  51. package/dist/core/queue/redisJobEvents.bridge.js +61 -0
  52. package/dist/core/queue/redisJobEvents.publisher.d.ts +14 -0
  53. package/dist/core/queue/redisJobEvents.publisher.js +43 -0
  54. package/dist/index.d.ts +19 -2
  55. package/dist/index.js +46 -3
  56. package/dist/rlCore.module.d.ts +1 -0
  57. package/dist/rlCore.module.js +30 -0
  58. package/package.json +2 -1
@@ -0,0 +1,151 @@
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 ExportEngineService_1;
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.ExportEngineService = void 0;
20
+ const common_1 = require("@nestjs/common");
21
+ const typeorm_1 = require("@nestjs/typeorm");
22
+ const exceljs_1 = __importDefault(require("exceljs"));
23
+ const typeorm_2 = require("typeorm");
24
+ const env_service_1 = require("../config/env.service");
25
+ const exportColumn_interface_1 = require("./exportColumn.interface");
26
+ const applyFilter_util_1 = require("../query/applyFilter.util");
27
+ const filterSort_util_1 = require("../query/filterSort.util");
28
+ const paginateWithFilter_util_1 = require("../query/paginateWithFilter.util");
29
+ const PROGRESS_EVERY_ROWS = 1000;
30
+ const EMPTY_CELL = "-";
31
+ const DEFAULT_WIDTH = 20;
32
+ let ExportEngineService = ExportEngineService_1 = class ExportEngineService {
33
+ constructor(dataSource, env) {
34
+ this.dataSource = dataSource;
35
+ this.env = env;
36
+ this.logger = new common_1.Logger(ExportEngineService_1.name);
37
+ }
38
+ async run(handler, payload, ownerId, sink, onProgress) {
39
+ const maxRows = this.env.get("EXPORT_MAX_ROWS");
40
+ const [sql, parameters] = this.buildQuery(handler, payload, ownerId);
41
+ const workbook = new exceljs_1.default.stream.xlsx.WorkbookWriter({
42
+ stream: sink,
43
+ useSharedStrings: false,
44
+ useStyles: false,
45
+ });
46
+ const worksheet = workbook.addWorksheet(handler.sheetName);
47
+ worksheet.columns = handler.columns.map((column) => ({
48
+ header: column.header,
49
+ key: column.field,
50
+ width: column.width ?? DEFAULT_WIDTH,
51
+ }));
52
+ const runner = this.dataSource.createQueryRunner();
53
+ let totalRows = 0;
54
+ try {
55
+ await runner.connect();
56
+ const rows = await runner.stream(sql, parameters);
57
+ for await (const row of rows) {
58
+ if (totalRows >= maxRows) {
59
+ throw new common_1.BadRequestException(`O relatório passou de ${maxRows.toLocaleString("pt-BR")} linhas. Refine o filtro e tente de novo.`);
60
+ }
61
+ worksheet
62
+ .addRow(handler.columns.map((column) => toCellValue(row, column)))
63
+ .commit();
64
+ totalRows += 1;
65
+ if (totalRows % PROGRESS_EVERY_ROWS === 0) {
66
+ await onProgress(totalRows);
67
+ await waitForDrain(sink);
68
+ }
69
+ }
70
+ worksheet.commit();
71
+ await workbook.commit();
72
+ await onProgress(totalRows);
73
+ this.logger.log(`Relatório "${handler.name}" gerado com ${totalRows} linha(s)`);
74
+ return { totalRows };
75
+ }
76
+ finally {
77
+ await runner.release();
78
+ }
79
+ }
80
+ buildQuery(handler, payload, ownerId) {
81
+ const qb = handler.buildQuery({ ownerId });
82
+ (0, applyFilter_util_1.applyFilter)(qb, payload.filter);
83
+ (0, paginateWithFilter_util_1.applySearch)(qb, payload.search, handler.catalog, handler.searchFields);
84
+ const order = (0, filterSort_util_1.resolveCatalogOrder)(payload.sortBy, payload.sortDir, handler.catalog, qb.alias, handler.defaultSort);
85
+ qb.orderBy(order.column, order.direction);
86
+ return qb.getQueryAndParameters();
87
+ }
88
+ };
89
+ exports.ExportEngineService = ExportEngineService;
90
+ exports.ExportEngineService = ExportEngineService = ExportEngineService_1 = __decorate([
91
+ (0, common_1.Injectable)(),
92
+ __param(0, (0, typeorm_1.InjectDataSource)()),
93
+ __metadata("design:paramtypes", [typeorm_2.DataSource,
94
+ env_service_1.EnvService])
95
+ ], ExportEngineService);
96
+ const waitForDrain = async (sink) => {
97
+ if (!sink.writableNeedDrain) {
98
+ return;
99
+ }
100
+ await new Promise((done) => sink.once("drain", () => done()));
101
+ };
102
+ const toCellValue = (row, column) => {
103
+ const value = row[column.field];
104
+ if (value === null || value === undefined) {
105
+ return EMPTY_CELL;
106
+ }
107
+ if (column.format === exportColumn_interface_1.ExportColumnFormat.Date ||
108
+ column.format === exportColumn_interface_1.ExportColumnFormat.DateTime) {
109
+ return toLocaleDate(value, column.format);
110
+ }
111
+ if (column.format === exportColumn_interface_1.ExportColumnFormat.Currency) {
112
+ return toCurrency(value);
113
+ }
114
+ if (typeof value === "string" || typeof value === "number") {
115
+ return value;
116
+ }
117
+ if (typeof value === "boolean") {
118
+ return value ? "Sim" : "Não";
119
+ }
120
+ if (value instanceof Date) {
121
+ return toLocaleDate(value, exportColumn_interface_1.ExportColumnFormat.Date);
122
+ }
123
+ if (Buffer.isBuffer(value)) {
124
+ return value.toString("utf8");
125
+ }
126
+ return JSON.stringify(value) ?? EMPTY_CELL;
127
+ };
128
+ const toLocaleDate = (value, format) => {
129
+ if (!(value instanceof Date) &&
130
+ typeof value !== "string" &&
131
+ typeof value !== "number") {
132
+ return EMPTY_CELL;
133
+ }
134
+ const date = value instanceof Date ? value : new Date(value);
135
+ if (Number.isNaN(date.getTime())) {
136
+ return EMPTY_CELL;
137
+ }
138
+ return date.toLocaleString("pt-BR", {
139
+ day: "2-digit",
140
+ month: "2-digit",
141
+ year: "numeric",
142
+ ...(format === exportColumn_interface_1.ExportColumnFormat.DateTime && {
143
+ hour: "2-digit",
144
+ minute: "2-digit",
145
+ }),
146
+ });
147
+ };
148
+ const toCurrency = (value) => {
149
+ const parsed = typeof value === "number" ? value : Number(value);
150
+ return Number.isNaN(parsed) ? EMPTY_CELL : parsed;
151
+ };
@@ -0,0 +1,32 @@
1
+ import { ObjectLiteral, SelectQueryBuilder } from "typeorm";
2
+ import { ExportColumn } from "./exportColumn.interface";
3
+ import { FilterCatalog, FilterNode } from "../query/filterField.interface";
4
+ import { SortDir } from "../utils/pagination.util";
5
+ export interface ExportContext {
6
+ ownerId: string;
7
+ }
8
+ export interface ExportHandler<T extends ObjectLiteral = ObjectLiteral> {
9
+ readonly name: string;
10
+ readonly permission?: string;
11
+ readonly catalog: FilterCatalog;
12
+ readonly defaultSort: string;
13
+ readonly searchFields?: readonly string[];
14
+ readonly sheetName: string;
15
+ readonly columns: ExportColumn[];
16
+ buildFileName(): string;
17
+ buildQuery(context: ExportContext): SelectQueryBuilder<T>;
18
+ }
19
+ export declare const EXPORT_HANDLER = "rl-core:export-handler";
20
+ export declare const ExportReport: () => ClassDecorator;
21
+ export interface ExportJobPayload {
22
+ handler: string;
23
+ filter: FilterNode | null;
24
+ search?: string;
25
+ sortBy?: string;
26
+ sortDir?: SortDir;
27
+ fileName: string;
28
+ }
29
+ export interface ExportJobSummary {
30
+ fileName: string;
31
+ totalRows: number;
32
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExportReport = exports.EXPORT_HANDLER = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.EXPORT_HANDLER = "rl-core:export-handler";
6
+ const ExportReport = () => (0, common_1.SetMetadata)(exports.EXPORT_HANDLER, true);
7
+ exports.ExportReport = ExportReport;
@@ -0,0 +1,13 @@
1
+ import { OnModuleInit } from "@nestjs/common";
2
+ import { DiscoveryService } from "@nestjs/core";
3
+ import { ExportHandler } from "./exportHandler.interface";
4
+ export declare class ExportHandlerRegistry implements OnModuleInit {
5
+ private readonly discovery;
6
+ private readonly logger;
7
+ private readonly handlers;
8
+ constructor(discovery: DiscoveryService);
9
+ onModuleInit(): void;
10
+ find(name: string): ExportHandler | undefined;
11
+ require(name: string): ExportHandler;
12
+ names(): string[];
13
+ }
@@ -0,0 +1,58 @@
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 ExportHandlerRegistry_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ExportHandlerRegistry = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const core_1 = require("@nestjs/core");
16
+ const exportHandler_interface_1 = require("./exportHandler.interface");
17
+ let ExportHandlerRegistry = ExportHandlerRegistry_1 = class ExportHandlerRegistry {
18
+ constructor(discovery) {
19
+ this.discovery = discovery;
20
+ this.logger = new common_1.Logger(ExportHandlerRegistry_1.name);
21
+ this.handlers = new Map();
22
+ }
23
+ onModuleInit() {
24
+ const found = this.discovery
25
+ .getProviders()
26
+ .filter((wrapper) => wrapper.metatype &&
27
+ Reflect.getMetadata(exportHandler_interface_1.EXPORT_HANDLER, wrapper.metatype) === true)
28
+ .map((wrapper) => wrapper.instance)
29
+ .filter(Boolean);
30
+ for (const handler of found) {
31
+ if (this.handlers.has(handler.name)) {
32
+ throw new Error(`Dois relatórios registrados com o nome "${handler.name}"`);
33
+ }
34
+ this.handlers.set(handler.name, handler);
35
+ }
36
+ if (this.handlers.size > 0) {
37
+ this.logger.log(`Relatórios disponíveis: ${this.names().join(", ")}`);
38
+ }
39
+ }
40
+ find(name) {
41
+ return this.handlers.get(name);
42
+ }
43
+ require(name) {
44
+ const handler = this.find(name);
45
+ if (!handler) {
46
+ throw new common_1.NotFoundException("Relatório não encontrado");
47
+ }
48
+ return handler;
49
+ }
50
+ names() {
51
+ return [...this.handlers.keys()];
52
+ }
53
+ };
54
+ exports.ExportHandlerRegistry = ExportHandlerRegistry;
55
+ exports.ExportHandlerRegistry = ExportHandlerRegistry = ExportHandlerRegistry_1 = __decorate([
56
+ (0, common_1.Injectable)(),
57
+ __metadata("design:paramtypes", [core_1.DiscoveryService])
58
+ ], ExportHandlerRegistry);
@@ -0,0 +1,13 @@
1
+ import { WriteStream } from "fs";
2
+ import { EnvService } from "../config/env.service";
3
+ export declare class ExportStorageService {
4
+ private readonly env;
5
+ private readonly logger;
6
+ private readonly root;
7
+ constructor(env: EnvService);
8
+ pathFor(ownerId: string, jobId: string): string;
9
+ openWriteStream(ownerId: string, jobId: string): Promise<WriteStream>;
10
+ exists(ownerId: string, jobId: string): Promise<boolean>;
11
+ remove(ownerId: string, jobId: string): Promise<void>;
12
+ removeExpired(maxAgeMs: number): Promise<number>;
13
+ }
@@ -0,0 +1,87 @@
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 ExportStorageService_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.ExportStorageService = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const fs_1 = require("fs");
16
+ const promises_1 = require("fs/promises");
17
+ const path_1 = require("path");
18
+ const env_service_1 = require("../config/env.service");
19
+ const SAFE_ID = /^[A-Za-z0-9_-]{1,64}$/;
20
+ const EXTENSION = ".xlsx";
21
+ let ExportStorageService = ExportStorageService_1 = class ExportStorageService {
22
+ constructor(env) {
23
+ this.env = env;
24
+ this.logger = new common_1.Logger(ExportStorageService_1.name);
25
+ const configured = this.env.get("EXPORT_DIR");
26
+ this.root = (0, path_1.isAbsolute)(configured)
27
+ ? configured
28
+ : (0, path_1.join)(process.cwd(), configured);
29
+ }
30
+ pathFor(ownerId, jobId) {
31
+ if (!SAFE_ID.test(ownerId) || !SAFE_ID.test(jobId)) {
32
+ throw new Error("Identificador inválido para caminho de relatório");
33
+ }
34
+ const path = (0, path_1.resolve)(this.root, ownerId, `${jobId}${EXTENSION}`);
35
+ if (!path.startsWith((0, path_1.resolve)(this.root))) {
36
+ throw new Error("Caminho de relatório fora da pasta de exportação");
37
+ }
38
+ return path;
39
+ }
40
+ async openWriteStream(ownerId, jobId) {
41
+ const path = this.pathFor(ownerId, jobId);
42
+ await (0, promises_1.mkdir)((0, path_1.dirname)(path), { recursive: true });
43
+ return (0, fs_1.createWriteStream)(path);
44
+ }
45
+ async exists(ownerId, jobId) {
46
+ return await (0, promises_1.stat)(this.pathFor(ownerId, jobId))
47
+ .then(() => true)
48
+ .catch(() => false);
49
+ }
50
+ async remove(ownerId, jobId) {
51
+ await (0, promises_1.rm)(this.pathFor(ownerId, jobId), { force: true });
52
+ }
53
+ async removeExpired(maxAgeMs) {
54
+ const cutoff = Date.now() - maxAgeMs;
55
+ const owners = await (0, promises_1.readdir)(this.root, { withFileTypes: true }).catch(() => []);
56
+ let removed = 0;
57
+ for (const owner of owners) {
58
+ if (!owner.isDirectory()) {
59
+ continue;
60
+ }
61
+ const ownerDir = (0, path_1.join)(this.root, owner.name);
62
+ const files = await (0, promises_1.readdir)(ownerDir).catch(() => []);
63
+ for (const file of files) {
64
+ const path = (0, path_1.join)(ownerDir, file);
65
+ const info = await (0, promises_1.stat)(path).catch(() => null);
66
+ if (!info?.isFile() || info.mtimeMs > cutoff) {
67
+ continue;
68
+ }
69
+ await (0, promises_1.rm)(path, { force: true });
70
+ removed += 1;
71
+ }
72
+ const left = await (0, promises_1.readdir)(ownerDir).catch(() => ["-"]);
73
+ if (left.length === 0) {
74
+ await (0, promises_1.rm)(ownerDir, { recursive: true, force: true });
75
+ }
76
+ }
77
+ if (removed > 0) {
78
+ this.logger.log(`Faxina de relatórios: ${removed} arquivo(s) removido(s)`);
79
+ }
80
+ return removed;
81
+ }
82
+ };
83
+ exports.ExportStorageService = ExportStorageService;
84
+ exports.ExportStorageService = ExportStorageService = ExportStorageService_1 = __decorate([
85
+ (0, common_1.Injectable)(),
86
+ __metadata("design:paramtypes", [env_service_1.EnvService])
87
+ ], ExportStorageService);
@@ -0,0 +1,2 @@
1
+ export declare class ExportWorkerModule {
2
+ }
@@ -0,0 +1,22 @@
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.ExportWorkerModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const export_module_1 = require("./export.module");
12
+ const export_processor_1 = require("./export.processor");
13
+ const queue_constants_1 = require("../queue/queue.constants");
14
+ let ExportWorkerModule = class ExportWorkerModule {
15
+ };
16
+ exports.ExportWorkerModule = ExportWorkerModule;
17
+ exports.ExportWorkerModule = ExportWorkerModule = __decorate([
18
+ (0, common_1.Module)({
19
+ imports: [export_module_1.ExportModule],
20
+ providers: (0, queue_constants_1.isRedisConfigured)() ? [export_processor_1.ExportProcessor] : [],
21
+ })
22
+ ], ExportWorkerModule);
@@ -0,0 +1,6 @@
1
+ export declare class RequestExportCommand {
2
+ filter?: string;
3
+ search?: string;
4
+ sortBy?: string;
5
+ sortDir?: "ASC" | "DESC";
6
+ }
@@ -0,0 +1,51 @@
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.RequestExportCommand = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ const class_validator_1 = require("class-validator");
15
+ const filter_constants_1 = require("../query/filter.constants");
16
+ const SEARCH_MAX_LENGTH = 200;
17
+ class RequestExportCommand {
18
+ }
19
+ exports.RequestExportCommand = RequestExportCommand;
20
+ __decorate([
21
+ (0, swagger_1.ApiPropertyOptional)({
22
+ description: "Filtro dinâmico, no mesmo formato da listagem",
23
+ example: '{"createdAt":{"$BETWEEN":["2026-01-01","2026-06-30"]}}',
24
+ }),
25
+ (0, class_validator_1.IsOptional)(),
26
+ (0, class_validator_1.IsString)(),
27
+ (0, class_validator_1.MaxLength)(filter_constants_1.FILTER_MAX_LENGTH),
28
+ __metadata("design:type", String)
29
+ ], RequestExportCommand.prototype, "filter", void 0);
30
+ __decorate([
31
+ (0, swagger_1.ApiPropertyOptional)({
32
+ description: "Busca livre, a mesma da toolbar da listagem",
33
+ example: "marvel",
34
+ }),
35
+ (0, class_validator_1.IsOptional)(),
36
+ (0, class_validator_1.IsString)(),
37
+ (0, class_validator_1.MaxLength)(SEARCH_MAX_LENGTH),
38
+ __metadata("design:type", String)
39
+ ], RequestExportCommand.prototype, "search", void 0);
40
+ __decorate([
41
+ (0, swagger_1.ApiPropertyOptional)({ description: "Campo de ordenação do catálogo" }),
42
+ (0, class_validator_1.IsOptional)(),
43
+ (0, class_validator_1.IsString)(),
44
+ __metadata("design:type", String)
45
+ ], RequestExportCommand.prototype, "sortBy", void 0);
46
+ __decorate([
47
+ (0, swagger_1.ApiPropertyOptional)({ enum: ["ASC", "DESC"] }),
48
+ (0, class_validator_1.IsOptional)(),
49
+ (0, class_validator_1.IsIn)(["ASC", "DESC"]),
50
+ __metadata("design:type", String)
51
+ ], RequestExportCommand.prototype, "sortDir", void 0);
@@ -9,3 +9,4 @@ export interface PaginateWithFilterOptions {
9
9
  searchFields?: readonly string[];
10
10
  }
11
11
  export declare const paginateWithFilter: <T extends ObjectLiteral>(qb: SelectQueryBuilder<T>, options: PaginateWithFilterOptions) => Promise<[T[], number]>;
12
+ export declare const applySearch: <T extends ObjectLiteral>(qb: SelectQueryBuilder<T>, search: string | undefined, catalog: FilterCatalog, searchFields: readonly string[] | undefined) => void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.paginateWithFilter = void 0;
3
+ exports.applySearch = exports.paginateWithFilter = void 0;
4
4
  const applyFilter_util_1 = require("./applyFilter.util");
5
5
  const filterOperator_enum_1 = require("./filterOperator.enum");
6
6
  const filterSort_util_1 = require("./filterSort.util");
@@ -8,7 +8,7 @@ const parseFilter_util_1 = require("./parseFilter.util");
8
8
  const paginateWithFilter = async (qb, options) => {
9
9
  const { query, catalog, defaultSort, permissions, searchFields } = options;
10
10
  (0, applyFilter_util_1.applyFilter)(qb, (0, parseFilter_util_1.parseFilter)(query.filter, catalog, permissions));
11
- applySearch(qb, query.search, catalog, searchFields);
11
+ (0, exports.applySearch)(qb, query.search, catalog, searchFields);
12
12
  const order = (0, filterSort_util_1.resolveCatalogOrder)(query.sortBy, query.sortDir, catalog, qb.alias, defaultSort);
13
13
  return await qb
14
14
  .orderBy(order.column, order.direction)
@@ -39,3 +39,4 @@ const applySearch = (qb, search, catalog, searchFields) => {
39
39
  }
40
40
  qb.andWhere(`(${parts.map((part) => part.sql).join(" OR ")})`, Object.assign({}, ...parts.map((part) => part.params)));
41
41
  };
42
+ exports.applySearch = applySearch;
@@ -1,3 +1,4 @@
1
+ import { QueueName } from "./queue.constants";
1
2
  export type JobState = "waiting" | "active" | "completed" | "failed" | "delayed" | "unknown";
2
3
  export interface JobRef {
3
4
  jobId: string;
@@ -36,6 +37,7 @@ export interface JobStatus<TSummary = unknown> {
36
37
  finishedAt: string | null;
37
38
  }
38
39
  export interface EnqueueOptions {
40
+ queue?: QueueName;
39
41
  ownerId: string;
40
42
  jobId?: string;
41
43
  attempts?: number;
@@ -45,6 +47,7 @@ export interface QueueJobSummary {
45
47
  name: string;
46
48
  state: JobState;
47
49
  ownerId: string | null;
50
+ payload: unknown;
48
51
  attemptsMade: number;
49
52
  percent: number | null;
50
53
  failedReason: string | null;
@@ -1,15 +1,17 @@
1
1
  import { OnApplicationBootstrap } from "@nestjs/common";
2
2
  import { Queue } from "bullmq";
3
3
  import { EnqueueOptions, JobEnvelope, JobRef, JobStatus } from "./job.interface";
4
+ import { QueueName } from "./queue.constants";
4
5
  export declare class JobProducer implements OnApplicationBootstrap {
5
- private readonly queue?;
6
6
  private readonly logger;
7
- constructor(queue?: Queue<JobEnvelope> | undefined);
7
+ private readonly queues;
8
+ constructor(jobs?: Queue<JobEnvelope>, exports?: Queue<JobEnvelope>);
8
9
  onApplicationBootstrap(): Promise<void>;
9
- isAvailable(): boolean;
10
+ isAvailable(queue?: QueueName): boolean;
10
11
  enqueue<TPayload>(name: string, payload: TPayload, options: EnqueueOptions): Promise<JobRef>;
11
- status<TSummary>(jobId: string, ownerId: string): Promise<JobStatus<TSummary>>;
12
+ status<TSummary>(jobId: string, ownerId: string, queueName?: QueueName): Promise<JobStatus<TSummary>>;
12
13
  redisStatus(): Promise<"up" | "down" | "off">;
13
- requireQueue(): Queue<JobEnvelope>;
14
+ requireQueue(name?: QueueName): Queue<JobEnvelope>;
15
+ private withTimeout;
14
16
  private assertRedisReachable;
15
17
  }
@@ -24,12 +24,19 @@ const ioredis_1 = __importDefault(require("ioredis"));
24
24
  const queue_constants_1 = require("./queue.constants");
25
25
  const redisConnection_1 = require("./redisConnection");
26
26
  const PROBE_TIMEOUT_MS = 5000;
27
+ const ENQUEUE_TIMEOUT_MS = 5000;
27
28
  const HEALTH_TIMEOUT_MS = 1500;
28
29
  const isStoredProgress = (value) => typeof value === "object" && value !== null && "percent" in value;
29
30
  let JobProducer = JobProducer_1 = class JobProducer {
30
- constructor(queue) {
31
- this.queue = queue;
31
+ constructor(jobs, exports) {
32
32
  this.logger = new common_1.Logger(JobProducer_1.name);
33
+ this.queues = new Map();
34
+ if (jobs) {
35
+ this.queues.set(queue_constants_1.JOBS_QUEUE, jobs);
36
+ }
37
+ if (exports) {
38
+ this.queues.set(queue_constants_1.EXPORTS_QUEUE, exports);
39
+ }
33
40
  }
34
41
  async onApplicationBootstrap() {
35
42
  if (!(0, queue_constants_1.isRedisConfigured)()) {
@@ -38,26 +45,26 @@ let JobProducer = JobProducer_1 = class JobProducer {
38
45
  }
39
46
  await this.assertRedisReachable();
40
47
  }
41
- isAvailable() {
42
- return Boolean(this.queue);
48
+ isAvailable(queue = queue_constants_1.JOBS_QUEUE) {
49
+ return this.queues.has(queue);
43
50
  }
44
51
  async enqueue(name, payload, options) {
45
- const queue = this.requireQueue();
52
+ const queue = this.requireQueue(options.queue);
46
53
  const envelope = {
47
54
  ownerId: options.ownerId,
48
55
  payload,
49
56
  };
50
- const job = await queue.add(name, envelope, {
57
+ const job = await this.withTimeout(queue.add(name, envelope, {
51
58
  jobId: options.jobId,
52
59
  attempts: options.attempts ?? queue_constants_1.JOB_ATTEMPTS,
53
60
  backoff: { type: "exponential", delay: queue_constants_1.JOB_BACKOFF_DELAY_MS },
54
61
  removeOnComplete: { age: queue_constants_1.COMPLETED_RETENTION_SECONDS, count: 1000 },
55
62
  removeOnFail: { age: queue_constants_1.FAILED_RETENTION_SECONDS },
56
- });
63
+ }));
57
64
  return { jobId: String(job.id), name };
58
65
  }
59
- async status(jobId, ownerId) {
60
- const queue = this.requireQueue();
66
+ async status(jobId, ownerId, queueName) {
67
+ const queue = this.requireQueue(queueName);
61
68
  const job = await queue.getJob(jobId);
62
69
  if (!job || job.data?.ownerId !== ownerId) {
63
70
  throw new common_1.NotFoundException("Job não encontrado");
@@ -78,23 +85,38 @@ let JobProducer = JobProducer_1 = class JobProducer {
78
85
  if (!(0, queue_constants_1.isRedisConfigured)()) {
79
86
  return "off";
80
87
  }
81
- if (!this.queue) {
88
+ const queue = this.queues.get(queue_constants_1.JOBS_QUEUE);
89
+ if (!queue) {
82
90
  return "down";
83
91
  }
84
92
  const timeout = new Promise((resolve) => setTimeout(() => resolve("down"), HEALTH_TIMEOUT_MS).unref());
85
93
  return Promise.race([
86
- this.queue
94
+ queue
87
95
  .getJobCounts("waiting")
88
96
  .then(() => "up")
89
97
  .catch(() => "down"),
90
98
  timeout,
91
99
  ]);
92
100
  }
93
- requireQueue() {
94
- if (!this.queue) {
101
+ requireQueue(name = queue_constants_1.JOBS_QUEUE) {
102
+ const queue = this.queues.get(name);
103
+ if (!queue) {
95
104
  throw new common_1.ServiceUnavailableException("Processamento em segundo plano indisponível neste ambiente");
96
105
  }
97
- return this.queue;
106
+ return queue;
107
+ }
108
+ async withTimeout(operation) {
109
+ let timer;
110
+ const expired = new Promise((_, reject) => {
111
+ timer = setTimeout(() => reject(new common_1.ServiceUnavailableException("Processamento em segundo plano não respondeu. Tente de novo em instantes")), ENQUEUE_TIMEOUT_MS);
112
+ timer.unref();
113
+ });
114
+ try {
115
+ return await Promise.race([operation, expired]);
116
+ }
117
+ finally {
118
+ clearTimeout(timer);
119
+ }
98
120
  }
99
121
  async assertRedisReachable() {
100
122
  const probe = new ioredis_1.default({
@@ -120,5 +142,8 @@ exports.JobProducer = JobProducer = JobProducer_1 = __decorate([
120
142
  (0, common_1.Injectable)(),
121
143
  __param(0, (0, common_1.Optional)()),
122
144
  __param(0, (0, bullmq_1.InjectQueue)(queue_constants_1.JOBS_QUEUE)),
123
- __metadata("design:paramtypes", [bullmq_2.Queue])
145
+ __param(1, (0, common_1.Optional)()),
146
+ __param(1, (0, bullmq_1.InjectQueue)(queue_constants_1.EXPORTS_QUEUE)),
147
+ __metadata("design:paramtypes", [bullmq_2.Queue,
148
+ bullmq_2.Queue])
124
149
  ], JobProducer);