@stemy/backend 5.1.1 → 5.2.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.
@@ -8,15 +8,15 @@ import fontKit_ from 'fontkit';
8
8
  import sharp_ from 'sharp';
9
9
  import { ObjectId as ObjectId$1 } from 'bson';
10
10
  import axios from 'axios';
11
- import { mkdir, unlink, readFile as readFile$1, writeFile as writeFile$1, lstat, readdir, access, constants, lstatSync, readFileSync, existsSync } from 'fs';
11
+ import { mkdir, unlink, readFile as readFile$1, writeFile as writeFile$1, lstat, readdir, access, constants, lstatSync, readFileSync, existsSync, mkdirSync, createWriteStream, createReadStream } from 'fs';
12
12
  import { gzip, gunzip } from 'zlib';
13
13
  import { fileURLToPath } from 'url';
14
14
  import { exec } from 'child_process';
15
15
  import { createHash } from 'crypto';
16
16
  import { Subscription, Observable, Subject, from, BehaviorSubject } from 'rxjs';
17
17
  import { canReportError } from 'rxjs/internal/util/canReportError';
18
- import { ObjectId, GridFSBucket } from 'mongodb';
19
- import mongoose from 'mongoose';
18
+ import { ObjectId } from 'mongodb';
19
+ import mongoose, { Types } from 'mongoose';
20
20
  import { Readable, PassThrough } from 'stream';
21
21
  import fileType from 'file-type/core';
22
22
  import dotenv from 'dotenv';
@@ -37,6 +37,8 @@ import * as Handlebars from 'handlebars';
37
37
  import { CommandsAddon, AnsiCodes } from '@stemy/terminal-commands-addon';
38
38
  import { compare } from 'bcrypt';
39
39
  import moment from 'moment';
40
+ import { GridFSBucket } from 'mongodb/lib/gridfs';
41
+ import { writeFile as writeFile$2, rm } from 'fs/promises';
40
42
  import { getModelForClass } from '@typegoose/typegoose';
41
43
  import { getValue as getValue$1, setValue } from 'mongoose/lib/utils';
42
44
 
@@ -53,6 +55,8 @@ const SOCKET_CONTROLLERS = Symbol.for("socket-controllers-token");
53
55
  const PARAMETER = Symbol.for("parameter-token");
54
56
  const DI_CONTAINER = Symbol.for("di-container-token");
55
57
  const OPENAPI_VALIDATION = Symbol.for("openapi-validation-token");
58
+ const LOCAL_DIR = Symbol.for('asset-local-dir');
59
+ const ASSET_DRIVER = Symbol.for('assets-driver');
56
60
  class Parameter {
57
61
  constructor(name, defaultValue, resolver = null) {
58
62
  this.name = name;
@@ -947,14 +951,10 @@ let MongoConnector = class MongoConnector {
947
951
  get database() {
948
952
  return this.db;
949
953
  }
950
- get bucket() {
951
- return this.fsBucket;
952
- }
953
954
  constructor(configuration) {
954
955
  this.configuration = configuration;
955
956
  this.conn = null;
956
957
  this.db = null;
957
- this.fsBucket = null;
958
958
  }
959
959
  connect() {
960
960
  return __awaiter(this, void 0, void 0, function* () {
@@ -966,7 +966,6 @@ let MongoConnector = class MongoConnector {
966
966
  pass: this.configuration.resolve("mongoPassword")
967
967
  })).connection;
968
968
  this.db = this.conn.db;
969
- this.fsBucket = new GridFSBucket(this.db, { bucketName: "assets" });
970
969
  });
971
970
  }
972
971
  };
