@stemy/backend 4.0.0 → 4.0.2

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 (41) hide show
  1. package/esm2020/commands/fixtures-command.mjs +5 -3
  2. package/esm2020/requests/asset-image-params.mjs +20 -11
  3. package/esm2020/rest-controllers/assets.controller.mjs +35 -10
  4. package/esm2020/rest-controllers/auth.controller.mjs +14 -5
  5. package/esm2020/rest-controllers/gallery.controller.mjs +9 -4
  6. package/esm2020/rest-controllers/progresses.controller.mjs +10 -4
  7. package/esm2020/rest-controllers/terminal.controller.mjs +17 -6
  8. package/esm2020/rest-middlewares/container.middleware.mjs +4 -3
  9. package/esm2020/rest-middlewares/error-handler.middleware.mjs +6 -3
  10. package/esm2020/rest-middlewares/language.middleware.mjs +5 -3
  11. package/esm2020/rest-middlewares/request-ended.middleware.mjs +5 -3
  12. package/esm2020/rest-middlewares/request-started.middleware.mjs +5 -3
  13. package/esm2020/services/asset-resolver.mjs +6 -3
  14. package/esm2020/services/assets.mjs +6 -3
  15. package/esm2020/services/backend-provider.mjs +4 -3
  16. package/esm2020/services/cache.mjs +7 -3
  17. package/esm2020/services/configuration.mjs +4 -3
  18. package/esm2020/services/fixtures.mjs +4 -3
  19. package/esm2020/services/gallery-cache.mjs +4 -3
  20. package/esm2020/services/gallery.mjs +6 -3
  21. package/esm2020/services/id-generator.mjs +5 -3
  22. package/esm2020/services/job-manager.mjs +7 -3
  23. package/esm2020/services/lazy-assets.mjs +13 -3
  24. package/esm2020/services/logger.mjs +5 -3
  25. package/esm2020/services/mail-sender.mjs +6 -3
  26. package/esm2020/services/memory-cache.mjs +5 -3
  27. package/esm2020/services/mongo-connector.mjs +5 -3
  28. package/esm2020/services/open-api.mjs +4 -3
  29. package/esm2020/services/progresses.mjs +6 -3
  30. package/esm2020/services/template-renderer.mjs +6 -3
  31. package/esm2020/services/terminal-manager.mjs +7 -3
  32. package/esm2020/services/token-generator.mjs +4 -3
  33. package/esm2020/services/translation-provider.mjs +6 -3
  34. package/esm2020/services/translator.mjs +5 -3
  35. package/esm2020/socket-controllers/progress.controller.mjs +14 -5
  36. package/esm2020/socket-controllers/terminal.controller.mjs +17 -6
  37. package/fesm2015/stemy-backend.mjs +1303 -1196
  38. package/fesm2015/stemy-backend.mjs.map +1 -1
  39. package/fesm2020/stemy-backend.mjs +1369 -1262
  40. package/fesm2020/stemy-backend.mjs.map +1 -1
  41. package/package.json +2 -2
@@ -4,7 +4,7 @@ import webToken from 'jsonwebtoken';
4
4
  import { injectable, scoped, Lifecycle, singleton, injectAll, inject, isFactoryProvider, container } from 'tsyringe';
5
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
6
  import { OnMessage, ConnectedSocket, MessageBody, SocketController, Middleware as Middleware$1, SocketControllers } from 'socket-controllers';
7
- import { __decorate, __param } from 'tslib';
7
+ import { __decorate, __param, __metadata } from 'tslib';
8
8
  import fontKit_ from 'fontkit';
9
9
  import sharp_ from 'sharp';
10
10
  import { ObjectId as ObjectId$1 } from 'bson';
@@ -19,19 +19,19 @@ import { ObjectId, GridFSBucket } from 'mongodb';
19
19
  import { Types, connect, model } from 'mongoose';
20
20
  import { Readable, PassThrough } from 'stream';
21
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
22
  import dotenv from 'dotenv';
26
- import { v4 } from 'uuid';
27
23
  import cron from 'node-cron';
28
24
  import { socket } from 'zeromq';
29
25
  import { filter as filter$1, map, first, timeout } from 'rxjs/operators';
26
+ import { createServer } from 'http';
27
+ import express_, { static as static$1 } from 'express';
28
+ import { Server } from 'socket.io';
29
+ import { v4 } from 'uuid';
30
30
  import { createTransport } from 'nodemailer';
31
+ import * as Handlebars from 'handlebars';
31
32
  import { routingControllersToSpec, OpenAPI, getStatusCode } from 'routing-controllers-openapi';
32
33
  import { validationMetadatasToSchemas } from 'class-validator-jsonschema';
33
34
  import { ValidatorConstraint, ValidationTypes, Min, Max, IsOptional, IsBoolean } from 'class-validator';
34
- import * as Handlebars from 'handlebars';
35
35
  import { CommandsAddon, AnsiCodes } from '@stemy/terminal-commands-addon';
36
36
  import { compare } from 'bcrypt';
37
37
  import moment from 'moment';
@@ -144,34 +144,6 @@ AssetProcessor = AssetProcessor_1 = __decorate([
144
144
  scoped(Lifecycle.ContainerScoped)
145
145
  ], AssetProcessor);
146
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
147
  const sharp$2 = sharp_;
176
148
  const diContainers = {
177
149
  appContainer: null
@@ -851,6 +823,99 @@ async function fileTypeFromBuffer(buffer) {
851
823
  return type;
852
824
  }
853
825
 
826
+ let Configuration = class Configuration {
827
+ constructor(params) {
828
+ dotenv.config();
829
+ this.paramMap = {};
830
+ this.paramValues = {};
831
+ (params || []).forEach(param => this.add(param));
832
+ }
833
+ add(param) {
834
+ const existingParam = this.paramMap[param.name] || param;
835
+ existingParam.defaultValue = param.defaultValue;
836
+ existingParam.resolver = param.resolver || existingParam.resolver;
837
+ this.paramMap[param.name] = existingParam;
838
+ }
839
+ resolveValue(param, alreadyResolved) {
840
+ const envName = param.name.replace(/\.?([A-Z|0-9]+)/g, function (x, y) {
841
+ return "_" + y.toLowerCase();
842
+ }).replace(/\./gi, "_").replace(/^_/, "").toUpperCase();
843
+ const envValue = process.env[envName];
844
+ const helper = (p) => this.resolveInternal(p, alreadyResolved);
845
+ if (typeof envValue !== "undefined") {
846
+ const value = isFunction(param.resolver)
847
+ ? param.resolver(envValue, helper)
848
+ : convertValue(envValue, getType(param.defaultValue));
849
+ console.log(colorize(`Processing param value`, ConsoleColor.FgYellow), colorize(param.name, ConsoleColor.FgGreen), colorize(envName, ConsoleColor.FgBlue), `"${envValue}"`, value);
850
+ return value;
851
+ }
852
+ else if (isFunction(param.resolver)) {
853
+ const value = param.resolver(param.defaultValue, helper);
854
+ console.log(colorize(`Processing default param value`, ConsoleColor.FgYellow), colorize(param.name, ConsoleColor.FgGreen), param.defaultValue, value);
855
+ return value;
856
+ }
857
+ console.log(colorize(`Using default param value`, ConsoleColor.FgYellow), colorize(param.name, ConsoleColor.FgGreen), param.defaultValue);
858
+ return param.defaultValue;
859
+ }
860
+ hasParam(name) {
861
+ return !!this.paramMap[name];
862
+ }
863
+ resolve(name) {
864
+ return this.resolveInternal(name, []);
865
+ }
866
+ resolveInternal(name, alreadyResolved) {
867
+ if (alreadyResolved.includes(name)) {
868
+ throw new Error(`Circular dependency detected: ${alreadyResolved.join(" -> ")} -> ${name}`);
869
+ }
870
+ alreadyResolved.push(name);
871
+ const param = this.paramMap[name];
872
+ if (!param)
873
+ throw new Error(`Parameter with name: '${name}' does not exists in configuration`);
874
+ if (!(name in this.paramValues)) {
875
+ this.paramValues[name] = this.resolveValue(param, alreadyResolved);
876
+ }
877
+ return this.paramValues[name];
878
+ }
879
+ };
880
+ Configuration = __decorate([
881
+ singleton(),
882
+ __param(0, injectAll(PARAMETER)),
883
+ __metadata("design:paramtypes", [Array])
884
+ ], Configuration);
885
+
886
+ let MongoConnector = class MongoConnector {
887
+ constructor(configuration) {
888
+ this.configuration = configuration;
889
+ this.conn = null;
890
+ this.db = null;
891
+ this.fsBucket = null;
892
+ }
893
+ get connection() {
894
+ return this.conn;
895
+ }
896
+ get database() {
897
+ return this.db;
898
+ }
899
+ get bucket() {
900
+ return this.fsBucket;
901
+ }
902
+ async connect() {
903
+ if (this.db)
904
+ return this.db;
905
+ this.conn = (await connect(this.configuration.resolve("mongoUri"), {
906
+ dbName: this.configuration.resolve("mongoDb"),
907
+ user: this.configuration.resolve("mongoUser"),
908
+ pass: this.configuration.resolve("mongoPassword")
909
+ })).connection;
910
+ this.db = this.conn.db;
911
+ this.fsBucket = new GridFSBucket(this.db, { bucketName: "assets" });
912
+ }
913
+ };
914
+ MongoConnector = __decorate([
915
+ singleton(),
916
+ __metadata("design:paramtypes", [Configuration])
917
+ ], MongoConnector);
918
+
854
919
  class BaseEntity {
855
920
  constructor(mId, data, collection) {
856
921
  this.mId = mId;
@@ -1090,640 +1155,534 @@ let Assets = class Assets {
1090
1155
  };
1091
1156
  Assets = __decorate([
1092
1157
  injectable(),
1093
- scoped(Lifecycle.ContainerScoped)
1158
+ scoped(Lifecycle.ContainerScoped),
1159
+ __metadata("design:paramtypes", [MongoConnector, AssetProcessor])
1094
1160
  ], Assets);
1095
1161
 
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);
1162
+ class LazyAsset extends BaseEntity {
1163
+ constructor(id, data, collection, logger, assets, progresses) {
1164
+ super(id, data, collection);
1165
+ this.logger = logger;
1166
+ this.assets = assets;
1167
+ this.progresses = progresses;
1102
1168
  }
1103
- get io() {
1104
- this.ioServer = this.ioServer || new Server(this.server, { path: "/socket" });
1105
- return this.ioServer;
1169
+ get jobName() {
1170
+ return this.data.jobName;
1106
1171
  }
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;
1172
+ get jobParams() {
1173
+ return this.data.jobParams;
1117
1174
  }
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 });
1175
+ get jobQue() {
1176
+ return this.data.jobQue;
1126
1177
  }
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;
1178
+ get progressId() {
1179
+ return this.data.progressId;
1141
1180
  }
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}`);
1181
+ get assetId() {
1182
+ return this.data.assetId;
1183
+ }
1184
+ async unlink() {
1185
+ await this.load();
1186
+ if (!this.progressId) {
1187
+ await this.collection.deleteOne({ _id: this.mId });
1151
1188
  }
1152
- return await this.cacheProcessor.deserialize(item.data);
1189
+ return deleteFromBucket(this.assets.bucket, new ObjectId$1(this.assetId));
1153
1190
  }
1154
- async getOrSet(key, valueCb, ttl, expirationTimestamp = null, tags = {}) {
1155
- try {
1156
- return await this.get(key);
1191
+ startWorking() {
1192
+ this.load().then(() => {
1193
+ if (this.deleted)
1194
+ return;
1195
+ this.progresses.get(this.progressId).then(p => {
1196
+ p?.cancel();
1197
+ });
1198
+ this.startWorkingOnAsset(false).then(() => {
1199
+ this.logger.log("lazy-assets", `Started working on lazy asset: ${this.id}`);
1200
+ }).catch(reason => {
1201
+ this.logger.log("lazy-assets", `Can't start working on lazy asset: ${this.id}\nReason: ${reason}`);
1202
+ });
1203
+ });
1204
+ }
1205
+ async loadAsset() {
1206
+ await this.load();
1207
+ if (this.deleted)
1208
+ return null;
1209
+ if (this.assetId) {
1210
+ return this.assets.read(this.assetId);
1157
1211
  }
1158
- catch (e) {
1159
- return await this.set(key, await valueCb(), ttl, expirationTimestamp, tags);
1212
+ if (this.progressId) {
1213
+ await this.progresses.waitToFinish(this.progressId);
1214
+ return this.loadAsset();
1160
1215
  }
1216
+ await this.startWorkingOnAsset(true);
1217
+ return this.loadAsset();
1161
1218
  }
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;
1219
+ async writeAsset(asset) {
1220
+ this.data.assetId = asset.id;
1221
+ await this.save();
1222
+ return asset;
1174
1223
  }
1175
- async deserialize(data) {
1176
- return data;
1224
+ async startWorkingOnAsset(fromLoad) {
1225
+ this.data.progressId = (await this.progresses.create()).id;
1226
+ this.data.assetId = null;
1227
+ await this.save();
1228
+ await this.progresses.jobMan.enqueueWithName(this.data.jobName, { ...this.data.jobParams, lazyId: this.id, fromLoad });
1177
1229
  }
1178
- };
1179
- CacheProcessor = __decorate([
1180
- injectable(),
1181
- scoped(Lifecycle.ContainerScoped)
1182
- ], CacheProcessor);
1230
+ }
1183
1231
 
