@smartsoft001/crud-shell-nestjs 2.76.0 → 2.80.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.
package/index.js ADDED
@@ -0,0 +1,1840 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
13
+
14
+ // packages/crud/shell/nestjs/src/lib/nestjs.module.ts
15
+ import { Module } from "@nestjs/common";
16
+ import { JwtModule } from "@nestjs/jwt";
17
+ import { PassportModule } from "@nestjs/passport";
18
+
19
+ // packages/crud/shell/app-services/src/lib/services/crud/crud.service.ts
20
+ import { Injectable, Logger } from "@nestjs/common";
21
+ import * as CombinedStream from "combined-stream";
22
+ import { Guid as Guid2 } from "guid-typescript";
23
+ import { Memoize } from "lodash-decorators";
24
+
25
+ // packages/shared/models/src/lib/symbols.ts
26
+ var SYMBOL_MODEL = Symbol.for("smartsoft:model");
27
+ var SYMBOL_FIELD = Symbol.for("smartsoft:field");
28
+
29
+ // packages/shared/models/src/lib/decorators/model/model.decorator.ts
30
+ import "reflect-metadata";
31
+
32
+ // packages/shared/models/src/lib/decorators/field/field.decorator.ts
33
+ import "reflect-metadata";
34
+
35
+ // packages/shared/utils/src/lib/services/password/password.service.ts
36
+ import * as md5_ from "md5";
37
+ var md5 = md5_;
38
+ var PasswordService = class _PasswordService {
39
+ /**
40
+ * Hash password text
41
+ * @param p {string} - text
42
+ * @return - hashed text
43
+ */
44
+ static hash(p) {
45
+ return Promise.resolve(md5(p));
46
+ }
47
+ /**
48
+ * Compare password text with hashed text
49
+ * @param p {string} - password text
50
+ * @param h {string} - hashed text
51
+ */
52
+ static async compare(p, h) {
53
+ const hp = await _PasswordService.hash(p);
54
+ return hp === h;
55
+ }
56
+ };
57
+
58
+ // packages/shared/utils/src/lib/services/object/object.service.ts
59
+ import { stringify } from "flatted";
60
+ var ObjectService = class {
61
+ /***
62
+ * Create object with data
63
+ * @param data {object} - data to set
64
+ * @param type {type} - new type
65
+ * @return - new type object
66
+ */
67
+ static createByType(data, type) {
68
+ if (!data)
69
+ return data;
70
+ try {
71
+ if (data instanceof type)
72
+ return data;
73
+ } catch (e) {
74
+ console.warn(e);
75
+ }
76
+ const result = new type();
77
+ Object.keys(data).forEach((key) => {
78
+ result[key] = data[key];
79
+ });
80
+ return result;
81
+ }
82
+ /***
83
+ * Remove object type from data
84
+ * @param obj {object} - object
85
+ * @return - object without type
86
+ */
87
+ static removeTypes(obj) {
88
+ if (!obj)
89
+ return obj;
90
+ const result = {};
91
+ Object.keys(obj).forEach((key) => {
92
+ if (obj[key] && obj[key].constructor && !(obj[key] instanceof Date)) {
93
+ let stringValue = "";
94
+ try {
95
+ stringValue = JSON.stringify(obj[key]);
96
+ } catch (e) {
97
+ console.warn("can't stringify without circular package");
98
+ stringValue = stringify(obj[key]);
99
+ }
100
+ result[key] = JSON.parse(stringValue);
101
+ } else {
102
+ result[key] = obj[key];
103
+ }
104
+ });
105
+ return result;
106
+ }
107
+ };
108
+
109
+ // packages/shared/utils/src/lib/services/guid/guid.service.ts
110
+ import { Guid } from "guid-typescript";
111
+ var GuidService = class {
112
+ /***
113
+ * Create guid as string
114
+ */
115
+ static create() {
116
+ return Guid.raw();
117
+ }
118
+ };
119
+
120
+ // packages/shared/utils/src/lib/services/array/array.service.ts
121
+ import * as _ from "lodash";
122
+
123
+ // packages/shared/models/src/lib/utils.ts
124
+ function getModelFieldKeys(type) {
125
+ if (!type["__fields"])
126
+ return [];
127
+ return Object.keys(type["__fields"]);
128
+ }
129
+ function getModelFieldOptions(instance, fieldKey) {
130
+ return Reflect.getMetadata(SYMBOL_FIELD, instance, fieldKey);
131
+ }
132
+ function getModelFieldsWithOptions(instance) {
133
+ const keys = getModelFieldKeys(instance.constructor);
134
+ return keys.map((item) => {
135
+ return {
136
+ key: item,
137
+ options: getModelFieldOptions(instance, item)
138
+ };
139
+ });
140
+ }
141
+ function isModel(instance) {
142
+ if (!instance || !instance.constructor)
143
+ return false;
144
+ return Reflect.hasMetadata(SYMBOL_MODEL, instance.constructor);
145
+ }
146
+ function getInvalidFields(instance, mode, permissions) {
147
+ const result = [];
148
+ getModelFieldsWithOptions(instance).forEach(({ key, options }) => {
149
+ let required = options.required;
150
+ if ((mode === "create" || mode === "update") && options[mode] && options[mode]?.constructor) {
151
+ required = options[mode].required;
152
+ if (required && permissions && options[mode].permissions) {
153
+ required = options[mode]?.permissions?.some(
154
+ (op) => permissions.some((p) => p === op)
155
+ );
156
+ }
157
+ }
158
+ if (required && (instance[key] === null || instance[key] === void 0 || instance[key] === ""))
159
+ result.push(key);
160
+ });
161
+ return result;
162
+ }
163
+ function castModel(instance, mode, permissions) {
164
+ if (!isModel(instance))
165
+ return;
166
+ const fieldsWithOptions = getModelFieldsWithOptions(instance);
167
+ Object.keys(instance).filter((key) => key !== "id").forEach((key) => {
168
+ const fieldWidthOptions = fieldsWithOptions.find((f) => f.key === key);
169
+ if (!fieldWidthOptions) {
170
+ delete instance[key];
171
+ return;
172
+ }
173
+ if ((mode === "create" || mode === "update") && (!fieldWidthOptions.options[mode] || permissions && fieldWidthOptions.options[mode].permissions && !fieldWidthOptions.options[mode]?.permissions?.some((op) => permissions.some((p) => op === p)))) {
174
+ delete instance[key];
175
+ return;
176
+ } else if (mode !== "create" && mode !== "update" && (!fieldWidthOptions.options.customs || !fieldWidthOptions.options.customs.some((c) => c.mode === mode))) {
177
+ delete instance[key];
178
+ return;
179
+ }
180
+ });
181
+ }
182
+
183
+ // packages/shared/domain-core/src/lib/errors.ts
184
+ var DomainValidationError = class _DomainValidationError extends Error {
185
+ constructor(msg) {
186
+ super(msg);
187
+ this.type = _DomainValidationError;
188
+ }
189
+ };
190
+ var DomainForbiddenError = class _DomainForbiddenError extends Error {
191
+ constructor(msg) {
192
+ super(msg);
193
+ this.type = _DomainForbiddenError;
194
+ }
195
+ };
196
+
197
+ // packages/shared/domain-core/src/lib/repositories.ts
198
+ var IUnitOfWork = class {
199
+ };
200
+ var IItemRepository = class {
201
+ };
202
+ var IAttachmentRepository = class {
203
+ };
204
+
205
+ // packages/crud/shell/app-services/src/lib/services/crud/crud.service.ts
206
+ var CrudService = class {
207
+ constructor(permissionService, repository, attachmentRepository) {
208
+ this.permissionService = permissionService;
209
+ this.repository = repository;
210
+ this.attachmentRepository = attachmentRepository;
211
+ this._logger = new Logger(CrudService.name, { timestamp: true });
212
+ }
213
+ async create(data, user) {
214
+ data.id = Guid2.raw();
215
+ try {
216
+ this.permissionService.valid("create", user);
217
+ castModel(data, "create", user.permissions);
218
+ this.checkValidCreate(data, user.permissions);
219
+ if (data["password"]) {
220
+ data["password"] = await PasswordService.hash(data["password"]);
221
+ }
222
+ if (data["passwordConfirm"]) {
223
+ delete data["passwordConfirm"];
224
+ }
225
+ await this.repository.create(data, user);
226
+ return data.id;
227
+ } catch (e) {
228
+ this._logger.error(e);
229
+ throw e;
230
+ }
231
+ }
232
+ async createMany(data, user, options) {
233
+ data.forEach((item) => {
234
+ item.id = Guid2.raw();
235
+ });
236
+ try {
237
+ this.permissionService.valid("create", user);
238
+ data.forEach((item) => {
239
+ castModel(item, "create", user.permissions);
240
+ this.checkValidCreate(item, user.permissions);
241
+ });
242
+ if (options && options.mode === "replace") {
243
+ await this.repository.clear(user);
244
+ }
245
+ for (let index = 0; index < data.length; index++) {
246
+ const item = data[index];
247
+ if (item["password"]) {
248
+ item["password"] = await PasswordService.hash(item["password"]);
249
+ }
250
+ if (item["passwordConfirm"]) {
251
+ delete item["passwordConfirm"];
252
+ }
253
+ }
254
+ await this.repository.createMany(data, user);
255
+ } catch (e) {
256
+ this._logger.error(e);
257
+ throw e;
258
+ }
259
+ return data;
260
+ }
261
+ async readById(id, user) {
262
+ try {
263
+ this.permissionService.valid("read", user);
264
+ const result = await this.repository.getById(id);
265
+ delete result["password"];
266
+ return result;
267
+ } catch (e) {
268
+ this._logger.error(e);
269
+ throw e;
270
+ }
271
+ }
272
+ async read(criteria, options, user) {
273
+ try {
274
+ this.permissionService.valid("read", user);
275
+ const result = await this.repository.getByCriteria(criteria, options);
276
+ result.data.forEach((item) => delete item["password"]);
277
+ return result;
278
+ } catch (e) {
279
+ this._logger.error(e);
280
+ throw e;
281
+ }
282
+ }
283
+ readBySpec(spec, options, user) {
284
+ return this.read(spec.criteria, options, user);
285
+ }
286
+ async update(id, data, user) {
287
+ try {
288
+ data.id = id;
289
+ this.permissionService.valid("update", user);
290
+ castModel(data, "update", user.permissions);
291
+ this.checkValidUpdate(data, user.permissions);
292
+ if (data["password"]) {
293
+ data["password"] = await PasswordService.hash(data["password"]);
294
+ }
295
+ if (data["passwordConfirm"]) {
296
+ delete data["passwordConfirm"];
297
+ }
298
+ await this.repository.update(data, user);
299
+ } catch (e) {
300
+ this._logger.error(e);
301
+ throw e;
302
+ }
303
+ }
304
+ async updatePartial(id, data, user) {
305
+ try {
306
+ data.id = id;
307
+ this.permissionService.valid("update", user);
308
+ castModel(data, "update", user.permissions);
309
+ this.checkValidUpdatePartial(data, user.permissions);
310
+ if (data["password"]) {
311
+ data["password"] = await PasswordService.hash(data["password"]);
312
+ }
313
+ if (data["passwordConfirm"]) {
314
+ delete data["passwordConfirm"];
315
+ }
316
+ await this.repository.updatePartial(data, user);
317
+ } catch (e) {
318
+ this._logger.error(e);
319
+ throw e;
320
+ }
321
+ }
322
+ async delete(id, user) {
323
+ try {
324
+ this.permissionService.valid("delete", user);
325
+ await this.repository.delete(id, user);
326
+ } catch (e) {
327
+ this._logger.error(e);
328
+ throw e;
329
+ }
330
+ }
331
+ async uploadAttachment(data, options) {
332
+ if (!data.id) {
333
+ data.id = GuidService.create();
334
+ }
335
+ let oldId = null;
336
+ if (options?.start) {
337
+ oldId = data.id;
338
+ const stream = await this.attachmentRepository.getStream(data.id, {
339
+ start: 0,
340
+ end: options.start - 1
341
+ });
342
+ const combinedStream = CombinedStream.create();
343
+ combinedStream.append(stream);
344
+ combinedStream.append(data.stream);
345
+ data.stream = combinedStream;
346
+ data.id = GuidService.create();
347
+ }
348
+ this.attachmentRepository.upload(data, options);
349
+ if (oldId)
350
+ await this.attachmentRepository.delete(data.id);
351
+ return data.id;
352
+ }
353
+ getAttachmentInfo(id) {
354
+ return this.attachmentRepository.getInfo(id);
355
+ }
356
+ getAttachmentStream(id, options) {
357
+ return this.attachmentRepository.getStream(id, options);
358
+ }
359
+ async deleteAttachment(id) {
360
+ return this.attachmentRepository.delete(id);
361
+ }
362
+ changes(criteria) {
363
+ return this.repository.changesByCriteria(criteria);
364
+ }
365
+ checkValidCreate(item, permissions) {
366
+ const array = getInvalidFields(item, "create", permissions);
367
+ if (array.length) {
368
+ throw new DomainValidationError("Required fields: " + array.join(", "));
369
+ }
370
+ }
371
+ checkValidUpdate(item, permissions) {
372
+ const array = getInvalidFields(item, "update", permissions);
373
+ if (array.length) {
374
+ throw new DomainValidationError("Required fields: " + array.join(", "));
375
+ }
376
+ }
377
+ checkValidUpdatePartial(item, permissions) {
378
+ if (!isModel(item))
379
+ return;
380
+ const keys = Object.keys(item);
381
+ const array = getInvalidFields(item, "update", permissions).filter(
382
+ (invalidField) => keys.some((key) => key === invalidField)
383
+ );
384
+ if (array.length) {
385
+ throw new DomainValidationError("Required fields: " + array.join(", "));
386
+ }
387
+ }
388
+ };
389
+ __decorateClass([
390
+ Memoize()
391
+ ], CrudService.prototype, "getAttachmentInfo", 1);
392
+ CrudService = __decorateClass([
393
+ Injectable()
394
+ ], CrudService);
395
+
396
+ // packages/crud/shell/app-services/src/lib/services/index.ts
397
+ var SERVICES = [CrudService];
398
+
399
+ // packages/shared/mongo/src/lib/mongo.config.ts
400
+ var MongoConfig = class {
401
+ };
402
+
403
+ // packages/shared/mongo/src/lib/mongo.unitofwork.ts
404
+ import { Injectable as Injectable2 } from "@nestjs/common";
405
+ import { MongoClient } from "mongodb";
406
+
407
+ // packages/shared/mongo/src/lib/mongo.utils.ts
408
+ function getMongoUrl(config) {
409
+ let url;
410
+ if (config.username && config.password)
411
+ url = `mongodb://${config.username}:${config.password}@${config.host}:${config.port}`;
412
+ else
413
+ url = `mongodb://${config.host}:${config.port}`;
414
+ url = url + "?authSource=" + config.database;
415
+ if (config.host.indexOf("ondigitalocean.com") > -1) {
416
+ url = url.replace("mongodb://", "mongodb+srv://");
417
+ url = url + "&tls=true";
418
+ }
419
+ return url;
420
+ }
421
+
422
+ // packages/shared/mongo/src/lib/mongo.unitofwork.ts
423
+ var MongoUnitOfWork = class extends IUnitOfWork {
424
+ constructor(config) {
425
+ super();
426
+ this.config = config;
427
+ }
428
+ async scope(definition) {
429
+ const client = await MongoClient.connect(this.getUrl());
430
+ const session = client.startSession();
431
+ const transactionOptions = {
432
+ readPreference: "primary",
433
+ readConcern: { level: "local" },
434
+ writeConcern: { w: "majority" }
435
+ };
436
+ let error = null;
437
+ try {
438
+ await session.withTransaction(async () => {
439
+ await definition({
440
+ session,
441
+ connection: client
442
+ });
443
+ }, transactionOptions);
444
+ } catch (e) {
445
+ error = e;
446
+ } finally {
447
+ await session.endSession();
448
+ await client.close();
449
+ }
450
+ if (error)
451
+ throw error;
452
+ }
453
+ getUrl() {
454
+ return getMongoUrl(this.config);
455
+ }
456
+ };
457
+ MongoUnitOfWork = __decorateClass([
458
+ Injectable2()
459
+ ], MongoUnitOfWork);
460
+
461
+ // packages/shared/mongo/src/lib/repositories/attachment.repository.ts
462
+ import { Injectable as Injectable3 } from "@nestjs/common";
463
+ import { MongoClient as MongoClient2 } from "mongodb";
464
+ import * as mongo from "mongodb";
465
+ var MongoAttachmentRepository = class extends IAttachmentRepository {
466
+ constructor(config) {
467
+ super();
468
+ this.config = config;
469
+ }
470
+ async upload(data, options) {
471
+ const client = await MongoClient2.connect(this.getUrl());
472
+ return await new Promise((res, rej) => {
473
+ const db = client.db(this.config.database);
474
+ const bucket = new mongo.GridFSBucket(db, {
475
+ bucketName: this.config.collection
476
+ });
477
+ const writeStream = bucket.openUploadStreamWithId(
478
+ data.id,
479
+ data.fileName,
480
+ {
481
+ contentType: data.mimeType
482
+ }
483
+ );
484
+ if (options?.streamCallback)
485
+ options.streamCallback(writeStream);
486
+ data.stream.pipe(writeStream);
487
+ writeStream.on("error", (error) => {
488
+ rej(error);
489
+ });
490
+ writeStream.on("finish", () => {
491
+ res();
492
+ });
493
+ });
494
+ }
495
+ async getInfo(id) {
496
+ const client = await MongoClient2.connect(this.getUrl());
497
+ const db = client.db(this.config.database);
498
+ const bucket = new mongo.GridFSBucket(db, {
499
+ bucketName: this.config.collection
500
+ });
501
+ const items = await bucket.find({
502
+ _id: id
503
+ }).toArray();
504
+ if (!items || items.length === 0)
505
+ return null;
506
+ return {
507
+ fileName: items[0].filename,
508
+ contentType: items[0].contentType,
509
+ length: items[0].length
510
+ };
511
+ }
512
+ async getStream(id, options) {
513
+ const client = await MongoClient2.connect(this.getUrl());
514
+ const db = client.db(this.config.database);
515
+ const bucket = new mongo.GridFSBucket(db, {
516
+ bucketName: this.config.collection
517
+ });
518
+ return bucket.openDownloadStream(id, options);
519
+ }
520
+ async delete(id) {
521
+ const client = await MongoClient2.connect(this.getUrl());
522
+ const db = client.db(this.config.database);
523
+ const bucket = new mongo.GridFSBucket(db, {
524
+ bucketName: this.config.collection
525
+ });
526
+ await bucket.delete(id);
527
+ await client.close();
528
+ }
529
+ getUrl() {
530
+ return getMongoUrl(this.config);
531
+ }
532
+ };
533
+ MongoAttachmentRepository = __decorateClass([
534
+ Injectable3()
535
+ ], MongoAttachmentRepository);
536
+
537
+ // packages/shared/mongo/src/lib/repositories/item.repository.ts
538
+ import { Injectable as Injectable4 } from "@nestjs/common";
539
+ import {
540
+ MongoClient as MongoClient3
541
+ } from "mongodb";
542
+ import { Observable } from "rxjs";
543
+ import { finalize, share } from "rxjs/operators";
544
+ var MongoItemRepository = class extends IItemRepository {
545
+ constructor(config) {
546
+ super();
547
+ this.config = config;
548
+ }
549
+ async create(item, user, repoOptions) {
550
+ await this.collectionContext(async (collection) => {
551
+ try {
552
+ await collection.insertOne(this.getModelToCreate(item, user), {
553
+ session: repoOptions?.transaction?.session
554
+ });
555
+ this.logChange("create", item, repoOptions, user, null).then();
556
+ } catch (errInsert) {
557
+ this.logChange("create", item, repoOptions, user, errInsert).then();
558
+ throw errInsert;
559
+ }
560
+ });
561
+ }
562
+ async clear(user, repoOptions) {
563
+ await this.collectionContext(async (collection) => {
564
+ try {
565
+ await collection.deleteMany(
566
+ {},
567
+ { session: repoOptions?.transaction?.session }
568
+ );
569
+ this.logChange("clear", null, repoOptions, user, null).then();
570
+ } catch (errClear) {
571
+ this.logChange("clear", null, repoOptions, user, errClear).then();
572
+ throw errClear;
573
+ }
574
+ });
575
+ }
576
+ async createMany(list, user, repoOptions) {
577
+ await this.collectionContext(async (collection) => {
578
+ try {
579
+ await collection.insertMany(
580
+ list.map((item) => this.getModelToCreate(item, user)),
581
+ { session: repoOptions?.transaction?.session }
582
+ );
583
+ this.logChange("createMany", null, repoOptions, user, null).then();
584
+ } catch (errInsert) {
585
+ this.logChange("createMany", null, repoOptions, user, errInsert).then();
586
+ throw errInsert;
587
+ }
588
+ });
589
+ }
590
+ async update(item, user, repoOptions) {
591
+ await this.collectionContext(async (collection) => {
592
+ try {
593
+ const info = await this.getInfo(item.id, collection);
594
+ await collection.replaceOne(
595
+ { _id: item.id },
596
+ this.getModelToUpdate(item, user, info),
597
+ { session: repoOptions?.transaction?.session }
598
+ );
599
+ this.logChange("update", item, repoOptions, user, null).then();
600
+ } catch (errInsert) {
601
+ this.logChange("update", item, repoOptions, user, errInsert).then();
602
+ throw errInsert;
603
+ }
604
+ });
605
+ }
606
+ async updatePartial(item, user, repoOptions) {
607
+ await this.collectionContext(async (collection) => {
608
+ try {
609
+ const info = await this.getInfo(item.id, collection);
610
+ await collection.updateOne(
611
+ { _id: item.id },
612
+ {
613
+ $set: this.getModelToUpdate(item, user, info)
614
+ },
615
+ { session: repoOptions?.transaction?.session }
616
+ );
617
+ this.logChange("updatePartial", item, repoOptions, user, null).then();
618
+ } catch (errUpdate) {
619
+ this.logChange(
620
+ "updatePartial",
621
+ item,
622
+ repoOptions,
623
+ user,
624
+ errUpdate
625
+ ).then();
626
+ throw errUpdate;
627
+ }
628
+ });
629
+ }
630
+ async updatePartialManyByCriteria(criteria, set, user, repoOptions) {
631
+ await this.collectionContext(async (collection) => {
632
+ try {
633
+ this.convertIdInCriteria(criteria);
634
+ await collection.updateMany(
635
+ criteria,
636
+ {
637
+ $set: {
638
+ ...set,
639
+ "__info.update": {
640
+ username: user?.username,
641
+ date: /* @__PURE__ */ new Date()
642
+ }
643
+ }
644
+ },
645
+ { session: repoOptions?.transaction?.session }
646
+ );
647
+ this.logChange(
648
+ "updatePartialManyByCriteria",
649
+ {
650
+ ...criteria,
651
+ set
652
+ },
653
+ repoOptions,
654
+ user,
655
+ null
656
+ ).then();
657
+ } catch (errUpdate) {
658
+ this.logChange(
659
+ "updatePartialManyByCriteria",
660
+ {
661
+ ...criteria,
662
+ set
663
+ },
664
+ repoOptions,
665
+ user,
666
+ errUpdate
667
+ ).then();
668
+ throw errUpdate;
669
+ }
670
+ });
671
+ }
672
+ updatePartialManyBySpecification(spec, set, user, repoOptions) {
673
+ return this.updatePartialManyByCriteria(
674
+ spec.criteria,
675
+ set,
676
+ user,
677
+ repoOptions
678
+ );
679
+ }
680
+ async delete(id, user, repoOptions) {
681
+ await this.collectionContext(async (collection) => {
682
+ try {
683
+ await collection.deleteOne(
684
+ { _id: id },
685
+ { session: repoOptions?.transaction?.session }
686
+ );
687
+ this.logChange(
688
+ "delete",
689
+ {
690
+ id
691
+ },
692
+ repoOptions,
693
+ user,
694
+ null
695
+ ).then();
696
+ } catch (errDelete) {
697
+ this.logChange(
698
+ "delete",
699
+ {
700
+ id
701
+ },
702
+ repoOptions,
703
+ user,
704
+ errDelete
705
+ ).then();
706
+ throw errDelete;
707
+ }
708
+ });
709
+ }
710
+ async getById(id, repoOptions) {
711
+ return await this.collectionContext(async (collection) => {
712
+ const item = await collection.findOne(
713
+ { _id: id },
714
+ {
715
+ session: repoOptions?.transaction?.session
716
+ }
717
+ );
718
+ return this.getModelToResult(item);
719
+ });
720
+ }
721
+ async getByCriteria(criteria, options = {}) {
722
+ return await this.collectionContext(async (collection) => {
723
+ this.convertIdInCriteria(criteria);
724
+ this.generateSearch(criteria);
725
+ const totalCount = await this.getCount(criteria, collection);
726
+ const aggregate = [];
727
+ if (criteria) {
728
+ aggregate.push({ $match: criteria });
729
+ }
730
+ if (options?.sort) {
731
+ aggregate.push({ $sort: options.sort });
732
+ }
733
+ if (options?.skip) {
734
+ aggregate.push({ $skip: options.skip });
735
+ }
736
+ if (options?.limit) {
737
+ aggregate.push({ $limit: options.limit });
738
+ }
739
+ if (options?.project) {
740
+ aggregate.push({ $project: options.project });
741
+ }
742
+ if (options?.min) {
743
+ aggregate.push({ $min: options.min });
744
+ }
745
+ if (options?.max) {
746
+ aggregate.push({ $max: options.max });
747
+ }
748
+ if (options?.group) {
749
+ aggregate.push({ $group: options.group });
750
+ }
751
+ const list = await collection.aggregate(aggregate, {
752
+ allowDiskUse: options?.allowDiskUse,
753
+ session: options?.session
754
+ }).toArray();
755
+ return {
756
+ data: list.map((item) => this.getModelToResult(item)),
757
+ totalCount
758
+ };
759
+ });
760
+ }
761
+ getBySpecification(spec, options = {}) {
762
+ return this.getByCriteria(spec.criteria, options);
763
+ }
764
+ async countByCriteria(criteria) {
765
+ return await this.collectionContext(async (collection) => {
766
+ this.convertIdInCriteria(criteria);
767
+ this.generateSearch(criteria);
768
+ return await this.getCount(criteria, collection);
769
+ });
770
+ }
771
+ countBySpecification(spec) {
772
+ return this.countByCriteria(spec.criteria);
773
+ }
774
+ changesByCriteria(criteria) {
775
+ let stream;
776
+ let client;
777
+ return new Observable((observer) => {
778
+ (async () => {
779
+ try {
780
+ client = await MongoClient3.connect(this.getUrl());
781
+ const db = client.db(this.config.database);
782
+ const collection = db.collection(this.config.collection);
783
+ const pipeline = criteria.id ? [
784
+ {
785
+ $match: {
786
+ "documentKey._id": criteria.id
787
+ }
788
+ }
789
+ ] : [];
790
+ stream = collection.watch(pipeline).on("change", (result) => {
791
+ observer.next({
792
+ id: result["documentKey"]["_id"],
793
+ type: this.mapChangeType(result.operationType),
794
+ data: result.operationType === "update" ? result["updateDescription"] : this.getModelToResult(result["fullDocument"])
795
+ });
796
+ });
797
+ } catch (err) {
798
+ observer.error(err);
799
+ }
800
+ })();
801
+ }).pipe(
802
+ finalize(async () => {
803
+ console.log("Stop watch");
804
+ await stream.close();
805
+ await client.close();
806
+ }),
807
+ share()
808
+ );
809
+ }
810
+ async getContext(handler) {
811
+ const client = await MongoClient3.connect(this.getUrl());
812
+ const db = client.db(this.config.database);
813
+ try {
814
+ const result = await handler(db);
815
+ await client.close();
816
+ return result;
817
+ } catch (e) {
818
+ await client.close();
819
+ throw e;
820
+ }
821
+ }
822
+ async getCount(criteria, collection) {
823
+ this.convertIdInCriteria(criteria);
824
+ return await collection.countDocuments(criteria);
825
+ }
826
+ async getInfo(id, collection) {
827
+ const array = await collection.aggregate([{ $match: { _id: id } }, { $project: { __info: 1 } }]).toArray();
828
+ return array[0] ? array[0]["__info"] : {};
829
+ }
830
+ getModelToCreate(item, user) {
831
+ const result = ObjectService.removeTypes(item);
832
+ result["_id"] = result.id;
833
+ delete result.id;
834
+ result["__info"] = {
835
+ create: {
836
+ username: user ? user.username : null,
837
+ date: /* @__PURE__ */ new Date()
838
+ }
839
+ };
840
+ return result;
841
+ }
842
+ mapChangeType(dbType) {
843
+ const map = {
844
+ insert: "create",
845
+ update: "update",
846
+ delete: "delete"
847
+ };
848
+ return map[dbType];
849
+ }
850
+ getModelToUpdate(item, user, info) {
851
+ const result = ObjectService.removeTypes(item);
852
+ result["_id"] = result.id;
853
+ delete result.id;
854
+ result["__info"] = {
855
+ ...info,
856
+ update: {
857
+ username: user ? user.username : null,
858
+ date: /* @__PURE__ */ new Date()
859
+ }
860
+ };
861
+ return result;
862
+ }
863
+ getModelToResult(item) {
864
+ if (!item)
865
+ return null;
866
+ const result = ObjectService.removeTypes(item);
867
+ result["id"] = result._id;
868
+ delete result._id;
869
+ delete result["__info"];
870
+ return result;
871
+ }
872
+ getUrl() {
873
+ return getMongoUrl(this.config);
874
+ }
875
+ async logChange(type, item, options, user, error) {
876
+ const client = await MongoClient3.connect(this.getUrl());
877
+ const db = client.db(this.config.database);
878
+ try {
879
+ await db.collection("changes").insertOne({
880
+ type,
881
+ collection: this.config.collection,
882
+ item,
883
+ options,
884
+ user,
885
+ error,
886
+ date: /* @__PURE__ */ new Date()
887
+ });
888
+ } catch (e) {
889
+ console.warn(e);
890
+ } finally {
891
+ await client.close();
892
+ }
893
+ }
894
+ generateSearch(criteria) {
895
+ if (!criteria["$search"])
896
+ return;
897
+ if (this.config.type) {
898
+ const modelFields = getModelFieldsWithOptions(
899
+ new this.config.type()
900
+ ).filter((i) => i.options.search);
901
+ if (modelFields.length) {
902
+ const searchArray = [];
903
+ modelFields.forEach((val) => {
904
+ const res = {};
905
+ res[val.key] = {
906
+ $regex: this.convertRegex(criteria["$search"]),
907
+ $options: "i"
908
+ };
909
+ searchArray.push(res);
910
+ });
911
+ if (!criteria["$or"])
912
+ criteria["$or"] = searchArray;
913
+ else if (criteria["$or"] && !criteria["$and"]) {
914
+ criteria["$and"] = [{ $or: criteria["$or"] }, { $or: searchArray }];
915
+ delete criteria["$or"];
916
+ } else if (criteria["$and"]) {
917
+ criteria["$and"] = [...criteria["$and"], { $or: searchArray }];
918
+ }
919
+ delete criteria["$search"];
920
+ return;
921
+ }
922
+ }
923
+ const customCriteria = {
924
+ $text: { $search: ' "' + this.convertRegex(criteria["$search"]) + '" ' }
925
+ };
926
+ delete criteria["$search"];
927
+ criteria = {
928
+ ...criteria,
929
+ ...customCriteria
930
+ };
931
+ }
932
+ convertIdInCriteria(criteria) {
933
+ if (criteria["id"]) {
934
+ criteria["_id"] = criteria["id"];
935
+ delete criteria["id"];
936
+ }
937
+ }
938
+ convertRegex(val) {
939
+ return val.toString().replace(/\*/g, "[*]");
940
+ }
941
+ async collectionContext(callback, repoOptions) {
942
+ const client = repoOptions?.transaction?.connection ? repoOptions.transaction.connection : await MongoClient3.connect(this.getUrl());
943
+ const db = client.db(this.config.database);
944
+ let result;
945
+ try {
946
+ result = await callback(db.collection(this.config.collection));
947
+ } finally {
948
+ if (!repoOptions?.transaction)
949
+ await client.close();
950
+ }
951
+ return result;
952
+ }
953
+ };
954
+ MongoItemRepository = __decorateClass([
955
+ Injectable4()
956
+ ], MongoItemRepository);
957
+
958
+ // packages/shared/mongo/src/lib/mongo.module.ts
959
+ var MongoModule = class _MongoModule {
960
+ static forRoot(config) {
961
+ const providers = [
962
+ { provide: MongoConfig, useValue: config },
963
+ { provide: IItemRepository, useClass: MongoItemRepository },
964
+ { provide: IAttachmentRepository, useClass: MongoAttachmentRepository },
965
+ { provide: IUnitOfWork, useClass: MongoUnitOfWork }
966
+ ];
967
+ return {
968
+ module: _MongoModule,
969
+ providers,
970
+ exports: providers
971
+ };
972
+ }
973
+ };
974
+
975
+ // packages/shared/nestjs/src/lib/shared.config.ts
976
+ import { Injectable as Injectable5 } from "@nestjs/common";
977
+ var SharedConfig = class {
978
+ };
979
+ SharedConfig = __decorateClass([
980
+ Injectable5()
981
+ ], SharedConfig);
982
+
983
+ // packages/shared/nestjs/src/lib/auth/jwt.strategy.ts
984
+ import { Injectable as Injectable6 } from "@nestjs/common";
985
+ import { PassportStrategy } from "@nestjs/passport";
986
+ import { ExtractJwt, Strategy } from "passport-jwt";
987
+ var JwtStrategy = class extends PassportStrategy(Strategy) {
988
+ constructor(config) {
989
+ super({
990
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
991
+ ignoreExpiration: false,
992
+ secretOrKey: config.tokenConfig?.secretOrPrivateKey
993
+ });
994
+ this.config = config;
995
+ }
996
+ async validate(payload) {
997
+ return {
998
+ ...payload,
999
+ permissions: payload.permissions,
1000
+ username: payload.sub
1001
+ };
1002
+ }
1003
+ };
1004
+ JwtStrategy = __decorateClass([
1005
+ Injectable6()
1006
+ ], JwtStrategy);
1007
+
1008
+ // packages/shared/nestjs/src/lib/auth/permission.service.ts
1009
+ import { Injectable as Injectable7 } from "@nestjs/common";
1010
+ var PermissionService = class {
1011
+ constructor(config) {
1012
+ this.config = config;
1013
+ }
1014
+ /**
1015
+ * Validates if the user has the required permissions for a given type.
1016
+ * @param {PermissionType} type - The type of permission to validate.
1017
+ * @param {IUser} user - The user object containing permissions.
1018
+ * @throws {DomainForbiddenError} If the user does not have the required permissions.
1019
+ */
1020
+ valid(type, user) {
1021
+ if (!this.config.permissions)
1022
+ return;
1023
+ const typeConfig = this.config.permissions[type];
1024
+ if (!typeConfig)
1025
+ return;
1026
+ if (!user.permissions)
1027
+ throw new DomainForbiddenError(`Context forbidden`);
1028
+ if (!typeConfig.some((tc) => user.permissions.some((up) => tc === up)))
1029
+ throw new DomainForbiddenError(`Context forbidden`);
1030
+ }
1031
+ };
1032
+ PermissionService = __decorateClass([
1033
+ Injectable7()
1034
+ ], PermissionService);
1035
+
1036
+ // packages/shared/nestjs/src/lib/shared.module.ts
1037
+ var SharedModule = class _SharedModule {
1038
+ /**
1039
+ * Configures the module's core functionalities.
1040
+ *
1041
+ * @param {SharedConfig} config - An object containing the module's configuration.
1042
+ * @returns {DynamicModule} A DynamicModule object with providers and exports.
1043
+ */
1044
+ static forFeature(config) {
1045
+ return {
1046
+ module: _SharedModule,
1047
+ providers: [
1048
+ { provide: SharedConfig, useValue: config },
1049
+ JwtStrategy,
1050
+ PermissionService
1051
+ ],
1052
+ exports: [
1053
+ { provide: SharedConfig, useValue: config },
1054
+ PermissionService,
1055
+ JwtStrategy
1056
+ ]
1057
+ };
1058
+ }
1059
+ /**
1060
+ * Extends the forFeature() configuration by adding database settings. Use in AppModule.
1061
+ *
1062
+ * @param {SharedConfig & { db: { host: string; port: number; database: string; username?: string; password?: string; }}} config - An extended SharedConfig object that includes additional database settings.
1063
+ * @returns {DynamicModule} A DynamicModule object that imports and exports the forFeature() configuration.
1064
+ */
1065
+ static forRoot(config) {
1066
+ return {
1067
+ module: _SharedModule,
1068
+ imports: [_SharedModule.forFeature(config)],
1069
+ exports: [_SharedModule.forFeature(config)]
1070
+ };
1071
+ }
1072
+ };
1073
+
1074
+ // packages/shared/nestjs/src/lib/filters/execution/exception.filter.ts
1075
+ import {
1076
+ Catch,
1077
+ HttpException,
1078
+ HttpStatus,
1079
+ Logger as Logger2
1080
+ } from "@nestjs/common";
1081
+ var AppExceptionFilter = class {
1082
+ /**
1083
+ * Method to catch and handle exceptions.
1084
+ *
1085
+ * @param {any} exception - The exception thrown.
1086
+ * @param {ArgumentsHost} host - The arguments host.
1087
+ */
1088
+ catch(exception, host) {
1089
+ const ctx = host.switchToHttp();
1090
+ const response = ctx.getResponse();
1091
+ let status = HttpStatus.INTERNAL_SERVER_ERROR;
1092
+ let message = null;
1093
+ if (exception instanceof HttpException) {
1094
+ status = exception.getStatus();
1095
+ message = exception["message"];
1096
+ } else if (exception["type"] === DomainValidationError) {
1097
+ status = HttpStatus.BAD_REQUEST;
1098
+ message = exception["message"];
1099
+ } else if (exception["type"] === DomainForbiddenError) {
1100
+ status = HttpStatus.FORBIDDEN;
1101
+ message = exception["message"];
1102
+ } else {
1103
+ status = HttpStatus.INTERNAL_SERVER_ERROR;
1104
+ message = "Internal server error";
1105
+ }
1106
+ Logger2.error(
1107
+ exception["stack"] || exception["message"] || exception,
1108
+ AppExceptionFilter.name
1109
+ );
1110
+ const result = response.status(status);
1111
+ if (message && result.json) {
1112
+ result.json({
1113
+ details: message
1114
+ });
1115
+ } else if (!message && result.json) {
1116
+ result.json();
1117
+ } else if (message && !result.json) {
1118
+ result.send({
1119
+ details: message
1120
+ });
1121
+ } else {
1122
+ result.send();
1123
+ }
1124
+ }
1125
+ };
1126
+ AppExceptionFilter = __decorateClass([
1127
+ Catch()
1128
+ ], AppExceptionFilter);
1129
+
1130
+ // packages/shared/nestjs/src/lib/decorators/user/user.decorator.ts
1131
+ import { createParamDecorator } from "@nestjs/common";
1132
+ var User = createParamDecorator((_3, context) => {
1133
+ const [req] = context.getArgs();
1134
+ return req.user;
1135
+ });
1136
+
1137
+ // packages/crud/shell/nestjs/src/lib/controllers/crud/crud.controller.ts
1138
+ import {
1139
+ Body,
1140
+ Controller,
1141
+ Delete,
1142
+ Get,
1143
+ HttpCode,
1144
+ NotFoundException,
1145
+ Param,
1146
+ Patch,
1147
+ Post,
1148
+ Put,
1149
+ Query,
1150
+ Req,
1151
+ Res,
1152
+ UseGuards
1153
+ } from "@nestjs/common";
1154
+ import * as Busboy from "busboy";
1155
+ import { Parser } from "json2csv";
1156
+ import * as _2 from "lodash";
1157
+ import * as moment from "moment-timezone";
1158
+ import * as XLSX from "xlsx";
1159
+ import { Readable } from "stream";
1160
+
1161
+ // packages/crud/shell/nestjs/src/lib/controllers/crud/query-to-mongo.ts
1162
+ import * as querystring from "querystring";
1163
+ var iso8601 = /^\d{4}(-(0[1-9]|1[0-2])(-(0[1-9]|[12][0-9]|3[01]))?)?(T([01][0-9]|2[0-3]):[0-5]\d(:[0-5]\d(\.\d+)?)?(Z|[+-]\d{2}:\d{2}))?$/;
1164
+ function fieldsToMongo(fields) {
1165
+ if (!fields)
1166
+ return null;
1167
+ const hash = {};
1168
+ fields.split(",").forEach(function(field) {
1169
+ hash[field.trim()] = 1;
1170
+ });
1171
+ return hash;
1172
+ }
1173
+ function convertRegex(val) {
1174
+ return val.toString().replace(/\*/g, "[*]");
1175
+ }
1176
+ function omitFieldsToMongo(omitFields) {
1177
+ if (!omitFields)
1178
+ return null;
1179
+ const hash = {};
1180
+ omitFields.split(",").forEach(function(omitField) {
1181
+ hash[omitField.trim()] = 0;
1182
+ });
1183
+ return hash;
1184
+ }
1185
+ function sortToMongo(sort) {
1186
+ if (!sort)
1187
+ return null;
1188
+ const hash = {};
1189
+ let c;
1190
+ sort.split(",").forEach(function(field) {
1191
+ c = field.charAt(0);
1192
+ if (c === "-")
1193
+ field = field.substr(1);
1194
+ hash[field.trim()] = c === "-" ? -1 : 1;
1195
+ });
1196
+ return hash;
1197
+ }
1198
+ function typedValue(value) {
1199
+ if (value[0] === "!")
1200
+ value = value.substr(1);
1201
+ const regex = value.match(/^\/(.*)\/(i?)$/);
1202
+ const quotedString = value.match(/(["'])(?:\\\1|.)*?\1/);
1203
+ if (regex) {
1204
+ return new RegExp(regex[1], regex[2]);
1205
+ } else if (quotedString) {
1206
+ return quotedString[0].substr(1, quotedString[0].length - 2);
1207
+ } else if (value === "true") {
1208
+ return true;
1209
+ } else if (value === "false") {
1210
+ return false;
1211
+ } else if (iso8601.test(value) && value.length !== 4 && value.length !== 10) {
1212
+ return new Date(value);
1213
+ } else if (!isNaN(Number(value))) {
1214
+ return Number(value);
1215
+ }
1216
+ return value;
1217
+ }
1218
+ function typedValues(svalue) {
1219
+ const commaSplit = /("[^"]*")|('[^']*')|(\/[^/]*\/i?)|([^,]+)/g;
1220
+ const values = [];
1221
+ svalue.match(commaSplit).forEach(function(value) {
1222
+ values.push(typedValue(value));
1223
+ });
1224
+ return values;
1225
+ }
1226
+ function comparisonToMongo(key, value) {
1227
+ const join = value === "" ? key : key.concat("=", value);
1228
+ const parts = join.match(/^(!?[^><~!=:]+)(?:=?([><]=?|~?=|!?=|:.+=)(.+))?$/);
1229
+ let op;
1230
+ const hash = {};
1231
+ if (!parts)
1232
+ return null;
1233
+ key = parts[1];
1234
+ op = parts[2];
1235
+ if (!op) {
1236
+ if (key[0] !== "!")
1237
+ value = { $exists: true };
1238
+ else {
1239
+ key = key.substr(1);
1240
+ value = { $exists: false };
1241
+ }
1242
+ } else if (op === "=" && parts[3] === "!") {
1243
+ value = { $exists: false };
1244
+ } else if (op === "=" || op === "!=") {
1245
+ if (op === "=" && parts[3][0] === "!")
1246
+ op = "!=";
1247
+ const array = typedValues(parts[3]);
1248
+ if (array.length > 1) {
1249
+ value = {};
1250
+ op = op === "=" ? "$in" : "$nin";
1251
+ value[op] = array;
1252
+ } else if (op === "!=") {
1253
+ value = array[0] instanceof RegExp ? { $not: array[0] } : { $ne: array[0] };
1254
+ } else if (array[0][0] === "!") {
1255
+ const sValue = array[0].substr(1);
1256
+ const regex = sValue.match(/^\/(.*)\/(i?)$/);
1257
+ value = regex ? { $not: new RegExp(regex[1], regex[2]) } : { $ne: sValue };
1258
+ } else {
1259
+ value = array[0];
1260
+ }
1261
+ } else if (op[0] === ":" && op[op.length - 1] === "=") {
1262
+ op = "$" + op.substr(1, op.length - 2);
1263
+ const array = [];
1264
+ parts[3].split(",").forEach(function(value2) {
1265
+ array.push(typedValue(value2));
1266
+ });
1267
+ value = {};
1268
+ value[op] = array.length === 1 ? array[0] : array;
1269
+ } else {
1270
+ value = typedValue(parts[3]);
1271
+ if (op === ">")
1272
+ value = { $gt: value };
1273
+ else if (op === ">=")
1274
+ value = { $gte: value };
1275
+ else if (op === "<")
1276
+ value = { $lt: value };
1277
+ else if (op === "<=")
1278
+ value = { $lte: value };
1279
+ else if (op === "~=")
1280
+ value = {
1281
+ $regex: value ? convertRegex(value) : "",
1282
+ $options: "i"
1283
+ };
1284
+ }
1285
+ hash.key = key;
1286
+ hash.value = value;
1287
+ return hash;
1288
+ }
1289
+ function hasOrdinalKeys(obj) {
1290
+ let c = 0;
1291
+ for (const key in obj) {
1292
+ if (Number(key) !== c++)
1293
+ return false;
1294
+ }
1295
+ return true;
1296
+ }
1297
+ function queryCriteriaToMongo(query, options = null) {
1298
+ const hash = {};
1299
+ let deep, p;
1300
+ options = options || {};
1301
+ for (const key in query) {
1302
+ if (Object.prototype.hasOwnProperty.call(query, key) && (!options.ignore || options.ignore.indexOf(key) === -1)) {
1303
+ deep = typeof query[key] === "object" && !hasOrdinalKeys(query[key]);
1304
+ if (deep) {
1305
+ p = {
1306
+ key,
1307
+ value: queryCriteriaToMongo(query[key])
1308
+ };
1309
+ } else {
1310
+ p = comparisonToMongo(key, query[key]);
1311
+ }
1312
+ if (p) {
1313
+ if (!hash[p.key]) {
1314
+ hash[p.key] = p.value;
1315
+ } else if (typeof p.value === "string") {
1316
+ hash[p.key] = Object.assign(hash[p.key], {
1317
+ $eq: p.value
1318
+ });
1319
+ } else {
1320
+ hash[p.key] = Object.assign(hash[p.key], p.value);
1321
+ }
1322
+ }
1323
+ }
1324
+ }
1325
+ return hash;
1326
+ }
1327
+ function queryOptionsToMongo(query, options) {
1328
+ const hash = {}, fields = fieldsToMongo(query[options.keywords.fields]), omitFields = omitFieldsToMongo(query[options.keywords.omit]), sort = sortToMongo(query[options.keywords.sort]), maxLimit = options.maxLimit || 9007199254740992;
1329
+ let limit = options.maxLimit || 0;
1330
+ if (fields)
1331
+ hash.fields = fields;
1332
+ if (omitFields)
1333
+ hash.fields = omitFields;
1334
+ if (sort)
1335
+ hash.sort = sort;
1336
+ if (query[options.keywords.offset])
1337
+ hash.skip = Number(query[options.keywords.offset]);
1338
+ if (query[options.keywords.limit])
1339
+ limit = Math.min(Number(query[options.keywords.limit]), maxLimit);
1340
+ if (limit) {
1341
+ hash.limit = limit;
1342
+ } else if (options.maxLimit) {
1343
+ hash.limit = maxLimit;
1344
+ }
1345
+ return hash;
1346
+ }
1347
+ function q2m(query = null, options = null) {
1348
+ query = query || {};
1349
+ options = options || {};
1350
+ options.keywords = options.keywords || {};
1351
+ const defaultKeywords = {
1352
+ fields: "fields",
1353
+ omit: "omit",
1354
+ sort: "sort",
1355
+ offset: "offset",
1356
+ limit: "limit"
1357
+ };
1358
+ options.keywords = Object.assign(defaultKeywords, options.keywords);
1359
+ const ignoreKeywords = [
1360
+ options.keywords.fields,
1361
+ options.keywords.omit,
1362
+ options.keywords.sort,
1363
+ options.keywords.offset,
1364
+ options.keywords.limit
1365
+ ];
1366
+ if (!options.ignore) {
1367
+ options.ignore = [];
1368
+ } else {
1369
+ options.ignore = typeof options.ignore === "string" ? [options.ignore] : options.ignore;
1370
+ }
1371
+ options.ignore = options.ignore.concat(ignoreKeywords);
1372
+ if (!options.parser)
1373
+ options.parser = querystring;
1374
+ if (typeof query === "string")
1375
+ query = options.parser.parse(query);
1376
+ return {
1377
+ criteria: queryCriteriaToMongo(query, options),
1378
+ options: queryOptionsToMongo(query, options),
1379
+ links: function(url, totalCount) {
1380
+ const offset = this.options.skip || 0;
1381
+ const limit = Math.min(this.options.limit || 0, totalCount);
1382
+ const links = {};
1383
+ const last = {};
1384
+ if (!limit)
1385
+ return null;
1386
+ options = options || {};
1387
+ if (offset > 0) {
1388
+ query[options.keywords.offset] = Math.max(offset - limit, 0);
1389
+ links["prev"] = url + "?" + options.parser.stringify(query);
1390
+ query[options.keywords.offset] = 0;
1391
+ links["first"] = url + "?" + options.parser.stringify(query);
1392
+ }
1393
+ if (offset + limit < totalCount) {
1394
+ last.pages = Math.ceil(totalCount / limit);
1395
+ last.offset = (last.pages - 1) * limit;
1396
+ query[options.keywords.offset] = Math.min(offset + limit, last.offset);
1397
+ links["next"] = url + "?" + options.parser.stringify(query);
1398
+ query[options.keywords.offset] = last.offset;
1399
+ links["last"] = url + "?" + options.parser.stringify(query);
1400
+ }
1401
+ return links;
1402
+ }
1403
+ };
1404
+ }
1405
+
1406
+ // packages/crud/shell/nestjs/src/lib/guards/auth/auth.guard.ts
1407
+ import { Injectable as Injectable8, UnauthorizedException, Logger as Logger3 } from "@nestjs/common";
1408
+ import { AuthGuard } from "@nestjs/passport";
1409
+ var AuthJwtGuard = class extends AuthGuard("jwt") {
1410
+ constructor() {
1411
+ super(...arguments);
1412
+ this.logger = new Logger3(AuthJwtGuard.name, { timestamp: true });
1413
+ }
1414
+ handleRequest(err, user, info) {
1415
+ if (err || !user) {
1416
+ this.logger.warn(JSON.stringify(info));
1417
+ throw err || new UnauthorizedException();
1418
+ }
1419
+ return user;
1420
+ }
1421
+ };
1422
+ AuthJwtGuard = __decorateClass([
1423
+ Injectable8()
1424
+ ], AuthJwtGuard);
1425
+ var AuthOrAnonymousJwtGuard = class extends AuthGuard("jwt") {
1426
+ handleRequest(err, user, info) {
1427
+ return user;
1428
+ }
1429
+ };
1430
+ AuthOrAnonymousJwtGuard = __decorateClass([
1431
+ Injectable8()
1432
+ ], AuthOrAnonymousJwtGuard);
1433
+
1434
+ // packages/crud/shell/nestjs/src/lib/controllers/crud/crud.controller.ts
1435
+ var CrudController = class {
1436
+ constructor(service) {
1437
+ this.service = service;
1438
+ }
1439
+ static getLink(req) {
1440
+ return req.protocol + "://" + req.headers.host + req.url;
1441
+ }
1442
+ async create(data, user, res) {
1443
+ const id = await this.service.create(data, user);
1444
+ res.set("Location", CrudController.getLink(res.req) + "/" + id);
1445
+ return res.send({
1446
+ id
1447
+ });
1448
+ }
1449
+ async createMany(data, user, res, mode) {
1450
+ const result = await this.service.createMany(data, user, { mode });
1451
+ return res.send(result);
1452
+ }
1453
+ async readById(params, user) {
1454
+ const result = await this.service.readById(params.id, user);
1455
+ if (!result) {
1456
+ throw new NotFoundException("Invalid id");
1457
+ }
1458
+ return result;
1459
+ }
1460
+ async read(user, req, res) {
1461
+ const object = this.getQueryObject(req.query);
1462
+ const { data, totalCount } = await this.service.read(
1463
+ object.criteria,
1464
+ {
1465
+ ...object.options,
1466
+ allowDiskUse: req.headers["content-type"] === "text/csv" || req.headers["content-type"] === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
1467
+ },
1468
+ user
1469
+ );
1470
+ if (req.headers["content-type"] === "text/csv") {
1471
+ res.set({
1472
+ "Content-Type": "text/csv"
1473
+ });
1474
+ res.send(this.parseToCsv(data));
1475
+ }
1476
+ if (req.headers["content-type"] === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") {
1477
+ res.set({
1478
+ "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
1479
+ });
1480
+ res.send(this.parseToXlsx(data));
1481
+ }
1482
+ res.send({
1483
+ data,
1484
+ totalCount,
1485
+ links: object.links(
1486
+ CrudController.getLink(req).split("?")[0],
1487
+ totalCount
1488
+ )
1489
+ });
1490
+ }
1491
+ async update(params, data, user) {
1492
+ await this.service.update(params.id, data, user);
1493
+ }
1494
+ async updatePartial(params, data, user) {
1495
+ await this.service.updatePartial(params.id, data, user);
1496
+ }
1497
+ async delete(params, user) {
1498
+ await this.service.delete(params.id, user);
1499
+ }
1500
+ uploadAttachment(request, response) {
1501
+ const busboy = new Busboy({
1502
+ headers: request.headers
1503
+ });
1504
+ const id = GuidService.create();
1505
+ const readable = new Readable();
1506
+ readable._read = () => {
1507
+ };
1508
+ let fileName, encoding, mimeType;
1509
+ busboy.on(
1510
+ "file",
1511
+ (field, file, resultFileName, resultEncoding, resultMimeType) => {
1512
+ fileName = resultFileName;
1513
+ encoding = resultEncoding;
1514
+ mimeType = resultMimeType;
1515
+ this.service.uploadAttachment({
1516
+ id,
1517
+ stream: readable,
1518
+ fileName,
1519
+ encoding,
1520
+ mimeType
1521
+ });
1522
+ file.on("data", (data) => {
1523
+ readable.push(data);
1524
+ });
1525
+ }
1526
+ );
1527
+ busboy.on("finish", function() {
1528
+ readable.push(null);
1529
+ response.set("Location", CrudController.getLink(response.req) + "/" + id);
1530
+ response.json({
1531
+ id,
1532
+ fileName,
1533
+ contentType: mimeType,
1534
+ length: readable.readableLength
1535
+ });
1536
+ response.end();
1537
+ });
1538
+ return request.pipe(busboy);
1539
+ }
1540
+ async downloadAttachment(id, request, response) {
1541
+ const fileInfo = await this.service.getAttachmentInfo(id);
1542
+ if (request.headers.range) {
1543
+ const range = request.headers.range.substr(6).split("-");
1544
+ const start = parseInt(range[0], 10);
1545
+ const end = parseInt(range[1], 10) || null;
1546
+ const readstream = await this.service.getAttachmentStream(id, {
1547
+ start,
1548
+ end
1549
+ });
1550
+ response.status(206);
1551
+ response.set({
1552
+ "Accept-Ranges": "bytes",
1553
+ "Content-Type": fileInfo.contentType,
1554
+ "Content-Range": `bytes ${start}-${end ? end : fileInfo.length - 1}/${fileInfo.length}`,
1555
+ "Content-Length": (end ? end : fileInfo.length) - start,
1556
+ "Content-Disposition": `attachment; filename="${encodeURI(fileInfo.fileName)}"`
1557
+ });
1558
+ response.on("close", () => {
1559
+ readstream.destroy();
1560
+ });
1561
+ readstream.pipe(response);
1562
+ } else {
1563
+ const readstream = await this.service.getAttachmentStream(id);
1564
+ response.on("close", () => {
1565
+ readstream.destroy();
1566
+ });
1567
+ response.status(200);
1568
+ response.set({
1569
+ "Accept-Range": "bytes",
1570
+ "Content-Type": fileInfo.contentType,
1571
+ "Content-Length": fileInfo.length,
1572
+ "Content-Disposition": `attachment; filename="${encodeURI(fileInfo.fileName)}"`
1573
+ });
1574
+ readstream.pipe(response);
1575
+ }
1576
+ }
1577
+ async deleteAttachment(id) {
1578
+ await this.service.deleteAttachment(id);
1579
+ }
1580
+ getQueryObject(queryObject) {
1581
+ let q = "";
1582
+ Object.keys(queryObject).forEach((key) => {
1583
+ q += `&${key}=${queryObject[key]}`;
1584
+ });
1585
+ const result = q2m(q);
1586
+ return result;
1587
+ }
1588
+ parseToXlsx(data) {
1589
+ if (!data || !data.length) {
1590
+ return "";
1591
+ }
1592
+ const { res } = this.getDataWithFields(data);
1593
+ const ws = XLSX.utils.json_to_sheet(res);
1594
+ const wb = XLSX.utils.book_new();
1595
+ XLSX.utils.book_append_sheet(wb, ws, "data");
1596
+ return XLSX.write(wb, { bookType: "xlsx", type: "buffer" });
1597
+ }
1598
+ parseToCsv(data) {
1599
+ if (!data || !data.length) {
1600
+ return "";
1601
+ }
1602
+ const { res, fields } = this.getDataWithFields(data);
1603
+ return new Parser(fields).parse(data);
1604
+ }
1605
+ getDataWithFields(data) {
1606
+ const fields = [];
1607
+ const execute = (item, baseKey, baseItem) => {
1608
+ Object.keys(item).forEach((key) => {
1609
+ if (item[key] && typeof item[key] === "string") {
1610
+ item[key] = item[key].replace(/<[^>]*>?/gm, "");
1611
+ }
1612
+ if (item[key] && item[key] instanceof Date) {
1613
+ item[key] = moment(item[key]).tz("Europe/Warsaw").format("YYYY-MM-DD HH:mm:ss");
1614
+ }
1615
+ const val = item[key];
1616
+ if (_2.isArray(val)) {
1617
+ return;
1618
+ } else if (_2.isObject(val) && Object.keys(val).length) {
1619
+ execute(val, baseKey + key + "_", baseItem);
1620
+ } else if (baseKey) {
1621
+ baseItem[baseKey + key] = val;
1622
+ if (!fields.some((f) => f === baseKey + key))
1623
+ fields.push(baseKey + key);
1624
+ } else {
1625
+ if (!fields.some((f) => f === key))
1626
+ fields.push(key);
1627
+ }
1628
+ });
1629
+ };
1630
+ data.forEach((item) => {
1631
+ execute(item, "", item);
1632
+ });
1633
+ data.forEach((item) => {
1634
+ Object.keys(item).forEach((key) => {
1635
+ if (!fields.some((f) => f === key)) {
1636
+ delete item[key];
1637
+ }
1638
+ });
1639
+ });
1640
+ return { res: data, fields };
1641
+ }
1642
+ };
1643
+ __decorateClass([
1644
+ UseGuards(AuthJwtGuard),
1645
+ Post(),
1646
+ HttpCode(200),
1647
+ __decorateParam(0, Body()),
1648
+ __decorateParam(1, User()),
1649
+ __decorateParam(2, Res())
1650
+ ], CrudController.prototype, "create", 1);
1651
+ __decorateClass([
1652
+ UseGuards(AuthJwtGuard),
1653
+ Post("bulk"),
1654
+ __decorateParam(0, Body()),
1655
+ __decorateParam(1, User()),
1656
+ __decorateParam(2, Res()),
1657
+ __decorateParam(3, Query("mode"))
1658
+ ], CrudController.prototype, "createMany", 1);
1659
+ __decorateClass([
1660
+ UseGuards(AuthOrAnonymousJwtGuard),
1661
+ Get(":id"),
1662
+ __decorateParam(0, Param()),
1663
+ __decorateParam(1, User())
1664
+ ], CrudController.prototype, "readById", 1);
1665
+ __decorateClass([
1666
+ UseGuards(AuthOrAnonymousJwtGuard),
1667
+ Get(),
1668
+ __decorateParam(0, User()),
1669
+ __decorateParam(1, Req()),
1670
+ __decorateParam(2, Res())
1671
+ ], CrudController.prototype, "read", 1);
1672
+ __decorateClass([
1673
+ UseGuards(AuthJwtGuard),
1674
+ Put(":id"),
1675
+ __decorateParam(0, Param()),
1676
+ __decorateParam(1, Body()),
1677
+ __decorateParam(2, User())
1678
+ ], CrudController.prototype, "update", 1);
1679
+ __decorateClass([
1680
+ UseGuards(AuthJwtGuard),
1681
+ Patch(":id"),
1682
+ __decorateParam(0, Param()),
1683
+ __decorateParam(1, Body()),
1684
+ __decorateParam(2, User())
1685
+ ], CrudController.prototype, "updatePartial", 1);
1686
+ __decorateClass([
1687
+ UseGuards(AuthJwtGuard),
1688
+ Delete(":id"),
1689
+ __decorateParam(0, Param()),
1690
+ __decorateParam(1, User())
1691
+ ], CrudController.prototype, "delete", 1);
1692
+ __decorateClass([
1693
+ Post("attachments"),
1694
+ __decorateParam(0, Req()),
1695
+ __decorateParam(1, Res())
1696
+ ], CrudController.prototype, "uploadAttachment", 1);
1697
+ __decorateClass([
1698
+ Get("attachments/:id"),
1699
+ __decorateParam(0, Param("id")),
1700
+ __decorateParam(1, Req()),
1701
+ __decorateParam(2, Res())
1702
+ ], CrudController.prototype, "downloadAttachment", 1);
1703
+ __decorateClass([
1704
+ Delete("attachments/:id"),
1705
+ __decorateParam(0, Param("id"))
1706
+ ], CrudController.prototype, "deleteAttachment", 1);
1707
+ CrudController = __decorateClass([
1708
+ Controller("")
1709
+ ], CrudController);
1710
+
1711
+ // packages/crud/shell/nestjs/src/lib/controllers/index.ts
1712
+ var CONTROLLERS = [CrudController];
1713
+
1714
+ // packages/crud/shell/nestjs/src/lib/gateways/crud/crud.gateway.ts
1715
+ import {
1716
+ ConnectedSocket,
1717
+ MessageBody,
1718
+ SubscribeMessage,
1719
+ WebSocketGateway
1720
+ } from "@nestjs/websockets";
1721
+ import { Observable as Observable2 } from "rxjs";
1722
+ var CrudGateway = class {
1723
+ constructor(service) {
1724
+ this.service = service;
1725
+ }
1726
+ handleFilter(data, client) {
1727
+ const event = "changes";
1728
+ return new Observable2((observer) => {
1729
+ this.clearSubscription(client);
1730
+ this._clientsSubscriptions.set(
1731
+ client.id,
1732
+ this.service.changes(data).subscribe(
1733
+ (res) => {
1734
+ observer.next({ event, data: res });
1735
+ },
1736
+ (error) => observer.error(error)
1737
+ )
1738
+ );
1739
+ });
1740
+ }
1741
+ afterInit(server) {
1742
+ this._clientsSubscriptions = /* @__PURE__ */ new Map();
1743
+ console.log("CrudGateway Init");
1744
+ }
1745
+ handleDisconnect(client) {
1746
+ this.clearSubscription(client);
1747
+ console.log(`Client disconnected: ${client.id}`);
1748
+ }
1749
+ clearSubscription(client) {
1750
+ if (this._clientsSubscriptions.has(client.id)) {
1751
+ this._clientsSubscriptions.get(client.id).unsubscribe();
1752
+ this._clientsSubscriptions.delete(client.id);
1753
+ }
1754
+ }
1755
+ handleConnection(client, ...args) {
1756
+ console.log(`Client connected: ${client.id}`);
1757
+ }
1758
+ };
1759
+ __decorateClass([
1760
+ SubscribeMessage("changes"),
1761
+ __decorateParam(0, MessageBody()),
1762
+ __decorateParam(1, ConnectedSocket())
1763
+ ], CrudGateway.prototype, "handleFilter", 1);
1764
+ CrudGateway = __decorateClass([
1765
+ WebSocketGateway({
1766
+ transports: ["websocket"],
1767
+ path: "/" + process.env.URL_PREFIX + "/_socket",
1768
+ namespace: "/" + process.env.URL_PREFIX
1769
+ })
1770
+ ], CrudGateway);
1771
+
1772
+ // packages/crud/shell/nestjs/src/lib/gateways/index.ts
1773
+ var GATEWAYS = [CrudGateway];
1774
+
1775
+ // packages/crud/shell/nestjs/src/lib/nestjs.module.ts
1776
+ var CrudShellNestjsModule = class {
1777
+ static forRoot(options) {
1778
+ return {
1779
+ module: CrudShellNestjsModule,
1780
+ controllers: options.restApi ? CONTROLLERS : [],
1781
+ providers: [
1782
+ ...SERVICES,
1783
+ ...options.socket ? GATEWAYS : [],
1784
+ AuthJwtGuard
1785
+ ],
1786
+ imports: [
1787
+ ...options.restApi && options.tokenConfig.secretOrPrivateKey ? [
1788
+ PassportModule.register({
1789
+ defaultStrategy: "jwt",
1790
+ session: false
1791
+ }),
1792
+ JwtModule.register({
1793
+ secret: options.tokenConfig.secretOrPrivateKey,
1794
+ signOptions: {
1795
+ expiresIn: options.tokenConfig.expiredIn
1796
+ }
1797
+ })
1798
+ ] : [],
1799
+ SharedModule.forFeature(options),
1800
+ MongoModule.forRoot(options.db)
1801
+ ],
1802
+ exports: [...SERVICES, AuthJwtGuard, MongoModule.forRoot(options.db)]
1803
+ };
1804
+ }
1805
+ };
1806
+ CrudShellNestjsModule = __decorateClass([
1807
+ Module({})
1808
+ ], CrudShellNestjsModule);
1809
+ var CrudShellNestjsCoreModule = class {
1810
+ static forRoot(options) {
1811
+ return {
1812
+ module: CrudShellNestjsModule,
1813
+ providers: [...SERVICES, ...GATEWAYS, AuthJwtGuard],
1814
+ imports: [
1815
+ PassportModule.register({ defaultStrategy: "jwt", session: false }),
1816
+ JwtModule.register({
1817
+ secret: options.tokenConfig.secretOrPrivateKey,
1818
+ signOptions: {
1819
+ expiresIn: options.tokenConfig.expiredIn
1820
+ }
1821
+ }),
1822
+ SharedModule.forRoot(options),
1823
+ MongoModule.forRoot(options.db)
1824
+ ],
1825
+ exports: []
1826
+ };
1827
+ }
1828
+ };
1829
+ CrudShellNestjsCoreModule = __decorateClass([
1830
+ Module({})
1831
+ ], CrudShellNestjsCoreModule);
1832
+ export {
1833
+ AuthJwtGuard,
1834
+ AuthOrAnonymousJwtGuard,
1835
+ CONTROLLERS,
1836
+ CrudController,
1837
+ CrudShellNestjsCoreModule,
1838
+ CrudShellNestjsModule,
1839
+ GATEWAYS
1840
+ };