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