@stemy/backend 3.6.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/commands/fixtures-command.d.ts +9 -0
  2. package/commands/index.d.ts +2 -0
  3. package/common-types.d.ts +11 -6
  4. package/esm2020/commands/clear-command.mjs +17 -0
  5. package/esm2020/commands/fixtures-command.mjs +23 -0
  6. package/esm2020/commands/index.mjs +7 -0
  7. package/esm2020/common-types.mjs +21 -0
  8. package/esm2020/public_api.mjs +379 -0
  9. package/esm2020/requests/asset-image-params.mjs +53 -0
  10. package/esm2020/rest-controllers/assets.controller.mjs +126 -0
  11. package/esm2020/rest-controllers/auth.controller.mjs +46 -0
  12. package/esm2020/rest-controllers/gallery.controller.mjs +21 -0
  13. package/esm2020/rest-controllers/progresses.controller.mjs +29 -0
  14. package/esm2020/rest-controllers/terminal-styles.mjs +67 -0
  15. package/esm2020/rest-controllers/terminal.controller.mjs +125 -0
  16. package/esm2020/rest-middlewares/container.middleware.mjs +20 -0
  17. package/esm2020/rest-middlewares/error-handler.middleware.mjs +74 -0
  18. package/esm2020/rest-middlewares/language.middleware.mjs +18 -0
  19. package/esm2020/rest-middlewares/request-ended.middleware.mjs +23 -0
  20. package/esm2020/rest-middlewares/request-started.middleware.mjs +22 -0
  21. package/esm2020/services/asset-processor.mjs +90 -0
  22. package/esm2020/services/asset-resolver.mjs +31 -0
  23. package/esm2020/services/assets.mjs +140 -0
  24. package/esm2020/services/backend-provider.mjs +22 -0
  25. package/esm2020/services/cache-processor.mjs +16 -0
  26. package/esm2020/services/cache.mjs +62 -0
  27. package/{esm2015/services/configuration.js → esm2020/services/configuration.mjs} +3 -15
  28. package/esm2020/services/endpoint-provider.mjs +13 -0
  29. package/esm2020/services/entities/asset.mjs +43 -0
  30. package/esm2020/services/entities/base-entity.mjs +26 -0
  31. package/esm2020/services/entities/lazy-asset.mjs +73 -0
  32. package/esm2020/services/entities/progress.mjs +176 -0
  33. package/esm2020/services/entities/temp-asset.mjs +45 -0
  34. package/esm2020/services/fixtures.mjs +26 -0
  35. package/esm2020/services/gallery-cache.mjs +27 -0
  36. package/esm2020/services/gallery-image.mjs +37 -0
  37. package/esm2020/services/gallery.mjs +116 -0
  38. package/esm2020/services/id-generator.mjs +42 -0
  39. package/esm2020/services/job-manager.mjs +187 -0
  40. package/esm2020/services/lazy-assets.mjs +48 -0
  41. package/esm2020/services/logger.mjs +21 -0
  42. package/esm2020/services/mail-sender.mjs +36 -0
  43. package/esm2020/services/memory-cache.mjs +57 -0
  44. package/esm2020/services/mongo-connector.mjs +37 -0
  45. package/esm2020/services/open-api.mjs +114 -0
  46. package/esm2020/services/progresses.mjs +86 -0
  47. package/esm2020/services/template-renderer.mjs +64 -0
  48. package/esm2020/services/terminal-manager.mjs +77 -0
  49. package/esm2020/services/token-generator.mjs +35 -0
  50. package/esm2020/services/translation-provider.mjs +34 -0
  51. package/esm2020/services/translator.mjs +63 -0
  52. package/esm2020/services/user-manager.mjs +27 -0
  53. package/esm2020/socket-controllers/progress.controller.mjs +52 -0
  54. package/esm2020/socket-controllers/terminal.controller.mjs +48 -0
  55. package/esm2020/socket-controllers/terminal.mjs +85 -0
  56. package/esm2020/socket-middlewares/compression.middleware.mjs +14 -0
  57. package/esm2020/static.mjs +23 -0
  58. package/esm2020/utilities/decorators.mjs +52 -0
  59. package/esm2020/utilities/di-container.mjs +83 -0
  60. package/esm2020/utilities/empty-job.mjs +13 -0
  61. package/esm2020/utilities/lazy-asset-generator.mjs +35 -0
  62. package/esm2020/utilities/mongoose.mjs +216 -0
  63. package/esm2020/utils.mjs +693 -0
  64. package/esm2020/validators.mjs +46 -0
  65. package/fesm2015/{stemy-backend.js → stemy-backend.mjs} +1638 -2403
  66. package/fesm2015/stemy-backend.mjs.map +1 -0
  67. package/fesm2020/stemy-backend.mjs +4202 -0
  68. package/fesm2020/stemy-backend.mjs.map +1 -0
  69. package/{stemy-backend.d.ts → index.d.ts} +1 -0
  70. package/package.json +35 -23
  71. package/public_api.d.ts +2 -2
  72. package/rest-controllers/terminal-styles.d.ts +2 -0
  73. package/rest-controllers/terminal.controller.d.ts +2 -0
  74. package/services/assets.d.ts +1 -0
  75. package/services/entities/asset.d.ts +1 -0
  76. package/services/entities/temp-asset.d.ts +1 -0
  77. package/services/fixtures.d.ts +2 -2
  78. package/utils.d.ts +3 -0
  79. package/bundles/stemy-backend.umd.js +0 -7553
  80. package/bundles/stemy-backend.umd.js.map +0 -1
  81. package/esm2015/common-types.js +0 -20
  82. package/esm2015/public_api.js +0 -380
  83. package/esm2015/requests/asset-image-params.js +0 -70
  84. package/esm2015/rest-controllers/assets.controller.js +0 -180
  85. package/esm2015/rest-controllers/auth.controller.js +0 -76
  86. package/esm2015/rest-controllers/gallery.controller.js +0 -37
  87. package/esm2015/rest-controllers/progresses.controller.js +0 -57
  88. package/esm2015/rest-controllers/terminal.controller.js +0 -151
  89. package/esm2015/rest-middlewares/container.middleware.js +0 -32
  90. package/esm2015/rest-middlewares/error-handler.middleware.js +0 -99
  91. package/esm2015/rest-middlewares/language.middleware.js +0 -28
  92. package/esm2015/rest-middlewares/request-ended.middleware.js +0 -33
  93. package/esm2015/rest-middlewares/request-started.middleware.js +0 -32
  94. package/esm2015/rest-openapi.js +0 -44
  95. package/esm2015/services/asset-processor.js +0 -108
  96. package/esm2015/services/asset-resolver.js +0 -53
  97. package/esm2015/services/assets.js +0 -181
  98. package/esm2015/services/backend-provider.js +0 -32
  99. package/esm2015/services/cache-processor.js +0 -34
  100. package/esm2015/services/cache.js +0 -93
  101. package/esm2015/services/endpoint-provider.js +0 -29
  102. package/esm2015/services/entities/asset.js +0 -60
  103. package/esm2015/services/entities/base-entity.js +0 -37
  104. package/esm2015/services/entities/lazy-asset.js +0 -90
  105. package/esm2015/services/entities/progress.js +0 -213
  106. package/esm2015/services/entities/temp-asset.js +0 -64
  107. package/esm2015/services/fixtures.js +0 -45
  108. package/esm2015/services/gallery-cache.js +0 -36
  109. package/esm2015/services/gallery-image.js +0 -48
  110. package/esm2015/services/gallery.js +0 -138
  111. package/esm2015/services/id-generator.js +0 -63
  112. package/esm2015/services/job-manager.js +0 -221
  113. package/esm2015/services/lazy-assets.js +0 -83
  114. package/esm2015/services/logger.js +0 -31
  115. package/esm2015/services/mail-sender.js +0 -58
  116. package/esm2015/services/memory-cache.js +0 -84
  117. package/esm2015/services/mongo-connector.js +0 -58
  118. package/esm2015/services/open-api.js +0 -140
  119. package/esm2015/services/progresses.js +0 -118
  120. package/esm2015/services/template-renderer.js +0 -88
  121. package/esm2015/services/terminal-manager.js +0 -94
  122. package/esm2015/services/token-generator.js +0 -55
  123. package/esm2015/services/translation-provider.js +0 -54
  124. package/esm2015/services/translator.js +0 -84
  125. package/esm2015/services/user-manager.js +0 -47
  126. package/esm2015/socket-controllers/progress.controller.js +0 -82
  127. package/esm2015/socket-controllers/terminal.controller.js +0 -83
  128. package/esm2015/socket-controllers/terminal.js +0 -98
  129. package/esm2015/socket-middlewares/compression.middleware.js +0 -19
  130. package/esm2015/static.js +0 -33
  131. package/esm2015/utilities/clear-command.js +0 -33
  132. package/esm2015/utilities/decorators.js +0 -54
  133. package/esm2015/utilities/di-container.js +0 -84
  134. package/esm2015/utilities/empty-job.js +0 -29
  135. package/esm2015/utilities/lazy-asset-generator.js +0 -46
  136. package/esm2015/utilities/mongoose.js +0 -225
  137. package/esm2015/utils.js +0 -699
  138. package/esm2015/validators.js +0 -51
  139. package/fesm2015/stemy-backend.js.map +0 -1
  140. package/rest-openapi.d.ts +0 -3
  141. package/stemy-backend.metadata.json +0 -1
  142. /package/{utilities → commands}/clear-command.d.ts +0 -0
  143. /package/{esm2015/stemy-backend.js → esm2020/stemy-backend.mjs} +0 -0
  144. /package/{esm2015/utilities/base-doc.js → esm2020/utilities/base-doc.mjs} +0 -0
  145. /package/{esm2015/utilities/tree.js → esm2020/utilities/tree.mjs} +0 -0