@@ -977,19 +976,19 @@ MongoConnector = __decorate([
977
976
 
978
977
  class BaseEntity {
979
978
  get id() {
980
- return this.mId.toHexString();
979
+ return this.oid.toHexString();
981
980
  }
982
- constructor(mId, data, collection) {
983
- this.mId = mId;
981
+ constructor(oid, data, collection) {
982
+ this.oid = oid;
984
983
  this.data = data;
985
984
  this.collection = collection;
986
985
  }
987
986
  save() {
988
- return this.collection.updateOne({ _id: this.mId }, { $set: this.toJSON() });
987
+ return this.collection.updateOne({ _id: this.oid }, { $set: this.toJSON() }, { upsert: true });
989
988
  }
990
989
  load() {
991
990
  return __awaiter(this, void 0, void 0, function* () {
992
- const res = yield this.collection.findOne({ _id: this.mId });
991
+ const res = yield this.collection.findOne({ _id: this.oid });
993
992
  this.deleted = !res;
994
993
  this.data = res || {};
995
994
  return this;
@@ -1016,21 +1015,34 @@ class Asset extends BaseEntity {
1016
1015
  return this.data.metadata;
1017
1016
  }
1018
1017
  get stream() {
1019
- return this.bucket.openDownloadStream(this.mId);
1018
+ return this.driver.openDownloadStream(this.oid);
1020
1019
  }
1021
- constructor(id, data, collection, bucket) {
1020
+ constructor(id, data, collection, driver) {
1022
1021
  super(id, data, collection);
1023
- this.bucket = bucket;
1022
+ this.driver = driver;
1024
1023
  }
1025
1024
  unlink() {
1026
1025
  return __awaiter(this, void 0, void 0, function* () {
1027
- return deleteFromBucket(this.bucket, this.mId);
1026
+ try {
1027
+ yield this.driver.delete(this.oid);
1028
+ yield this.collection.deleteOne({ _id: this.oid });
1029
+ }
1030
+ catch (error) {
1031
+ let err = error;
1032
+ if (error) {
1033
+ err = error.message || error || "";
1034
+ if (!isString(err) || !err.startsWith("FileNotFound")) {
1035
+ throw err;
1036
+ }
1037
+ }
1038
+ }
1039
+ return this.id;
1028
1040
  });
1029
1041
  }
1030
1042
  setMeta(metadata) {
1031
1043
  return __awaiter(this, void 0, void 0, function* () {
1032
1044
  metadata = Object.assign(this.metadata, metadata || {});
1033
- yield this.collection.updateOne({ _id: this.mId }, { $set: { metadata } });
1045
+ yield this.collection.updateOne({ _id: this.oid }, { $set: { metadata } });
1034
1046
  });
1035
1047
  }
1036
1048
  getBuffer() {
@@ -1044,7 +1056,7 @@ class Asset extends BaseEntity {
1044
1056
  : metadata.downloadCount + 1;
1045
1057
  metadata.firstDownload = metadata.firstDownload || new Date();
1046
1058
  metadata.lastDownload = new Date();
1047
- yield this.collection.updateOne({ _id: this.mId }, { $set: { metadata } });
1059
+ yield this.collection.updateOne({ _id: this.oid }, { $set: { metadata } });
1048
1060
  return this.stream;
1049
1061
  });
1050
1062
  }
@@ -1119,12 +1131,12 @@ class TempAsset {
1119
1131
  }
1120
1132
 
1121
1133
  let Assets = class Assets {
1122
- constructor(connector, assetProcessor) {
1134
+ constructor(connector, assetProcessor, driver) {
1123
1135
  var _a;
1124
1136
  this.connector = connector;
1125
1137
  this.assetProcessor = assetProcessor;
1126
- this.bucket = connector.bucket;
1127
- this.collection = (_a = connector.database) === null || _a === void 0 ? void 0 : _a.collection("assets.files");
1138
+ this.driver = driver;
1139
+ this.collection = (_a = connector.database) === null || _a === void 0 ? void 0 : _a.collection(driver.metaCollection);
1128
1140
  }
1129
1141
  write(stream, contentType = null, metadata = null) {
1130
1142
  return __awaiter(this, void 0, void 0, function* () {
@@ -1204,7 +1216,7 @@ let Assets = class Assets {
1204
1216
  find(where) {
1205
1217
  return __awaiter(this, void 0, void 0, function* () {
1206
1218
  const data = yield this.collection.findOne(where);
1207
- return !data ? null : new Asset(data._id, data, this.collection, this.bucket);
1219
+ return !data ? null : new Asset(data._id, data, this.collection, this.driver);
1208
1220
  });
1209
1221
  }
1210
1222
  findMany(where) {
@@ -1215,7 +1227,7 @@ let Assets = class Assets {
1215
1227
  for (let item of items) {
1216
1228
  if (!item)
1217
1229
  continue;
1218
- result.push(new Asset(item._id, item, this.collection, this.bucket));
1230
+ result.push(new Asset(item._id, item, this.collection, this.driver));
1219
1231
  }
1220
1232
  return result;
1221
1233
  });
@@ -1245,7 +1257,11 @@ let Assets = class Assets {
1245
1257
  metadata.filename = metadata.filename || new ObjectId$1().toHexString();
1246
1258
  metadata.extension = (fileType.ext || "").trim();
1247
1259
  return new Promise(((resolve, reject) => {
1248
- const uploaderStream = this.bucket.openUploadStream(metadata.filename);
1260
+ const uploaderStream = this.driver.openUploadStream(metadata.filename, {
1261
+ chunkSizeBytes: 1048576,
1262
+ metadata,
1263
+ contentType: fileType.mime
1264
+ });
1249
1265
  stream.pipe(uploaderStream)
1250
1266
  .on("error", error => {
1251
1267
  reject(error.message || error);
@@ -1255,7 +1271,7 @@ let Assets = class Assets {
1255
1271
  filename: metadata.filename,
1256
1272
  contentType,
1257
1273
  metadata
1258
- }, this.collection, this.bucket);
1274
+ }, this.collection, this.driver);
1259
1275
  asset.save().then(() => {
1260
1276
  resolve(asset);
1261
1277
  }, error => {
@@ -1269,7 +1285,9 @@ let Assets = class Assets {
1269
1285
  Assets = __decorate([
1270
1286
  injectable(),
1271
1287
  scoped(Lifecycle.ContainerScoped),
1272
- __metadata("design:paramtypes", [MongoConnector, AssetProcessor])
1288
+ __param(2, inject(ASSET_DRIVER)),
1289
+ __metadata("design:paramtypes", [MongoConnector,
1290
+ AssetProcessor, Object])
1273
1291
  ], Assets);
1274
1292
 
1275
1293
  class LazyAsset extends BaseEntity {
@@ -1304,9 +1322,9 @@ class LazyAsset extends BaseEntity {
1304
1322
  return __awaiter(this, void 0, void 0, function* () {
1305
1323
  yield this.load();
1306
1324
  if (!this.progressId) {
1307
- yield this.collection.deleteOne({ _id: this.mId });
1325
+ yield this.collection.deleteOne({ _id: this.oid });
1308
1326
  }
1309
- return deleteFromBucket(this.assets.bucket, new ObjectId$1(this.assetId));
1327
+ return this.assets.unlink(this.assetId);
1310
1328
  });
1311
1329
  }
1312
1330
  startWorking() {
@@ -1355,7 +1373,7 @@ class LazyAsset extends BaseEntity {
1355
1373
  this.data.progressId = (yield this.progresses.create()).id;
1356
1374
  this.data.assetId = null;
1357
1375
  yield this.save();
1358
- yield deleteFromBucket(this.assets.bucket, oldAsset);
1376
+ yield this.assets.unlink(oldAsset);
1359
1377
  const jobParams = JSON.parse(yield gunzipPromised(this.data.jobParams));
1360
1378
  yield this.progresses.jobMan.enqueueWithName(this.data.jobName, Object.assign(Object.assign({}, jobParams), { lazyId: this.id, fromLoad }));
1361
1379
  });
@@ -3028,12 +3046,9 @@ let AssetsController = class AssetsController {
3028
3046
  return asset.downloadImage(params);
3029
3047
  });
3030
3048
  }
3031
- setAssetHeaders(type, asset, res) {
3032
- var _a, _b;
3033
- if ((_a = asset.metadata) === null || _a === void 0 ? void 0 : _a.classified) {
3034
- throw new HttpError(403, `${type} is classified, and can be only downloaded from a custom url.`);
3035
- }
3036
- const ext = (_b = asset.metadata) === null || _b === void 0 ? void 0 : _b.extension;
3049
+ setAssetHeaders(asset, res) {
3050
+ var _a;
3051
+ const ext = (_a = asset.metadata) === null || _a === void 0 ? void 0 : _a.extension;
3037
3052
  if (ext) {
3038
3053
  res.header("content-disposition", `inline; filename=${asset.filename}.${ext}`);
3039
3054
  }
@@ -3043,21 +3058,35 @@ let AssetsController = class AssetsController {
3043
3058
  }
3044
3059
  getAsset(type, id, lazy, res) {
3045
3060
  return __awaiter(this, void 0, void 0, function* () {
3046
- const asset = yield this.assetResolver.resolve(id, lazy);
3061
+ let asset = yield this.assetResolver.resolve(id, lazy);
3047
3062
  if (!asset) {
3048
3063
  throw new HttpError(404, `${type} with id: '${id}' not found.`);
3049
3064
  }
3050
- this.setAssetHeaders(type, asset, res);
3065
+ asset = yield this.resolveFinalAsset(type, asset);
3066
+ this.setAssetHeaders(asset, res);
3051
3067
  return asset;
3052
3068
  });
3053
3069
  }
3054
3070
  getAssetByName(type, filename, res) {
3055
3071
  return __awaiter(this, void 0, void 0, function* () {
3056
- const asset = yield this.assets.find({ filename });
3072
+ let asset = yield this.assets.find({ filename });
3057
3073
  if (!asset) {
3058
3074
  throw new HttpError(404, `${type} with filename: '${filename}' not found.`);
3059
3075
  }
3060
- this.setAssetHeaders(type, asset, res);
3076
+ asset = yield this.resolveFinalAsset(type, asset);
3077
+ this.setAssetHeaders(asset, res);
3078
+ return asset;
3079
+ });
3080
+ }
3081
+ resolveFinalAsset(type, asset) {
3082
+ var _a;
3083
+ return __awaiter(this, void 0, void 0, function* () {
3084
+ if ((_a = asset.metadata) === null || _a === void 0 ? void 0 : _a.classified) {
3085
+ throw new HttpError(403, `${type} is classified, and can be only downloaded from a custom url.`);
3086
+ }
3087
+ if (type == 'Image' && asset.metadata.preview) {
3088
+ return this.resolveFinalAsset(type, yield this.assetResolver.resolve(asset.metadata.preview));
3089
+ }
3061
3090
  return asset;
3062
3091
  });
3063
3092
  }
@@ -4075,6 +4104,58 @@ const fixtures = [
4075
4104
  TtlFixture,
4076
4105
  ];
4077
4106
 
4107
+ let AssetGridDriver = class AssetGridDriver {
4108
+ constructor(connector) {
4109
+ this.bucket = new GridFSBucket(connector.database, { bucketName: 'assets' });
4110
+ this.metaCollection = "assets.files";
4111
+ }
4112
+ openUploadStream(filename, opts) {
4113
+ return this.bucket.openUploadStream(filename, opts);
4114
+ }
4115
+ openDownloadStream(id) {
4116
+ return this.bucket.openDownloadStream(id);
4117
+ }
4118
+ delete(id) {
4119
+ return this.bucket.delete(id);
4120
+ }
4121
+ };
4122
+ AssetGridDriver = __decorate([
4123
+ injectable(),
4124
+ __metadata("design:paramtypes", [MongoConnector])
4125
+ ], AssetGridDriver);
4126
+
4127
+ let AssetLocalDriver = class AssetLocalDriver {
4128
+ constructor(dir) {
4129
+ this.dir = dir;
4130
+ this.metaCollection = "assets.local";
4131
+ }
4132
+ openUploadStream(filename, opts) {
4133
+ const id = new Types.ObjectId();
4134
+ const dir = `${this.dir}/${id.toHexString()}`;
4135
+ mkdirSync(dir, { recursive: true });
4136
+ const stream = createWriteStream(`${dir}/file.bin`);
4137
+ stream.id = id;
4138
+ stream.done = false;
4139
+ stream.on('finish', () => {
4140
+ writeFile$2(`${dir}/filename.txt`, filename);
4141
+ writeFile$2(`${dir}/metadata.json`, JSON.stringify((opts === null || opts === void 0 ? void 0 : opts.metadata) || {}));
4142
+ stream.done = true;
4143
+ });
4144
+ return stream;
4145
+ }
4146
+ openDownloadStream(id) {
4147
+ return createReadStream(`${this.dir}/${id.toHexString()}/file.bin`, { autoClose: true, emitClose: true });
4148
+ }
4149
+ delete(id) {
4150
+ return rm(`${this.dir}/${id.toHexString()}`, { recursive: true, force: true });
4151
+ }
4152
+ };
4153
+ AssetLocalDriver = __decorate([
4154
+ injectable(),
4155
+ __param(0, inject(LOCAL_DIR)),
4156
+ __metadata("design:paramtypes", [String])
4157
+ ], AssetLocalDriver);
4158
+
4078
4159
  class BaseDoc {
4079
4160
  /**
4080
4161
  * Casts this to DocumentType<this> to allow using document methods in get/set-s
@@ -4605,6 +4686,12 @@ function setupBackend(config, providers, parent) {
4605
4686
  diContainer.register(OPENAPI_VALIDATION, {
4606
4687
  useValue: config.customValidation || (() => null)
4607
4688
  });
4689
+ diContainer.register(LOCAL_DIR, {
4690
+ useValue: config.assetLocalDir || "assets_files"
4691
+ });
4692
+ diContainer.register(ASSET_DRIVER, {
4693
+ useClass: config.assetDriver || AssetGridDriver
4694
+ });
4608
4695
  diContainers.appContainer = diContainers.appContainer || diContainer;
4609
4696
  // Authentication
4610
4697
  restOptions.authorizationChecker = (action, roles) => __awaiter(this, void 0, void 0, function* () {
@@ -4672,5 +4759,5 @@ function setupBackend(config, providers, parent) {
4672
4759
  * Generated bundle index. Do not edit.
4673
4760
  */
4674
4761
 
4675
- 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, fileTypeFromStream, filter, firstItem, flatten, getConstructorName, getDirName, getExtension, getFileName, getFunctionParams, getType, getValue, groupBy, gunzipPromised, gzipPromised, 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 };
4762
+ export { AssetGridDriver, AssetImageParams, AssetLocalDriver, 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, fileTypeFromStream, filter, firstItem, flatten, getConstructorName, getDirName, getExtension, getFileName, getFunctionParams, getType, getValue, groupBy, gunzipPromised, gzipPromised, 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 };
4676
4763
  //# sourceMappingURL=stemy-backend.mjs.map