1184
- let Configuration = class Configuration {
1185
- constructor(params) {
1186
- dotenv.config();
1187
- this.paramMap = {};
1188
- this.paramValues = {};
1189
- (params || []).forEach(param => this.add(param));
1232
+ let Logger = class Logger {
1233
+ constructor(config) {
1234
+ this.config = config;
1235
+ this.tags = this.config.resolve("logTags");
1236
+ this.ignoredTags = this.config.resolve("ignoredLogTags");
1190
1237
  }
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;
1238
+ log(tag, ...params) {
1239
+ if (this.ignoredTags.includes(tag))
1240
+ return;
1241
+ if (this.tags.length === 0 || this.tags.includes(tag)) {
1242
+ console.log(`[${tag}]`, ...params);
1243
+ }
1196
1244
  }
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;
1245
+ };
1246
+ Logger = __decorate([
1247
+ singleton(),
1248
+ __metadata("design:paramtypes", [Configuration])
1249
+ ], Logger);
1250
+
1251
+ let JobManager = class JobManager {
1252
+ constructor(config, logger, container, jobTypes) {
1253
+ this.config = config;
1254
+ this.logger = logger;
1255
+ this.container = container;
1256
+ this.jobTypes = jobTypes || [];
1257
+ this.jobs = this.jobTypes.reduce((res, jobType) => {
1258
+ const jobName = getConstructorName(jobType);
1259
+ res[jobName] = (jobParams, uniqueId) => {
1260
+ const job = this.resolveJobInstance(jobType, jobParams, uniqueId);
1261
+ const messageBridge = {
1262
+ sendMessage: (message, params) => {
1263
+ params.uniqueId = uniqueId;
1264
+ this.workerPush.send([message, JSON.stringify(params)]);
1265
+ }
1266
+ };
1267
+ messageBridge.sendMessage(`job-started`, { name: jobName });
1268
+ return job.process(messageBridge);
1269
+ };
1270
+ return res;
1271
+ }, {});
1272
+ this.messages = new Subject();
1273
+ this.processing = false;
1274
+ this.maxTimeout = this.config.resolve("jobTimeout");
1275
+ }
1276
+ on(message, cb) {
1277
+ return this.messages
1278
+ .pipe(filter$1(t => t.message === message))
1279
+ .pipe(map(t => t.params)).subscribe(cb);
1280
+ }
1281
+ async process(jobType, params = {}) {
1282
+ let instance = null;
1283
+ try {
1284
+ instance = this.resolveJobInstance(jobType, params);
1209
1285
  }
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;
1286
+ catch (e) {
1287
+ const jobName = getConstructorName(jobType);
1288
+ throw new Error(`Can't resolve params for job: ${jobName}, with params: ${JSON.stringify(params)}. Reason: ${e}`);
1214
1289
  }
1215
- console.log(colorize(`Using default param value`, ConsoleColor.FgYellow), colorize(param.name, ConsoleColor.FgGreen), param.defaultValue);
1216
- return param.defaultValue;
1290
+ return instance.process();
1217
1291
  }
1218
- hasParam(name) {
1219
- return !!this.paramMap[name];
1292
+ async enqueueWithName(name, params = {}) {
1293
+ return this.sendToWorkers(this.tryResolveFromName(name, params), params);
1220
1294
  }
1221
- resolve(name) {
1222
- return this.resolveInternal(name, []);
1295
+ async enqueue(jobType, params = {}) {
1296
+ return this.sendToWorkers(this.tryResolveAndInit(jobType, params), params);
1223
1297
  }
1224
- resolveInternal(name, alreadyResolved) {
1225
- if (alreadyResolved.includes(name)) {
1226
- throw new Error(`Circular dependency detected: ${alreadyResolved.join(" -> ")} -> ${name}`);
1298
+ schedule(minute, hour, dayOfMonth, month, dayOfWeek, jobType, params = {}) {
1299
+ const expression = [minute, hour, dayOfMonth, month, dayOfWeek].map(t => {
1300
+ if (isObject(t)) {
1301
+ const range = t;
1302
+ return `${range.min || 0}-${range.max || 0}`;
1303
+ }
1304
+ if (isArray(t)) {
1305
+ return t.join(",");
1306
+ }
1307
+ return `${t}`;
1308
+ }).join(" ");
1309
+ const jobName = getConstructorName(jobType);
1310
+ if (!cron.validate(expression)) {
1311
+ this.logger.log("job-manager", `Can't schedule the task: '${jobName}' because time expression is invalid.`);
1312
+ return null;
1227
1313
  }
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);
1314
+ return cron.schedule(expression, () => {
1315
+ this.enqueue(jobType, params).catch(e => {
1316
+ this.logger.log("job-manager", `Can't enqueue job: '${jobName}' because: ${e}`);
1317
+ });
1318
+ });
1319
+ }
1320
+ async startProcessing() {
1321
+ if (this.processing)
1322
+ return null;
1323
+ this.processing = true;
1324
+ if (!this.config.resolve("isWorker")) {
1325
+ this.logger.log("job-manager", colorize(`Processing can not be started because this is NOT a worker process!`, ConsoleColor.FgRed));
1326
+ return null;
1234
1327
  }
1235
- return this.paramValues[name];
1328
+ const host = this.config.resolve("zmqRemoteHost");
1329
+ const pushHost = `${host}:${this.config.resolve("zmqBackPort")}`;
1330
+ this.workerPush = socket("push");
1331
+ await this.workerPush.connect(pushHost);
1332
+ this.logger.log("job-manager", `Worker producer connected to: ${pushHost}`);
1333
+ const pullHost = `${host}:${this.config.resolve("zmqPort")}`;
1334
+ this.workerPull = socket("pull");
1335
+ await this.workerPull.connect(pullHost);
1336
+ this.logger.log("job-manager", `Worker consumer connected to: ${pullHost}`);
1337
+ this.workerPull.on("message", async (name, args, uniqId) => {
1338
+ try {
1339
+ const jobName = name.toString("utf8");
1340
+ const jobParams = JSON.parse(args.toString("utf8"));
1341
+ const uniqueId = uniqId?.toString("utf8");
1342
+ console.time(uniqueId);
1343
+ console.timeLog(uniqueId, `Started working on background job: ${colorize(jobName, ConsoleColor.FgCyan)} with args: \n${jsonHighlight(jobParams)}\n\n`);
1344
+ try {
1345
+ await Promise.race([this.jobs[jobName](jobParams, uniqueId), promiseTimeout(this.maxTimeout, true)]);
1346
+ console.timeLog(uniqueId, `Finished working on background job: ${colorize(jobName, ConsoleColor.FgCyan)}\n\n`);
1347
+ }
1348
+ catch (e) {
1349
+ console.timeLog(uniqueId, `Background job failed: ${colorize(jobName, ConsoleColor.FgRed)}\n${e}\n\n`);
1350
+ }
1351
+ console.timeEnd(uniqueId);
1352
+ }
1353
+ catch (e) {
1354
+ this.logger.log("job-manager", `Failed to start job: ${e.message}`);
1355
+ }
1356
+ });
1236
1357
  }
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}`);
1358
+ tryResolve(jobType, params) {
1359
+ const jobName = getConstructorName(jobType);
1360
+ if (!this.jobs[jobName]) {
1361
+ throw `Can't find job with name: ${jobName} so it can't be enqueued!`;
1362
+ }
1363
+ try {
1364
+ this.resolveJobInstance(jobType, params);
1365
+ }
1366
+ catch (e) {
1367
+ throw `Can't resolve params for job: ${jobName}, with params: ${JSON.stringify(params)}. Reason: ${e}`;
1368
+ }
1369
+ return jobName;
1246
1370
  }
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;
1371
+ tryResolveFromName(jobName, params) {
1372
+ const jobType = this.jobTypes.find(type => {
1373
+ return getConstructorName(type) == jobName;
1374
+ });
1375
+ if (!jobType) {
1376
+ throw `Can't find job type with name: ${jobName} so it can't be enqueued!`;
1377
+ }
1378
+ return this.tryResolveAndInit(jobType, params);
1256
1379
  }
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);
1380
+ tryResolveAndInit(jobType, params) {
1381
+ if (!this.apiPush) {
1382
+ const port = this.config.resolve("zmqPort");
1383
+ this.apiPush = socket("push");
1384
+ this.apiPush.bind(`tcp://0.0.0.0:${port}`);
1385
+ this.logger.log("job-manager", `API producer bound to port: ${port}`);
1386
+ }
1387
+ if (!this.apiPull) {
1388
+ const backPort = this.config.resolve("zmqBackPort");
1389
+ this.apiPull = socket("pull");
1390
+ this.apiPull.bind(`tcp://0.0.0.0:${backPort}`);
1391
+ this.apiPull.on("message", (name, args) => {
1392
+ const message = name.toString("utf8");
1393
+ const params = JSON.parse(args?.toString("utf8") || "{}");
1394
+ const paramTypes = Object.keys(params).reduce((res, key) => {
1395
+ res[key] = getType(params[key]);
1396
+ return res;
1397
+ }, {});
1398
+ this.logger.log("job-manager", `Received a message from worker: "${colorize(message, ConsoleColor.FgCyan)}" with args: ${jsonHighlight(paramTypes)}\n\n`);
1399
+ this.messages.next({ message, params });
1400
+ });
1401
+ this.logger.log("job-manager", `API consumer bound to port: ${backPort}`);
1266
1402
  }
1403
+ return this.tryResolve(jobType, params);
1404
+ }
1405
+ resolveJobInstance(jobType, params, uniqueId = "") {
1406
+ const container = this.container.createChildContainer();
1407
+ Object.keys(params).map((name) => {
1408
+ container.register(name, { useValue: params[name] });
1409
+ });
1410
+ container.register("uniqueId", { useValue: uniqueId });
1411
+ container.register(jobType, jobType);
1412
+ return container.resolve(jobType);
1413
+ }
1414
+ async sendToWorkers(jobName, params) {
1415
+ const publisher = await this.apiPush;
1416
+ const uniqueId = new ObjectId$1().toHexString();
1417
+ await publisher.send([jobName, JSON.stringify(params), uniqueId]);
1418
+ return uniqueId;
1267
1419
  }
1268
1420
  };
1269
- Fixtures = __decorate([
1421
+ JobManager = __decorate([
1270
1422
  injectable(),
1271
1423
  scoped(Lifecycle.ContainerScoped),
1272
- __param(0, injectAll(FIXTURE))
1273
- ], Fixtures);
1424
+ __param(2, inject(DI_CONTAINER)),
1425
+ __param(3, injectAll(JOB)),
1426
+ __metadata("design:paramtypes", [Configuration,
1427
+ Logger, Object, Array])
1428
+ ], JobManager);
1274
1429
 
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];
1430
+ class Progress extends BaseEntity {
1431
+ constructor(id, data, collection) {
1432
+ super(id, data, collection);
1295
1433
  }
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
- });
1434
+ get current() {
1435
+ return this.data.current;
1377
1436
  }
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;
1437
+ get max() {
1438
+ return this.data.max;
1394
1439
  }
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);
1440
+ get message() {
1441
+ return this.data.message;
1416
1442
  }