@@ -0,0 +1,4202 @@
1
+ import { dirname, basename, join, resolve } from 'path';
2
+ import bodyParser from 'body-parser';
3
+ import webToken from 'jsonwebtoken';
4
+ import { injectable, scoped, Lifecycle, singleton, injectAll, inject, isFactoryProvider, container } from 'tsyringe';
5
+ import { HttpError, getMetadataArgsStorage, Authorized, Post, UploadedFile, Body, Get, Param, QueryParam, Res, QueryParams, Controller, UnauthorizedError, CurrentUser, Header, BadRequestError, Middleware, createParamDecorator, useContainer, useExpressServer } from 'routing-controllers';
6
+ import { OnMessage, ConnectedSocket, MessageBody, SocketController, Middleware as Middleware$1, SocketControllers } from 'socket-controllers';
7
+ import { __decorate, __param } from 'tslib';
8
+ import fontKit_ from 'fontkit';
9
+ import sharp_ from 'sharp';
10
+ import { ObjectId as ObjectId$1 } from 'bson';
11
+ import axios from 'axios';
12
+ import { mkdir, unlink, readFile as readFile$1, writeFile as writeFile$1, lstat, readdir, access, constants, lstatSync, readFileSync, existsSync } from 'fs';
13
+ import { fileURLToPath } from 'url';
14
+ import { exec } from 'child_process';
15
+ import { createHash } from 'crypto';
16
+ import { Subscription, Observable, Subject, from, BehaviorSubject } from 'rxjs';
17
+ import { canReportError } from 'rxjs/internal/util/canReportError';
18
+ import { ObjectId, GridFSBucket } from 'mongodb';
19
+ import { Types, connect, model } from 'mongoose';
20
+ import { Readable, PassThrough } from 'stream';
21
+ import { fileTypeFromStream } from 'file-type/core';
22
+ import { createServer } from 'http';
23
+ import express_, { static as static$1 } from 'express';
24
+ import { Server } from 'socket.io';
25
+ import dotenv from 'dotenv';
26
+ import { v4 } from 'uuid';
27
+ import cron from 'node-cron';
28
+ import { socket } from 'zeromq';
29
+ import { filter as filter$1, map, first, timeout } from 'rxjs/operators';
30
+ import { createTransport } from 'nodemailer';
31
+ import { routingControllersToSpec, OpenAPI, getStatusCode } from 'routing-controllers-openapi';
32
+ import { validationMetadatasToSchemas } from 'class-validator-jsonschema';
33
+ import { ValidatorConstraint, ValidationTypes, Min, Max, IsOptional, IsBoolean } from 'class-validator';
34
+ import * as Handlebars from 'handlebars';
35
+ import { CommandsAddon, AnsiCodes } from '@stemy/terminal-commands-addon';
36
+ import { compare } from 'bcrypt';
37
+ import moment from 'moment';
38
+ import { getModelForClass } from '@typegoose/typegoose';
39
+ import { getValue as getValue$1, setValue } from 'mongoose/lib/utils';
40
+
41
+ // --- DI functions ---
42
+ const Type = Function;
43
+ // --- Injection tokens ---
44
+ const FIXTURE = Symbol.for("fixture-token");
45
+ const JOB = Symbol.for("job-token");
46
+ const TERMINAL_COMMAND = Symbol.for("terminal-command-token");
47
+ const EXPRESS = Symbol.for("express-token");
48
+ const HTTP_SERVER = Symbol.for("http-server-token");
49
+ const SOCKET_SERVER = Symbol.for("socket-server-token");
50
+ const SOCKET_CONTROLLERS = Symbol.for("socket-controllers-token");
51
+ const PARAMETER = Symbol.for("parameter-token");
52
+ const DI_CONTAINER = Symbol.for("di-container-token");
53
+ const OPENAPI_VALIDATION = Symbol.for("openapi-validation-token");
54
+ class Parameter {
55
+ constructor(name, defaultValue, resolver = null) {
56
+ this.name = name;
57
+ this.defaultValue = defaultValue;
58
+ this.resolver = resolver;
59
+ }
60
+ }
61
+
62
+ var AssetProcessor_1;
63
+ const sharp$3 = sharp_;
64
+ const fontKit = fontKit_;
65
+ const fontTypes = [
66
+ "application/font-woff", "application/font-woff2", "application/x-font-opentype", "application/x-font-truetype", "application/x-font-datafork",
67
+ "font/woff", "font/woff2", "font/otf", "font/ttf", "font/datafork"
68
+ ];
69
+ const imageTypes = ["image/jpeg", "image/jpg", "image/png", "image/svg+xml"];
70
+ const fontProps = [
71
+ "postscriptName", "fullName", "familyName", "subfamilyName",
72
+ "copyright", "version", "unitsPerEm", "ascent", "descent", "lineGap",
73
+ "underlinePosition", "underlineThickness", "italicAngle", "capHeight",
74
+ "xHeight", "numGlyphs", "characterSet", "availableFeatures"
75
+ ];
76
+ let AssetProcessor = AssetProcessor_1 = class AssetProcessor {
77
+ static extractFontFormat(font) {
78
+ const name = font.constructor.name;
79
+ const tag = font["directory"].tag;
80
+ switch (name) {
81
+ case "TTFFont":
82
+ return tag === "OTTO" ? "opentype" : "truetype";
83
+ case "WOFF2Font":
84
+ return "woff2";
85
+ case "WOFFFont":
86
+ return "woff";
87
+ case "DFont":
88
+ return "datafork";
89
+ }
90
+ return null;
91
+ }
92
+ static isImage(contentType) {
93
+ return imageTypes.indexOf(contentType) >= 0;
94
+ }
95
+ static async copyImageMeta(buffer, metadata, fileType) {
96
+ if (fileType.mime === "image/svg+xml") {
97
+ const match = /<svg([^<>]+)>/gi.exec(buffer.toString("utf8"));
98
+ if (match && match.length > 1) {
99
+ const attrs = match[1].match(/([a-z]+)="([^"]+)"/gi);
100
+ attrs.forEach(attr => {
101
+ if (attr.length < 5)
102
+ return;
103
+ const [name, value] = attr.split("=");
104
+ const val = value.replace(/"/gi, "");
105
+ metadata[name] = isNaN(val) ? val : Number(val);
106
+ });
107
+ if (metadata.viewBox && (isNaN(metadata.width) || isNaN(metadata.height))) {
108
+ const parts = metadata.viewBox.split(" ");
109
+ metadata.width = Number(parts[0]) + Number(parts[2]);
110
+ metadata.height = Number(parts[1]) + Number(parts[3]);
111
+ }
112
+ if (!isNaN(metadata.width) && !isNaN(metadata.height)) {
113
+ metadata.svgSize = { x: metadata.width, y: metadata.height };
114
+ }
115
+ }
116
+ return buffer;
117
+ }
118
+ const output = await sharp$3(buffer).rotate().toBuffer({ resolveWithObject: true });
119
+ Object.assign(metadata, output.info);
120
+ return output.data;
121
+ }
122
+ static isFont(contentType) {
123
+ return fontTypes.indexOf(contentType) >= 0;
124
+ }
125
+ static copyFontMeta(buffer, metadata) {
126
+ const font = fontKit.create(buffer);
127
+ metadata.format = AssetProcessor_1.extractFontFormat(font);
128
+ fontProps.forEach(prop => {
129
+ metadata[prop] = font[prop];
130
+ });
131
+ }
132
+ async process(buffer, metadata, fileType) {
133
+ if (AssetProcessor_1.isImage(fileType.mime)) {
134
+ buffer = await AssetProcessor_1.copyImageMeta(buffer, metadata, fileType);
135
+ }
136
+ if (AssetProcessor_1.isFont(fileType.mime)) {
137
+ AssetProcessor_1.copyFontMeta(buffer, metadata);
138
+ }
139
+ return buffer;
140
+ }
141
+ };
142
+ AssetProcessor = AssetProcessor_1 = __decorate([
143
+ injectable(),
144
+ scoped(Lifecycle.ContainerScoped)
145
+ ], AssetProcessor);
146
+
147
+ let AssetResolver = class AssetResolver {
148
+ constructor(assets, lazyAssets) {
149
+ this.assets = assets;
150
+ this.lazyAssets = lazyAssets;
151
+ }
152
+ async resolve(id, lazy = false) {
153
+ let asset = null;
154
+ if (lazy) {
155
+ const lazyAsset = await this.lazyAssets.read(id);
156
+ if (!lazyAsset)
157
+ return null;
158
+ return lazyAsset.loadAsset();
159
+ }
160
+ asset = await this.assets.read(id);
161
+ if (!asset) {
162
+ const lazyAsset = await this.lazyAssets.read(id);
163
+ if (!lazyAsset)
164
+ return null;
165
+ return lazyAsset.loadAsset();
166
+ }
167
+ return asset;
168
+ }
169
+ };
170
+ AssetResolver = __decorate([
171
+ injectable(),
172
+ scoped(Lifecycle.ContainerScoped)
173
+ ], AssetResolver);
174
+
175
+ const sharp$2 = sharp_;
176
+ const diContainers = {
177
+ appContainer: null
178
+ };
179
+ function isNullOrUndefined(value) {
180
+ return value == null || typeof value == "undefined";
181
+ }
182
+ function isDefined(value) {
183
+ return !isNullOrUndefined(value);
184
+ }
185
+ function getType(obj) {
186
+ const regex = new RegExp("\\s([a-zA-Z]+)");
187
+ return Object.prototype.toString.call(obj).match(regex)[1].toLowerCase();
188
+ }
189
+ function isObject(value) {
190
+ return getType(value) == "object";
191
+ }
192
+ function isArray(value) {
193
+ return Array.isArray(value);
194
+ }
195
+ function isBuffer(value) {
196
+ return value instanceof Buffer;
197
+ }
198
+ function isBoolean(value) {
199
+ return typeof value === "boolean";
200
+ }
201
+ function isDate(value) {
202
+ return !!value && value[Symbol.toPrimitive] && !isNaN(value) && "undefined" !== typeof value.getDate;
203
+ }
204
+ function isPrimitive(value) {
205
+ const type = typeof value;
206
+ return value == null || (type !== "object" && type !== "function");
207
+ }
208
+ function isString(value) {
209
+ return typeof value === "string";
210
+ }
211
+ function isFunction(value) {
212
+ return typeof value === "function";
213
+ }
214
+ function isConstructor(value) {
215
+ return (value && typeof value === "function" && value.prototype && value.prototype.constructor) === value && value.name !== "Object";
216
+ }
217
+ function isType(value) {
218
+ return isConstructor(value);
219
+ }
220
+ function isInterface(obj, interFaceObject) {
221
+ if (!obj || typeof obj !== "object" || isArray(obj) || !isObject(interFaceObject))
222
+ return false;
223
+ const keys = Object.keys(interFaceObject);
224
+ for (const key of keys) {
225
+ let type = interFaceObject[key] || "";
226
+ if (type.startsWith("*")) {
227
+ type = type.substr(1);
228
+ if (obj.hasOwnProperty(key) && getType(obj[key]) !== type)
229
+ return false;
230
+ }
231
+ else if (!obj.hasOwnProperty(key) || getType(obj[key]) !== type) {
232
+ return false;
233
+ }
234
+ }
235
+ return true;
236
+ }
237
+ function ucFirst(value) {
238
+ if (!value)
239
+ return "";
240
+ return value[0].toUpperCase() + value.substr(1);
241
+ }
242
+ function lcFirst(value) {
243
+ if (!value)
244
+ return "";
245
+ return value[0].toLowerCase() + value.substr(1);
246
+ }
247
+ function isObjectId(id) {
248
+ return typeof id === "string" && id.length == 24 && !isNaN(Number("0x" + id));
249
+ }
250
+ function firstItem(value) {
251
+ return value[0];
252
+ }
253
+ function lastItem(value) {
254
+ return value[value.length - 1];
255
+ }
256
+ function regroup(value, comparator) {
257
+ const result = [];
258
+ if (!isArray(value) || value.length == 0)
259
+ return result;
260
+ value = Array.from(value);
261
+ result.push([value.shift()]);
262
+ value.forEach(item => {
263
+ const group = result.find(g => g.some(a => comparator(a, item)));
264
+ if (group) {
265
+ group.push(item);
266
+ return;
267
+ }
268
+ result.push([item]);
269
+ });
270
+ return result;
271
+ }
272
+ function uniqueItems(value) {
273
+ return value.filter((v, ix) => value.indexOf(v) === ix);
274
+ }
275
+ function getValue(obj, key, defaultValue, treeFallback = false) {
276
+ key = key || "";
277
+ const keys = key.split(".");
278
+ let curKey = "";
279
+ do {
280
+ curKey += keys.shift();
281
+ if (isDefined(obj) && isDefined(obj[curKey]) && (typeof obj[curKey] === "object" || !keys.length)) {
282
+ obj = obj[curKey];
283
+ curKey = "";
284
+ }
285
+ else if (!keys.length) {
286
+ defaultValue = typeof defaultValue == "undefined" ? key.replace(new RegExp(`${curKey}$`), `{${curKey}}`) : defaultValue;
287
+ obj = treeFallback ? obj || defaultValue : defaultValue;
288
+ }
289
+ else {
290
+ curKey += ".";
291
+ }
292
+ } while (keys.length);
293
+ return obj;
294
+ }
295
+ function groupBy(items, cb) {
296
+ return items.reduce((res, item) => {
297
+ const group = cb(item);
298
+ res[group] = res[group] || [];
299
+ res[group].push(item);
300
+ return res;
301
+ }, {});
302
+ }
303
+ function convertValue(value, type) {
304
+ switch (type) {
305
+ case "boolean":
306
+ value = typeof value == "string" ? value.toLowerCase() : value;
307
+ return (value == "no" || value == "false" || value == "0") ? false : !!value;
308
+ case "number":
309
+ const val = parseFloat(value);
310
+ return isNaN(val) ? 0 : val;
311
+ case "array":
312
+ try {
313
+ return JSON.parse(value);
314
+ }
315
+ catch (e) {
316
+ return `${value}`.split(", ");
317
+ }
318
+ }
319
+ return value;
320
+ }
321
+ const cropInterface = {
322
+ x: "number",
323
+ y: "number",
324
+ w: "number",
325
+ h: "number"
326
+ };
327
+ function toCropRegion(cropInfo) {
328
+ let crop = cropInfo;
329
+ if (isString(cropInfo)) {
330
+ try {
331
+ crop = JSON.parse(cropInfo);
332
+ }
333
+ catch (e) {
334
+ return null;
335
+ }
336
+ }
337
+ if (!isInterface(crop, cropInterface))
338
+ return null;
339
+ return {
340
+ width: Math.round(crop.w),
341
+ height: Math.round(crop.h),
342
+ top: Math.round(crop.y),
343
+ left: Math.round(crop.x)
344
+ };
345
+ }
346
+ async function toImage(src, params, meta) {
347
+ // Default params and meta
348
+ params = params || {};
349
+ meta = meta || {};
350
+ // Get default crop info
351
+ const crop = toCropRegion(meta.crop);
352
+ // Return the src if there are no params and no default crop exists
353
+ if (meta.extension === "svg" || (Object.keys(params).length == 0 && !crop)) {
354
+ return src;
355
+ }
356
+ // Parse params
357
+ params.rotation = isNaN(params.rotation) ? 0 : Math.round(params.rotation / 90) * 90;
358
+ params.canvasScaleX = isNaN(params.canvasScaleX) ? 1 : Number(params.canvasScaleX);
359
+ params.canvasScaleY = isNaN(params.canvasScaleY) ? 1 : Number(params.canvasScaleY);
360
+ params.scaleX = isNaN(params.scaleX) ? 1 : Number(params.scaleX);
361
+ params.scaleY = isNaN(params.scaleY) ? 1 : Number(params.scaleY);
362
+ params.crop = isBoolean(params.crop) ? params.crop : params.crop == "true";
363
+ let buffer = src instanceof Readable ? await streamToBuffer(src) : src;
364
+ try {
365
+ // Get crop info
366
+ const cropBefore = toCropRegion(params.cropBefore || (params.crop ? meta.cropBefore : null));
367
+ const cropAfter = toCropRegion(params.cropAfter || (params.crop ? meta.cropAfter : null));
368
+ // Get metadata
369
+ let img = sharp$2(buffer);
370
+ let { width, height } = await img.metadata();
371
+ // Crop before resize
372
+ if (cropBefore) {
373
+ width = cropBefore.width;
374
+ height = cropBefore.height;
375
+ img = img.extract(cropBefore);
376
+ }
377
+ else if (crop) {
378
+ width = crop.width;
379
+ height = crop.height;
380
+ img = img.extract(crop);
381
+ }
382
+ // Resize canvas
383
+ const canvasScaleX = meta?.canvasScaleX || 1;
384
+ const canvasScaleY = meta?.canvasScaleY || 1;
385
+ if (params.canvasScaleX !== canvasScaleX || params.canvasScaleY !== canvasScaleY) {
386
+ width = Math.round(width * params.canvasScaleX);
387
+ height = Math.round(height * params.canvasScaleY);
388
+ img = img.resize({ width, height, background: "#00000000", fit: "contain" });
389
+ }
390
+ // Resize image
391
+ if (params.scaleX !== 1 || params.scaleY !== 1) {
392
+ width = Math.round(width * params.scaleX);
393
+ height = Math.round(height * params.scaleY);
394
+ img = img.resize({ width, height, background: "#00000000", fit: "fill" });
395
+ }
396
+ // Crop after resize
397
+ if (cropAfter) {
398
+ img = img.extract(cropAfter);
399
+ }
400
+ // Rotate
401
+ if (params.rotation !== 0) {
402
+ buffer = await img.toBuffer();
403
+ img = sharp$2(buffer).rotate(params.rotation);
404
+ }
405
+ buffer = await img.toBuffer();
406
+ src = src instanceof Readable ? bufferToStream(buffer) : buffer;
407
+ return src;
408
+ }
409
+ catch (e) {
410
+ console.log("Image conversion error", e);
411
+ src = src instanceof Readable ? bufferToStream(buffer) : buffer;
412
+ return src;
413
+ }
414
+ }
415
+ function bufferToStream(buffer) {
416
+ const readStream = new PassThrough();
417
+ readStream.end(buffer);
418
+ return readStream;
419
+ }
420
+ function streamToBuffer(stream) {
421
+ return new Promise((resolve, reject) => {
422
+ const concat = [];
423
+ stream.on("data", data => {
424
+ concat.push(data);
425
+ });
426
+ stream.on("error", reject);
427
+ stream.on("end", () => resolve(Buffer.concat(concat)));
428
+ });
429
+ }
430
+ class ReadableStreamClone extends Readable {
431
+ constructor(readableStream, opts) {
432
+ super(opts);
433
+ readableStream?.on("data", chunk => {
434
+ this.push(chunk);
435
+ });
436
+ readableStream?.on("end", () => {
437
+ this.push(null);
438
+ });
439
+ readableStream?.on("error", err => {
440
+ this.emit("error", err);
441
+ });
442
+ }
443
+ _read(size) {
444
+ }
445
+ }
446
+ function copyStream(stream, opts) {
447
+ return new ReadableStreamClone(stream, opts);
448
+ }
449
+ function mkdirRecursive(path, mode = null) {
450
+ return new Promise((resolve, reject) => {
451
+ mkdir(path, { mode: mode || 0o777, recursive: true }, err => {
452
+ if (err) {
453
+ reject(err);
454
+ return;
455
+ }
456
+ resolve();
457
+ });
458
+ });
459
+ }
460
+ function deleteFile(path) {
461
+ return new Promise((resolve, reject) => {
462
+ unlink(path, err => {
463
+ if (err) {
464
+ reject(err);
465
+ return;
466
+ }
467
+ resolve();
468
+ });
469
+ });
470
+ }
471
+ function readFile(path) {
472
+ return new Promise((res, rej) => {
473
+ readFile$1(path, (err, data) => {
474
+ if (err) {
475
+ rej(err);
476
+ return;
477
+ }
478
+ res(data);
479
+ });
480
+ });
481
+ }
482
+ async function readAndDeleteFile(path, timeout = 5000) {
483
+ const data = await readFile(path);
484
+ setTimeout(() => {
485
+ unlink(path, () => {
486
+ });
487
+ }, timeout);
488
+ return data;
489
+ }
490
+ async function writeFile(path, data) {
491
+ await mkdirRecursive(dirname(path));
492
+ return new Promise((res, rej) => {
493
+ writeFile$1(path, data, err => {
494
+ if (err) {
495
+ rej(err);
496
+ return;
497
+ }
498
+ res(data);
499
+ });
500
+ });
501
+ }
502
+ function valueToPromise(value) {
503
+ return value instanceof Promise ? value : Promise.resolve(value);
504
+ }
505
+ function promiseTimeout(timeout = 1000, error = false) {
506
+ return new Promise((resolve, reject) => {
507
+ setTimeout(() => {
508
+ if (error) {
509
+ reject(`Timeout exceeded: ${timeout}ms`);
510
+ return;
511
+ }
512
+ resolve(`Timeout: ${timeout}ms`);
513
+ }, timeout);
514
+ });
515
+ }
516
+ function getConstructorName(type) {
517
+ return type.prototype.constructor.name;
518
+ }
519
+ function getFunctionParams(func) {
520
+ // Remove comments of the form /* ... */
521
+ // Removing comments of the form //
522
+ // Remove body of the function { ... }
523
+ // removing "=>" if func is arrow function
524
+ const str = func.toString()
525
+ .replace(/\/\*[\s\S]*?\*\//g, "")
526
+ .replace(/\/\/(.)*/g, "")
527
+ .replace(/{[\s\S]*}/, "")
528
+ .replace(/=>/g, "")
529
+ .trim();
530
+ // Start parameter names after first "("
531
+ const start = str.indexOf("(") + 1;
532
+ // End parameter names is just before last ")"
533
+ const end = str.length - 1;
534
+ const result = str.substring(start, end).split(", ");
535
+ const params = [];
536
+ result.forEach(element => {
537
+ // Removing any default value
538
+ element = element.replace(/=[\s\S]*/g, "").trim();
539
+ if (element.length > 0)
540
+ params.push(element);
541
+ });
542
+ return params;
543
+ }
544
+ function getFileName(path, withExtension = false) {
545
+ const name = basename(path || "");
546
+ return withExtension ? name : name.split(".").slice(0, -1).join(".");
547
+ }
548
+ function getExtension(path) {
549
+ const name = basename(path || "");
550
+ return name.split(".").pop();
551
+ }
552
+ function createIdString() {
553
+ return new ObjectId().toHexString();
554
+ }
555
+ function idToString(value) {
556
+ if (Array.isArray(value)) {
557
+ return value.map(idToString);
558
+ }
559
+ return value instanceof ObjectId || value instanceof Types.ObjectId
560
+ ? value.toHexString()
561
+ : (isString(value) ? value : value || null);
562
+ }
563
+ function createTransformer(transform) {
564
+ return (doc, ret, options) => {
565
+ ret.id = idToString(ret.id) || ret.id;
566
+ if (doc._id) {
567
+ ret._id = idToString(doc._id);
568
+ ret.id = ret.id || ret._id;
569
+ }
570
+ delete ret.__v;
571
+ return isFunction(transform) ? transform(doc, ret, options) || ret : ret;
572
+ };
573
+ }
574
+ function broadcast(socketServer, cb) {
575
+ Array.from(Object.values(socketServer.sockets.sockets)).forEach(cb);
576
+ }
577
+ function rand(min, max) {
578
+ return Math.round(random(min, max));
579
+ }
580
+ function random(min, max) {
581
+ return min + Math.random() * (max - min);
582
+ }
583
+ function multiSubscription(...subscriptions) {
584
+ return new Subscription(() => {
585
+ subscriptions.forEach(s => {
586
+ s.unsubscribe();
587
+ });
588
+ });
589
+ }
590
+ function observableFromFunction(callbackFunc) {
591
+ let subject;
592
+ return new Observable((subscriber) => {
593
+ if (!subject) {
594
+ subject = new Subject();
595
+ try {
596
+ subject = from(callbackFunc());
597
+ }
598
+ catch (err) {
599
+ if (canReportError(subject)) {
600
+ subject.error(err);
601
+ }
602
+ else {
603
+ console.warn(err);
604
+ }
605
+ }
606
+ }
607
+ return subject.subscribe(subscriber);
608
+ });
609
+ }
610
+ function padLeft(value, count = 3, padWith = "0") {
611
+ return `${value}`.padStart(count, padWith);
612
+ }
613
+ function padRight(value, count = 3, padWith = "0") {
614
+ return `${value}`.padEnd(count, padWith);
615
+ }
616
+ function camelCaseToDash(str) {
617
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
618
+ }
619
+ function deleteFromBucket(bucket, fileId) {
620
+ return new Promise(((resolve, reject) => {
621
+ bucket.delete(fileId, error => {
622
+ let err = error;
623
+ if (error) {
624
+ err = error.message || error || "";
625
+ if (!isString(err) || !err.startsWith("FileNotFound")) {
626
+ reject(err);
627
+ return;
628
+ }
629
+ }
630
+ resolve(fileId.toHexString());
631
+ });
632
+ }));
633
+ }
634
+ const defaultPredicate = () => true;
635
+ function copyRecursive(target, source, predicate) {
636
+ predicate = predicate || defaultPredicate;
637
+ if (isPrimitive(source) || isDate(source) || isFunction(source))
638
+ return source;
639
+ if (isArray(source)) {
640
+ target = isArray(target) ? Array.from(target) : [];
641
+ source.forEach((item, index) => {
642
+ if (!predicate(item, index, target, source))
643
+ return;
644
+ if (target.length > index)
645
+ target[index] = copyRecursive(target[index], item, predicate);
646
+ else
647
+ target.push(copyRecursive(null, item, predicate));
648
+ });
649
+ return target;
650
+ }
651
+ if (isBuffer(source))
652
+ return Buffer.from(source);
653
+ const shouldCopy = isFunction(source.__shouldCopy) ? source.__shouldCopy : () => true;
654
+ if (isConstructor(source.constructor)) {
655
+ if (source.__shouldCopy === false)
656
+ return source;
657
+ if (!target) {
658
+ try {
659
+ target = new source.constructor();
660
+ }
661
+ catch (e) {
662
+ const proto = source.constructor.prototype || source.prototype;
663
+ target = Object.create(proto);
664
+ }
665
+ }
666
+ }
667
+ else {
668
+ target = Object.assign({}, target || {});
669
+ }
670
+ // Copy map entries
671
+ if (target instanceof Map) {
672
+ if (source instanceof Map) {
673
+ for (let [key, value] of source.entries()) {
674
+ if (!predicate(value, key, target, source))
675
+ continue;
676
+ target.set(key, !shouldCopy(key, value) ? value : copyRecursive(target.get(key), value, predicate));
677
+ }
678
+ }
679
+ return target;
680
+ }
681
+ // Copy object members
682
+ let keys = Object.keys(source);
683
+ target = keys.reduce((result, key) => {
684
+ if (!predicate(source[key], key, result, source))
685
+ return result;
686
+ result[key] = !shouldCopy(key, source[key]) ? source[key] : copyRecursive(result[key], source[key], predicate);
687
+ return result;
688
+ }, target);
689
+ // Copy object properties
690
+ const descriptors = Object.getOwnPropertyDescriptors(source);
691
+ keys = Object.keys(descriptors).filter(k => keys.indexOf(k) < 0);
692
+ keys.forEach(key => {
693
+ Object.defineProperty(target, key, descriptors[key]);
694
+ });
695
+ return target;
696
+ }
697
+ function filter(obj, predicate) {
698
+ return copyRecursive(null, obj, predicate);
699
+ }
700
+ function copy(obj) {
701
+ return copyRecursive(null, obj);
702
+ }
703
+ function assign(target, source, predicate) {
704
+ return copyRecursive(target, source, predicate);
705
+ }
706
+ function md5(data) {
707
+ if (isObject(data)) {
708
+ data = JSON.stringify(data);
709
+ }
710
+ if (!isString(data)) {
711
+ throw `Can't md5 other that raw object or string`;
712
+ }
713
+ return createHash("md5").update(data).digest("hex");
714
+ }
715
+ function runCommand(scriptPath, expectedCode = 0) {
716
+ return new Promise((resolve, reject) => {
717
+ const cp = exec(scriptPath, (error, stdout) => {
718
+ if (error && expectedCode !== error.code) {
719
+ console.log(error);
720
+ reject(error);
721
+ return;
722
+ }
723
+ const lines = (stdout || "").split("\n");
724
+ let line = null;
725
+ while (!line && lines.length > 0) {
726
+ line = lines.pop();
727
+ }
728
+ resolve(line);
729
+ });
730
+ cp.stdout.on("data", function (data) {
731
+ console.log(data.toString());
732
+ });
733
+ cp.stderr.on("data", function (data) {
734
+ console.error(data.toString());
735
+ });
736
+ });
737
+ }
738
+ var ConsoleColor;
739
+ (function (ConsoleColor) {
740
+ ConsoleColor["Reset"] = "\u001B[0m";
741
+ ConsoleColor["Bright"] = "\u001B[1m";
742
+ ConsoleColor["Dim"] = "\u001B[2m";
743
+ ConsoleColor["Underscore"] = "\u001B[4m";
744
+ ConsoleColor["Blink"] = "\u001B[5m";
745
+ ConsoleColor["Reverse"] = "\u001B[7m";
746
+ ConsoleColor["Hidden"] = "\u001B[8m";
747
+ ConsoleColor["FgBlack"] = "\u001B[30m";
748
+ ConsoleColor["FgRed"] = "\u001B[31m";
749
+ ConsoleColor["FgGreen"] = "\u001B[32m";
750
+ ConsoleColor["FgYellow"] = "\u001B[33m";
751
+ ConsoleColor["FgBlue"] = "\u001B[34m";
752
+ ConsoleColor["FgMagenta"] = "\u001B[35m";
753
+ ConsoleColor["FgCyan"] = "\u001B[36m";
754
+ ConsoleColor["FgWhite"] = "\u001B[37m";
755
+ ConsoleColor["FgDefault"] = "\u001B[38m";
756
+ ConsoleColor["BgBlack"] = "\u001B[40m";
757
+ ConsoleColor["BgRed"] = "\u001B[41m";
758
+ ConsoleColor["BgGreen"] = "\u001B[42m";
759
+ ConsoleColor["BgYellow"] = "\u001B[43m";
760
+ ConsoleColor["BgBlue"] = "\u001B[44m";
761
+ ConsoleColor["BgMagenta"] = "\u001B[45m";
762
+ ConsoleColor["BgCyan"] = "\u001B[46m";
763
+ ConsoleColor["BgWhite"] = "\u001B[47m";
764
+ ConsoleColor["BgDefault"] = "\u001B[48m";
765
+ })(ConsoleColor || (ConsoleColor = {}));
766
+ const defaultColors = {
767
+ keyColor: ConsoleColor.FgWhite,
768
+ numberColor: ConsoleColor.FgBlue,
769
+ stringColor: ConsoleColor.FgYellow,
770
+ trueColor: ConsoleColor.FgGreen,
771
+ falseColor: ConsoleColor.FgRed,
772
+ nullColor: ConsoleColor.BgMagenta
773
+ };
774
+ function colorize(input, color) {
775
+ return `${color}${input}${ConsoleColor.Reset}`;
776
+ }
777
+ function jsonHighlight(input, colorOptions) {
778
+ const colors = Object.assign({}, defaultColors, colorOptions);
779
+ const json = (isString(input) ? input : JSON.stringify(input, null, 2)).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
780
+ return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+]?\d+)?)/g, (match) => {
781
+ let color = colors.numberColor;
782
+ if (/^"/.test(match)) {
783
+ if (/:$/.test(match)) {
784
+ color = colors.keyColor;
785
+ }
786
+ else {
787
+ color = colors.stringColor;
788
+ match = '"' + match.substr(1, match.length - 2) + '"';
789
+ }
790
+ }
791
+ else {
792
+ color = /true/.test(match)
793
+ ? colors.trueColor
794
+ : /false/.test(match)
795
+ ? colors.falseColor
796
+ : /null/.test(match)
797
+ ? colors.nullColor
798
+ : color;
799
+ }
800
+ return `${color}${match}${ConsoleColor.Reset}`;
801
+ });
802
+ }
803
+ function replaceSpecialChars(str, to = "-") {
804
+ return `${str}`.replace(/[&\/\\#, +()$~%.@'":*?<>{}]/g, to);
805
+ }
806
+ function regexEscape(str) {
807
+ return `${str}`.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
808
+ }
809
+ function flatten(arr) {
810
+ return arr.reduce((flat, toFlatten) => {
811
+ return flat.concat(isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
812
+ }, []);
813
+ }
814
+ function wrapError(e, message, httpCode = 500) {
815
+ if (axios.isAxiosError(e)) {
816
+ e.message = message;
817
+ return e;
818
+ }
819
+ return new HttpError(httpCode, `${message}: ${e}`);
820
+ }
821
+ function getDirName() {
822
+ if (typeof __dirname === "undefined") {
823
+ const __filename = fileURLToPath(import.meta.url);
824
+ return dirname(__filename);
825
+ }
826
+ return __dirname;
827
+ }
828
+ function prepareUrl(ending = "/") {
829
+ return url => {
830
+ return url ? `${url.replace(/\/+$/, "")}${ending}` : ending;
831
+ };
832
+ }
833
+ const prepareUrlSlash = prepareUrl("/");
834
+ const prepareUrlEmpty = prepareUrl("");
835
+ function checkTextFileType(type) {
836
+ return type.mime.indexOf("text") >= 0 || type.mime.indexOf("xml") >= 0;
837
+ }
838
+ function fixTextFileType(type, buffer) {
839
+ const text = buffer.toString("utf8");
840
+ if (text.indexOf("<svg") >= 0) {
841
+ return { ext: "svg", mime: "image/svg+xml" };
842
+ }
843
+ return type;
844
+ }
845
+ async function fileTypeFromBuffer(buffer) {
846
+ const stream = bufferToStream(buffer);
847
+ const type = (await fileTypeFromStream(stream) ?? { ext: "txt", mime: "text/plain" });
848
+ if (checkTextFileType(type)) {
849
+ return fixTextFileType(type, buffer);
850
+ }
851
+ return type;
852
+ }
853
+
854
+ class BaseEntity {
855
+ constructor(mId, data, collection) {
856
+ this.mId = mId;
857
+ this.data = data;
858
+ this.collection = collection;
859
+ }
860
+ get id() {
861
+ return this.mId.toHexString();
862
+ }
863
+ save() {
864
+ return this.collection.updateOne({ _id: this.mId }, { $set: this.toJSON() });
865
+ }
866
+ async load() {
867
+ const res = await this.collection.findOne({ _id: this.mId });
868
+ this.deleted = !res;
869
+ this.data = res || {};
870
+ return this;
871
+ }
872
+ toJSON() {
873
+ const ret = Object.assign({}, this.data);
874
+ delete ret._id;
875
+ ret.id = this.id;
876
+ return ret;
877
+ }
878
+ }
879
+
880
+ class Asset extends BaseEntity {
881
+ constructor(id, data, collection, bucket) {
882
+ super(id, data, collection);
883
+ this.bucket = bucket;
884
+ }
885
+ get filename() {
886
+ return this.data.filename;
887
+ }
888
+ get contentType() {
889
+ return this.data.contentType;
890
+ }
891
+ get metadata() {
892
+ return this.data.metadata;
893
+ }
894
+ get stream() {
895
+ return this.bucket.openDownloadStream(this.mId);
896
+ }
897
+ async unlink() {
898
+ return deleteFromBucket(this.bucket, this.mId);
899
+ }
900
+ getBuffer() {
901
+ return streamToBuffer(this.stream);
902
+ }
903
+ async download(metadata) {
904
+ metadata = Object.assign(this.metadata, metadata || {});
905
+ metadata.downloadCount = isNaN(metadata.downloadCount) || !metadata.firstDownload
906
+ ? 1
907
+ : metadata.downloadCount + 1;
908
+ metadata.firstDownload = metadata.firstDownload || new Date();
909
+ metadata.lastDownload = new Date();
910
+ await this.collection.updateOne({ _id: this.mId }, { $set: { metadata } });
911
+ return this.stream;
912
+ }
913
+ async getImage(params = null) {
914
+ return toImage(this.stream, params, this.metadata);
915
+ }
916
+ async downloadImage(params, metadata) {
917
+ return toImage(await this.download(metadata), params, this.metadata);
918
+ }
919
+ }
920
+
921
+ class TempAsset {
922
+ constructor(buffer, filename, contentType, metadata) {
923
+ this.buffer = buffer;
924
+ this.filename = filename;
925
+ this.contentType = contentType;
926
+ this.metadata = metadata;
927
+ this.id = new ObjectId$1().toHexString();
928
+ }
929
+ get stream() {
930
+ return bufferToStream(this.buffer);
931
+ }
932
+ async unlink() {
933
+ throw new Error(`Temp asset '${this.id}' can not be removed!`);
934
+ }
935
+ async getBuffer() {
936
+ return this.buffer;
937
+ }
938
+ async download(metadata) {
939
+ return this.stream;
940
+ }
941
+ downloadImage(params, metadata) {
942
+ Object.assign(this.metadata, metadata || {});
943
+ return toImage(this.stream, params, this.metadata);
944
+ }
945
+ getImage(params) {
946
+ return this.downloadImage(params);
947
+ }
948
+ async save() {
949
+ return this;
950
+ }
951
+ async load() {
952
+ return this;
953
+ }
954
+ toJSON() {
955
+ return {
956
+ id: this.id,
957
+ filename: this.filename,
958
+ contentType: this.contentType,
959
+ metadata: this.metadata
960
+ };
961
+ }
962
+ }
963
+
964
+ let Assets = class Assets {
965
+ constructor(connector, assetProcessor) {
966
+ this.connector = connector;
967
+ this.assetProcessor = assetProcessor;
968
+ this.bucket = connector.bucket;
969
+ this.collection = connector.database?.collection("assets.files");
970
+ }
971
+ async write(stream, contentType = null, metadata = null) {
972
+ const uploadStream = copyStream(stream);
973
+ const buffer = await streamToBuffer(stream);
974
+ let fileType = { ext: "", mime: contentType };
975
+ try {
976
+ fileType = await fileTypeFromBuffer(buffer);
977
+ }
978
+ catch (e) {
979
+ if (!fileType.mime) {
980
+ throw new Error(`Can't determine mime type: ${e}`);
981
+ }
982
+ console.log(`Can't determine mime type`, e);
983
+ }
984
+ metadata = metadata || {};
985
+ return this.upload(uploadStream, fileType, metadata);
986
+ }
987
+ async writeBuffer(buffer, metadata = null, contentType = null) {
988
+ let fileType = { ext: "", mime: contentType };
989
+ try {
990
+ fileType = await fileTypeFromBuffer(buffer);
991
+ }
992
+ catch (e) {
993
+ if (!fileType.mime) {
994
+ throw `Can't determine mime type`;
995
+ }
996
+ console.log(`Can't determine mime type`, e);
997
+ }
998
+ metadata = metadata || {};
999
+ buffer = await this.assetProcessor.process(buffer, metadata, fileType);
1000
+ return this.upload(bufferToStream(buffer), fileType, metadata);
1001
+ }
1002
+ async writeUrl(url, metadata = null) {
1003
+ metadata = metadata || {};
1004
+ metadata.filename = metadata.filename || url;
1005
+ metadata.url = url;
1006
+ metadata.uploadTime = new Date().getTime();
1007
+ const oneWeek = 1000 * 3600 * 24 * 7;
1008
+ const asset = await this.find({ "metadata.url": url, "metadata.uploadTime": { $gt: metadata.uploadTime - oneWeek } });
1009
+ if (asset)
1010
+ return asset;
1011
+ const buffer = (await axios({ url, responseType: "arraybuffer" })).data;
1012
+ return this.writeBuffer(buffer, metadata);
1013
+ }
1014
+ async download(url, contentType = null) {
1015
+ let buffer = (await axios({ url, responseType: "arraybuffer" })).data;
1016
+ let fileType = { ext: "", mime: contentType };
1017
+ try {
1018
+ fileType = await fileTypeFromBuffer(buffer);
1019
+ }
1020
+ catch (e) {
1021
+ if (!fileType.mime) {
1022
+ throw `Can't determine mime type`;
1023
+ }
1024
+ console.log(`Can't determine mime type`, e);
1025
+ }
1026
+ const metadata = {
1027
+ filename: url,
1028
+ extension: (fileType.ext || "").trim()
1029
+ };
1030
+ buffer = await this.assetProcessor.process(buffer, metadata, fileType);
1031
+ return new TempAsset(buffer, url, fileType.mime, metadata);
1032
+ }
1033
+ async read(id) {
1034
+ return !id ? null : this.find({ _id: new ObjectId$1(id) });
1035
+ }
1036
+ async find(where) {
1037
+ const data = await this.collection.findOne(where);
1038
+ return !data ? null : new Asset(data._id, data, this.collection, this.bucket);
1039
+ }
1040
+ async findMany(where) {
1041
+ const cursor = this.collection.find(where);
1042
+ const items = await cursor.toArray() || [];
1043
+ const result = [];
1044
+ for (let item of items) {
1045
+ if (!item)
1046
+ continue;
1047
+ result.push(new Asset(item._id, item, this.collection, this.bucket));
1048
+ }
1049
+ return result;
1050
+ }
1051
+ async deleteMany(where) {
1052
+ const assets = await this.findMany(where);
1053
+ return Promise.all(assets.map(a => a.unlink()));
1054
+ }
1055
+ async unlink(id) {
1056
+ const asset = await this.read(id);
1057
+ if (!asset)
1058
+ return null;
1059
+ return asset.unlink();
1060
+ }
1061
+ async upload(stream, fileType, metadata) {
1062
+ const contentType = fileType.mime.trim();
1063
+ metadata = Object.assign({
1064
+ downloadCount: 0,
1065
+ firstDownload: null,
1066
+ lastDownload: null
1067
+ }, metadata || {});
1068
+ metadata.filename = metadata.filename || new ObjectId$1().toHexString();
1069
+ metadata.extension = (fileType.ext || "").trim();
1070
+ return new Promise(((resolve, reject) => {
1071
+ const uploaderStream = this.bucket.openUploadStream(metadata.filename);
1072
+ stream.pipe(uploaderStream)
1073
+ .on("error", error => {
1074
+ reject(error.message || error);
1075
+ })
1076
+ .on("finish", () => {
1077
+ const asset = new Asset(uploaderStream.id, {
1078
+ filename: metadata.filename,
1079
+ contentType,
1080
+ metadata
1081
+ }, this.collection, this.bucket);
1082
+ asset.save().then(() => {
1083
+ resolve(asset);
1084
+ }, error => {
1085
+ reject(error.message || error);
1086
+ });
1087
+ });
1088
+ }));
1089
+ }
1090
+ };
1091
+ Assets = __decorate([
1092
+ injectable(),
1093
+ scoped(Lifecycle.ContainerScoped)
1094
+ ], Assets);
1095
+
1096
+ const express = express_;
1097
+ let BackendProvider = class BackendProvider {
1098
+ constructor() {
1099
+ this.express = express();
1100
+ this.express.set("trust proxy", true);
1101
+ this.server = createServer(this.express);
1102
+ }
1103
+ get io() {
1104
+ this.ioServer = this.ioServer || new Server(this.server, { path: "/socket" });
1105
+ return this.ioServer;
1106
+ }
1107
+ };
1108
+ BackendProvider = __decorate([
1109
+ singleton()
1110
+ ], BackendProvider);
1111
+
1112
+ let Cache = class Cache {
1113
+ constructor(connector, config, cacheProcessor) {
1114
+ this.connector = connector;
1115
+ this.config = config;
1116
+ this.cacheProcessor = cacheProcessor;
1117
+ }
1118
+ async prepare() {
1119
+ if (this.collection)
1120
+ return;
1121
+ if (!this.connector.database) {
1122
+ throw new Error(`You can't use cache without mongo connection!`);
1123
+ }
1124
+ this.collection = this.connector.database.collection(this.config.resolve("cacheCollection"));
1125
+ await this.collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
1126
+ }
1127
+ async set(key, value, ttl, expirationTimestamp = null, tags = {}) {
1128
+ await this.prepare();
1129
+ const item = {
1130
+ _id: key,
1131
+ data: await this.cacheProcessor.serialize(value),
1132
+ tags: await this.cacheProcessor.serialize(tags),
1133
+ expirationTimestamp,
1134
+ };
1135
+ if (ttl) {
1136
+ const now = Math.round(new Date().getTime() / 1000);
1137
+ item.expiresAt = now + ttl;
1138
+ }
1139
+ await this.collection.updateOne({ _id: key }, { $set: item }, { upsert: true });
1140
+ return value;
1141
+ }
1142
+ async get(key) {
1143
+ await this.prepare();
1144
+ let item = await this.collection.findOne({ _id: key });
1145
+ const now = Math.round(new Date().getTime() / 1000);
1146
+ if (item && item.expiresAt && item.expiresAt < now) {
1147
+ item = null;
1148
+ }
1149
+ if (!item) {
1150
+ throw new Error(`Cache probably doesn't exists with key: ${key}`);
1151
+ }
1152
+ return await this.cacheProcessor.deserialize(item.data);
1153
+ }
1154
+ async getOrSet(key, valueCb, ttl, expirationTimestamp = null, tags = {}) {
1155
+ try {
1156
+ return await this.get(key);
1157
+ }
1158
+ catch (e) {
1159
+ return await this.set(key, await valueCb(), ttl, expirationTimestamp, tags);
1160
+ }
1161
+ }
1162
+ async delete(key) {
1163
+ await this.prepare();
1164
+ await this.collection.deleteOne({ _id: key });
1165
+ }
1166
+ };
1167
+ Cache = __decorate([
1168
+ singleton()
1169
+ ], Cache);
1170
+
1171
+ let CacheProcessor = class CacheProcessor {
1172
+ async serialize(data) {
1173
+ return data;
1174
+ }
1175
+ async deserialize(data) {
1176
+ return data;
1177
+ }
1178
+ };
1179
+ CacheProcessor = __decorate([
1180
+ injectable(),
1181
+ scoped(Lifecycle.ContainerScoped)
1182
+ ], CacheProcessor);
1183
+
1184
+ let Configuration = class Configuration {
1185
+ constructor(params) {
1186
+ dotenv.config();
1187
+ this.paramMap = {};
1188
+ this.paramValues = {};
1189
+ (params || []).forEach(param => this.add(param));
1190
+ }
1191
+ add(param) {
1192
+ const existingParam = this.paramMap[param.name] || param;
1193
+ existingParam.defaultValue = param.defaultValue;
1194
+ existingParam.resolver = param.resolver || existingParam.resolver;
1195
+ this.paramMap[param.name] = existingParam;
1196
+ }
1197
+ resolveValue(param, alreadyResolved) {
1198
+ const envName = param.name.replace(/\.?([A-Z|0-9]+)/g, function (x, y) {
1199
+ return "_" + y.toLowerCase();
1200
+ }).replace(/\./gi, "_").replace(/^_/, "").toUpperCase();
1201
+ const envValue = process.env[envName];
1202
+ const helper = (p) => this.resolveInternal(p, alreadyResolved);
1203
+ if (typeof envValue !== "undefined") {
1204
+ const value = isFunction(param.resolver)
1205
+ ? param.resolver(envValue, helper)
1206
+ : convertValue(envValue, getType(param.defaultValue));
1207
+ console.log(colorize(`Processing param value`, ConsoleColor.FgYellow), colorize(param.name, ConsoleColor.FgGreen), colorize(envName, ConsoleColor.FgBlue), `"${envValue}"`, value);
1208
+ return value;
1209
+ }
1210
+ else if (isFunction(param.resolver)) {
1211
+ const value = param.resolver(param.defaultValue, helper);
1212
+ console.log(colorize(`Processing default param value`, ConsoleColor.FgYellow), colorize(param.name, ConsoleColor.FgGreen), param.defaultValue, value);
1213
+ return value;
1214
+ }
1215
+ console.log(colorize(`Using default param value`, ConsoleColor.FgYellow), colorize(param.name, ConsoleColor.FgGreen), param.defaultValue);
1216
+ return param.defaultValue;
1217
+ }
1218
+ hasParam(name) {
1219
+ return !!this.paramMap[name];
1220
+ }
1221
+ resolve(name) {
1222
+ return this.resolveInternal(name, []);
1223
+ }
1224
+ resolveInternal(name, alreadyResolved) {
1225
+ if (alreadyResolved.includes(name)) {
1226
+ throw new Error(`Circular dependency detected: ${alreadyResolved.join(" -> ")} -> ${name}`);
1227
+ }
1228
+ alreadyResolved.push(name);
1229
+ const param = this.paramMap[name];
1230
+ if (!param)
1231
+ throw new Error(`Parameter with name: '${name}' does not exists in configuration`);
1232
+ if (!(name in this.paramValues)) {
1233
+ this.paramValues[name] = this.resolveValue(param, alreadyResolved);
1234
+ }
1235
+ return this.paramValues[name];
1236
+ }
1237
+ };
1238
+ Configuration = __decorate([
1239
+ singleton(),
1240
+ __param(0, injectAll(PARAMETER))
1241
+ ], Configuration);
1242
+
1243
+ let EndpointProvider = class EndpointProvider {
1244
+ async configure(app) {
1245
+ console.log(`Express app is mounted to: ${app.mountpath}`);
1246
+ }
1247
+ };
1248
+ EndpointProvider = __decorate([
1249
+ injectable(),
1250
+ scoped(Lifecycle.ContainerScoped)
1251
+ ], EndpointProvider);
1252
+
1253
+ let Fixtures = class Fixtures {
1254
+ constructor(fixtures) {
1255
+ this.fixtures = fixtures;
1256
+ }
1257
+ async load(output) {
1258
+ if (!this.fixtures)
1259
+ return;
1260
+ output = output || {
1261
+ write: console.log,
1262
+ writeln: t => console.log(t + "\n")
1263
+ };
1264
+ for (let fixture of this.fixtures) {
1265
+ await fixture.load(output);
1266
+ }
1267
+ }
1268
+ };
1269
+ Fixtures = __decorate([
1270
+ injectable(),
1271
+ scoped(Lifecycle.ContainerScoped),
1272
+ __param(0, injectAll(FIXTURE))
1273
+ ], Fixtures);
1274
+
1275
+ const sharp$1 = sharp_;
1276
+ let Gallery = class Gallery {
1277
+ constructor(config, galleryCache) {
1278
+ this.config = config;
1279
+ this.galleryCache = galleryCache;
1280
+ this.cache = {};
1281
+ this.dir = this.config.resolve("galleryDir");
1282
+ this.output = join(this.config.resolve("cacheDir"), "gallery");
1283
+ }
1284
+ async getFolder(folder, size = null) {
1285
+ this.cache[folder] = this.cache[folder] || new Promise(resolve => {
1286
+ lstat(join(this.dir, folder), (err, stats) => {
1287
+ if (err || !stats.isDirectory()) {
1288
+ resolve([]);
1289
+ return;
1290
+ }
1291
+ this.readRecursive(folder, "", size).then(resolve, () => resolve([]));
1292
+ });
1293
+ });
1294
+ return this.cache[folder];
1295
+ }
1296
+ readRecursive(path, folder, size) {
1297
+ return new Promise(resolve => {
1298
+ readdir(join(this.dir, path), (err, files) => {
1299
+ if (err) {
1300
+ resolve([]);
1301
+ return;
1302
+ }
1303
+ const promises = files.map(file => {
1304
+ return new Promise(async (resolve) => {
1305
+ const filePath = join(path, file);
1306
+ const absoluteFilePath = join(this.dir, filePath);
1307
+ lstat(absoluteFilePath, (err, stats) => {
1308
+ if (err) {
1309
+ resolve([]);
1310
+ return;
1311
+ }
1312
+ if (stats.isDirectory()) {
1313
+ this.readRecursive(filePath, join(folder, file), size).then(resolve);
1314
+ return;
1315
+ }
1316
+ const sharpImg = sharp$1(absoluteFilePath);
1317
+ sharpImg.rotate().metadata().then(() => {
1318
+ const getResultPath = (isThumb) => {
1319
+ return join(this.output, filePath.replace(/.([a-z|A-Z]+)$/gi, function (ext) {
1320
+ const suffix = isThumb ? 'thumb' : 'big';
1321
+ return `-${suffix}${ext}`;
1322
+ }));
1323
+ };
1324
+ resolve([this.galleryCache.create(folder, size, {
1325
+ getOriginal: () => {
1326
+ return new Promise((res, rej) => {
1327
+ readFile$1(absoluteFilePath, (err, data) => {
1328
+ if (err) {
1329
+ rej(err);
1330
+ return;
1331
+ }
1332
+ res(data);
1333
+ });
1334
+ });
1335
+ },
1336
+ writeResult: (isThumb, buffer) => {
1337
+ return new Promise(async (res, rej) => {
1338
+ const resultPath = getResultPath(isThumb);
1339
+ await mkdirRecursive(dirname(resultPath));
1340
+ writeFile$1(resultPath, buffer, err => {
1341
+ if (err) {
1342
+ rej(err);
1343
+ return;
1344
+ }
1345
+ res();
1346
+ });
1347
+ });
1348
+ },
1349
+ hasResult: (isThumb) => {
1350
+ return new Promise(res => {
1351
+ access(getResultPath(isThumb), constants.R_OK, err => {
1352
+ res(!err);
1353
+ });
1354
+ });
1355
+ },
1356
+ serveResult: (isThumb) => {
1357
+ return new Promise((res, rej) => {
1358
+ readFile$1(getResultPath(isThumb), (err, data) => {
1359
+ if (err) {
1360
+ rej(err);
1361
+ return;
1362
+ }
1363
+ res(data);
1364
+ });
1365
+ });
1366
+ }
1367
+ })]);
1368
+ }, () => resolve([]));
1369
+ });
1370
+ });
1371
+ });
1372
+ Promise.all(promises).then(folders => {
1373
+ resolve([].concat.apply([], folders));
1374
+ });
1375
+ });
1376
+ });
1377
+ }
1378
+ };
1379
+ Gallery = __decorate([
1380
+ injectable(),
1381
+ scoped(Lifecycle.ContainerScoped)
1382
+ ], Gallery);
1383
+
1384
+ const sharp = sharp_;
1385
+ const bigSize = 1500;
1386
+ const thumbSize = 250;
1387
+ class GalleryImage {
1388
+ constructor(folder, size, handler) {
1389
+ this.folder = folder;
1390
+ this.handler = handler;
1391
+ this.thumb = v4();
1392
+ this.big = v4();
1393
+ this.targetSize = !size ? { width: thumbSize, height: thumbSize } : size;
1394
+ }
1395
+ async serve(id) {
1396
+ const isThumb = id == this.thumb;
1397
+ if (await this.handler.hasResult(isThumb)) {
1398
+ return this.handler.serveResult(isThumb);
1399
+ }
1400
+ const original = sharp(await this.handler.getOriginal()).rotate();
1401
+ const meta = await original.metadata();
1402
+ const ratio = meta.width / meta.height;
1403
+ const sizeRatio = isThumb ? this.targetSize.width / this.targetSize.height : 1;
1404
+ const size = isThumb ? Math.max(this.targetSize.width, this.targetSize.height) : bigSize;
1405
+ const targetHeight = ratio > sizeRatio ? size : Math.round(size / ratio);
1406
+ const targetWidth = Math.round(targetHeight * ratio);
1407
+ const resized = original.resize(targetWidth, targetHeight);
1408
+ const buffer = await (isThumb ? resized.extract({
1409
+ left: Math.floor((targetWidth - this.targetSize.width) / 2),
1410
+ top: Math.floor((targetHeight - this.targetSize.height) / 2),
1411
+ width: this.targetSize.width,
1412
+ height: this.targetSize.height
1413
+ }).toBuffer() : resized.toBuffer());
1414
+ await this.handler.writeResult(isThumb, buffer);
1415
+ return this.handler.serveResult(isThumb);
1416
+ }
1417
+ }
1418
+
1419
+ let GalleryCache = class GalleryCache {
1420
+ constructor() {
1421
+ this.imgCache = {};
1422
+ }
1423
+ put(img) {
1424
+ this.imgCache[img.thumb] = img;
1425
+ this.imgCache[img.big] = img;
1426
+ }
1427
+ serve(id) {
1428
+ const img = this.imgCache[id];
1429
+ return !img ? null : img.serve(id);
1430
+ }
1431
+ create(folder, targetSize, handler) {
1432
+ const image = new GalleryImage(folder, targetSize, handler);
1433
+ this.put(image);
1434
+ return image;
1435
+ }
1436
+ };
1437
+ GalleryCache = __decorate([
1438
+ injectable(),
1439
+ scoped(Lifecycle.ContainerScoped)
1440
+ ], GalleryCache);
1441
+
1442
+ let IdGenerator = class IdGenerator {
1443
+ constructor(config) {
1444
+ this.config = config;
1445
+ this.prefix = config.resolve("idPrefix");
1446
+ this.separator = config.resolve("idSeparator");
1447
+ this.chars = config.resolve("idChars");
1448
+ this.parts = config.resolve("idParts");
1449
+ }
1450
+ async generate(checkCb) {
1451
+ let id = null;
1452
+ let tries = 0;
1453
+ let notGood = true;
1454
+ while (notGood && tries < 5) {
1455
+ id = this.generateId();
1456
+ notGood = await checkCb(id);
1457
+ tries++;
1458
+ }
1459
+ if (notGood) {
1460
+ throw `Couldn't generate an unique id..`;
1461
+ }
1462
+ return id;
1463
+ }
1464
+ generateId() {
1465
+ return this.prefix + this.parts.map(num => {
1466
+ let s = "";
1467
+ for (let i = 0; i < num; i++) {
1468
+ const ix = rand(0, this.chars.length - 1);
1469
+ s += this.chars[ix];
1470
+ }
1471
+ return s;
1472
+ }).join(this.separator);
1473
+ }
1474
+ };
1475
+ IdGenerator = __decorate([
1476
+ injectable(),
1477
+ scoped(Lifecycle.ContainerScoped)
1478
+ ], IdGenerator);
1479
+
1480
+ let JobManager = class JobManager {
1481
+ constructor(config, logger, container, jobTypes) {
1482
+ this.config = config;
1483
+ this.logger = logger;
1484
+ this.container = container;
1485
+ this.jobTypes = jobTypes || [];
1486
+ this.jobs = this.jobTypes.reduce((res, jobType) => {
1487
+ const jobName = getConstructorName(jobType);
1488
+ res[jobName] = (jobParams, uniqueId) => {
1489
+ const job = this.resolveJobInstance(jobType, jobParams, uniqueId);
1490
+ const messageBridge = {
1491
+ sendMessage: (message, params) => {
1492
+ params.uniqueId = uniqueId;
1493
+ this.workerPush.send([message, JSON.stringify(params)]);
1494
+ }
1495
+ };
1496
+ messageBridge.sendMessage(`job-started`, { name: jobName });
1497
+ return job.process(messageBridge);
1498
+ };
1499
+ return res;
1500
+ }, {});
1501
+ this.messages = new Subject();
1502
+ this.processing = false;
1503
+ this.maxTimeout = this.config.resolve("jobTimeout");
1504
+ }
1505
+ on(message, cb) {
1506
+ return this.messages
1507
+ .pipe(filter$1(t => t.message === message))
1508
+ .pipe(map(t => t.params)).subscribe(cb);
1509
+ }
1510
+ async process(jobType, params = {}) {
1511
+ let instance = null;
1512
+ try {
1513
+ instance = this.resolveJobInstance(jobType, params);
1514
+ }
1515
+ catch (e) {
1516
+ const jobName = getConstructorName(jobType);
1517
+ throw new Error(`Can't resolve params for job: ${jobName}, with params: ${JSON.stringify(params)}. Reason: ${e}`);
1518
+ }
1519
+ return instance.process();
1520
+ }
1521
+ async enqueueWithName(name, params = {}) {
1522
+ return this.sendToWorkers(this.tryResolveFromName(name, params), params);
1523
+ }
1524
+ async enqueue(jobType, params = {}) {
1525
+ return this.sendToWorkers(this.tryResolveAndInit(jobType, params), params);
1526
+ }
1527
+ schedule(minute, hour, dayOfMonth, month, dayOfWeek, jobType, params = {}) {
1528
+ const expression = [minute, hour, dayOfMonth, month, dayOfWeek].map(t => {
1529
+ if (isObject(t)) {
1530
+ const range = t;
1531
+ return `${range.min || 0}-${range.max || 0}`;
1532
+ }
1533
+ if (isArray(t)) {
1534
+ return t.join(",");
1535
+ }
1536
+ return `${t}`;
1537
+ }).join(" ");
1538
+ const jobName = getConstructorName(jobType);
1539
+ if (!cron.validate(expression)) {
1540
+ this.logger.log("job-manager", `Can't schedule the task: '${jobName}' because time expression is invalid.`);
1541
+ return null;
1542
+ }
1543
+ return cron.schedule(expression, () => {
1544
+ this.enqueue(jobType, params).catch(e => {
1545
+ this.logger.log("job-manager", `Can't enqueue job: '${jobName}' because: ${e}`);
1546
+ });
1547
+ });
1548
+ }
1549
+ async startProcessing() {
1550
+ if (this.processing)
1551
+ return null;
1552
+ this.processing = true;
1553
+ if (!this.config.resolve("isWorker")) {
1554
+ this.logger.log("job-manager", colorize(`Processing can not be started because this is NOT a worker process!`, ConsoleColor.FgRed));
1555
+ return null;
1556
+ }
1557
+ const host = this.config.resolve("zmqRemoteHost");
1558
+ const pushHost = `${host}:${this.config.resolve("zmqBackPort")}`;
1559
+ this.workerPush = socket("push");
1560
+ await this.workerPush.connect(pushHost);
1561
+ this.logger.log("job-manager", `Worker producer connected to: ${pushHost}`);
1562
+ const pullHost = `${host}:${this.config.resolve("zmqPort")}`;
1563
+ this.workerPull = socket("pull");
1564
+ await this.workerPull.connect(pullHost);
1565
+ this.logger.log("job-manager", `Worker consumer connected to: ${pullHost}`);
1566
+ this.workerPull.on("message", async (name, args, uniqId) => {
1567
+ try {
1568
+ const jobName = name.toString("utf8");
1569
+ const jobParams = JSON.parse(args.toString("utf8"));
1570
+ const uniqueId = uniqId?.toString("utf8");
1571
+ console.time(uniqueId);
1572
+ console.timeLog(uniqueId, `Started working on background job: ${colorize(jobName, ConsoleColor.FgCyan)} with args: \n${jsonHighlight(jobParams)}\n\n`);
1573
+ try {
1574
+ await Promise.race([this.jobs[jobName](jobParams, uniqueId), promiseTimeout(this.maxTimeout, true)]);
1575
+ console.timeLog(uniqueId, `Finished working on background job: ${colorize(jobName, ConsoleColor.FgCyan)}\n\n`);
1576
+ }
1577
+ catch (e) {
1578
+ console.timeLog(uniqueId, `Background job failed: ${colorize(jobName, ConsoleColor.FgRed)}\n${e}\n\n`);
1579
+ }
1580
+ console.timeEnd(uniqueId);
1581
+ }
1582
+ catch (e) {
1583
+ this.logger.log("job-manager", `Failed to start job: ${e.message}`);
1584
+ }
1585
+ });
1586
+ }
1587
+ tryResolve(jobType, params) {
1588
+ const jobName = getConstructorName(jobType);
1589
+ if (!this.jobs[jobName]) {
1590
+ throw `Can't find job with name: ${jobName} so it can't be enqueued!`;
1591
+ }
1592
+ try {
1593
+ this.resolveJobInstance(jobType, params);
1594
+ }
1595
+ catch (e) {
1596
+ throw `Can't resolve params for job: ${jobName}, with params: ${JSON.stringify(params)}. Reason: ${e}`;
1597
+ }
1598
+ return jobName;
1599
+ }
1600
+ tryResolveFromName(jobName, params) {
1601
+ const jobType = this.jobTypes.find(type => {
1602
+ return getConstructorName(type) == jobName;
1603
+ });
1604
+ if (!jobType) {
1605
+ throw `Can't find job type with name: ${jobName} so it can't be enqueued!`;
1606
+ }
1607
+ return this.tryResolveAndInit(jobType, params);
1608
+ }
1609
+ tryResolveAndInit(jobType, params) {
1610
+ if (!this.apiPush) {
1611
+ const port = this.config.resolve("zmqPort");
1612
+ this.apiPush = socket("push");
1613
+ this.apiPush.bind(`tcp://0.0.0.0:${port}`);
1614
+ this.logger.log("job-manager", `API producer bound to port: ${port}`);
1615
+ }
1616
+ if (!this.apiPull) {
1617
+ const backPort = this.config.resolve("zmqBackPort");
1618
+ this.apiPull = socket("pull");
1619
+ this.apiPull.bind(`tcp://0.0.0.0:${backPort}`);
1620
+ this.apiPull.on("message", (name, args) => {
1621
+ const message = name.toString("utf8");
1622
+ const params = JSON.parse(args?.toString("utf8") || "{}");
1623
+ const paramTypes = Object.keys(params).reduce((res, key) => {
1624
+ res[key] = getType(params[key]);
1625
+ return res;
1626
+ }, {});
1627
+ this.logger.log("job-manager", `Received a message from worker: "${colorize(message, ConsoleColor.FgCyan)}" with args: ${jsonHighlight(paramTypes)}\n\n`);
1628
+ this.messages.next({ message, params });
1629
+ });
1630
+ this.logger.log("job-manager", `API consumer bound to port: ${backPort}`);
1631
+ }
1632
+ return this.tryResolve(jobType, params);
1633
+ }
1634
+ resolveJobInstance(jobType, params, uniqueId = "") {
1635
+ const container = this.container.createChildContainer();
1636
+ Object.keys(params).map((name) => {
1637
+ container.register(name, { useValue: params[name] });
1638
+ });
1639
+ container.register("uniqueId", { useValue: uniqueId });
1640
+ container.register(jobType, jobType);
1641
+ return container.resolve(jobType);
1642
+ }
1643
+ async sendToWorkers(jobName, params) {
1644
+ const publisher = await this.apiPush;
1645
+ const uniqueId = new ObjectId$1().toHexString();
1646
+ await publisher.send([jobName, JSON.stringify(params), uniqueId]);
1647
+ return uniqueId;
1648
+ }
1649
+ };
1650
+ JobManager = __decorate([
1651
+ injectable(),
1652
+ scoped(Lifecycle.ContainerScoped),
1653
+ __param(2, inject(DI_CONTAINER)),
1654
+ __param(3, injectAll(JOB))
1655
+ ], JobManager);
1656
+
1657
+ class LazyAsset extends BaseEntity {
1658
+ constructor(id, data, collection, logger, assets, progresses) {
1659
+ super(id, data, collection);
1660
+ this.logger = logger;
1661
+ this.assets = assets;
1662
+ this.progresses = progresses;
1663
+ }
1664
+ get jobName() {
1665
+ return this.data.jobName;
1666
+ }
1667
+ get jobParams() {
1668
+ return this.data.jobParams;
1669
+ }
1670
+ get jobQue() {
1671
+ return this.data.jobQue;
1672
+ }
1673
+ get progressId() {
1674
+ return this.data.progressId;
1675
+ }
1676
+ get assetId() {
1677
+ return this.data.assetId;
1678
+ }
1679
+ async unlink() {
1680
+ await this.load();
1681
+ if (!this.progressId) {
1682
+ await this.collection.deleteOne({ _id: this.mId });
1683
+ }
1684
+ return deleteFromBucket(this.assets.bucket, new ObjectId$1(this.assetId));
1685
+ }
1686
+ startWorking() {
1687
+ this.load().then(() => {
1688
+ if (this.deleted)
1689
+ return;
1690
+ this.progresses.get(this.progressId).then(p => {
1691
+ p?.cancel();
1692
+ });
1693
+ this.startWorkingOnAsset(false).then(() => {
1694
+ this.logger.log("lazy-assets", `Started working on lazy asset: ${this.id}`);
1695
+ }).catch(reason => {
1696
+ this.logger.log("lazy-assets", `Can't start working on lazy asset: ${this.id}\nReason: ${reason}`);
1697
+ });
1698
+ });
1699
+ }
1700
+ async loadAsset() {
1701
+ await this.load();
1702
+ if (this.deleted)
1703
+ return null;
1704
+ if (this.assetId) {
1705
+ return this.assets.read(this.assetId);
1706
+ }
1707
+ if (this.progressId) {
1708
+ await this.progresses.waitToFinish(this.progressId);
1709
+ return this.loadAsset();
1710
+ }
1711
+ await this.startWorkingOnAsset(true);
1712
+ return this.loadAsset();
1713
+ }
1714
+ async writeAsset(asset) {
1715
+ this.data.assetId = asset.id;
1716
+ await this.save();
1717
+ return asset;
1718
+ }
1719
+ async startWorkingOnAsset(fromLoad) {
1720
+ this.data.progressId = (await this.progresses.create()).id;
1721
+ this.data.assetId = null;
1722
+ await this.save();
1723
+ await this.progresses.jobMan.enqueueWithName(this.data.jobName, { ...this.data.jobParams, lazyId: this.id, fromLoad });
1724
+ }
1725
+ }
1726
+
1727
+ let LazyAssets = class LazyAssets {
1728
+ constructor(connector, assets, progresses, logger, jobMan) {
1729
+ this.connector = connector;
1730
+ this.assets = assets;
1731
+ this.progresses = progresses;
1732
+ this.logger = logger;
1733
+ this.jobMan = jobMan;
1734
+ this.collection = connector.database.collection("lazyassets");
1735
+ }
1736
+ async create(jobType, jobParams = {}, jobQue = "main") {
1737
+ const jobName = this.jobMan.tryResolve(jobType, { ...jobParams, lazyId: "" });
1738
+ const data = {
1739
+ jobName,
1740
+ jobParams,
1741
+ jobQue
1742
+ };
1743
+ const existingAsset = await this.find(data);
1744
+ if (existingAsset)
1745
+ return existingAsset;
1746
+ const res = await this.collection.insertOne(data);
1747
+ return new LazyAsset(res.insertedId, data, this.collection, this.logger, this.assets, this.progresses);
1748
+ }
1749
+ async read(id) {
1750
+ return !id ? null : this.find({ _id: new ObjectId$1(id) });
1751
+ }
1752
+ async find(where) {
1753
+ const data = await this.collection.findOne(where);
1754
+ return !data
1755
+ ? null
1756
+ : new LazyAsset(data._id, data, this.collection, this.logger, this.assets, this.progresses);
1757
+ }
1758
+ async unlink(id) {
1759
+ const asset = await this.read(id);
1760
+ if (!asset)
1761
+ return null;
1762
+ return asset.unlink();
1763
+ }
1764
+ };
1765
+ LazyAssets = __decorate([
1766
+ injectable(),
1767
+ scoped(Lifecycle.ContainerScoped)
1768
+ ], LazyAssets);
1769
+
1770
+ let Logger = class Logger {
1771
+ constructor(config) {
1772
+ this.config = config;
1773
+ this.tags = this.config.resolve("logTags");
1774
+ this.ignoredTags = this.config.resolve("ignoredLogTags");
1775
+ }
1776
+ log(tag, ...params) {
1777
+ if (this.ignoredTags.includes(tag))
1778
+ return;
1779
+ if (this.tags.length === 0 || this.tags.includes(tag)) {
1780
+ console.log(`[${tag}]`, ...params);
1781
+ }
1782
+ }
1783
+ };
1784
+ Logger = __decorate([
1785
+ singleton()
1786
+ ], Logger);
1787
+
1788
+ let MailSender = class MailSender {
1789
+ constructor(config, renderer) {
1790
+ this.config = config;
1791
+ this.renderer = renderer;
1792
+ this.transporter = createTransport({
1793
+ host: this.config.resolve("smtpHost"),
1794
+ port: this.config.resolve("smtpPort"),
1795
+ auth: {
1796
+ user: this.config.resolve("smtpUser"),
1797
+ pass: this.config.resolve("smtpPassword"),
1798
+ }
1799
+ });
1800
+ }
1801
+ get translator() {
1802
+ return this.renderer.translator;
1803
+ }
1804
+ async sendMail(language, options) {
1805
+ const subject = await this.translator.getTranslation(language, options.subject || "-");
1806
+ const html = await this.renderer.render(options.template, language, options.context);
1807
+ return this.transporter.sendMail({
1808
+ from: options.from || this.config.resolve("mailSenderAddress"),
1809
+ to: options.to,
1810
+ attachments: options.attachments,
1811
+ subject,
1812
+ html
1813
+ });
1814
+ }
1815
+ };
1816
+ MailSender = __decorate([
1817
+ singleton()
1818
+ ], MailSender);
1819
+
1820
+ let MemoryCache = class MemoryCache {
1821
+ constructor(cache) {
1822
+ this.cache = cache;
1823
+ this.cacheMap = new Map();
1824
+ }
1825
+ async set(key, value, ttl, expirationTimestamp = null, tags = {}) {
1826
+ const now = Math.round(new Date().getTime() / 1000);
1827
+ const expTimestamp = Math.min(isNaN(ttl) ? Number.MAX_SAFE_INTEGER : ttl, 3600);
1828
+ this.cacheMap.set(key, {
1829
+ _id: key,
1830
+ data: value,
1831
+ expirationTimestamp: expTimestamp,
1832
+ expiresAt: now + expTimestamp,
1833
+ });
1834
+ return this.cache.set(key, value, ttl, expirationTimestamp, tags);
1835
+ }
1836
+ async get(key) {
1837
+ let item = this.cacheMap.get(key);
1838
+ const now = Math.round(new Date().getTime() / 1000);
1839
+ let expTimestamp = 3600;
1840
+ if (item && item.expiresAt && item.expiresAt < now) {
1841
+ expTimestamp = item.expirationTimestamp;
1842
+ item = null;
1843
+ }
1844
+ if (!item) {
1845
+ const value = await this.cache.get(key);
1846
+ this.cacheMap.set(key, {
1847
+ _id: key,
1848
+ data: value,
1849
+ expirationTimestamp: expTimestamp,
1850
+ expiresAt: now + expTimestamp,
1851
+ });
1852
+ return value;
1853
+ }
1854
+ return item.data;
1855
+ }
1856
+ async getOrSet(key, valueCb, ttl, expirationTimestamp = null, tags = {}) {
1857
+ try {
1858
+ return await this.get(key);
1859
+ }
1860
+ catch (e) {
1861
+ return await this.set(key, await valueCb(), ttl, expirationTimestamp, tags);
1862
+ }
1863
+ }
1864
+ async delete(key) {
1865
+ this.cacheMap.delete(key);
1866
+ await this.cacheMap.delete(key);
1867
+ }
1868
+ };
1869
+ MemoryCache = __decorate([
1870
+ injectable(),
1871
+ scoped(Lifecycle.ContainerScoped)
1872
+ ], MemoryCache);
1873
+
1874
+ let MongoConnector = class MongoConnector {
1875
+ constructor(configuration) {
1876
+ this.configuration = configuration;
1877
+ this.conn = null;
1878
+ this.db = null;
1879
+ this.fsBucket = null;
1880
+ }
1881
+ get connection() {
1882
+ return this.conn;
1883
+ }
1884
+ get database() {
1885
+ return this.db;
1886
+ }
1887
+ get bucket() {
1888
+ return this.fsBucket;
1889
+ }
1890
+ async connect() {
1891
+ if (this.db)
1892
+ return this.db;
1893
+ this.conn = (await connect(this.configuration.resolve("mongoUri"), {
1894
+ dbName: this.configuration.resolve("mongoDb"),
1895
+ user: this.configuration.resolve("mongoUser"),
1896
+ pass: this.configuration.resolve("mongoPassword")
1897
+ })).connection;
1898
+ this.db = this.conn.db;
1899
+ this.fsBucket = new GridFSBucket(this.db, { bucketName: "assets" });
1900
+ }
1901
+ };
1902
+ MongoConnector = __decorate([
1903
+ singleton()
1904
+ ], MongoConnector);
1905
+
1906
+ function checkValue(multi, value) {
1907
+ if (multi) {
1908
+ return Array.isArray(value) && value.every(v => {
1909
+ try {
1910
+ const id = new ObjectId$1(v);
1911
+ return id instanceof ObjectId$1;
1912
+ }
1913
+ catch (e) {
1914
+ return false;
1915
+ }
1916
+ });
1917
+ }
1918
+ if (null === value)
1919
+ return true;
1920
+ try {
1921
+ const id = new ObjectId$1(value);
1922
+ return id instanceof ObjectId$1;
1923
+ }
1924
+ catch (e) {
1925
+ return false;
1926
+ }
1927
+ }
1928
+ let IsFile = class IsFile {
1929
+ validate(value, validationArguments) {
1930
+ const [multi] = (validationArguments.constraints || []);
1931
+ return checkValue(multi, value);
1932
+ }
1933
+ };
1934
+ IsFile = __decorate([
1935
+ ValidatorConstraint()
1936
+ ], IsFile);
1937
+ let IsObjectId = class IsObjectId {
1938
+ validate(value, validationArguments) {
1939
+ const [_, multi] = (validationArguments.constraints || []);
1940
+ return checkValue(multi, value);
1941
+ }
1942
+ };
1943
+ IsObjectId = __decorate([
1944
+ ValidatorConstraint()
1945
+ ], IsObjectId);
1946
+
1947
+ let OpenApi = class OpenApi {
1948
+ constructor(container, customValidation) {
1949
+ this.container = container;
1950
+ this.customValidation = customValidation;
1951
+ this.docs = null;
1952
+ }
1953
+ get apiDocs() {
1954
+ if (!this.docs)
1955
+ this.docs = this.createApiDocs();
1956
+ return this.docs;
1957
+ }
1958
+ get apiDocsStr() {
1959
+ if (!this.docsStr)
1960
+ this.docsStr = JSON.stringify(this.apiDocs);
1961
+ return this.docsStr;
1962
+ }
1963
+ async schemaToExample(src, req) {
1964
+ if (src.$ref) {
1965
+ const schemas = this.apiDocs.components.schemas;
1966
+ const schema = src.$ref
1967
+ .replace("#/components/schemas/", "")
1968
+ .replace("#/definitions/", "");
1969
+ return this.schemaToExample(schemas[schema], req);
1970
+ }
1971
+ let schema = src;
1972
+ if (schema.oneOf) {
1973
+ schema = Object.assign({}, schema, schema.oneOf[0]);
1974
+ }
1975
+ if (schema.type === "object") {
1976
+ const result = {};
1977
+ await Promise.all(Object.keys(schema.properties).map(async (key) => {
1978
+ result[key] = await this.schemaToExample(schema.properties[key], req);
1979
+ }));
1980
+ return result;
1981
+ }
1982
+ if (schema.type === "array") {
1983
+ return [await this.schemaToExample(schema.items, req)];
1984
+ }
1985
+ if (schema.type === "string") {
1986
+ if (isDefined(schema.default)) {
1987
+ if (isFunction(schema.default)) {
1988
+ return schema.default(this.container);
1989
+ }
1990
+ return schema.default;
1991
+ }
1992
+ if (schema.format == "date") {
1993
+ return new Date().toISOString().substr(0, 10);
1994
+ }
1995
+ if (schema.format == "date-time") {
1996
+ return new Date().toISOString();
1997
+ }
1998
+ if (schema.enum) {
1999
+ return schema.enum[0];
2000
+ }
2001
+ return "string";
2002
+ }
2003
+ if (schema.type === "number") {
2004
+ return schema.default ?? 0;
2005
+ }
2006
+ else if (schema.type === "boolean") {
2007
+ return schema.default ?? false;
2008
+ }
2009
+ else {
2010
+ return schema.default ?? null;
2011
+ }
2012
+ }
2013
+ createApiDocs() {
2014
+ const storage = getMetadataArgsStorage();
2015
+ const docs = routingControllersToSpec(storage);
2016
+ docs.basePath = "/api/";
2017
+ docs.definitions = validationMetadatasToSchemas({
2018
+ additionalConverters: {
2019
+ [ValidationTypes.CUSTOM_VALIDATION]: (meta, options) => {
2020
+ const res = isFunction(this.customValidation) ? this.customValidation(meta, options) : this.customValidation;
2021
+ if (isObject(res))
2022
+ return res;
2023
+ const constraints = meta.constraints || [];
2024
+ if (meta.constraintCls === IsFile) {
2025
+ return {
2026
+ multi: constraints[0] || false,
2027
+ type: "file"
2028
+ };
2029
+ }
2030
+ if (meta.constraintCls === IsObjectId) {
2031
+ return {
2032
+ endpoint: constraints[0] || false,
2033
+ multi: constraints[1] || false,
2034
+ type: "list"
2035
+ };
2036
+ }
2037
+ return null;
2038
+ }
2039
+ }
2040
+ });
2041
+ docs.components.schemas = docs.definitions;
2042
+ return docs;
2043
+ }
2044
+ };
2045
+ OpenApi = __decorate([
2046
+ singleton(),
2047
+ __param(0, inject(DI_CONTAINER)),
2048
+ __param(1, inject(OPENAPI_VALIDATION))
2049
+ ], OpenApi);
2050
+
2051
+ class Progress extends BaseEntity {
2052
+ constructor(id, data, collection) {
2053
+ super(id, data, collection);
2054
+ }
2055
+ get current() {
2056
+ return this.data.current;
2057
+ }
2058
+ get max() {
2059
+ return this.data.max;
2060
+ }
2061
+ get message() {
2062
+ return this.data.message;
2063
+ }
2064
+ get error() {
2065
+ return this.data.error;
2066
+ }
2067
+ get canceled() {
2068
+ return this.data.canceled;
2069
+ }
2070
+ get percent() {
2071
+ return this.max > 0 ? Math.round(this.current / this.max * 100) : 0;
2072
+ }
2073
+ get remaining() {
2074
+ return this.max > 0 ? this.max - this.current : 0;
2075
+ }
2076
+ setMessageBridge(messageBridge) {
2077
+ this.messageBridge = messageBridge || this.messageBridge;
2078
+ return this;
2079
+ }
2080
+ async createSubProgress(progressValue, max, message) {
2081
+ if (max <= 0 && progressValue > 0) {
2082
+ await this.advance(progressValue);
2083
+ }
2084
+ if (message !== null) {
2085
+ this.data.message = message;
2086
+ await this.save();
2087
+ }
2088
+ return new SubProgress(this, this.current, progressValue, Math.max(max, 1));
2089
+ }
2090
+ async setMax(max) {
2091
+ if (isNaN(max) || max <= 0) {
2092
+ throw "Max progress value must be bigger than zero";
2093
+ }
2094
+ this.data.max = max;
2095
+ await this.save();
2096
+ }
2097
+ async setMessage(message) {
2098
+ this.data.message = message;
2099
+ await this.save();
2100
+ }
2101
+ async setError(error) {
2102
+ this.data.error = error;
2103
+ await this.save();
2104
+ }
2105
+ async advance(value = 1) {
2106
+ if (isNaN(value) || value <= 0) {
2107
+ throw new Error(`Advance value must be bigger than zero: ${this.id}`);
2108
+ }
2109
+ await this.load();
2110
+ if (this.deleted || this.canceled) {
2111
+ const status = this.deleted ? "deleted" : "canceled";
2112
+ throw new Error(`Can't advance ${status} progress: ${this.id}`);
2113
+ }
2114
+ this.data.current = Math.min(this.max, this.current + value);
2115
+ await this.save();
2116
+ }
2117
+ async cancel() {
2118
+ this.data.canceled = true;
2119
+ await this.save();
2120
+ }
2121
+ save() {
2122
+ if (this.messageBridge) {
2123
+ this.messageBridge.sendMessage(`progress-changed`, this.toJSON());
2124
+ }
2125
+ return super.save();
2126
+ }
2127
+ }
2128
+ class SubProgress {
2129
+ constructor(parent, progressFrom, progressValue, mMax = 100) {
2130
+ this.parent = parent;
2131
+ this.progressFrom = progressFrom;
2132
+ this.progressValue = progressValue;
2133
+ this.mMax = mMax;
2134
+ if (progressFrom < 0) {
2135
+ throw "Progress from must be bigger than or zero";
2136
+ }
2137
+ if (progressValue <= 0) {
2138
+ throw "Progress value must be bigger than zero";
2139
+ }
2140
+ this.mCurrent = 0;
2141
+ }
2142
+ get id() {
2143
+ return this.parent.id;
2144
+ }
2145
+ get current() {
2146
+ return this.mCurrent;
2147
+ }
2148
+ get max() {
2149
+ return this.mMax;
2150
+ }
2151
+ get message() {
2152
+ return this.parent.message;
2153
+ }
2154
+ get error() {
2155
+ return this.parent.error;
2156
+ }
2157
+ get percent() {
2158
+ return this.parent.percent;
2159
+ }
2160
+ get remaining() {
2161
+ return this.max - this.mCurrent;
2162
+ }
2163
+ get canceled() {
2164
+ return !this.parent || this.parent.canceled;
2165
+ }
2166
+ setMessageBridge(messageBridge) {
2167
+ if (!this.parent)
2168
+ return this;
2169
+ this.parent.setMessageBridge(messageBridge);
2170
+ return this;
2171
+ }
2172
+ async createSubProgress(progressValue, max, message) {
2173
+ if (max <= 0 && progressValue > 0) {
2174
+ await this.advance(progressValue);
2175
+ }
2176
+ if (message !== null) {
2177
+ await this.setMessage(message);
2178
+ }
2179
+ return new SubProgress(this, this.current, progressValue, Math.max(max, 1));
2180
+ }
2181
+ async setMax(max) {
2182
+ if (isNaN(max) || max <= 0) {
2183
+ throw "Max progress value must be bigger than zero";
2184
+ }
2185
+ this.mMax = max;
2186
+ await this.save();
2187
+ }
2188
+ async setMessage(message) {
2189
+ if (!this.parent)
2190
+ return null;
2191
+ await this.parent.setMessage(message);
2192
+ }
2193
+ async setError(error) {
2194
+ if (!this.parent)
2195
+ return null;
2196
+ await this.parent.setError(error);
2197
+ }
2198
+ async advance(value = 1) {
2199
+ if (isNaN(value) || value <= 0) {
2200
+ throw "Advance value must be bigger than zero";
2201
+ }
2202
+ this.mCurrent = Math.min(this.max, this.mCurrent + value);
2203
+ await this.save();
2204
+ }
2205
+ async cancel() {
2206
+ if (!this.parent)
2207
+ return null;
2208
+ await this.parent.cancel();
2209
+ }
2210
+ async save() {
2211
+ const ratio = this.max > 0 ? this.mCurrent / this.max : 0;
2212
+ const newProgress = this.progressFrom + Math.round(this.progressValue * ratio);
2213
+ const current = this.parent.current;
2214
+ if (newProgress <= current)
2215
+ return null;
2216
+ await this.parent.advance(newProgress);
2217
+ }
2218
+ async load() {
2219
+ return null;
2220
+ }
2221
+ toJSON() {
2222
+ return this.parent.toJSON();
2223
+ }
2224
+ }
2225
+
2226
+ let Progresses = class Progresses {
2227
+ constructor(connector, jobMan) {
2228
+ this.connector = connector;
2229
+ this.jobMan = jobMan;
2230
+ this.collection = connector.database.collection("progresses");
2231
+ this.progresses = {};
2232
+ this.jobMan.on("progress-changed", progress => {
2233
+ const id = progress.id;
2234
+ this.progresses[id] = new Progress(new ObjectId$1(id), progress, this.collection);
2235
+ });
2236
+ }
2237
+ async waitToFinish(id) {
2238
+ return Promise.race([
2239
+ this.waitForProgress(id, async () => {
2240
+ let progress = this.progresses[id];
2241
+ if (!progress || progress.percent < 100) {
2242
+ progress = await this.get(id);
2243
+ }
2244
+ if (!progress) {
2245
+ throw new Error(`Progress does not exists with id: ${id}`);
2246
+ }
2247
+ return progress;
2248
+ }, 500),
2249
+ this.waitForProgress(id, async () => {
2250
+ return this.progresses[id] || null;
2251
+ }, 25)
2252
+ ]);
2253
+ }
2254
+ async get(id) {
2255
+ return !id ? null : this.find({ _id: new ObjectId$1(id) });
2256
+ }
2257
+ async find(where) {
2258
+ const data = await this.collection.findOne(where);
2259
+ return !data ? null : new Progress(data._id, data, this.collection);
2260
+ }
2261
+ async create(max = 100) {
2262
+ if (isNaN(max) || max <= 0) {
2263
+ throw new Error(`Max progress value must be bigger than zero`);
2264
+ }
2265
+ const data = {
2266
+ current: 0,
2267
+ max: max,
2268
+ message: "",
2269
+ error: "",
2270
+ canceled: false
2271
+ };
2272
+ const res = await this.collection.insertOne(data);
2273
+ return new Progress(res.insertedId, data, this.collection);
2274
+ }
2275
+ async remove(id) {
2276
+ await this.collection.deleteOne({ _id: new ObjectId$1(id) });
2277
+ return id;
2278
+ }
2279
+ async waitForProgress(id, cb, delay) {
2280
+ let isFinished = false;
2281
+ let progress = null;
2282
+ let waitTime = 0;
2283
+ while (!isFinished) {
2284
+ progress = await cb();
2285
+ waitTime += delay;
2286
+ if (progress) {
2287
+ if (progress.error) {
2288
+ throw new Error(progress.error);
2289
+ }
2290
+ isFinished = progress.percent >= 100;
2291
+ }
2292
+ if (!isFinished) {
2293
+ if (waitTime >= this.jobMan.maxTimeout) {
2294
+ throw new Error(`Progress with id: ${id} probably never will be finished!`);
2295
+ }
2296
+ await promiseTimeout(delay);
2297
+ }
2298
+ }
2299
+ return progress;
2300
+ }
2301
+ };
2302
+ Progresses = __decorate([
2303
+ singleton()
2304
+ ], Progresses);
2305
+
2306
+ let TemplateRenderer = class TemplateRenderer {
2307
+ constructor(translator, config) {
2308
+ this.translator = translator;
2309
+ this.config = config;
2310
+ this.templates = {};
2311
+ Handlebars.registerHelper(`object`, function ({ hash }) {
2312
+ return hash;
2313
+ });
2314
+ Handlebars.registerHelper(`now`, function () {
2315
+ return new Date().getTime();
2316
+ });
2317
+ Handlebars.registerHelper(`keys`, function (obj) {
2318
+ return !obj ? [] : Object.keys(obj);
2319
+ });
2320
+ Handlebars.registerHelper(`translate`, function (key, params) {
2321
+ return translator.getTranslationSync(this.language, key, params);
2322
+ });
2323
+ }
2324
+ init() {
2325
+ this.initPromise = this.initPromise || this.parseTemplates(this.config.resolve("templatesDir"), []);
2326
+ return this.initPromise;
2327
+ }
2328
+ async parseTemplates(dir, dirPath) {
2329
+ return new Promise(resolve => {
2330
+ readdir(dir, async (err, files) => {
2331
+ for (let file of files) {
2332
+ const path = join(dir, file);
2333
+ if (lstatSync(path).isDirectory()) {
2334
+ await this.parseTemplates(join(dir, file), dirPath.concat([file]));
2335
+ continue;
2336
+ }
2337
+ const parts = file.split(".");
2338
+ parts.pop();
2339
+ const name = parts.join(".");
2340
+ const fullName = dirPath.concat([name]).join("-");
2341
+ const content = readFileSync(path).toString("utf8");
2342
+ this.templates[fullName] = Handlebars.compile(content);
2343
+ Handlebars.registerPartial(fullName, content);
2344
+ }
2345
+ resolve();
2346
+ });
2347
+ });
2348
+ }
2349
+ async render(template, language, context) {
2350
+ await this.init();
2351
+ await this.translator.getDictionary(language);
2352
+ if (!this.templates[template]) {
2353
+ return Promise.reject(`Template not found with name: ${template}`);
2354
+ }
2355
+ context = Object.assign({ language }, context || {});
2356
+ const res = this.templates[template](context);
2357
+ return res instanceof Error ? await Promise.reject(res) : res;
2358
+ }
2359
+ };
2360
+ TemplateRenderer = __decorate([
2361
+ singleton()
2362
+ ], TemplateRenderer);
2363
+
2364
+ let TerminalManager = class TerminalManager {
2365
+ constructor(logger, config, commands) {
2366
+ this.logger = logger;
2367
+ this.config = config;
2368
+ this.servicePassword = config.resolve("servicePassword");
2369
+ this.suggestions = {
2370
+ login: async (args) => {
2371
+ if (args.length > 2) {
2372
+ return null;
2373
+ }
2374
+ const input = `${args.at(1).label}`;
2375
+ return (!input) ? [] : [{
2376
+ id: input,
2377
+ label: input,
2378
+ masked: true
2379
+ }];
2380
+ },
2381
+ ...commands.reduce((acc, command) => {
2382
+ command.name = camelCaseToDash(command.name || command.constructor.name || "");
2383
+ if (!command.name || !command.suggest)
2384
+ return acc;
2385
+ acc[command.name] = async (a, t) => command.suggest(a, t);
2386
+ return acc;
2387
+ }, {})
2388
+ };
2389
+ this.commands = commands.reduce((acc, command) => {
2390
+ if (!command.name)
2391
+ return acc;
2392
+ acc[command.name] = async (a, t) => command.execute(a, t);
2393
+ return acc;
2394
+ }, {});
2395
+ this.loggedOutCommands = ["login", "clear"];
2396
+ this.loggedInCommands = Object.keys(this.commands);
2397
+ this.loggedInCommands.push("logout");
2398
+ console.log(`Current service password is: ${colorize(this.servicePassword, ConsoleColor.FgGreen)}`);
2399
+ }
2400
+ loadAddons(terminal) {
2401
+ let loggedIn = false;
2402
+ const addon = new CommandsAddon({
2403
+ commands: {
2404
+ login: async (args, terminal) => {
2405
+ if (args.at(1).label === this.servicePassword) {
2406
+ loggedIn = true;
2407
+ terminal.writeln("Logged in as admin");
2408
+ }
2409
+ else {
2410
+ throw new Error("Invalid login");
2411
+ }
2412
+ },
2413
+ logout: async (args, terminal) => {
2414
+ loggedIn = false;
2415
+ terminal.writeln("Logged out");
2416
+ },
2417
+ ...this.commands
2418
+ },
2419
+ suggestCommands: async () => {
2420
+ if (loggedIn) {
2421
+ return this.loggedInCommands;
2422
+ }
2423
+ return this.loggedOutCommands;
2424
+ },
2425
+ suggestions: this.suggestions
2426
+ });
2427
+ terminal.loadAddon(addon);
2428
+ }
2429
+ };
2430
+ TerminalManager = __decorate([
2431
+ singleton(),
2432
+ __param(2, injectAll(TERMINAL_COMMAND))
2433
+ ], TerminalManager);
2434
+
2435
+ let TokenGenerator = class TokenGenerator {
2436
+ constructor() {
2437
+ this.chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2438
+ }
2439
+ async generate(checkCb) {
2440
+ let token = null;
2441
+ let tries = 0;
2442
+ let notGood = true;
2443
+ while (notGood && tries < 5) {
2444
+ token = this.generateToken();
2445
+ notGood = await checkCb(token);
2446
+ tries++;
2447
+ }
2448
+ if (notGood) {
2449
+ throw new Error(`Couldn't generate an unique token..`);
2450
+ }
2451
+ return token;
2452
+ }
2453
+ generateToken() {
2454
+ let s = "";
2455
+ for (let i = 0; i < 15; i++) {
2456
+ const ix = rand(0, this.chars.length - 1);
2457
+ s += this.chars[ix];
2458
+ }
2459
+ return s;
2460
+ }
2461
+ };
2462
+ TokenGenerator = __decorate([
2463
+ singleton()
2464
+ ], TokenGenerator);
2465
+
2466
+ let TranslationProvider = class TranslationProvider {
2467
+ constructor(config, cache) {
2468
+ this.config = config;
2469
+ this.cache = cache;
2470
+ }
2471
+ getDictionary(language) {
2472
+ return this.cache.getOrSet(`translations-${language}`, async () => {
2473
+ try {
2474
+ const url = this.config.resolve("translationsTemplate")
2475
+ .replace(`__lang__`, language)
2476
+ .replace(`[lang]`, language);
2477
+ const data = await axios.get(url).then(t => t.data);
2478
+ if (isObject(data[language])) {
2479
+ return data[language];
2480
+ }
2481
+ return data;
2482
+ }
2483
+ catch (e) {
2484
+ return {
2485
+ message: `${e}`
2486
+ };
2487
+ }
2488
+ }, 5 * 60);
2489
+ }
2490
+ };
2491
+ TranslationProvider = __decorate([
2492
+ singleton()
2493
+ ], TranslationProvider);
2494
+
2495
+ let Translator = class Translator {
2496
+ constructor(translationProvider) {
2497
+ this.translationProvider = translationProvider;
2498
+ this.dictionaries = {};
2499
+ }
2500
+ async getDictionary(language) {
2501
+ this.dictionaries[language] = await this.translationProvider.getDictionary(language);
2502
+ return this.dictionaries[language];
2503
+ }
2504
+ getTranslationSync(language, key, params) {
2505
+ if (!isString(key) || !key.length) {
2506
+ throw new Error(`Parameter "key" required`);
2507
+ }
2508
+ const dictionary = this.dictionaries[language];
2509
+ const translation = getValue(dictionary, key, key) || key;
2510
+ return this.interpolate(translation, params);
2511
+ }
2512
+ getTranslation(language, key, params) {
2513
+ if (!isString(key) || !key.length) {
2514
+ throw new Error(`Parameter "key" required`);
2515
+ }
2516
+ return this.getDictionary(language).then(dictionary => {
2517
+ const translation = getValue(dictionary, key, key) || key;
2518
+ return this.interpolate(translation, params);
2519
+ });
2520
+ }
2521
+ getTranslations(language, ...keys) {
2522
+ return new Promise(resolve => {
2523
+ Promise.all(keys.map(key => this.getTranslation(language, key))).then(translations => {
2524
+ resolve(keys.reduce((result, key, i) => {
2525
+ result[key] = translations[i];
2526
+ return result;
2527
+ }, {}));
2528
+ });
2529
+ });
2530
+ }
2531
+ interpolate(expr, params) {
2532
+ if (typeof expr === "string") {
2533
+ return this.interpolateString(expr, params);
2534
+ }
2535
+ if (typeof expr === "function") {
2536
+ return expr(params);
2537
+ }
2538
+ return expr;
2539
+ }
2540
+ interpolateString(expr, params) {
2541
+ if (!expr || !params)
2542
+ return expr;
2543
+ return expr.replace(/{{\s?([^{}\s]*)\s?}}/g, (substring, b) => {
2544
+ const r = getValue(params, b);
2545
+ return isDefined(r) ? r : substring;
2546
+ });
2547
+ }
2548
+ };
2549
+ Translator = __decorate([
2550
+ injectable(),
2551
+ singleton()
2552
+ ], Translator);
2553
+
2554
+ const sampleUser = {
2555
+ id: "5a3cdf7c6a9cf0ba32feccdf",
2556
+ email: "admin@site.com",
2557
+ password: "",
2558
+ roles: ["admin"]
2559
+ };
2560
+ let UserManager = class UserManager {
2561
+ async getByCredentials(credentials) {
2562
+ return (sampleUser.email == credentials.email) ? sampleUser : await Promise.reject("message.login.error");
2563
+ }
2564
+ async getById(id) {
2565
+ return (sampleUser.id == id) ? sampleUser : null;
2566
+ }
2567
+ async serialize(user) {
2568
+ const res = Object.assign({}, user);
2569
+ delete res.password;
2570
+ return res;
2571
+ }
2572
+ };
2573
+ UserManager = __decorate([
2574
+ injectable(),
2575
+ scoped(Lifecycle.ContainerScoped)
2576
+ ], UserManager);
2577
+
2578
+ let AssetsController = class AssetsController {
2579
+ constructor(assets, assetResolver) {
2580
+ this.assets = assets;
2581
+ this.assetResolver = assetResolver;
2582
+ }
2583
+ async upload(file) {
2584
+ try {
2585
+ const contentType = file.mimetype === "application/octet-stream" ? null : file.mimetype;
2586
+ const asset = await this.assets.writeBuffer(file.buffer, { filename: file.filename }, contentType);
2587
+ return asset.toJSON();
2588
+ }
2589
+ catch (e) {
2590
+ const msg = e?.message || e || "Unknown error";
2591
+ throw new HttpError(400, `Asset can't be uploaded.\n${msg}`);
2592
+ }
2593
+ }
2594
+ async uploadUrl(body) {
2595
+ try {
2596
+ const asset = await this.assets.writeUrl(body.url, body);
2597
+ return asset.toJSON();
2598
+ }
2599
+ catch (e) {
2600
+ const msg = e?.message || e || "Unknown error";
2601
+ throw new HttpError(400, `Asset can't be uploaded.\n${msg}`);
2602
+ }
2603
+ }
2604
+ async getFile(id, lazy, res) {
2605
+ const asset = await this.getAsset("Asset", id, lazy, res);
2606
+ return asset.download();
2607
+ }
2608
+ async getImageRotation(id, params, res, rotation = 0) {
2609
+ const asset = await this.getAsset("Image", id, params.lazy, res);
2610
+ if (rotation !== 0) {
2611
+ params.rotation = params.rotation || rotation;
2612
+ }
2613
+ return asset.downloadImage(params);
2614
+ }
2615
+ async getImage(id, params, res) {
2616
+ return this.getImageRotation(id, params, res);
2617
+ }
2618
+ async getFileByName(name, res) {
2619
+ const asset = await this.getAssetByName("Asset", name, res);
2620
+ return asset.download();
2621
+ }
2622
+ async getImageByName(name, params, res) {
2623
+ const asset = await this.getAssetByName("Image", name, res);
2624
+ return asset.downloadImage(params);
2625
+ }
2626
+ setAssetHeaders(type, asset, res) {
2627
+ if (asset.metadata?.classified) {
2628
+ throw new HttpError(403, `${type} is classified, and can be only downloaded from a custom url.`);
2629
+ }
2630
+ const ext = asset.metadata?.extension;
2631
+ if (ext) {
2632
+ res.header("content-disposition", `inline; filename=${asset.filename}.${ext}`);
2633
+ }
2634
+ if (asset.contentType) {
2635
+ res.header("content-type", asset.contentType);
2636
+ }
2637
+ }
2638
+ async getAsset(type, id, lazy, res) {
2639
+ const asset = await this.assetResolver.resolve(id, lazy);
2640
+ if (!asset) {
2641
+ throw new HttpError(404, `${type} with id: '${id}' not found.`);
2642
+ }
2643
+ this.setAssetHeaders(type, asset, res);
2644
+ return asset;
2645
+ }
2646
+ async getAssetByName(type, filename, res) {
2647
+ const asset = await this.assets.find({ filename });
2648
+ if (!asset) {
2649
+ throw new HttpError(404, `${type} with filename: '${filename}' not found.`);
2650
+ }
2651
+ this.setAssetHeaders(type, asset, res);
2652
+ return asset;
2653
+ }
2654
+ };
2655
+ __decorate([
2656
+ Authorized(),
2657
+ Post(""),
2658
+ __param(0, UploadedFile("file"))
2659
+ ], AssetsController.prototype, "upload", null);
2660
+ __decorate([
2661
+ Authorized(),
2662
+ Post("url"),
2663
+ __param(0, Body())
2664
+ ], AssetsController.prototype, "uploadUrl", null);
2665
+ __decorate([
2666
+ Get("/:id"),
2667
+ __param(0, Param("id")),
2668
+ __param(1, QueryParam("lazy")),
2669
+ __param(2, Res())
2670
+ ], AssetsController.prototype, "getFile", null);
2671
+ __decorate([
2672
+ Get("/image/:id/:rotation"),
2673
+ __param(0, Param("id")),
2674
+ __param(1, QueryParams()),
2675
+ __param(2, Res()),
2676
+ __param(3, Param("rotation"))
2677
+ ], AssetsController.prototype, "getImageRotation", null);
2678
+ __decorate([
2679
+ Get("/image/:id"),
2680
+ __param(0, Param("id")),
2681
+ __param(1, QueryParams()),
2682
+ __param(2, Res())
2683
+ ], AssetsController.prototype, "getImage", null);
2684
+ __decorate([
2685
+ Get("/by-name/:name"),
2686
+ __param(0, Param("name")),
2687
+ __param(1, Res())
2688
+ ], AssetsController.prototype, "getFileByName", null);
2689
+ __decorate([
2690
+ Get("/by-name/image/:name"),
2691
+ __param(0, Param("name")),
2692
+ __param(1, QueryParams()),
2693
+ __param(2, Res())
2694
+ ], AssetsController.prototype, "getImageByName", null);
2695
+ AssetsController = __decorate([
2696
+ injectable(),
2697
+ Controller("/assets")
2698
+ ], AssetsController);
2699
+
2700
+ let AuthController = class AuthController {
2701
+ constructor(config, userManager) {
2702
+ this.config = config;
2703
+ this.userManager = userManager;
2704
+ }
2705
+ async login(credentials, res) {
2706
+ let user = null;
2707
+ try {
2708
+ user = await this.userManager.getByCredentials(credentials);
2709
+ }
2710
+ catch (reason) {
2711
+ throw new HttpError(401, reason);
2712
+ }
2713
+ const valid = !user ? false : await compare(credentials.password, user.password);
2714
+ if (valid !== true)
2715
+ throw new UnauthorizedError(`message.login.error`);
2716
+ return {
2717
+ token: webToken.sign({ id: user._id || user.id }, this.config.resolve("jwtSecret")),
2718
+ user: await this.userManager.serialize(user)
2719
+ };
2720
+ }
2721
+ getProfile(user) {
2722
+ return this.userManager.serialize(user);
2723
+ }
2724
+ };
2725
+ __decorate([
2726
+ Post("/login"),
2727
+ __param(0, Body()),
2728
+ __param(1, Res())
2729
+ ], AuthController.prototype, "login", null);
2730
+ __decorate([
2731
+ Authorized(),
2732
+ Get("/user"),
2733
+ __param(0, CurrentUser())
2734
+ ], AuthController.prototype, "getProfile", null);
2735
+ AuthController = __decorate([
2736
+ injectable(),
2737
+ Controller()
2738
+ ], AuthController);
2739
+
2740
+ let GalleryController = class GalleryController {
2741
+ constructor(galleryCache) {
2742
+ this.galleryCache = galleryCache;
2743
+ }
2744
+ getFile(id) {
2745
+ return this.galleryCache.serve(id);
2746
+ }
2747
+ };
2748
+ __decorate([
2749
+ Get("/:id"),
2750
+ __param(0, Param("id"))
2751
+ ], GalleryController.prototype, "getFile", null);
2752
+ GalleryController = __decorate([
2753
+ injectable(),
2754
+ Controller("/gallery")
2755
+ ], GalleryController);
2756
+
2757
+ let ProgressesController = class ProgressesController {
2758
+ constructor(progresses, config) {
2759
+ this.progresses = progresses;
2760
+ this.config = config;
2761
+ const mainEndpoint = this.config.resolve("mainEndpoint");
2762
+ this.connectionType = !mainEndpoint ? "polling" : "socket";
2763
+ }
2764
+ async getProgress(id) {
2765
+ const progress = await this.progresses.get(id);
2766
+ if (!progress)
2767
+ return null;
2768
+ const json = progress.toJSON();
2769
+ json.connectionType = this.connectionType;
2770
+ return json;
2771
+ }
2772
+ };
2773
+ __decorate([
2774
+ Get("/:id"),
2775
+ __param(0, Param("id"))
2776
+ ], ProgressesController.prototype, "getProgress", null);
2777
+ ProgressesController = __decorate([
2778
+ injectable(),
2779
+ Controller("/progresses")
2780
+ ], ProgressesController);
2781
+
2782
+ // Add a comment hint for webstorm to style the string as css
2783
+ var styles = `
2784
+
2785
+ body {
2786
+ margin: 0;
2787
+ padding: 0;
2788
+ background: #1e1e1e;
2789
+ }
2790
+ body * {
2791
+ box-sizing: border-box;
2792
+ }
2793
+
2794
+ #terminal {
2795
+ margin: 40px;
2796
+ background: black;
2797
+ border: 1px solid #dedede;
2798
+ border-radius: 5px;
2799
+ overflow: hidden;
2800
+ }
2801
+
2802
+ #terminal .xterm {
2803
+ margin: 10px;
2804
+ height: calc(100vh - 120px);
2805
+ border-radius: 5px;
2806
+ }
2807
+
2808
+ #terminal .xterm-viewport::-webkit-scrollbar {
2809
+ width: 0.4em;
2810
+ background-color: #222;
2811
+ }
2812
+
2813
+ #terminal .xterm-viewport::-webkit-scrollbar-thumb {
2814
+ background-color: #555;
2815
+ }
2816
+
2817
+ #terminal .window-header {
2818
+ display: flex;
2819
+ align-items: center;
2820
+ justify-content: flex-end;
2821
+ background-color: #f1f1f1;
2822
+ padding: 10px;
2823
+ }
2824
+
2825
+ #terminal .window-header button {
2826
+ background-color: transparent;
2827
+ border: none;
2828
+ font-size: 14px;
2829
+ font-weight: bold;
2830
+ color: #444;
2831
+ cursor: pointer;
2832
+ margin-left: 10px;
2833
+ }
2834
+
2835
+ .minimize-button {
2836
+ content: "-";
2837
+ }
2838
+
2839
+ .maximize-button {
2840
+ content: "+";
2841
+ }
2842
+
2843
+ .close-button {
2844
+ content: "x";
2845
+ }
2846
+
2847
+ `;
2848
+
2849
+ let TerminalController$1 = class TerminalController {
2850
+ constructor(config) {
2851
+ this.config = config;
2852
+ this.serviceName = config.resolve("serviceName");
2853
+ this.serviceUrl = config.resolve("serviceUrl");
2854
+ }
2855
+ terminal() {
2856
+ return this.generateClient("terminal");
2857
+ }
2858
+ console() {
2859
+ return this.generateClient("console");
2860
+ }
2861
+ generateClient(alias) {
2862
+ return `
2863
+ <!DOCTYPE html>
2864
+ <html>
2865
+ <head>
2866
+ <meta charset="utf-8">
2867
+ <title>${this.serviceName} ${alias}</title>
2868
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.1.0/css/xterm.min.css"/>
2869
+ <script type="text/javascript" src="${this.serviceUrl}/socket/socket.io.js"></script>
2870
+ <style>
2871
+ ${styles}
2872
+ </style>
2873
+ </head>
2874
+ <body>
2875
+ <div id="terminal"></div>
2876
+ <script type="module">
2877
+ // Import terminal modules
2878
+ import xterm from 'https://cdn.jsdelivr.net/npm/xterm@5.1.0/+esm';
2879
+ import xtermFit from 'https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.7.0/+esm';
2880
+ // Initialize variables
2881
+ var terminal = new xterm.Terminal();
2882
+ var fitAddon = new xtermFit.FitAddon();
2883
+ var socket = io("${this.serviceUrl}", {path: "/socket"});
2884
+ var clear = function () {
2885
+ terminal.clear();
2886
+ terminal.reset();
2887
+ // Prev line
2888
+ terminal.write("\u001B[F");
2889
+ }
2890
+ // Initialize terminal
2891
+ terminal.loadAddon(fitAddon);
2892
+ terminal.open(document.getElementById('terminal'));
2893
+ terminal.onData(function (data) {
2894
+ socket.emit("terminal-data", data.charCodeAt(0) === 127 ? "\b" : data);
2895
+ });
2896
+ // Socket listeners
2897
+ socket.on("terminal-data", function (data) {
2898
+ if (data === "\x1bc") {
2899
+ clear();
2900
+ return;
2901
+ }
2902
+ terminal.write(data);
2903
+ });
2904
+ socket.on("terminal-upload", function (data) {
2905
+ var input = document.createElement("input");
2906
+ input.type = "file";
2907
+ input.accept = data.accept;
2908
+ input.onchange = function () {
2909
+ var file = input.files[0];
2910
+ var reader = new FileReader();
2911
+ reader.onload = function () {
2912
+ socket.emit("terminal-upload", {
2913
+ id: data.id,
2914
+ label: file.name,
2915
+ content: reader.result,
2916
+ accept: data.accept
2917
+ });
2918
+ };
2919
+ reader.onerror = function () {
2920
+ socket.emit("terminal-upload", {
2921
+ id: data.id,
2922
+ label: file.name,
2923
+ error: reader.error,
2924
+ accept: data.accept
2925
+ });
2926
+ };
2927
+ reader.readAsDataURL(file);
2928
+ };
2929
+ input.click();
2930
+ });
2931
+ socket.on("terminal-download", function (data) {
2932
+ var link = document.createElement("a");
2933
+ link.href = data.content;
2934
+ link.download = data.filename;
2935
+ link.click();
2936
+ });
2937
+ socket.on("connect", function () {
2938
+ clear();
2939
+ terminal.writeln("Welcome to ${this.serviceName} service's ${alias}!");
2940
+ socket.emit("terminal-init");
2941
+ });
2942
+ socket.on("disconnect", function () {
2943
+ clear();
2944
+ terminal.writeln("Disconnected from ${this.serviceName} service's ${alias}.");
2945
+ });
2946
+ </script>
2947
+ </body>
2948
+ </html>
2949
+ `;
2950
+ }
2951
+ };
2952
+ __decorate([
2953
+ Get("/terminal"),
2954
+ Header("Content-Type", "text/html")
2955
+ ], TerminalController$1.prototype, "terminal", null);
2956
+ __decorate([
2957
+ Get("/console"),
2958
+ Header("Content-Type", "text/html")
2959
+ ], TerminalController$1.prototype, "console", null);
2960
+ __decorate([
2961
+ Get(),
2962
+ Header("Content-Type", "text/html")
2963
+ ], TerminalController$1.prototype, "generateClient", null);
2964
+ TerminalController$1 = __decorate([
2965
+ injectable(),
2966
+ Controller()
2967
+ ], TerminalController$1);
2968
+
2969
+ let ErrorHandlerMiddleware = class ErrorHandlerMiddleware {
2970
+ constructor(configuration, translator) {
2971
+ this.configuration = configuration;
2972
+ this.translator = translator;
2973
+ }
2974
+ get isDev() {
2975
+ return this.configuration.resolve("nodeEnv") === "development";
2976
+ }
2977
+ async error(error, req, res, next) {
2978
+ const result = await this.getResult(error, req, res);
2979
+ if (this.isDev) {
2980
+ if (axios.isAxiosError(error)) {
2981
+ console.log(`Axios error:`, result);
2982
+ console.log(error.config.data);
2983
+ }
2984
+ else {
2985
+ console.log(error.constructor?.name || "Error", result, res.statusCode);
2986
+ }
2987
+ }
2988
+ res.json(result);
2989
+ }
2990
+ async getResult(error, req, res) {
2991
+ const result = {};
2992
+ if (axios.isAxiosError(error)) {
2993
+ res.status(error.response.status);
2994
+ result.message = error.message;
2995
+ result.error = error.response.data;
2996
+ result.headers = error.response.headers;
2997
+ result.url = `${error.config.baseURL}${error.config.url}`;
2998
+ return result;
2999
+ }
3000
+ if (error instanceof BadRequestError) {
3001
+ res.status(400);
3002
+ if (error.constructor.name === "ParamRequiredError") {
3003
+ result.message = await this.translator.getTranslation(req.language, "message.parameter-required.error");
3004
+ result.param = error.message;
3005
+ }
3006
+ else {
3007
+ result.message = error.message || await this.translator.getTranslation(req.language, "message.form-validation.error");
3008
+ result.errors = error["errors"];
3009
+ if (error.stack && this.isDev) {
3010
+ result.stack = error.stack;
3011
+ }
3012
+ }
3013
+ return result;
3014
+ }
3015
+ res.status(error.httpCode || 500);
3016
+ if (error instanceof Error) {
3017
+ if (error.name) {
3018
+ result.name = error.name;
3019
+ }
3020
+ if (error.message) {
3021
+ result.message = error.message;
3022
+ }
3023
+ if (error.stack && this.isDev) {
3024
+ result.stack = error.stack;
3025
+ }
3026
+ }
3027
+ else if (typeof error === "string") {
3028
+ result.message = error;
3029
+ }
3030
+ return result;
3031
+ }
3032
+ };
3033
+ ErrorHandlerMiddleware = __decorate([
3034
+ injectable(),
3035
+ Middleware({ type: "after" })
3036
+ ], ErrorHandlerMiddleware);
3037
+
3038
+ let ContainerMiddleware = class ContainerMiddleware {
3039
+ constructor(container) {
3040
+ this.container = container;
3041
+ }
3042
+ use(request, response, next) {
3043
+ request.container = this.container;
3044
+ next(null);
3045
+ }
3046
+ };
3047
+ ContainerMiddleware = __decorate([
3048
+ injectable(),
3049
+ Middleware({ type: "before" }),
3050
+ __param(0, inject(DI_CONTAINER))
3051
+ ], ContainerMiddleware);
3052
+
3053
+ let LanguageMiddleware = class LanguageMiddleware {
3054
+ constructor(config) {
3055
+ this.config = config;
3056
+ }
3057
+ use(request, response, next) {
3058
+ request.language = request.query.language || this.config.resolve("defaultLanguage");
3059
+ next(null);
3060
+ }
3061
+ };
3062
+ LanguageMiddleware = __decorate([
3063
+ injectable(),
3064
+ Middleware({ type: "before" })
3065
+ ], LanguageMiddleware);
3066
+
3067
+ let RequestEndedMiddleware = class RequestEndedMiddleware {
3068
+ constructor(logger) {
3069
+ this.logger = logger;
3070
+ }
3071
+ use(request, response, next) {
3072
+ request.ended = moment();
3073
+ const diff = request.ended.diff(request.started);
3074
+ const duration = moment.duration(diff);
3075
+ this.logger.log("request-time", `Request '${request.id}' ended at: ${request.ended.format("YYYY-MM-DD HH:mm:ss")} [${request.method}] ${request.url}`);
3076
+ this.logger.log("request-time", `Request '${request.id}' lasted: ${duration.asMilliseconds()}ms`);
3077
+ next(null);
3078
+ }
3079
+ };
3080
+ RequestEndedMiddleware = __decorate([
3081
+ injectable(),
3082
+ Middleware({ type: "after" })
3083
+ ], RequestEndedMiddleware);
3084
+
3085
+ let RequestStartedMiddleware = class RequestStartedMiddleware {
3086
+ constructor(logger) {
3087
+ this.logger = logger;
3088
+ }
3089
+ use(request, response, next) {
3090
+ request.id = new ObjectId$1().toHexString();
3091
+ request.started = moment();
3092
+ this.logger.log("request-time", `Request '${request.id}' started at: ${request.started.format("YYYY-MM-DD HH:mm:ss")} [${request.method}] ${request.url}`);
3093
+ next(null);
3094
+ }
3095
+ };
3096
+ RequestStartedMiddleware = __decorate([
3097
+ injectable(),
3098
+ Middleware({ type: "before" })
3099
+ ], RequestStartedMiddleware);
3100
+
3101
+ let ProgressController = class ProgressController {
3102
+ constructor(progresses, socketServer) {
3103
+ this.progresses = progresses;
3104
+ this.socketServer = socketServer;
3105
+ }
3106
+ async advanceProgress(client, progressId) {
3107
+ const progress = await this.progresses.get(progressId);
3108
+ if (!progress)
3109
+ return;
3110
+ const json = progress.toJSON();
3111
+ broadcast(this.socketServer, c => {
3112
+ if (c.interestedProgresses instanceof Set && c.interestedProgresses.has(progressId)) {
3113
+ client.emit("background-progress-changed", json);
3114
+ }
3115
+ });
3116
+ console.log(`progress changed: ${client.id}, data: ${JSON.stringify(json)}`);
3117
+ }
3118
+ async setProgressInterest(client, progressId) {
3119
+ const progress = await this.progresses.get(progressId);
3120
+ if (!progress)
3121
+ return;
3122
+ const json = progress.toJSON();
3123
+ client.interestedProgresses = client.interestedProgresses || new Set();
3124
+ if (client.interestedProgresses.has(progressId))
3125
+ return;
3126
+ client.interestedProgresses.add(progressId);
3127
+ client.emit("background-progress-changed", json);
3128
+ console.log(`progress interest added: ${client.id}, data: ${JSON.stringify(json)}`);
3129
+ }
3130
+ };
3131
+ __decorate([
3132
+ OnMessage("background-progress"),
3133
+ __param(0, ConnectedSocket()),
3134
+ __param(1, MessageBody())
3135
+ ], ProgressController.prototype, "advanceProgress", null);
3136
+ __decorate([
3137
+ OnMessage("background-progress-interest"),
3138
+ __param(0, ConnectedSocket()),
3139
+ __param(1, MessageBody())
3140
+ ], ProgressController.prototype, "setProgressInterest", null);
3141
+ ProgressController = __decorate([
3142
+ singleton(),
3143
+ SocketController(),
3144
+ __param(1, inject(SOCKET_SERVER))
3145
+ ], ProgressController);
3146
+
3147
+ class Terminal {
3148
+ constructor(client) {
3149
+ this.client = client;
3150
+ this.addons = [];
3151
+ this.files$ = new BehaviorSubject([]);
3152
+ this.input$ = new Subject();
3153
+ }
3154
+ onData(cb) {
3155
+ const sub = this.input$.pipe(map(v => {
3156
+ if (v === "\b") {
3157
+ return "\x7f";
3158
+ }
3159
+ return v;
3160
+ })).subscribe(cb);
3161
+ return {
3162
+ dispose: () => sub.unsubscribe()
3163
+ };
3164
+ }
3165
+ write(data) {
3166
+ this.client.emit("terminal-data", data);
3167
+ }
3168
+ writeln(data) {
3169
+ this.write(data + "\n");
3170
+ }
3171
+ loadAddon(addon) {
3172
+ addon.activate(this);
3173
+ this.addons.push(addon);
3174
+ }
3175
+ dispose() {
3176
+ this.input$.complete();
3177
+ this.client = null;
3178
+ this.addons.forEach(a => a.dispose());
3179
+ }
3180
+ async suggestFiles(accept) {
3181
+ const rand = Math.round(Math.random() * 1000);
3182
+ const id = `${Date.now()}-${rand}`;
3183
+ const files = this.files$.value;
3184
+ return files.filter(f => f.accept === accept).concat([
3185
+ {
3186
+ id,
3187
+ label: "...",
3188
+ onAccept: async () => {
3189
+ this.client.emit("terminal-upload", {
3190
+ id,
3191
+ accept
3192
+ });
3193
+ const file = await this.files$
3194
+ .pipe(first(v => v.some(f => f.id === id)))
3195
+ .pipe(timeout(120000))
3196
+ .pipe(map(v => v.find(f => f.id === id)))
3197
+ .toPromise();
3198
+ if (file.error) {
3199
+ this.files$.next(this.files$.value.filter(f => f.id !== id));
3200
+ throw new Error(file.error);
3201
+ }
3202
+ return file;
3203
+ },
3204
+ showAlways: true,
3205
+ accept: accept,
3206
+ }
3207
+ ]);
3208
+ }
3209
+ async downloadFile(filename, buffer) {
3210
+ const type = await fileTypeFromBuffer(buffer);
3211
+ const data = buffer.toString("base64");
3212
+ const content = `data:${type.mime};base64,${data}`;
3213
+ this.client.emit("terminal-download", {
3214
+ filename,
3215
+ content
3216
+ });
3217
+ }
3218
+ addFile(upload) {
3219
+ if (upload.content) {
3220
+ upload.buffer = Buffer.from(upload.content, "base64");
3221
+ }
3222
+ this.files$.next(this.files$.value.concat(upload));
3223
+ }
3224
+ input(data) {
3225
+ this.input$.next(data);
3226
+ }
3227
+ }
3228
+
3229
+ let TerminalController = class TerminalController {
3230
+ constructor(manager) {
3231
+ this.manager = manager;
3232
+ this.terminals = {};
3233
+ }
3234
+ async terminalInit(client) {
3235
+ const terminal = new Terminal(client);
3236
+ this.manager.loadAddons(terminal);
3237
+ this.terminals[client.id] = terminal;
3238
+ client.on("disconnect", () => terminal.dispose());
3239
+ }
3240
+ async terminalData(client, data) {
3241
+ const terminal = this.terminals[client.id];
3242
+ if (!terminal)
3243
+ return;
3244
+ terminal.input(data);
3245
+ }
3246
+ async terminalUpload(client, upload) {
3247
+ const terminal = this.terminals[client.id];
3248
+ if (!terminal || !upload)
3249
+ return;
3250
+ terminal.addFile(upload);
3251
+ }
3252
+ };
3253
+ __decorate([
3254
+ OnMessage("terminal-init"),
3255
+ __param(0, ConnectedSocket())
3256
+ ], TerminalController.prototype, "terminalInit", null);
3257
+ __decorate([
3258
+ OnMessage("terminal-data"),
3259
+ __param(0, ConnectedSocket()),
3260
+ __param(1, MessageBody())
3261
+ ], TerminalController.prototype, "terminalData", null);
3262
+ __decorate([
3263
+ OnMessage("terminal-upload"),
3264
+ __param(0, ConnectedSocket()),
3265
+ __param(1, MessageBody())
3266
+ ], TerminalController.prototype, "terminalUpload", null);
3267
+ TerminalController = __decorate([
3268
+ singleton(),
3269
+ SocketController()
3270
+ ], TerminalController);
3271
+
3272
+ let CompressionMiddleware = class CompressionMiddleware {
3273
+ use(socket, next) {
3274
+ next();
3275
+ }
3276
+ };
3277
+ CompressionMiddleware = __decorate([
3278
+ injectable(),
3279
+ Middleware$1()
3280
+ ], CompressionMiddleware);
3281
+
3282
+ class Tree {
3283
+ constructor(container, exists, path) {
3284
+ this.container = container;
3285
+ this.exists = exists;
3286
+ this.path = path;
3287
+ this.map = new Map();
3288
+ }
3289
+ get parentTree() {
3290
+ return this.container.parent.tree;
3291
+ }
3292
+ resolveService() {
3293
+ return !this.exists ? null : this.container.resolve(this.path);
3294
+ }
3295
+ resolveLeaves() {
3296
+ let map;
3297
+ try {
3298
+ const parentTree = this.parentTree.resolvePath(this.path);
3299
+ map = parentTree.resolveLeaves();
3300
+ }
3301
+ catch (e) {
3302
+ map = new Map();
3303
+ }
3304
+ const visitor = (treeMap, path) => {
3305
+ treeMap.forEach((tree, key) => {
3306
+ const subKey = !path ? key : `${path}.${key}`;
3307
+ if (tree.map.size == 0) {
3308
+ map.set(subKey, tree);
3309
+ return;
3310
+ }
3311
+ visitor(tree.map, subKey);
3312
+ });
3313
+ };
3314
+ visitor(this.map, "");
3315
+ return map;
3316
+ }
3317
+ resolveServices() {
3318
+ const map = new Map();
3319
+ this.resolveLeaves().forEach((leaf, key) => {
3320
+ map.set(key, leaf.resolveService());
3321
+ });
3322
+ return map;
3323
+ }
3324
+ resolveAncestor(path) {
3325
+ if (!isString(path) || path.length == 0) {
3326
+ return this;
3327
+ }
3328
+ let parentTree;
3329
+ try {
3330
+ parentTree = this.parentTree.resolvePath(this.path);
3331
+ parentTree = parentTree.resolveAncestor(path);
3332
+ }
3333
+ catch (e) {
3334
+ parentTree = new Tree(this.container, false, "");
3335
+ }
3336
+ const pathParts = path.split(".");
3337
+ let tree = this;
3338
+ let previousTree = this;
3339
+ for (let part of pathParts) {
3340
+ tree = tree.map.get(part);
3341
+ if (!tree) {
3342
+ if (previousTree == this) {
3343
+ if (parentTree.path.length > 0) {
3344
+ return parentTree;
3345
+ }
3346
+ throw new Error(`Ancestor '${path}' not found in current tree: '${this.path}'`);
3347
+ }
3348
+ return previousTree;
3349
+ }
3350
+ previousTree = tree;
3351
+ }
3352
+ return parentTree.path.length > previousTree.path.length
3353
+ ? parentTree : previousTree;
3354
+ }
3355
+ resolvePath(path, throwError = true) {
3356
+ const absolutePath = !this.path ? path : `${this.path}.${path}`;
3357
+ let tree = new Tree(this.container, false, absolutePath);
3358
+ try {
3359
+ tree = this.resolveAncestor(path);
3360
+ }
3361
+ catch (e) {
3362
+ if (throwError) {
3363
+ throw new Error(`Path '${path}' not found in current tree: '${this.path}'`);
3364
+ }
3365
+ return tree;
3366
+ }
3367
+ if (tree.path !== absolutePath) {
3368
+ if (throwError) {
3369
+ throw new Error(`Path '${path}' not found in current tree: '${this.path}'`);
3370
+ }
3371
+ return tree;
3372
+ }
3373
+ return tree;
3374
+ }
3375
+ addPath(path) {
3376
+ if (!isString(path) || path.length == 0) {
3377
+ return this;
3378
+ }
3379
+ const pathParts = path.split(".");
3380
+ const maxIx = pathParts.length - 1;
3381
+ let tree = this;
3382
+ path = this.path;
3383
+ for (let ix = 0; ix <= maxIx; ix++) {
3384
+ const part = pathParts[ix];
3385
+ if (!tree.map.has(part)) {
3386
+ tree.map.set(part, new Tree(this.container, false, !path ? part : `${path}.${part}`));
3387
+ }
3388
+ tree = tree.map.get(part);
3389
+ tree.exists = tree.exists || ix == maxIx;
3390
+ path = tree.path;
3391
+ }
3392
+ return this;
3393
+ }
3394
+ }
3395
+
3396
+ class DiContainer {
3397
+ constructor(container, parent = null) {
3398
+ this.container = container;
3399
+ this.parent = parent;
3400
+ container["wrapperContainer"] = this;
3401
+ this.tokens = [];
3402
+ this.tokenSet = new Set();
3403
+ this.root = new Tree(this, false, "");
3404
+ }
3405
+ get registeredTokens() {
3406
+ return (this.parent?.registeredTokens || []).concat(this.tokens);
3407
+ }
3408
+ get tree() {
3409
+ return this.root;
3410
+ }
3411
+ beforeResolution(token, callback, options) {
3412
+ this.container.beforeResolution(token, callback, options);
3413
+ }
3414
+ afterResolution(token, callback, options) {
3415
+ this.container.afterResolution(token, callback, options);
3416
+ }
3417
+ clearInstances() {
3418
+ this.container.clearInstances();
3419
+ }
3420
+ createChildContainer() {
3421
+ return new DiContainer(this.container.createChildContainer(), this);
3422
+ }
3423
+ isRegistered(token, recursive) {
3424
+ return this.container.isRegistered(token, recursive);
3425
+ }
3426
+ register(token, provider, options) {
3427
+ if (isFactoryProvider(provider)) {
3428
+ this.container.register(token, {
3429
+ useFactory: dc => {
3430
+ return provider.useFactory(dc["wrapperContainer"]);
3431
+ }
3432
+ });
3433
+ return this.addToken(token);
3434
+ }
3435
+ this.container.register(token, provider);
3436
+ return this.addToken(token);
3437
+ }
3438
+ registerInstance(token, instance) {
3439
+ this.container.registerInstance(token, instance);
3440
+ return this.addToken(token);
3441
+ }
3442
+ registerSingleton(from, to) {
3443
+ this.container.registerSingleton(from, to);
3444
+ return this.addToken(from);
3445
+ }
3446
+ registerType(from, to) {
3447
+ this.container.registerType(from, to);
3448
+ return this.addToken(from);
3449
+ }
3450
+ reset() {
3451
+ this.tokens = [];
3452
+ this.tokenSet = new Set();
3453
+ this.container.reset();
3454
+ }
3455
+ resolve(token) {
3456
+ return this.container.resolve(token);
3457
+ }
3458
+ resolveAll(token) {
3459
+ return this.container.resolveAll(token);
3460
+ }
3461
+ get(token) {
3462
+ return this.container.resolve(token);
3463
+ }
3464
+ addToken(token) {
3465
+ if (this.tokenSet.has(token))
3466
+ return this;
3467
+ this.tokenSet.add(token);
3468
+ this.tokens.push(token);
3469
+ if (isString(token)) {
3470
+ this.root.addPath(token);
3471
+ }
3472
+ return this;
3473
+ }
3474
+ }
3475
+
3476
+ let EmptyJob = class EmptyJob {
3477
+ async process() {
3478
+ return null;
3479
+ }
3480
+ };
3481
+ EmptyJob = __decorate([
3482
+ injectable(),
3483
+ scoped(Lifecycle.ContainerScoped)
3484
+ ], EmptyJob);
3485
+
3486
+ async function setupStatic(rootFolder, container) {
3487
+ const browserFolder = resolve(rootFolder || getDirName(), `public_html`);
3488
+ const app = container.get(EXPRESS);
3489
+ const ep = container.get(EndpointProvider);
3490
+ console.log(browserFolder, existsSync(browserFolder));
3491
+ if (existsSync(browserFolder)) {
3492
+ console.log(`public_html exists. setting up static files serving...`);
3493
+ app.use(static$1(browserFolder, {
3494
+ maxAge: "1y"
3495
+ }));
3496
+ }
3497
+ else {
3498
+ console.log(`public_html does not exist on path: "${browserFolder}"`);
3499
+ }
3500
+ await ep.configure(app);
3501
+ }
3502
+
3503
+ let ClearCommand = class ClearCommand {
3504
+ constructor() {
3505
+ this.name = "clear";
3506
+ }
3507
+ async execute(args, terminal) {
3508
+ terminal.write(AnsiCodes.clear);
3509
+ }
3510
+ };
3511
+ ClearCommand = __decorate([
3512
+ injectable(),
3513
+ scoped(Lifecycle.ContainerScoped)
3514
+ ], ClearCommand);
3515
+
3516
+ let FixturesCommand = class FixturesCommand {
3517
+ constructor(fixtures) {
3518
+ this.fixtures = fixtures;
3519
+ this.name = "fixtures";
3520
+ }
3521
+ async execute(args, terminal) {
3522
+ const output = {
3523
+ write: text => terminal.writeln(text),
3524
+ writeln: text => terminal.writeln(text)
3525
+ };
3526
+ await this.fixtures.load(output);
3527
+ terminal.writeln(colorize(`Fixtures loaded`, ConsoleColor.FgGreen));
3528
+ }
3529
+ };
3530
+ FixturesCommand = __decorate([
3531
+ injectable(),
3532
+ scoped(Lifecycle.ContainerScoped)
3533
+ ], FixturesCommand);
3534
+
3535
+ const commands = [
3536
+ ClearCommand,
3537
+ FixturesCommand
3538
+ ];
3539
+
3540
+ class AssetImageParams {
3541
+ constructor() {
3542
+ this.rotation = 0;
3543
+ this.canvasScaleX = 1;
3544
+ this.canvasScaleY = 1;
3545
+ this.scaleX = 1;
3546
+ this.scaleY = 1;
3547
+ this.lazy = false;
3548
+ this.crop = false;
3549
+ this.cropBefore = false;
3550
+ this.cropAfter = false;
3551
+ }
3552
+ }
3553
+ __decorate([
3554
+ Min(-360),
3555
+ Max(360),
3556
+ IsOptional()
3557
+ ], AssetImageParams.prototype, "rotation", void 0);
3558
+ __decorate([
3559
+ Min(0.0001),
3560
+ IsOptional()
3561
+ ], AssetImageParams.prototype, "canvasScaleX", void 0);
3562
+ __decorate([
3563
+ Min(0.0001),
3564
+ IsOptional()
3565
+ ], AssetImageParams.prototype, "canvasScaleY", void 0);
3566
+ __decorate([
3567
+ Min(0.0001),
3568
+ IsOptional()
3569
+ ], AssetImageParams.prototype, "scaleX", void 0);
3570
+ __decorate([
3571
+ Min(0.0001),
3572
+ IsOptional()
3573
+ ], AssetImageParams.prototype, "scaleY", void 0);
3574
+ __decorate([
3575
+ IsBoolean(),
3576
+ IsOptional()
3577
+ ], AssetImageParams.prototype, "lazy", void 0);
3578
+ __decorate([
3579
+ IsBoolean(),
3580
+ IsOptional()
3581
+ ], AssetImageParams.prototype, "crop", void 0);
3582
+ __decorate([
3583
+ IsBoolean(),
3584
+ IsOptional()
3585
+ ], AssetImageParams.prototype, "cropBefore", void 0);
3586
+ __decorate([
3587
+ IsBoolean(),
3588
+ IsOptional()
3589
+ ], AssetImageParams.prototype, "cropAfter", void 0);
3590
+
3591
+ class BaseDoc {
3592
+ /**
3593
+ * Casts this to DocumentType<this> to allow using document methods in get/set-s
3594
+ */
3595
+ cast() {
3596
+ return this;
3597
+ }
3598
+ /**
3599
+ * Gets a pre-compiled model from typegoose cache by its class type
3600
+ * @param type
3601
+ */
3602
+ model(type) {
3603
+ return getModelForClass(type);
3604
+ }
3605
+ }
3606
+ const PrimitiveArray = Types.Array;
3607
+ const DocumentArray = Types.DocumentArray;
3608
+
3609
+ function IsDocumented(summary = null, paramDescriptions = {}) {
3610
+ return OpenAPI(op => {
3611
+ op.summary = summary || op.summary;
3612
+ op.tags = ["Documented"].concat(op.tags || []);
3613
+ op.parameters?.forEach(p => {
3614
+ if (p.$ref)
3615
+ return;
3616
+ const schema = p;
3617
+ schema.description = paramDescriptions[schema.name]
3618
+ || schema.description
3619
+ || `param.${op.operationId}.${schema.name}`.toLowerCase();
3620
+ });
3621
+ return op;
3622
+ });
3623
+ }
3624
+ function JsonResponse(description = "Success", statusCode = null) {
3625
+ return OpenAPI((op, route) => {
3626
+ const status = statusCode ?? getStatusCode(route) + "";
3627
+ op.responses = op.responses || {};
3628
+ op.responses[status] = {
3629
+ description,
3630
+ content: {
3631
+ "application/json": {}
3632
+ }
3633
+ };
3634
+ return op;
3635
+ });
3636
+ }
3637
+ function ResponseType(type, options = {}) {
3638
+ return OpenAPI((op, route) => {
3639
+ const contentType = "application/json";
3640
+ const statusCode = options?.statusCode ?? getStatusCode(route) + "";
3641
+ const reference = {
3642
+ $ref: `#/components/schemas/${type.name}`,
3643
+ };
3644
+ const schema = options.isArray
3645
+ ? { items: reference, type: "array" }
3646
+ : reference;
3647
+ op.responses = op.responses || {};
3648
+ op.responses[statusCode] = {
3649
+ description: options.description || "Success",
3650
+ content: {
3651
+ [contentType]: {
3652
+ schema
3653
+ }
3654
+ }
3655
+ };
3656
+ return op;
3657
+ });
3658
+ }
3659
+
3660
+ class LazyAssetGenerator {
3661
+ constructor(assetResolver, progresses, lazyId) {
3662
+ this.assetResolver = assetResolver;
3663
+ this.progresses = progresses;
3664
+ this.lazyId = lazyId;
3665
+ }
3666
+ get assets() {
3667
+ return this.assetResolver.assets;
3668
+ }
3669
+ get lazyAssets() {
3670
+ return this.assetResolver.lazyAssets;
3671
+ }
3672
+ async process(messaging) {
3673
+ const lazyAsset = await this.lazyAssets.read(this.lazyId);
3674
+ let progress = await this.progresses.get(lazyAsset.progressId);
3675
+ if (!progress || progress.canceled)
3676
+ return null;
3677
+ progress.setMessageBridge(messaging);
3678
+ try {
3679
+ const asset = await this.generate(progress, messaging);
3680
+ progress = await progress.load();
3681
+ if (!progress || progress.canceled)
3682
+ return null;
3683
+ await lazyAsset.writeAsset(asset);
3684
+ }
3685
+ catch (e) {
3686
+ await progress.setError(e.message || e);
3687
+ throw e;
3688
+ }
3689
+ if (progress.remaining > 0) {
3690
+ await progress.advance(progress.remaining);
3691
+ }
3692
+ }
3693
+ }
3694
+
3695
+ const pluginsKey = "typegoose:plugins";
3696
+ /**
3697
+ * A mongoose/typegoose plugin to inject services from the main di container to a schema as virtuals
3698
+ * @param schema
3699
+ * @param services
3700
+ */
3701
+ function injectServices(schema, services) {
3702
+ const serviceMap = {};
3703
+ if (!isObject(services)) {
3704
+ throw new Error(`services object should be defined to inject services to schema!`);
3705
+ }
3706
+ Object.keys(services).forEach(prop => {
3707
+ schema
3708
+ .virtual(prop)
3709
+ .get(() => {
3710
+ const diContainer = diContainers.appContainer;
3711
+ serviceMap[prop] = serviceMap[prop] || (!diContainer ? {} : diContainer.resolve(services[prop]));
3712
+ return serviceMap[prop];
3713
+ });
3714
+ });
3715
+ }
3716
+ /**
3717
+ * Decorates a property to inject a service with the help of the injectServices mongoose/typegoose plugin
3718
+ * @param token optional InjectionToken to use
3719
+ * @return PropertyDecorator
3720
+ */
3721
+ function service(token) {
3722
+ return (target, propertyKey) => {
3723
+ const propertyType = Reflect.getOwnMetadata("design:type", target, propertyKey);
3724
+ const plugins = Array.from(Reflect.getMetadata(pluginsKey, target.constructor) ?? []);
3725
+ let plugin = plugins.find(t => t.mongoosePlugin === injectServices);
3726
+ if (!plugin) {
3727
+ plugin = { mongoosePlugin: injectServices, options: {} };
3728
+ plugins.push(plugin);
3729
+ }
3730
+ plugin.options = Object.assign(plugin.options || {}, { [propertyKey]: token ?? propertyType });
3731
+ Reflect.defineMetadata(pluginsKey, plugins, target.constructor);
3732
+ };
3733
+ }
3734
+ /**
3735
+ * Paginate using a typegoose model using a simple where query and pagination params
3736
+ * @param model Typegoose model
3737
+ * @param where Simple query to filter the results
3738
+ * @param params Pagination params
3739
+ */
3740
+ function paginate(model, where, params) {
3741
+ return model.countDocuments(where).then(count => {
3742
+ let query = model.find(where);
3743
+ if (isString(params.sort)) {
3744
+ query = query.sort(params.sort);
3745
+ }
3746
+ if (isArray(params.populate)) {
3747
+ params.populate.forEach(field => {
3748
+ query = query.populate(field);
3749
+ });
3750
+ }
3751
+ return (params.limit > 0 ? query.skip(params.page * params.limit).limit(params.limit) : query).then(items => {
3752
+ const meta = { total: count };
3753
+ return { count, items, meta };
3754
+ });
3755
+ });
3756
+ }
3757
+ function lookupStages(from, localField, as = null, foreignField = "_id", shouldUnwind = true) {
3758
+ as = as || localField.replace("Id", "");
3759
+ const stages = [
3760
+ {
3761
+ $lookup: {
3762
+ from,
3763
+ localField,
3764
+ foreignField,
3765
+ as
3766
+ }
3767
+ },
3768
+ {
3769
+ $unwind: {
3770
+ path: `$${as}`,
3771
+ preserveNullAndEmptyArrays: true
3772
+ }
3773
+ }
3774
+ ];
3775
+ if (!shouldUnwind) {
3776
+ stages.splice(1, 1);
3777
+ }
3778
+ return stages;
3779
+ }
3780
+ function letsLookupStage(from, pipeline, as = null, letFields = null) {
3781
+ as = as || from;
3782
+ letFields = letFields || { id: "$_id" };
3783
+ return {
3784
+ $lookup: {
3785
+ from,
3786
+ let: letFields,
3787
+ pipeline,
3788
+ as
3789
+ }
3790
+ };
3791
+ }
3792
+ function matchStage(match) {
3793
+ return { $match: match };
3794
+ }
3795
+ function matchField(field, filter, when) {
3796
+ return { field, filter, when };
3797
+ }
3798
+ function matchFieldStages(...fields) {
3799
+ const match = {};
3800
+ fields.forEach(field => {
3801
+ if (field.when) {
3802
+ match[field.field] = field.filter;
3803
+ }
3804
+ });
3805
+ return Object.keys(match).length > 0 ? [matchStage(match)] : [];
3806
+ }
3807
+ function projectStage(fields) {
3808
+ return { $project: fields };
3809
+ }
3810
+ function unwindStage(fieldOrOpts) {
3811
+ return { $unwind: fieldOrOpts };
3812
+ }
3813
+ function hydratePopulated(modelType, json) {
3814
+ let object = modelType.hydrate(json);
3815
+ for (const [path, obj] of Object.entries(modelType.schema.obj)) {
3816
+ let { ref, type } = obj;
3817
+ if (Array.isArray(type) && type.length > 0) {
3818
+ ref = type[0].ref;
3819
+ }
3820
+ if (!ref)
3821
+ continue;
3822
+ const value = getValue$1(path, json);
3823
+ const hydrateVal = val => {
3824
+ if (val == null || val instanceof Types.ObjectId)
3825
+ return val;
3826
+ return hydratePopulated(model(ref), val);
3827
+ };
3828
+ if (Array.isArray(value)) {
3829
+ setValue(path, value.map(hydrateVal), object);
3830
+ continue;
3831
+ }
3832
+ setValue(path, hydrateVal(value), object);
3833
+ }
3834
+ return object;
3835
+ }
3836
+ async function paginateAggregations(model, aggregations, params, metaProjection = {}) {
3837
+ const sortField = !isString(params.sort) || !params.sort ? null : (params.sort.startsWith("-") ? params.sort.substr(1) : params.sort);
3838
+ const sortAggregation = !sortField ? [] : [{
3839
+ $sort: { [sortField]: sortField == params.sort ? 1 : -1 }
3840
+ }];
3841
+ const result = await model.aggregate([
3842
+ ...aggregations,
3843
+ ...sortAggregation,
3844
+ {
3845
+ $group: {
3846
+ _id: "results",
3847
+ result: { $push: "$$CURRENT" }
3848
+ }
3849
+ },
3850
+ {
3851
+ $project: {
3852
+ _id: 0,
3853
+ items: params.limit > 0 ? { $slice: ["$result", params.page * params.limit, params.limit] } : "$result",
3854
+ count: { $size: "$result" },
3855
+ meta: {
3856
+ total: { $size: "$result" },
3857
+ ...metaProjection
3858
+ }
3859
+ }
3860
+ }
3861
+ ]);
3862
+ const pagination = result[0];
3863
+ if (!pagination) {
3864
+ return { items: [], count: 0, meta: { total: 0 } };
3865
+ }
3866
+ pagination.items = pagination.items.map(i => hydratePopulated(model, i));
3867
+ return pagination;
3868
+ }
3869
+ function ResolveEntity(model, extraCheck) {
3870
+ const modelName = model.modelName;
3871
+ const paramName = modelName.toLowerCase();
3872
+ return createParamDecorator({
3873
+ required: false,
3874
+ value: async (action) => {
3875
+ const req = action.request;
3876
+ const token = req.header(`x-${paramName}-token`);
3877
+ const id = req.params[`${paramName}Id`];
3878
+ if (!id && !token) {
3879
+ throw new BadRequestError(`${modelName} id or token should be defined!`);
3880
+ }
3881
+ const query = !token
3882
+ ? model.findById(id)
3883
+ : model.findOne({ token });
3884
+ let doc = null;
3885
+ if (isFunction(extraCheck)) {
3886
+ try {
3887
+ doc = await valueToPromise(extraCheck(query, action));
3888
+ }
3889
+ catch (e) {
3890
+ throw new BadRequestError(`${modelName} check error: ${e.message || e}`);
3891
+ }
3892
+ }
3893
+ else {
3894
+ doc = await query;
3895
+ }
3896
+ if (!doc) {
3897
+ throw new HttpError(404, !token
3898
+ ? `${modelName} could not be found with id: ${id}`
3899
+ : `${modelName} could not be found with token: ${token}`);
3900
+ }
3901
+ action.request[paramName] = doc;
3902
+ return doc;
3903
+ }
3904
+ });
3905
+ }
3906
+
3907
+ async function resolveUser(req) {
3908
+ if (req.user)
3909
+ return req.user;
3910
+ const container = req.container;
3911
+ const auth = req.header("Authorization") || "";
3912
+ let payload = null;
3913
+ try {
3914
+ const config = container.resolve(Configuration);
3915
+ payload = webToken.verify(auth.split(" ")[1], config.resolve("jwtSecret"));
3916
+ }
3917
+ catch (e) {
3918
+ throw new HttpError(401, `Authentication failed. (${e.message})`);
3919
+ }
3920
+ if (!payload) {
3921
+ throw new HttpError(401, `Authentication failed. (Maybe invalid token)`);
3922
+ }
3923
+ req.user = await container.resolve(UserManager).getById(payload.id);
3924
+ return req.user;
3925
+ }
3926
+ function createServices() {
3927
+ // List of parameters
3928
+ const dirName = getDirName();
3929
+ const params = [
3930
+ new Parameter("serviceName", "Backend"),
3931
+ new Parameter("servicePassword", Math.random().toString(36).substring(7)),
3932
+ new Parameter("serviceUrl", "http://localhost:3000", (value, helper) => {
3933
+ // Replace port number to empty string
3934
+ const url = prepareUrlEmpty(`${value}`.replace(/:[0-9]+$/, ""), helper);
3935
+ const port = helper("appPort");
3936
+ return (!port || port === 80 || port === 443) ? url : `${url}:${port}`;
3937
+ }),
3938
+ new Parameter("templatesDir", join(dirName, "templates")),
3939
+ new Parameter("galleryDir", join(dirName, "gallery")),
3940
+ new Parameter("cacheDir", join(dirName, "cache")),
3941
+ new Parameter("defaultLanguage", "en"),
3942
+ new Parameter("smtpHost", "smtp.sendgrid.net"),
3943
+ new Parameter("smtpPort", 587),
3944
+ new Parameter("smtpUser", "apikey"),
3945
+ new Parameter("smtpPassword", ""),
3946
+ new Parameter("mailSenderAddress", "info@stemy.hu"),
3947
+ new Parameter("translationsTemplate", "https://translation.service/[lang]"),
3948
+ new Parameter("jwtSecret", "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9"),
3949
+ new Parameter("mongoUri", ""),
3950
+ new Parameter("mongoDb", "node-backend"),
3951
+ new Parameter("mongoUser", null),
3952
+ new Parameter("mongoPassword", null),
3953
+ new Parameter("nodeEnv", "production"),
3954
+ new Parameter("appPort", 80),
3955
+ new Parameter("zmqPort", 3000),
3956
+ new Parameter("zmqBackPort", 3100),
3957
+ new Parameter("zmqRemoteHost", "tcp://127.0.0.1"),
3958
+ new Parameter("isWorker", false),
3959
+ new Parameter("mainEndpoint", ""),
3960
+ new Parameter("idChars", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
3961
+ new Parameter("idSeparator", "-"),
3962
+ new Parameter("idPrefix", "ID-"),
3963
+ new Parameter("idParts", [4, 4]),
3964
+ new Parameter("jsonLimit", "250mb"),
3965
+ new Parameter("jobTimeout", 5 * 60 * 1000),
3966
+ new Parameter("cacheCollection", "cache"),
3967
+ new Parameter("logTags", []),
3968
+ new Parameter("ignoredLogTags", []),
3969
+ ];
3970
+ // Convert parameters to providers
3971
+ const paramProviders = params.map(p => {
3972
+ return {
3973
+ provide: PARAMETER,
3974
+ useValue: p
3975
+ };
3976
+ });
3977
+ // List of services
3978
+ const services = [
3979
+ AssetProcessor,
3980
+ AssetResolver,
3981
+ Assets,
3982
+ BackendProvider,
3983
+ Cache,
3984
+ CacheProcessor,
3985
+ Configuration,
3986
+ EndpointProvider,
3987
+ Fixtures,
3988
+ Gallery,
3989
+ GalleryCache,
3990
+ IdGenerator,
3991
+ JobManager,
3992
+ LazyAssets,
3993
+ Logger,
3994
+ MailSender,
3995
+ MemoryCache,
3996
+ MongoConnector,
3997
+ OpenApi,
3998
+ Progresses,
3999
+ TemplateRenderer,
4000
+ TerminalManager,
4001
+ TokenGenerator,
4002
+ TranslationProvider,
4003
+ Translator,
4004
+ UserManager
4005
+ ];
4006
+ // Create container
4007
+ const diContainer = new DiContainer(container.createChildContainer());
4008
+ paramProviders.forEach(provider => {
4009
+ diContainer.register(provider.provide, provider);
4010
+ });
4011
+ services.forEach(service => {
4012
+ if (!container.isRegistered(service))
4013
+ diContainer.register(service, service);
4014
+ });
4015
+ return diContainer;
4016
+ }
4017
+ async function setupBackend(config, providers, parent) {
4018
+ providers = Array.isArray(providers) ? providers : [];
4019
+ parent = parent || createServices();
4020
+ // Create fixtures
4021
+ const fixtureTypes = (config.fixtures || []);
4022
+ const fixtureProviders = fixtureTypes.map(fixture => {
4023
+ return {
4024
+ provide: FIXTURE,
4025
+ useClass: fixture
4026
+ };
4027
+ });
4028
+ // Create params
4029
+ const paramProviders = (config.params || []).map(p => {
4030
+ return {
4031
+ provide: PARAMETER,
4032
+ useValue: p
4033
+ };
4034
+ });
4035
+ // Create jobs
4036
+ const jobProviders = [EmptyJob].concat(config.jobs || []).map(jobType => {
4037
+ return {
4038
+ provide: JOB,
4039
+ useValue: jobType
4040
+ };
4041
+ });
4042
+ // Create commands
4043
+ const commandProviders = commands.concat(config.commands || []).map(commandType => {
4044
+ return {
4045
+ provide: TERMINAL_COMMAND,
4046
+ useClass: commandType
4047
+ };
4048
+ });
4049
+ // Create DI container
4050
+ const diContainer = parent.createChildContainer();
4051
+ // Setup rest API
4052
+ const restOptions = {
4053
+ routePrefix: config.routePrefix || "/api",
4054
+ defaultErrorHandler: false,
4055
+ ...(config.restOptions || {})
4056
+ };
4057
+ restOptions.cors = Object.assign({
4058
+ credentials: true,
4059
+ exposedHeaders: ["content-disposition"],
4060
+ origin: (origin, callback) => {
4061
+ callback(null, true);
4062
+ }
4063
+ }, restOptions.cors || {});
4064
+ restOptions.routePrefix = restOptions.routePrefix == "/" ? "" : restOptions.routePrefix;
4065
+ restOptions.middlewares = [ErrorHandlerMiddleware, ContainerMiddleware, LanguageMiddleware, RequestStartedMiddleware, RequestEndedMiddleware]
4066
+ .concat(restOptions.middlewares || []);
4067
+ restOptions.controllers = [AssetsController, AuthController, GalleryController, ProgressesController, TerminalController$1]
4068
+ .concat(restOptions.controllers || []);
4069
+ // Setup socket API
4070
+ const socketOptions = {
4071
+ container: {
4072
+ get(someClass) {
4073
+ return diContainer.get(someClass);
4074
+ }
4075
+ },
4076
+ ...(config.socketOptions || {})
4077
+ };
4078
+ socketOptions.middlewares = [CompressionMiddleware].concat(socketOptions.middlewares || []);
4079
+ socketOptions.controllers = [ProgressController, TerminalController].concat(socketOptions.controllers || []);
4080
+ // Create providers
4081
+ const allProviders = [];
4082
+ // Add multi tokens to sub container
4083
+ [PARAMETER].forEach(provide => {
4084
+ const values = parent.resolveAll(provide);
4085
+ values.forEach(useValue => {
4086
+ allProviders.push({ provide, useValue });
4087
+ });
4088
+ });
4089
+ // Add other providers
4090
+ allProviders.push(...fixtureTypes, ...fixtureProviders, ...paramProviders, ...jobProviders, ...commandProviders, ...restOptions.middlewares, ...restOptions.controllers, ...socketOptions.middlewares, ...socketOptions.controllers, ...providers, {
4091
+ provide: EXPRESS,
4092
+ useFactory: (container) => {
4093
+ return container.resolve(BackendProvider).express;
4094
+ }
4095
+ }, {
4096
+ provide: HTTP_SERVER,
4097
+ useFactory: (container) => {
4098
+ return container.resolve(BackendProvider).server;
4099
+ }
4100
+ }, {
4101
+ provide: SOCKET_SERVER,
4102
+ useFactory: (container) => {
4103
+ return container.resolve(BackendProvider).io;
4104
+ }
4105
+ });
4106
+ allProviders.forEach(provider => {
4107
+ if (isType(provider)) {
4108
+ if (container.isRegistered(provider))
4109
+ return;
4110
+ diContainer.register(provider, provider);
4111
+ return;
4112
+ }
4113
+ diContainer.register(provider.provide, provider);
4114
+ });
4115
+ diContainer.register(DI_CONTAINER, {
4116
+ useValue: diContainer
4117
+ });
4118
+ diContainer.register(OPENAPI_VALIDATION, {
4119
+ useValue: config.customValidation || (() => null)
4120
+ });
4121
+ diContainers.appContainer = diContainers.appContainer || diContainer;
4122
+ // Authentication
4123
+ restOptions.authorizationChecker = async (action, roles) => {
4124
+ const user = await resolveUser(action.request);
4125
+ if (!user) {
4126
+ throw new HttpError(401, "Authentication failed. (User can't be found.)");
4127
+ }
4128
+ const userRoles = Array.isArray(user.roles) ? user.roles : [];
4129
+ if (Array.isArray(roles) && roles.length > 0) {
4130
+ let lastError = null;
4131
+ for (let role of roles) {
4132
+ if (isFunction(role)) {
4133
+ try {
4134
+ if (await valueToPromise(role(user, action))) {
4135
+ return true;
4136
+ }
4137
+ }
4138
+ catch (e) {
4139
+ lastError = e;
4140
+ }
4141
+ }
4142
+ if (userRoles.indexOf(role) >= 0)
4143
+ return true;
4144
+ }
4145
+ const error = !lastError || (!lastError.message && !isString(lastError))
4146
+ ? "User doesn't have access to this resource"
4147
+ : lastError.message || lastError;
4148
+ throw new HttpError(401, `Authentication failed. (${error})`);
4149
+ }
4150
+ return true;
4151
+ };
4152
+ restOptions.currentUserChecker = async (action) => {
4153
+ try {
4154
+ return await resolveUser(action.request);
4155
+ }
4156
+ catch (e) {
4157
+ return null;
4158
+ }
4159
+ };
4160
+ // Final setup
4161
+ const configuration = diContainer.resolve(Configuration);
4162
+ const bp = diContainer.resolve(BackendProvider);
4163
+ if (config.restOptions) {
4164
+ bp.express.use(bodyParser.json({
4165
+ limit: configuration.hasParam("jsonLimit")
4166
+ ? configuration.resolve("jsonLimit")
4167
+ : "250mb"
4168
+ }));
4169
+ useContainer(diContainer);
4170
+ useExpressServer(bp.express, restOptions);
4171
+ // Setup rest ai docs
4172
+ let openApi = null;
4173
+ bp.express.get("/api-docs", (req, res) => {
4174
+ openApi = openApi || diContainer.get(OpenApi);
4175
+ res.header("Content-Type", "application/json")
4176
+ .status(200)
4177
+ .end(openApi.apiDocsStr);
4178
+ });
4179
+ }
4180
+ diContainer.register(SOCKET_CONTROLLERS, {
4181
+ useValue: new SocketControllers({
4182
+ io: bp.io,
4183
+ ...socketOptions,
4184
+ })
4185
+ });
4186
+ // Connect to mongo if necessary
4187
+ if (configuration.hasParam("mongoUri") && configuration.resolve("mongoUri")) {
4188
+ console.log("Connecting to MongoDB...");
4189
+ const connector = diContainer.resolve(MongoConnector);
4190
+ await connector.connect();
4191
+ console.log("Successfully connected to MongoDB.");
4192
+ }
4193
+ await setupStatic(config.rootFolder, diContainer);
4194
+ return diContainer;
4195
+ }
4196
+
4197
+ /**
4198
+ * Generated bundle index. Do not edit.
4199
+ */
4200
+
4201
+ export { AssetImageParams, AssetProcessor, AssetResolver, Assets, AuthController, BackendProvider, BaseDoc, Cache, CacheProcessor, Configuration, ConsoleColor, DI_CONTAINER, DocumentArray, EXPRESS, EndpointProvider, ErrorHandlerMiddleware, FIXTURE, Fixtures, Gallery, GalleryCache, GalleryController, HTTP_SERVER, IdGenerator, IsDocumented, IsFile, IsObjectId, JOB, JobManager, JsonResponse, LanguageMiddleware, LazyAssetGenerator, LazyAssets, Logger, MailSender, MemoryCache, MongoConnector, OPENAPI_VALIDATION, OpenApi, PARAMETER, Parameter, PrimitiveArray, Progresses, ResolveEntity, ResponseType, SOCKET_CONTROLLERS, SOCKET_SERVER, TERMINAL_COMMAND, TemplateRenderer, TerminalManager, TokenGenerator, TranslationProvider, Translator, Type, UserManager, assign, broadcast, bufferToStream, camelCaseToDash, colorize, convertValue, copy, copyStream, createIdString, createServices, createTransformer, deleteFile, deleteFromBucket, fileTypeFromBuffer, filter, firstItem, flatten, getConstructorName, getDirName, getExtension, getFileName, getFunctionParams, getType, getValue, groupBy, hydratePopulated, idToString, injectServices, isArray, isBoolean, isBuffer, isConstructor, isDate, isDefined, isFunction, isInterface, isNullOrUndefined, isObject, isObjectId, isPrimitive, isString, isType, jsonHighlight, lastItem, lcFirst, letsLookupStage, lookupStages, matchField, matchFieldStages, matchStage, md5, mkdirRecursive, multiSubscription, observableFromFunction, padLeft, padRight, paginate, paginateAggregations, prepareUrl, prepareUrlEmpty, prepareUrlSlash, projectStage, promiseTimeout, rand, random, readAndDeleteFile, readFile, regexEscape, regroup, replaceSpecialChars, resolveUser, runCommand, service, setupBackend, streamToBuffer, toImage, ucFirst, uniqueItems, unwindStage, valueToPromise, wrapError, writeFile };
4202
+ //# sourceMappingURL=stemy-backend.mjs.map