@smartsoft001/crud-shell-nestjs 2.75.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 +1840 -0
- package/package.json +3 -3
- package/src/lib/controllers/crud/crud.controller.d.ts +39 -0
- package/src/lib/controllers/crud/query-to-mongo.d.ts +5 -0
- package/src/lib/controllers/{index.ts → index.d.ts} +1 -3
- package/src/lib/gateways/crud/crud.gateway.d.ts +18 -0
- package/src/lib/gateways/index.d.ts +2 -0
- package/src/lib/guards/auth/auth.guard.d.ts +10 -0
- package/src/lib/nestjs.module.d.ts +31 -0
- package/.eslintrc +0 -13
- package/jest.config.ts +0 -27
- package/project.json +0 -43
- package/src/lib/controllers/crud/crud.controller.spec.ts +0 -111
- package/src/lib/controllers/crud/crud.controller.ts +0 -355
- package/src/lib/controllers/crud/query-to-mongo.spec.ts +0 -32
- package/src/lib/controllers/crud/query-to-mongo.ts +0 -295
- package/src/lib/gateways/crud/crud.gateway.spec.ts +0 -122
- package/src/lib/gateways/crud/crud.gateway.ts +0 -73
- package/src/lib/gateways/index.ts +0 -3
- package/src/lib/guards/auth/auth.guard.spec.ts +0 -61
- package/src/lib/guards/auth/auth.guard.ts +0 -22
- package/src/lib/nestjs.module.ts +0 -94
- package/tsconfig.json +0 -13
- package/tsconfig.lib.json +0 -11
- package/tsconfig.spec.json +0 -20
- /package/src/{index.ts → index.d.ts} +0 -0
- /package/{test-setup.ts → test-setup.d.ts} +0 -0
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
|
+
};
|