1417
- }
1418
-
1419
- let GalleryCache = class GalleryCache {
1420
- constructor() {
1421
- this.imgCache = {};
1443
+ get error() {
1444
+ return this.data.error;
1422
1445
  }
1423
- put(img) {
1424
- this.imgCache[img.thumb] = img;
1425
- this.imgCache[img.big] = img;
1446
+ get canceled() {
1447
+ return this.data.canceled;
1426
1448
  }
1427
- serve(id) {
1428
- const img = this.imgCache[id];
1429
- return !img ? null : img.serve(id);
1449
+ get percent() {
1450
+ return this.max > 0 ? Math.round(this.current / this.max * 100) : 0;
1430
1451
  }
1431
- create(folder, targetSize, handler) {
1432
- const image = new GalleryImage(folder, targetSize, handler);
1433
- this.put(image);
1434
- return image;
1452
+ get remaining() {
1453
+ return this.max > 0 ? this.max - this.current : 0;
1435
1454
  }
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");
1455
+ setMessageBridge(messageBridge) {
1456
+ this.messageBridge = messageBridge || this.messageBridge;
1457
+ return this;
1449
1458
  }
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++;
1459
+ async createSubProgress(progressValue, max, message) {
1460
+ if (max <= 0 && progressValue > 0) {
1461
+ await this.advance(progressValue);
1458
1462
  }
1459
- if (notGood) {
1460
- throw `Couldn't generate an unique id..`;
1463
+ if (message !== null) {
1464
+ this.data.message = message;
1465
+ await this.save();
1461
1466
  }
1462
- return id;
1467
+ return new SubProgress(this, this.current, progressValue, Math.max(max, 1));
1463
1468
  }
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);
1469
+ async setMax(max) {
1470
+ if (isNaN(max) || max <= 0) {
1471
+ throw "Max progress value must be bigger than zero";
1472
+ }
1473
+ this.data.max = max;
1474
+ await this.save();
1473
1475
  }
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");
1476
+ async setMessage(message) {
1477
+ this.data.message = message;
1478
+ await this.save();
1504
1479
  }
1505
- on(message, cb) {
1506
- return this.messages
1507
- .pipe(filter$1(t => t.message === message))
1508
- .pipe(map(t => t.params)).subscribe(cb);
1480
+ async setError(error) {
1481
+ this.data.error = error;
1482
+ await this.save();
1509
1483
  }
1510
- async process(jobType, params = {}) {
1511
- let instance = null;
1512
- try {
1513
- instance = this.resolveJobInstance(jobType, params);
1484
+ async advance(value = 1) {
1485
+ if (isNaN(value) || value <= 0) {
1486
+ throw new Error(`Advance value must be bigger than zero: ${this.id}`);
1514
1487
  }
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}`);
1488
+ await this.load();
1489
+ if (this.deleted || this.canceled) {
1490
+ const status = this.deleted ? "deleted" : "canceled";
1491
+ throw new Error(`Can't advance ${status} progress: ${this.id}`);
1518
1492
  }
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);
1493
+ this.data.current = Math.min(this.max, this.current + value);
1494
+ await this.save();
1526
1495
  }
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
- });
1496
+ async cancel() {
1497
+ this.data.canceled = true;
1498
+ await this.save();
1548
1499
  }
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;
1500
+ save() {
1501
+ if (this.messageBridge) {
1502
+ this.messageBridge.sendMessage(`progress-changed`, this.toJSON());
1556
1503
  }
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
- });
1504
+ return super.save();
1586
1505
  }
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);
1506
+ }
1507
+ class SubProgress {
1508
+ constructor(parent, progressFrom, progressValue, mMax = 100) {
1509
+ this.parent = parent;
1510
+ this.progressFrom = progressFrom;
1511
+ this.progressValue = progressValue;
1512
+ this.mMax = mMax;
1513
+ if (progressFrom < 0) {
1514
+ throw "Progress from must be bigger than or zero";
1594
1515
  }
1595
- catch (e) {
1596
- throw `Can't resolve params for job: ${jobName}, with params: ${JSON.stringify(params)}. Reason: ${e}`;
1516
+ if (progressValue <= 0) {
1517
+ throw "Progress value must be bigger than zero";
1597
1518
  }
1598
- return jobName;
1519
+ this.mCurrent = 0;
1599
1520
  }
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);
1521
+ get id() {
1522
+ return this.parent.id;
1608
1523
  }
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);
1524
+ get current() {
1525
+ return this.mCurrent;
1633
1526
  }
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);
1527
+ get max() {
1528
+ return this.mMax;
1642
1529
  }
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;
1530
+ get message() {
1531
+ return this.parent.message;
1648
1532
  }
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;
1533
+ get error() {
1534
+ return this.parent.error;
1663
1535
  }
1664
- get jobName() {
1665
- return this.data.jobName;
1536
+ get percent() {
1537
+ return this.parent.percent;
1666
1538
  }
1667
- get jobParams() {
1668
- return this.data.jobParams;
1539
+ get remaining() {
1540
+ return this.max - this.mCurrent;
1669
1541
  }
1670
- get jobQue() {
1671
- return this.data.jobQue;
1542
+ get canceled() {
1543
+ return !this.parent || this.parent.canceled;
1672
1544
  }
1673
- get progressId() {
1674
- return this.data.progressId;
1545
+ setMessageBridge(messageBridge) {
1546
+ if (!this.parent)
1547
+ return this;
1548
+ this.parent.setMessageBridge(messageBridge);
1549
+ return this;
1675
1550
  }
1676
- get assetId() {
1677
- return this.data.assetId;
1551
+ async createSubProgress(progressValue, max, message) {
1552
+ if (max <= 0 && progressValue > 0) {
1553
+ await this.advance(progressValue);
1554
+ }
1555
+ if (message !== null) {
1556
+ await this.setMessage(message);
1557
+ }
1558
+ return new SubProgress(this, this.current, progressValue, Math.max(max, 1));
1678
1559
  }
1679
- async unlink() {
1680
- await this.load();
1681
- if (!this.progressId) {
1682
- await this.collection.deleteOne({ _id: this.mId });
1560
+ async setMax(max) {
1561
+ if (isNaN(max) || max <= 0) {
1562
+ throw "Max progress value must be bigger than zero";
1683
1563
  }
1684
- return deleteFromBucket(this.assets.bucket, new ObjectId$1(this.assetId));
1564
+ this.mMax = max;
1565
+ await this.save();
1685
1566
  }
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
- });
1567
+ async setMessage(message) {
1568
+ if (!this.parent)
1569
+ return null;
1570
+ await this.parent.setMessage(message);
1699
1571
  }
1700
- async loadAsset() {
1701
- await this.load();
1702
- if (this.deleted)
1572
+ async setError(error) {
1573
+ if (!this.parent)
1703
1574
  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();
1575
+ await this.parent.setError(error);
1713
1576
  }
1714
- async writeAsset(asset) {
1715
- this.data.assetId = asset.id;
1577
+ async advance(value = 1) {
1578
+ if (isNaN(value) || value <= 0) {
1579
+ throw "Advance value must be bigger than zero";
1580
+ }
1581
+ this.mCurrent = Math.min(this.max, this.mCurrent + value);
1716
1582
  await this.save();
1717
- return asset;
1718
1583
  }
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 });
1584
+ async cancel() {
1585
+ if (!this.parent)
1586
+ return null;
1587
+ await this.parent.cancel();
1588
+ }
1589
+ async save() {
1590
+ const ratio = this.max > 0 ? this.mCurrent / this.max : 0;
1591
+ const newProgress = this.progressFrom + Math.round(this.progressValue * ratio);
1592
+ const current = this.parent.current;
1593
+ if (newProgress <= current)
1594
+ return null;
1595
+ await this.parent.advance(newProgress);
1596
+ }
1597
+ async load() {
1598
+ return null;
1599
+ }
1600
+ toJSON() {
1601
+ return this.parent.toJSON();
1724
1602
  }
1725
1603
  }
1726
1604
 
1605
+ let Progresses = class Progresses {
1606
+ constructor(connector, jobMan) {
1607
+ this.connector = connector;
1608
+ this.jobMan = jobMan;
1609
+ this.collection = connector.database.collection("progresses");
1610
+ this.progresses = {};
1611
+ this.jobMan.on("progress-changed", progress => {
1612
+ const id = progress.id;
1613
+ this.progresses[id] = new Progress(new ObjectId$1(id), progress, this.collection);
1614
+ });
1615
+ }
1616
+ async waitToFinish(id) {
1617
+ return Promise.race([
1618
+ this.waitForProgress(id, async () => {
1619
+ let progress = this.progresses[id];
1620
+ if (!progress || progress.percent < 100) {
1621
+ progress = await this.get(id);
1622
+ }
1623
+ if (!progress) {
1624
+ throw new Error(`Progress does not exists with id: ${id}`);
1625
+ }
1626
+ return progress;
1627
+ }, 500),
1628
+ this.waitForProgress(id, async () => {
1629
+ return this.progresses[id] || null;
1630
+ }, 25)
1631
+ ]);
1632
+ }
1633
+ async get(id) {
1634
+ return !id ? null : this.find({ _id: new ObjectId$1(id) });
1635
+ }
1636
+ async find(where) {
1637
+ const data = await this.collection.findOne(where);
1638
+ return !data ? null : new Progress(data._id, data, this.collection);
1639
+ }
1640
+ async create(max = 100) {
1641
+ if (isNaN(max) || max <= 0) {
1642
+ throw new Error(`Max progress value must be bigger than zero`);
1643
+ }
1644
+ const data = {
1645
+ current: 0,
1646
+ max: max,
1647
+ message: "",
1648
+ error: "",
1649
+ canceled: false
1650
+ };
1651
+ const res = await this.collection.insertOne(data);
1652
+ return new Progress(res.insertedId, data, this.collection);
1653
+ }
1654
+ async remove(id) {
1655
+ await this.collection.deleteOne({ _id: new ObjectId$1(id) });
1656
+ return id;
1657
+ }
1658
+ async waitForProgress(id, cb, delay) {
1659
+ let isFinished = false;
1660
+ let progress = null;
1661
+ let waitTime = 0;
1662
+ while (!isFinished) {
1663
+ progress = await cb();
1664
+ waitTime += delay;
1665
+ if (progress) {
1666
+ if (progress.error) {
1667
+ throw new Error(progress.error);
1668
+ }
1669
+ isFinished = progress.percent >= 100;
1670
+ }
1671
+ if (!isFinished) {
1672
+ if (waitTime >= this.jobMan.maxTimeout) {
1673
+ throw new Error(`Progress with id: ${id} probably never will be finished!`);
1674
+ }
1675
+ await promiseTimeout(delay);
1676
+ }
1677
+ }
1678
+ return progress;
1679
+ }
1680
+ };
1681
+ Progresses = __decorate([
1682
+ singleton(),
1683
+ __metadata("design:paramtypes", [MongoConnector, JobManager])
1684
+ ], Progresses);
1685
+
1727
1686
  let LazyAssets = class LazyAssets {
1728
1687
  constructor(connector, assets, progresses, logger, jobMan) {
1729
1688
  this.connector = connector;
@@ -1764,94 +1723,114 @@ let LazyAssets = class LazyAssets {
1764
1723
  };
1765
1724
  LazyAssets = __decorate([
1766
1725
  injectable(),
1767
- scoped(Lifecycle.ContainerScoped)
1726
+ scoped(Lifecycle.ContainerScoped),
1727
+ __metadata("design:paramtypes", [MongoConnector,
1728
+ Assets,
1729
+ Progresses,
1730
+ Logger,
1731
+ JobManager])
1768
1732
  ], LazyAssets);
1769
1733
 
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");
1734
+ let AssetResolver = class AssetResolver {
1735
+ constructor(assets, lazyAssets) {
1736
+ this.assets = assets;
1737
+ this.lazyAssets = lazyAssets;
1775
1738
  }
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);
1739
+ async resolve(id, lazy = false) {
1740
+ let asset = null;
1741
+ if (lazy) {
1742
+ const lazyAsset = await this.lazyAssets.read(id);
1743
+ if (!lazyAsset)
1744
+ return null;
1745
+ return lazyAsset.loadAsset();
1746
+ }
1747
+ asset = await this.assets.read(id);
1748
+ if (!asset) {
1749
+ const lazyAsset = await this.lazyAssets.read(id);
1750
+ if (!lazyAsset)
1751
+ return null;
1752
+ return lazyAsset.loadAsset();
1781
1753
  }
1754
+ return asset;
1782
1755
  }
1783
1756
  };
1784
- Logger = __decorate([
1785
- singleton()
1786
- ], Logger);
1757
+ AssetResolver = __decorate([
1758
+ injectable(),
1759
+ scoped(Lifecycle.ContainerScoped),
1760
+ __metadata("design:paramtypes", [Assets, LazyAssets])
1761
+ ], AssetResolver);
1787
1762
 
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
- });
1763
+ const express = express_;
1764
+ let BackendProvider = class BackendProvider {
1765
+ constructor() {
1766
+ this.express = express();
1767
+ this.express.set("trust proxy", true);
1768
+ this.server = createServer(this.express);
1800
1769
  }
1801
- get translator() {
1802
- return this.renderer.translator;
1770
+ get io() {
1771
+ this.ioServer = this.ioServer || new Server(this.server, { path: "/socket" });
1772
+ return this.ioServer;
1803
1773
  }
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
- });
1774
+ };
1775
+ BackendProvider = __decorate([
1776
+ singleton(),
1777
+ __metadata("design:paramtypes", [])
1778
+ ], BackendProvider);
1779
+
1780
+ let CacheProcessor = class CacheProcessor {
1781
+ async serialize(data) {
1782
+ return data;
1783
+ }
1784
+ async deserialize(data) {
1785
+ return data;
1814
1786
  }
1815
1787
  };
1816
- MailSender = __decorate([
1817
- singleton()
1818
- ], MailSender);
1788
+ CacheProcessor = __decorate([
1789
+ injectable(),
1790
+ scoped(Lifecycle.ContainerScoped)
1791
+ ], CacheProcessor);
1819
1792
 
1820
- let MemoryCache = class MemoryCache {
1821
- constructor(cache) {
1822
- this.cache = cache;
1823
- this.cacheMap = new Map();
1793
+ let Cache = class Cache {
1794
+ constructor(connector, config, cacheProcessor) {
1795
+ this.connector = connector;
1796
+ this.config = config;
1797
+ this.cacheProcessor = cacheProcessor;
1798
+ }
1799
+ async prepare() {
1800
+ if (this.collection)
1801
+ return;
1802
+ if (!this.connector.database) {
1803
+ throw new Error(`You can't use cache without mongo connection!`);
1804
+ }
1805
+ this.collection = this.connector.database.collection(this.config.resolve("cacheCollection"));
1806
+ await this.collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
1824
1807
  }
1825
1808
  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, {
1809
+ await this.prepare();
1810
+ const item = {
1829
1811
  _id: key,
1830
- data: value,
1831
- expirationTimestamp: expTimestamp,
1832
- expiresAt: now + expTimestamp,
1833
- });
1834
- return this.cache.set(key, value, ttl, expirationTimestamp, tags);
1812
+ data: await this.cacheProcessor.serialize(value),
1813
+ tags: await this.cacheProcessor.serialize(tags),
1814
+ expirationTimestamp,
1815
+ };
1816
+ if (ttl) {
1817
+ const now = Math.round(new Date().getTime() / 1000);
1818
+ item.expiresAt = now + ttl;
1819
+ }
1820
+ await this.collection.updateOne({ _id: key }, { $set: item }, { upsert: true });
1821
+ return value;
1835
1822
  }
1836
1823
  async get(key) {
1837
- let item = this.cacheMap.get(key);
1824
+ await this.prepare();
1825
+ let item = await this.collection.findOne({ _id: key });
1838
1826
  const now = Math.round(new Date().getTime() / 1000);
1839
- let expTimestamp = 3600;
1840
1827
  if (item && item.expiresAt && item.expiresAt < now) {
1841
- expTimestamp = item.expirationTimestamp;
1842
1828
  item = null;
1843
1829
  }
1844
1830
  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;
1831
+ throw new Error(`Cache probably doesn't exists with key: ${key}`);
1853
1832
  }
1854
- return item.data;
1833
+ return await this.cacheProcessor.deserialize(item.data);
1855
1834
  }
1856
1835
  async getOrSet(key, valueCb, ttl, expirationTimestamp = null, tags = {}) {
1857
1836
  try {
@@ -1862,446 +1841,345 @@ let MemoryCache = class MemoryCache {
1862
1841
  }
1863
1842
  }
1864
1843
  async delete(key) {
1865
- this.cacheMap.delete(key);
1866
- await this.cacheMap.delete(key);
1844
+ await this.prepare();
1845
+ await this.collection.deleteOne({ _id: key });
1867
1846
  }
1868
1847
  };
1869
- MemoryCache = __decorate([
1870
- injectable(),
1871
- scoped(Lifecycle.ContainerScoped)
1872
- ], MemoryCache);
1848
+ Cache = __decorate([
1849
+ singleton(),
1850
+ __metadata("design:paramtypes", [MongoConnector, Configuration, CacheProcessor])
1851
+ ], Cache);
1873
1852
 
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" });
1853
+ let EndpointProvider = class EndpointProvider {
1854
+ async configure(app) {
1855
+ console.log(`Express app is mounted to: ${app.mountpath}`);
1900
1856
  }
1901
1857
  };
1902
- MongoConnector = __decorate([
1903
- singleton()
1904
- ], MongoConnector);
1858
+ EndpointProvider = __decorate([
1859
+ injectable(),
1860
+ scoped(Lifecycle.ContainerScoped)
1861
+ ], EndpointProvider);
1905
1862
 
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);
1863
+ let Fixtures = class Fixtures {
1864
+ constructor(fixtures) {
1865
+ this.fixtures = fixtures;
1932
1866
  }
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);
1867
+ async load(output) {
1868
+ if (!this.fixtures)
1869
+ return;
1870
+ output = output || {
1871
+ write: console.log,
1872
+ writeln: t => console.log(t + "\n")
1873
+ };
1874
+ for (let fixture of this.fixtures) {
1875
+ await fixture.load(output);
1876
+ }
1941
1877
  }
1942
1878
  };
1943
- IsObjectId = __decorate([
1944
- ValidatorConstraint()
1945
- ], IsObjectId);
1879
+ Fixtures = __decorate([
1880
+ injectable(),
1881
+ scoped(Lifecycle.ContainerScoped),
1882
+ __param(0, injectAll(FIXTURE)),
1883
+ __metadata("design:paramtypes", [Array])
1884
+ ], Fixtures);
1946
1885
 
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;
1886
+ const sharp$1 = sharp_;
1887
+ const bigSize = 1500;
1888
+ const thumbSize = 250;
1889
+ class GalleryImage {
1890
+ constructor(folder, size, handler) {
1891
+ this.folder = folder;
1892
+ this.handler = handler;
1893
+ this.thumb = v4();
1894
+ this.big = v4();
1895
+ this.targetSize = !size ? { width: thumbSize, height: thumbSize } : size;
1962
1896
  }
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;
1897
+ async serve(id) {
1898
+ const isThumb = id == this.thumb;
1899
+ if (await this.handler.hasResult(isThumb)) {
1900
+ return this.handler.serveResult(isThumb);
2011
1901
  }
1902
+ const original = sharp$1(await this.handler.getOriginal()).rotate();
1903
+ const meta = await original.metadata();
1904
+ const ratio = meta.width / meta.height;
1905
+ const sizeRatio = isThumb ? this.targetSize.width / this.targetSize.height : 1;
1906
+ const size = isThumb ? Math.max(this.targetSize.width, this.targetSize.height) : bigSize;
1907
+ const targetHeight = ratio > sizeRatio ? size : Math.round(size / ratio);
1908
+ const targetWidth = Math.round(targetHeight * ratio);
1909
+ const resized = original.resize(targetWidth, targetHeight);
1910
+ const buffer = await (isThumb ? resized.extract({
1911
+ left: Math.floor((targetWidth - this.targetSize.width) / 2),
1912
+ top: Math.floor((targetHeight - this.targetSize.height) / 2),
1913
+ width: this.targetSize.width,
1914
+ height: this.targetSize.height
1915
+ }).toBuffer() : resized.toBuffer());
1916
+ await this.handler.writeResult(isThumb, buffer);
1917
+ return this.handler.serveResult(isThumb);
2012
1918
  }
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);
1919
+ }
2050
1920
 
2051
- class Progress extends BaseEntity {
2052
- constructor(id, data, collection) {
2053
- super(id, data, collection);
1921
+ let GalleryCache = class GalleryCache {
1922
+ constructor() {
1923
+ this.imgCache = {};
2054
1924
  }
2055
- get current() {
2056
- return this.data.current;
1925
+ put(img) {
1926
+ this.imgCache[img.thumb] = img;
1927
+ this.imgCache[img.big] = img;
2057
1928
  }
2058
- get max() {
2059
- return this.data.max;
1929
+ serve(id) {
1930
+ const img = this.imgCache[id];
1931
+ return !img ? null : img.serve(id);
2060
1932
  }
2061
- get message() {
2062
- return this.data.message;
1933
+ create(folder, targetSize, handler) {
1934
+ const image = new GalleryImage(folder, targetSize, handler);
1935
+ this.put(image);
1936
+ return image;
2063
1937
  }
2064
- get error() {
2065
- return this.data.error;
1938
+ };
1939
+ GalleryCache = __decorate([
1940
+ injectable(),
1941
+ scoped(Lifecycle.ContainerScoped),
1942
+ __metadata("design:paramtypes", [])
1943
+ ], GalleryCache);
1944
+
1945
+ const sharp = sharp_;
1946
+ let Gallery = class Gallery {
1947
+ constructor(config, galleryCache) {
1948
+ this.config = config;
1949
+ this.galleryCache = galleryCache;
1950
+ this.cache = {};
1951
+ this.dir = this.config.resolve("galleryDir");
1952
+ this.output = join(this.config.resolve("cacheDir"), "gallery");
2066
1953
  }
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;
1954
+ async getFolder(folder, size = null) {
1955
+ this.cache[folder] = this.cache[folder] || new Promise(resolve => {
1956
+ lstat(join(this.dir, folder), (err, stats) => {
1957
+ if (err || !stats.isDirectory()) {
1958
+ resolve([]);
1959
+ return;
1960
+ }
1961
+ this.readRecursive(folder, "", size).then(resolve, () => resolve([]));
1962
+ });
1963
+ });
1964
+ return this.cache[folder];
2165
1965
  }
2166
- setMessageBridge(messageBridge) {
2167
- if (!this.parent)
2168
- return this;
2169
- this.parent.setMessageBridge(messageBridge);
2170
- return this;
1966
+ readRecursive(path, folder, size) {
1967
+ return new Promise(resolve => {
1968
+ readdir(join(this.dir, path), (err, files) => {
1969
+ if (err) {
1970
+ resolve([]);
1971
+ return;
1972
+ }
1973
+ const promises = files.map(file => {
1974
+ return new Promise(async (resolve) => {
1975
+ const filePath = join(path, file);
1976
+ const absoluteFilePath = join(this.dir, filePath);
1977
+ lstat(absoluteFilePath, (err, stats) => {
1978
+ if (err) {
1979
+ resolve([]);
1980
+ return;
1981
+ }
1982
+ if (stats.isDirectory()) {
1983
+ this.readRecursive(filePath, join(folder, file), size).then(resolve);
1984
+ return;
1985
+ }
1986
+ const sharpImg = sharp(absoluteFilePath);
1987
+ sharpImg.rotate().metadata().then(() => {
1988
+ const getResultPath = (isThumb) => {
1989
+ return join(this.output, filePath.replace(/.([a-z|A-Z]+)$/gi, function (ext) {
1990
+ const suffix = isThumb ? 'thumb' : 'big';
1991
+ return `-${suffix}${ext}`;
1992
+ }));
1993
+ };
1994
+ resolve([this.galleryCache.create(folder, size, {
1995
+ getOriginal: () => {
1996
+ return new Promise((res, rej) => {
1997
+ readFile$1(absoluteFilePath, (err, data) => {
1998
+ if (err) {
1999
+ rej(err);
2000
+ return;
2001
+ }
2002
+ res(data);
2003
+ });
2004
+ });
2005
+ },
2006
+ writeResult: (isThumb, buffer) => {
2007
+ return new Promise(async (res, rej) => {
2008
+ const resultPath = getResultPath(isThumb);
2009
+ await mkdirRecursive(dirname(resultPath));
2010
+ writeFile$1(resultPath, buffer, err => {
2011
+ if (err) {
2012
+ rej(err);
2013
+ return;
2014
+ }
2015
+ res();
2016
+ });
2017
+ });
2018
+ },
2019
+ hasResult: (isThumb) => {
2020
+ return new Promise(res => {
2021
+ access(getResultPath(isThumb), constants.R_OK, err => {
2022
+ res(!err);
2023
+ });
2024
+ });
2025
+ },
2026
+ serveResult: (isThumb) => {
2027
+ return new Promise((res, rej) => {
2028
+ readFile$1(getResultPath(isThumb), (err, data) => {
2029
+ if (err) {
2030
+ rej(err);
2031
+ return;
2032
+ }
2033
+ res(data);
2034
+ });
2035
+ });
2036
+ }
2037
+ })]);
2038
+ }, () => resolve([]));
2039
+ });
2040
+ });
2041
+ });
2042
+ Promise.all(promises).then(folders => {
2043
+ resolve([].concat.apply([], folders));
2044
+ });
2045
+ });
2046
+ });
2171
2047
  }
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));
2048
+ };
2049
+ Gallery = __decorate([
2050
+ injectable(),
2051
+ scoped(Lifecycle.ContainerScoped),
2052
+ __metadata("design:paramtypes", [Configuration, GalleryCache])
2053
+ ], Gallery);
2054
+
2055
+ let IdGenerator = class IdGenerator {
2056
+ constructor(config) {
2057
+ this.config = config;
2058
+ this.prefix = config.resolve("idPrefix");
2059
+ this.separator = config.resolve("idSeparator");
2060
+ this.chars = config.resolve("idChars");
2061
+ this.parts = config.resolve("idParts");
2180
2062
  }
2181
- async setMax(max) {
2182
- if (isNaN(max) || max <= 0) {
2183
- throw "Max progress value must be bigger than zero";
2063
+ async generate(checkCb) {
2064
+ let id = null;
2065
+ let tries = 0;
2066
+ let notGood = true;
2067
+ while (notGood && tries < 5) {
2068
+ id = this.generateId();
2069
+ notGood = await checkCb(id);
2070
+ tries++;
2184
2071
  }
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";
2072
+ if (notGood) {
2073
+ throw `Couldn't generate an unique id..`;
2201
2074
  }
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;
2075
+ return id;
2220
2076
  }
2221
- toJSON() {
2222
- return this.parent.toJSON();
2077
+ generateId() {
2078
+ return this.prefix + this.parts.map(num => {
2079
+ let s = "";
2080
+ for (let i = 0; i < num; i++) {
2081
+ const ix = rand(0, this.chars.length - 1);
2082
+ s += this.chars[ix];
2083
+ }
2084
+ return s;
2085
+ }).join(this.separator);
2223
2086
  }
2224
- }
2087
+ };
2088
+ IdGenerator = __decorate([
2089
+ injectable(),
2090
+ scoped(Lifecycle.ContainerScoped),
2091
+ __metadata("design:paramtypes", [Configuration])
2092
+ ], IdGenerator);
2225
2093
 
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
- });
2094
+ let TranslationProvider = class TranslationProvider {
2095
+ constructor(config, cache) {
2096
+ this.config = config;
2097
+ this.cache = cache;
2236
2098
  }
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}`);
2099
+ getDictionary(language) {
2100
+ return this.cache.getOrSet(`translations-${language}`, async () => {
2101
+ try {
2102
+ const url = this.config.resolve("translationsTemplate")
2103
+ .replace(`__lang__`, language)
2104
+ .replace(`[lang]`, language);
2105
+ const data = await axios.get(url).then(t => t.data);
2106
+ if (isObject(data[language])) {
2107
+ return data[language];
2246
2108
  }
2247
- return progress;
2248
- }, 500),
2249
- this.waitForProgress(id, async () => {
2250
- return this.progresses[id] || null;
2251
- }, 25)
2252
- ]);
2109
+ return data;
2110
+ }
2111
+ catch (e) {
2112
+ return {
2113
+ message: `${e}`
2114
+ };
2115
+ }
2116
+ }, 5 * 60);
2253
2117
  }
2254
- async get(id) {
2255
- return !id ? null : this.find({ _id: new ObjectId$1(id) });
2118
+ };
2119
+ TranslationProvider = __decorate([
2120
+ singleton(),
2121
+ __metadata("design:paramtypes", [Configuration, Cache])
2122
+ ], TranslationProvider);
2123
+
2124
+ let Translator = class Translator {
2125
+ constructor(translationProvider) {
2126
+ this.translationProvider = translationProvider;
2127
+ this.dictionaries = {};
2256
2128
  }
2257
- async find(where) {
2258
- const data = await this.collection.findOne(where);
2259
- return !data ? null : new Progress(data._id, data, this.collection);
2129
+ async getDictionary(language) {
2130
+ this.dictionaries[language] = await this.translationProvider.getDictionary(language);
2131
+ return this.dictionaries[language];
2260
2132
  }
2261
- async create(max = 100) {
2262
- if (isNaN(max) || max <= 0) {
2263
- throw new Error(`Max progress value must be bigger than zero`);
2133
+ getTranslationSync(language, key, params) {
2134
+ if (!isString(key) || !key.length) {
2135
+ throw new Error(`Parameter "key" required`);
2264
2136
  }
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;
2137
+ const dictionary = this.dictionaries[language];
2138
+ const translation = getValue(dictionary, key, key) || key;
2139
+ return this.interpolate(translation, params);
2278
2140
  }
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
- }
2141
+ getTranslation(language, key, params) {
2142
+ if (!isString(key) || !key.length) {
2143
+ throw new Error(`Parameter "key" required`);
2298
2144
  }
2299
- return progress;
2145
+ return this.getDictionary(language).then(dictionary => {
2146
+ const translation = getValue(dictionary, key, key) || key;
2147
+ return this.interpolate(translation, params);
2148
+ });
2149
+ }
2150
+ getTranslations(language, ...keys) {
2151
+ return new Promise(resolve => {
2152
+ Promise.all(keys.map(key => this.getTranslation(language, key))).then(translations => {
2153
+ resolve(keys.reduce((result, key, i) => {
2154
+ result[key] = translations[i];
2155
+ return result;
2156
+ }, {}));
2157
+ });
2158
+ });
2159
+ }
2160
+ interpolate(expr, params) {
2161
+ if (typeof expr === "string") {
2162
+ return this.interpolateString(expr, params);
2163
+ }
2164
+ if (typeof expr === "function") {
2165
+ return expr(params);
2166
+ }
2167
+ return expr;
2168
+ }
2169
+ interpolateString(expr, params) {
2170
+ if (!expr || !params)
2171
+ return expr;
2172
+ return expr.replace(/{{\s?([^{}\s]*)\s?}}/g, (substring, b) => {
2173
+ const r = getValue(params, b);
2174
+ return isDefined(r) ? r : substring;
2175
+ });
2300
2176
  }
2301
2177
  };
2302
- Progresses = __decorate([
2303
- singleton()
2304
- ], Progresses);
2178
+ Translator = __decorate([
2179
+ injectable(),
2180
+ singleton(),
2181
+ __metadata("design:paramtypes", [TranslationProvider])
2182
+ ], Translator);
2305
2183
 
2306
2184
  let TemplateRenderer = class TemplateRenderer {
2307
2185
  constructor(translator, config) {
@@ -2325,41 +2203,276 @@ let TemplateRenderer = class TemplateRenderer {
2325
2203
  this.initPromise = this.initPromise || this.parseTemplates(this.config.resolve("templatesDir"), []);
2326
2204
  return this.initPromise;
2327
2205
  }
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;
2206
+ async parseTemplates(dir, dirPath) {
2207
+ return new Promise(resolve => {
2208
+ readdir(dir, async (err, files) => {
2209
+ for (let file of files) {
2210
+ const path = join(dir, file);
2211
+ if (lstatSync(path).isDirectory()) {
2212
+ await this.parseTemplates(join(dir, file), dirPath.concat([file]));
2213
+ continue;
2214
+ }
2215
+ const parts = file.split(".");
2216
+ parts.pop();
2217
+ const name = parts.join(".");
2218
+ const fullName = dirPath.concat([name]).join("-");
2219
+ const content = readFileSync(path).toString("utf8");
2220
+ this.templates[fullName] = Handlebars.compile(content);
2221
+ Handlebars.registerPartial(fullName, content);
2222
+ }
2223
+ resolve();
2224
+ });
2225
+ });
2226
+ }
2227
+ async render(template, language, context) {
2228
+ await this.init();
2229
+ await this.translator.getDictionary(language);
2230
+ if (!this.templates[template]) {
2231
+ return Promise.reject(`Template not found with name: ${template}`);
2232
+ }
2233
+ context = Object.assign({ language }, context || {});
2234
+ const res = this.templates[template](context);
2235
+ return res instanceof Error ? await Promise.reject(res) : res;
2236
+ }
2237
+ };
2238
+ TemplateRenderer = __decorate([
2239
+ singleton(),
2240
+ __metadata("design:paramtypes", [Translator, Configuration])
2241
+ ], TemplateRenderer);
2242
+
2243
+ let MailSender = class MailSender {
2244
+ constructor(config, renderer) {
2245
+ this.config = config;
2246
+ this.renderer = renderer;
2247
+ this.transporter = createTransport({
2248
+ host: this.config.resolve("smtpHost"),
2249
+ port: this.config.resolve("smtpPort"),
2250
+ auth: {
2251
+ user: this.config.resolve("smtpUser"),
2252
+ pass: this.config.resolve("smtpPassword"),
2253
+ }
2254
+ });
2255
+ }
2256
+ get translator() {
2257
+ return this.renderer.translator;
2258
+ }
2259
+ async sendMail(language, options) {
2260
+ const subject = await this.translator.getTranslation(language, options.subject || "-");
2261
+ const html = await this.renderer.render(options.template, language, options.context);
2262
+ return this.transporter.sendMail({
2263
+ from: options.from || this.config.resolve("mailSenderAddress"),
2264
+ to: options.to,
2265
+ attachments: options.attachments,
2266
+ subject,
2267
+ html
2268
+ });
2269
+ }
2270
+ };
2271
+ MailSender = __decorate([
2272
+ singleton(),
2273
+ __metadata("design:paramtypes", [Configuration, TemplateRenderer])
2274
+ ], MailSender);
2275
+
2276
+ let MemoryCache = class MemoryCache {
2277
+ constructor(cache) {
2278
+ this.cache = cache;
2279
+ this.cacheMap = new Map();
2280
+ }
2281
+ async set(key, value, ttl, expirationTimestamp = null, tags = {}) {
2282
+ const now = Math.round(new Date().getTime() / 1000);
2283
+ const expTimestamp = Math.min(isNaN(ttl) ? Number.MAX_SAFE_INTEGER : ttl, 3600);
2284
+ this.cacheMap.set(key, {
2285
+ _id: key,
2286
+ data: value,
2287
+ expirationTimestamp: expTimestamp,
2288
+ expiresAt: now + expTimestamp,
2289
+ });
2290
+ return this.cache.set(key, value, ttl, expirationTimestamp, tags);
2291
+ }
2292
+ async get(key) {
2293
+ let item = this.cacheMap.get(key);
2294
+ const now = Math.round(new Date().getTime() / 1000);
2295
+ let expTimestamp = 3600;
2296
+ if (item && item.expiresAt && item.expiresAt < now) {
2297
+ expTimestamp = item.expirationTimestamp;
2298
+ item = null;
2299
+ }
2300
+ if (!item) {
2301
+ const value = await this.cache.get(key);
2302
+ this.cacheMap.set(key, {
2303
+ _id: key,
2304
+ data: value,
2305
+ expirationTimestamp: expTimestamp,
2306
+ expiresAt: now + expTimestamp,
2307
+ });
2308
+ return value;
2309
+ }
2310
+ return item.data;
2311
+ }
2312
+ async getOrSet(key, valueCb, ttl, expirationTimestamp = null, tags = {}) {
2313
+ try {
2314
+ return await this.get(key);
2315
+ }
2316
+ catch (e) {
2317
+ return await this.set(key, await valueCb(), ttl, expirationTimestamp, tags);
2318
+ }
2319
+ }
2320
+ async delete(key) {
2321
+ this.cacheMap.delete(key);
2322
+ await this.cacheMap.delete(key);
2323
+ }
2324
+ };
2325
+ MemoryCache = __decorate([
2326
+ injectable(),
2327
+ scoped(Lifecycle.ContainerScoped),
2328
+ __metadata("design:paramtypes", [Cache])
2329
+ ], MemoryCache);
2330
+
2331
+ function checkValue(multi, value) {
2332
+ if (multi) {
2333
+ return Array.isArray(value) && value.every(v => {
2334
+ try {
2335
+ const id = new ObjectId$1(v);
2336
+ return id instanceof ObjectId$1;
2337
+ }
2338
+ catch (e) {
2339
+ return false;
2340
+ }
2341
+ });
2342
+ }
2343
+ if (null === value)
2344
+ return true;
2345
+ try {
2346
+ const id = new ObjectId$1(value);
2347
+ return id instanceof ObjectId$1;
2348
+ }
2349
+ catch (e) {
2350
+ return false;
2351
+ }
2352
+ }
2353
+ let IsFile = class IsFile {
2354
+ validate(value, validationArguments) {
2355
+ const [multi] = (validationArguments.constraints || []);
2356
+ return checkValue(multi, value);
2357
+ }
2358
+ };
2359
+ IsFile = __decorate([
2360
+ ValidatorConstraint()
2361
+ ], IsFile);
2362
+ let IsObjectId = class IsObjectId {
2363
+ validate(value, validationArguments) {
2364
+ const [_, multi] = (validationArguments.constraints || []);
2365
+ return checkValue(multi, value);
2366
+ }
2367
+ };
2368
+ IsObjectId = __decorate([
2369
+ ValidatorConstraint()
2370
+ ], IsObjectId);
2371
+
2372
+ let OpenApi = class OpenApi {
2373
+ constructor(container, customValidation) {
2374
+ this.container = container;
2375
+ this.customValidation = customValidation;
2376
+ this.docs = null;
2377
+ }
2378
+ get apiDocs() {
2379
+ if (!this.docs)
2380
+ this.docs = this.createApiDocs();
2381
+ return this.docs;
2382
+ }
2383
+ get apiDocsStr() {
2384
+ if (!this.docsStr)
2385
+ this.docsStr = JSON.stringify(this.apiDocs);
2386
+ return this.docsStr;
2387
+ }
2388
+ async schemaToExample(src, req) {
2389
+ if (src.$ref) {
2390
+ const schemas = this.apiDocs.components.schemas;
2391
+ const schema = src.$ref
2392
+ .replace("#/components/schemas/", "")
2393
+ .replace("#/definitions/", "");
2394
+ return this.schemaToExample(schemas[schema], req);
2395
+ }
2396
+ let schema = src;
2397
+ if (schema.oneOf) {
2398
+ schema = Object.assign({}, schema, schema.oneOf[0]);
2399
+ }
2400
+ if (schema.type === "object") {
2401
+ const result = {};
2402
+ await Promise.all(Object.keys(schema.properties).map(async (key) => {
2403
+ result[key] = await this.schemaToExample(schema.properties[key], req);
2404
+ }));
2405
+ return result;
2406
+ }
2407
+ if (schema.type === "array") {
2408
+ return [await this.schemaToExample(schema.items, req)];
2409
+ }
2410
+ if (schema.type === "string") {
2411
+ if (isDefined(schema.default)) {
2412
+ if (isFunction(schema.default)) {
2413
+ return schema.default(this.container);
2414
+ }
2415
+ return schema.default;
2416
+ }
2417
+ if (schema.format == "date") {
2418
+ return new Date().toISOString().substr(0, 10);
2419
+ }
2420
+ if (schema.format == "date-time") {
2421
+ return new Date().toISOString();
2422
+ }
2423
+ if (schema.enum) {
2424
+ return schema.enum[0];
2425
+ }
2426
+ return "string";
2427
+ }
2428
+ if (schema.type === "number") {
2429
+ return schema.default ?? 0;
2430
+ }
2431
+ else if (schema.type === "boolean") {
2432
+ return schema.default ?? false;
2433
+ }
2434
+ else {
2435
+ return schema.default ?? null;
2436
+ }
2437
+ }
2438
+ createApiDocs() {
2439
+ const storage = getMetadataArgsStorage();
2440
+ const docs = routingControllersToSpec(storage);
2441
+ docs.basePath = "/api/";
2442
+ docs.definitions = validationMetadatasToSchemas({
2443
+ additionalConverters: {
2444
+ [ValidationTypes.CUSTOM_VALIDATION]: (meta, options) => {
2445
+ const res = isFunction(this.customValidation) ? this.customValidation(meta, options) : this.customValidation;
2446
+ if (isObject(res))
2447
+ return res;
2448
+ const constraints = meta.constraints || [];
2449
+ if (meta.constraintCls === IsFile) {
2450
+ return {
2451
+ multi: constraints[0] || false,
2452
+ type: "file"
2453
+ };
2336
2454
  }
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);
2455
+ if (meta.constraintCls === IsObjectId) {
2456
+ return {
2457
+ endpoint: constraints[0] || false,
2458
+ multi: constraints[1] || false,
2459
+ type: "list"
2460
+ };
2461
+ }
2462
+ return null;
2344
2463
  }
2345
- resolve();
2346
- });
2464
+ }
2347
2465
  });
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;
2466
+ docs.components.schemas = docs.definitions;
2467
+ return docs;
2358
2468
  }
2359
2469
  };
2360
- TemplateRenderer = __decorate([
2361
- singleton()
2362
- ], TemplateRenderer);
2470
+ OpenApi = __decorate([
2471
+ singleton(),
2472
+ __param(0, inject(DI_CONTAINER)),
2473
+ __param(1, inject(OPENAPI_VALIDATION)),
2474
+ __metadata("design:paramtypes", [Object, Object])
2475
+ ], OpenApi);
2363
2476
 
2364
2477
  let TerminalManager = class TerminalManager {
2365
2478
  constructor(logger, config, commands) {
@@ -2429,7 +2542,9 @@ let TerminalManager = class TerminalManager {
2429
2542
  };
2430
2543
  TerminalManager = __decorate([
2431
2544
  singleton(),
2432
- __param(2, injectAll(TERMINAL_COMMAND))
2545
+ __param(2, injectAll(TERMINAL_COMMAND)),
2546
+ __metadata("design:paramtypes", [Logger,
2547
+ Configuration, Array])
2433
2548
  ], TerminalManager);
2434
2549
 
2435
2550
  let TokenGenerator = class TokenGenerator {
@@ -2460,97 +2575,10 @@ let TokenGenerator = class TokenGenerator {
2460
2575
  }
2461
2576
  };
2462
2577
  TokenGenerator = __decorate([
2463
- singleton()
2578
+ singleton(),
2579
+ __metadata("design:paramtypes", [])
2464
2580
  ], TokenGenerator);
2465
2581
 
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
2582
  const sampleUser = {
2555
2583
  id: "5a3cdf7c6a9cf0ba32feccdf",
2556
2584
  email: "admin@site.com",
@@ -2575,6 +2603,66 @@ UserManager = __decorate([
2575
2603
  scoped(Lifecycle.ContainerScoped)
2576
2604
  ], UserManager);
2577
2605
 
2606
+ class AssetImageParams {
2607
+ constructor() {
2608
+ this.rotation = 0;
2609
+ this.canvasScaleX = 1;
2610
+ this.canvasScaleY = 1;
2611
+ this.scaleX = 1;
2612
+ this.scaleY = 1;
2613
+ this.lazy = false;
2614
+ this.crop = false;
2615
+ this.cropBefore = false;
2616
+ this.cropAfter = false;
2617
+ }
2618
+ }
2619
+ __decorate([
2620
+ Min(-360),
2621
+ Max(360),
2622
+ IsOptional(),
2623
+ __metadata("design:type", Number)
2624
+ ], AssetImageParams.prototype, "rotation", void 0);
2625
+ __decorate([
2626
+ Min(0.0001),
2627
+ IsOptional(),
2628
+ __metadata("design:type", Number)
2629
+ ], AssetImageParams.prototype, "canvasScaleX", void 0);
2630
+ __decorate([
2631
+ Min(0.0001),
2632
+ IsOptional(),
2633
+ __metadata("design:type", Number)
2634
+ ], AssetImageParams.prototype, "canvasScaleY", void 0);
2635
+ __decorate([
2636
+ Min(0.0001),
2637
+ IsOptional(),
2638
+ __metadata("design:type", Number)
2639
+ ], AssetImageParams.prototype, "scaleX", void 0);
2640
+ __decorate([
2641
+ Min(0.0001),
2642
+ IsOptional(),
2643
+ __metadata("design:type", Number)
2644
+ ], AssetImageParams.prototype, "scaleY", void 0);
2645
+ __decorate([
2646
+ IsBoolean(),
2647
+ IsOptional(),
2648
+ __metadata("design:type", Boolean)
2649
+ ], AssetImageParams.prototype, "lazy", void 0);
2650
+ __decorate([
2651
+ IsBoolean(),
2652
+ IsOptional(),
2653
+ __metadata("design:type", Boolean)
2654
+ ], AssetImageParams.prototype, "crop", void 0);
2655
+ __decorate([
2656
+ IsBoolean(),
2657
+ IsOptional(),
2658
+ __metadata("design:type", Boolean)
2659
+ ], AssetImageParams.prototype, "cropBefore", void 0);
2660
+ __decorate([
2661
+ IsBoolean(),
2662
+ IsOptional(),
2663
+ __metadata("design:type", Boolean)
2664
+ ], AssetImageParams.prototype, "cropAfter", void 0);
2665
+
2578
2666
  let AssetsController = class AssetsController {
2579
2667
  constructor(assets, assetResolver) {
2580
2668
  this.assets = assets;
@@ -2655,46 +2743,68 @@ let AssetsController = class AssetsController {
2655
2743
  __decorate([
2656
2744
  Authorized(),
2657
2745
  Post(""),
2658
- __param(0, UploadedFile("file"))
2746
+ __param(0, UploadedFile("file")),
2747
+ __metadata("design:type", Function),
2748
+ __metadata("design:paramtypes", [Object]),
2749
+ __metadata("design:returntype", Promise)
2659
2750
  ], AssetsController.prototype, "upload", null);
2660
2751
  __decorate([
2661
2752
  Authorized(),
2662
2753
  Post("url"),
2663
- __param(0, Body())
2754
+ __param(0, Body()),
2755
+ __metadata("design:type", Function),
2756
+ __metadata("design:paramtypes", [Object]),
2757
+ __metadata("design:returntype", Promise)
2664
2758
  ], AssetsController.prototype, "uploadUrl", null);
2665
2759
  __decorate([
2666
2760
  Get("/:id"),
2667
2761
  __param(0, Param("id")),
2668
2762
  __param(1, QueryParam("lazy")),
2669
- __param(2, Res())
2763
+ __param(2, Res()),
2764
+ __metadata("design:type", Function),
2765
+ __metadata("design:paramtypes", [String, Boolean, Object]),
2766
+ __metadata("design:returntype", Promise)
2670
2767
  ], AssetsController.prototype, "getFile", null);
2671
2768
  __decorate([
2672
2769
  Get("/image/:id/:rotation"),
2673
2770
  __param(0, Param("id")),
2674
2771
  __param(1, QueryParams()),
2675
2772
  __param(2, Res()),
2676
- __param(3, Param("rotation"))
2773
+ __param(3, Param("rotation")),
2774
+ __metadata("design:type", Function),
2775
+ __metadata("design:paramtypes", [String, AssetImageParams, Object, Number]),
2776
+ __metadata("design:returntype", Promise)
2677
2777
  ], AssetsController.prototype, "getImageRotation", null);
2678
2778
  __decorate([
2679
2779
  Get("/image/:id"),
2680
2780
  __param(0, Param("id")),
2681
2781
  __param(1, QueryParams()),
2682
- __param(2, Res())
2782
+ __param(2, Res()),
2783
+ __metadata("design:type", Function),
2784
+ __metadata("design:paramtypes", [String, AssetImageParams, Object]),
2785
+ __metadata("design:returntype", Promise)
2683
2786
  ], AssetsController.prototype, "getImage", null);
2684
2787
  __decorate([
2685
2788
  Get("/by-name/:name"),
2686
2789
  __param(0, Param("name")),
2687
- __param(1, Res())
2790
+ __param(1, Res()),
2791
+ __metadata("design:type", Function),
2792
+ __metadata("design:paramtypes", [String, Object]),
2793
+ __metadata("design:returntype", Promise)
2688
2794
  ], AssetsController.prototype, "getFileByName", null);
2689
2795
  __decorate([
2690
2796
  Get("/by-name/image/:name"),
2691
2797
  __param(0, Param("name")),
2692
2798
  __param(1, QueryParams()),
2693
- __param(2, Res())
2799
+ __param(2, Res()),
2800
+ __metadata("design:type", Function),
2801
+ __metadata("design:paramtypes", [String, AssetImageParams, Object]),
2802
+ __metadata("design:returntype", Promise)
2694
2803
  ], AssetsController.prototype, "getImageByName", null);
2695
2804
  AssetsController = __decorate([
2696
2805
  injectable(),
2697
- Controller("/assets")
2806
+ Controller("/assets"),
2807
+ __metadata("design:paramtypes", [Assets, AssetResolver])
2698
2808
  ], AssetsController);
2699
2809
 
2700
2810
  let AuthController = class AuthController {
@@ -2725,16 +2835,23 @@ let AuthController = class AuthController {
2725
2835
  __decorate([
2726
2836
  Post("/login"),
2727
2837
  __param(0, Body()),
2728
- __param(1, Res())
2838
+ __param(1, Res()),
2839
+ __metadata("design:type", Function),
2840
+ __metadata("design:paramtypes", [Object, Object]),
2841
+ __metadata("design:returntype", Promise)
2729
2842
  ], AuthController.prototype, "login", null);
2730
2843
  __decorate([
2731
2844
  Authorized(),
2732
2845
  Get("/user"),
2733
- __param(0, CurrentUser())
2846
+ __param(0, CurrentUser()),
2847
+ __metadata("design:type", Function),
2848
+ __metadata("design:paramtypes", [Object]),
2849
+ __metadata("design:returntype", void 0)
2734
2850
  ], AuthController.prototype, "getProfile", null);
2735
2851
  AuthController = __decorate([
2736
2852
  injectable(),
2737
- Controller()
2853
+ Controller(),
2854
+ __metadata("design:paramtypes", [Configuration, UserManager])
2738
2855
  ], AuthController);
2739
2856
 
2740
2857
  let GalleryController = class GalleryController {
@@ -2747,11 +2864,15 @@ let GalleryController = class GalleryController {
2747
2864
  };
2748
2865
  __decorate([
2749
2866
  Get("/:id"),
2750
- __param(0, Param("id"))
2867
+ __param(0, Param("id")),
2868
+ __metadata("design:type", Function),
2869
+ __metadata("design:paramtypes", [String]),
2870
+ __metadata("design:returntype", void 0)
2751
2871
  ], GalleryController.prototype, "getFile", null);
2752
2872
  GalleryController = __decorate([
2753
2873
  injectable(),
2754
- Controller("/gallery")
2874
+ Controller("/gallery"),
2875
+ __metadata("design:paramtypes", [GalleryCache])
2755
2876
  ], GalleryController);
2756
2877
 
2757
2878
  let ProgressesController = class ProgressesController {
@@ -2772,11 +2893,15 @@ let ProgressesController = class ProgressesController {
2772
2893
  };
2773
2894
  __decorate([
2774
2895
  Get("/:id"),
2775
- __param(0, Param("id"))
2896
+ __param(0, Param("id")),
2897
+ __metadata("design:type", Function),
2898
+ __metadata("design:paramtypes", [String]),
2899
+ __metadata("design:returntype", Promise)
2776
2900
  ], ProgressesController.prototype, "getProgress", null);
2777
2901
  ProgressesController = __decorate([
2778
2902
  injectable(),
2779
- Controller("/progresses")
2903
+ Controller("/progresses"),
2904
+ __metadata("design:paramtypes", [Progresses, Configuration])
2780
2905
  ], ProgressesController);
2781
2906
 
2782
2907
  // Add a comment hint for webstorm to style the string as css
@@ -2951,19 +3076,29 @@ let TerminalController$1 = class TerminalController {
2951
3076
  };
2952
3077
  __decorate([
2953
3078
  Get("/terminal"),
2954
- Header("Content-Type", "text/html")
3079
+ Header("Content-Type", "text/html"),
3080
+ __metadata("design:type", Function),
3081
+ __metadata("design:paramtypes", []),
3082
+ __metadata("design:returntype", void 0)
2955
3083
  ], TerminalController$1.prototype, "terminal", null);
2956
3084
  __decorate([
2957
3085
  Get("/console"),
2958
- Header("Content-Type", "text/html")
3086
+ Header("Content-Type", "text/html"),
3087
+ __metadata("design:type", Function),
3088
+ __metadata("design:paramtypes", []),
3089
+ __metadata("design:returntype", void 0)
2959
3090
  ], TerminalController$1.prototype, "console", null);
2960
3091
  __decorate([
2961
3092
  Get(),
2962
- Header("Content-Type", "text/html")
3093
+ Header("Content-Type", "text/html"),
3094
+ __metadata("design:type", Function),
3095
+ __metadata("design:paramtypes", [String]),
3096
+ __metadata("design:returntype", String)
2963
3097
  ], TerminalController$1.prototype, "generateClient", null);
2964
3098
  TerminalController$1 = __decorate([
2965
3099
  injectable(),
2966
- Controller()
3100
+ Controller(),
3101
+ __metadata("design:paramtypes", [Configuration])
2967
3102
  ], TerminalController$1);
2968
3103
 
2969
3104
  let ErrorHandlerMiddleware = class ErrorHandlerMiddleware {
@@ -3032,7 +3167,8 @@ let ErrorHandlerMiddleware = class ErrorHandlerMiddleware {
3032
3167
  };
3033
3168
  ErrorHandlerMiddleware = __decorate([
3034
3169
  injectable(),
3035
- Middleware({ type: "after" })
3170
+ Middleware({ type: "after" }),
3171
+ __metadata("design:paramtypes", [Configuration, Translator])
3036
3172
  ], ErrorHandlerMiddleware);
3037
3173
 
3038
3174
  let ContainerMiddleware = class ContainerMiddleware {
@@ -3047,7 +3183,8 @@ let ContainerMiddleware = class ContainerMiddleware {
3047
3183
  ContainerMiddleware = __decorate([
3048
3184
  injectable(),
3049
3185
  Middleware({ type: "before" }),
3050
- __param(0, inject(DI_CONTAINER))
3186
+ __param(0, inject(DI_CONTAINER)),
3187
+ __metadata("design:paramtypes", [Object])
3051
3188
  ], ContainerMiddleware);
3052
3189
 
3053
3190
  let LanguageMiddleware = class LanguageMiddleware {
@@ -3061,7 +3198,8 @@ let LanguageMiddleware = class LanguageMiddleware {
3061
3198
  };
3062
3199
  LanguageMiddleware = __decorate([
3063
3200
  injectable(),
3064
- Middleware({ type: "before" })
3201
+ Middleware({ type: "before" }),
3202
+ __metadata("design:paramtypes", [Configuration])
3065
3203
  ], LanguageMiddleware);
3066
3204
 
3067
3205
  let RequestEndedMiddleware = class RequestEndedMiddleware {
@@ -3079,7 +3217,8 @@ let RequestEndedMiddleware = class RequestEndedMiddleware {
3079
3217
  };
3080
3218
  RequestEndedMiddleware = __decorate([
3081
3219
  injectable(),
3082
- Middleware({ type: "after" })
3220
+ Middleware({ type: "after" }),
3221
+ __metadata("design:paramtypes", [Logger])
3083
3222
  ], RequestEndedMiddleware);
3084
3223
 
3085
3224
  let RequestStartedMiddleware = class RequestStartedMiddleware {
@@ -3095,7 +3234,8 @@ let RequestStartedMiddleware = class RequestStartedMiddleware {
3095
3234
  };
3096
3235
  RequestStartedMiddleware = __decorate([
3097
3236
  injectable(),
3098
- Middleware({ type: "before" })
3237
+ Middleware({ type: "before" }),
3238
+ __metadata("design:paramtypes", [Logger])
3099
3239
  ], RequestStartedMiddleware);
3100
3240
 
3101
3241
  let ProgressController = class ProgressController {
@@ -3131,17 +3271,24 @@ let ProgressController = class ProgressController {
3131
3271
  __decorate([
3132
3272
  OnMessage("background-progress"),
3133
3273
  __param(0, ConnectedSocket()),
3134
- __param(1, MessageBody())
3274
+ __param(1, MessageBody()),
3275
+ __metadata("design:type", Function),
3276
+ __metadata("design:paramtypes", [Object, String]),
3277
+ __metadata("design:returntype", Promise)
3135
3278
  ], ProgressController.prototype, "advanceProgress", null);
3136
3279
  __decorate([
3137
3280
  OnMessage("background-progress-interest"),
3138
3281
  __param(0, ConnectedSocket()),
3139
- __param(1, MessageBody())
3282
+ __param(1, MessageBody()),
3283
+ __metadata("design:type", Function),
3284
+ __metadata("design:paramtypes", [Object, String]),
3285
+ __metadata("design:returntype", Promise)
3140
3286
  ], ProgressController.prototype, "setProgressInterest", null);
3141
3287
  ProgressController = __decorate([
3142
3288
  singleton(),
3143
3289
  SocketController(),
3144
- __param(1, inject(SOCKET_SERVER))
3290
+ __param(1, inject(SOCKET_SERVER)),
3291
+ __metadata("design:paramtypes", [Progresses, Server])
3145
3292
  ], ProgressController);
3146
3293
 
3147
3294
  class Terminal {
@@ -3252,21 +3399,31 @@ let TerminalController = class TerminalController {
3252
3399
  };
3253
3400
  __decorate([
3254
3401
  OnMessage("terminal-init"),
3255
- __param(0, ConnectedSocket())
3402
+ __param(0, ConnectedSocket()),
3403
+ __metadata("design:type", Function),
3404
+ __metadata("design:paramtypes", [Object]),
3405
+ __metadata("design:returntype", Promise)
3256
3406
  ], TerminalController.prototype, "terminalInit", null);
3257
3407
  __decorate([
3258
3408
  OnMessage("terminal-data"),
3259
3409
  __param(0, ConnectedSocket()),
3260
- __param(1, MessageBody())
3410
+ __param(1, MessageBody()),
3411
+ __metadata("design:type", Function),
3412
+ __metadata("design:paramtypes", [Object, String]),
3413
+ __metadata("design:returntype", Promise)
3261
3414
  ], TerminalController.prototype, "terminalData", null);
3262
3415
  __decorate([
3263
3416
  OnMessage("terminal-upload"),
3264
3417
  __param(0, ConnectedSocket()),
3265
- __param(1, MessageBody())
3418
+ __param(1, MessageBody()),
3419
+ __metadata("design:type", Function),
3420
+ __metadata("design:paramtypes", [Object, Object]),
3421
+ __metadata("design:returntype", Promise)
3266
3422
  ], TerminalController.prototype, "terminalUpload", null);
3267
3423
  TerminalController = __decorate([
3268
3424
  singleton(),
3269
- SocketController()
3425
+ SocketController(),
3426
+ __metadata("design:paramtypes", [TerminalManager])
3270
3427
  ], TerminalController);
3271
3428
 
3272
3429
  let CompressionMiddleware = class CompressionMiddleware {
@@ -3529,7 +3686,8 @@ let FixturesCommand = class FixturesCommand {
3529
3686
  };
3530
3687
  FixturesCommand = __decorate([
3531
3688
  injectable(),
3532
- scoped(Lifecycle.ContainerScoped)
3689
+ scoped(Lifecycle.ContainerScoped),
3690
+ __metadata("design:paramtypes", [Fixtures])
3533
3691
  ], FixturesCommand);
3534
3692
 
3535
3693
  const commands = [
@@ -3537,57 +3695,6 @@ const commands = [
3537
3695
  FixturesCommand
3538
3696
  ];
3539
3697
 
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
3698
  class BaseDoc {
3592
3699
  /**
3593
3700
  * Casts this to DocumentType<this> to allow using document methods in get/set-s