@social-mail/social-mail-web-server 1.16.554 → 1.16.556

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/common/DurationParser.d.ts +5 -0
  3. package/dist/common/DurationParser.d.ts.map +1 -0
  4. package/dist/common/DurationParser.js +31 -0
  5. package/dist/common/DurationParser.js.map +1 -0
  6. package/dist/server/seed/ui/seed-ui.js +1 -1
  7. package/dist/server/services/cache/CacheService.d.ts +2 -2
  8. package/dist/server/services/cache/CacheService.d.ts.map +1 -1
  9. package/dist/server/services/cache/CacheService.js.map +1 -1
  10. package/dist/server/services/convert/CachedFileConversionService.d.ts +22 -0
  11. package/dist/server/services/convert/CachedFileConversionService.d.ts.map +1 -0
  12. package/dist/server/services/convert/CachedFileConversionService.js +68 -0
  13. package/dist/server/services/convert/CachedFileConversionService.js.map +1 -0
  14. package/dist/server/services/convert/FileConversionService.js +1 -1
  15. package/dist/server/services/convert/FileConversionService.js.map +1 -1
  16. package/dist/server/services/ssr/SSRService.d.ts +1 -1
  17. package/dist/server/services/ssr/SSRService.d.ts.map +1 -1
  18. package/dist/server/smtp/services/CachedEmailService.d.ts +3 -3
  19. package/dist/server/smtp/services/CachedEmailService.d.ts.map +1 -1
  20. package/dist/wwwroot/routes/social-mail/cached/[ttl]/[type]/get.d.ts +15 -0
  21. package/dist/wwwroot/routes/social-mail/cached/[ttl]/[type]/get.d.ts.map +1 -0
  22. package/dist/wwwroot/routes/social-mail/cached/[ttl]/[type]/get.js +87 -0
  23. package/dist/wwwroot/routes/social-mail/cached/[ttl]/[type]/get.js.map +1 -0
  24. package/dist/wwwroot/routes/social-mail/cached/options.d.ts +3 -0
  25. package/dist/wwwroot/routes/social-mail/cached/options.d.ts.map +1 -0
  26. package/dist/wwwroot/routes/social-mail/cached/options.js +3 -0
  27. package/dist/wwwroot/routes/social-mail/cached/options.js.map +1 -0
  28. package/dist/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.d.ts +19 -0
  29. package/dist/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.d.ts.map +1 -0
  30. package/dist/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.js +118 -0
  31. package/dist/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.js.map +1 -0
  32. package/dist/wwwroot/routes/social-mail/site/v2/preview-video/[host]/[type]/[screen]/[cacheKey]/get.d.ts +22 -0
  33. package/dist/wwwroot/routes/social-mail/site/v2/preview-video/[host]/[type]/[screen]/[cacheKey]/get.d.ts.map +1 -0
  34. package/dist/wwwroot/routes/social-mail/site/v2/preview-video/[host]/[type]/[screen]/[cacheKey]/get.js +124 -0
  35. package/dist/wwwroot/routes/social-mail/site/v2/preview-video/[host]/[type]/[screen]/[cacheKey]/get.js.map +1 -0
  36. package/package.json +1 -1
  37. package/src/common/DurationParser.ts +32 -0
  38. package/src/server/seed/ui/seed-ui.ts +1 -1
  39. package/src/server/services/cache/CacheService.ts +1 -116
  40. package/src/server/services/convert/CachedFileConversionService.ts +70 -0
  41. package/src/server/services/convert/FileConversionService.ts +1 -1
  42. package/src/wwwroot/routes/social-mail/cached/[ttl]/[type]/get.tsx +86 -0
  43. package/src/wwwroot/routes/social-mail/cached/options.tsx +3 -0
  44. package/src/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.tsx +121 -0
  45. package/src/wwwroot/routes/social-mail/site/v2/preview-video/[host]/[type]/[screen]/[cacheKey]/get.tsx +128 -0
@@ -0,0 +1,124 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import Inject from "@entity-access/entity-access/dist/di/di.js";
11
+ import Page from "@entity-access/server-pages/dist/Page.js";
12
+ import { Route } from "@entity-access/server-pages/dist/core/Route.js";
13
+ import CacheService from "../../../../../../../../../../server/services/cache/CacheService.js";
14
+ import FileConversionService from "../../../../../../../../../../server/services/convert/FileConversionService.js";
15
+ import DBConfig, { UIPackageConfig } from "../../../../../../../../../../server/services/DBConfig.js";
16
+ import WebSiteContentService from "../../../../../../../../../../server/services/files/WebSiteContentService.js";
17
+ import PuppeteerService from "../../../../../../../../../../server/services/puppeteer/PuppeteerService.js";
18
+ import TempFileService from "../../../../../../../../../../server/storage/TempFileService.js";
19
+ import CachedFileConversionService from "../../../../../../../../../../server/services/convert/CachedFileConversionService.js";
20
+ export default class default_1 extends Page {
21
+ async run() {
22
+ const key = this.request.url;
23
+ const [fileName = "preview.webm"] = this.childPath;
24
+ return await this.cfs.getOrCreateResult({
25
+ cacheKey: key,
26
+ senderDomain: void 0,
27
+ type: this.type,
28
+ fileName,
29
+ factory: () => this.getFile()
30
+ });
31
+ }
32
+ async getFile() {
33
+ const snapshot = await this.wcs.getCurrentSnapshotFolder(this.host);
34
+ const [fileName = "preview.webm"] = this.childPath;
35
+ let pageStopScript = void 0;
36
+ // check if we have page-ready.js page...
37
+ const hasStopScript = await this.wcs.getSnapshotFile(snapshot, ["stop-preview.js"]);
38
+ if (hasStopScript) {
39
+ pageStopScript = "./stop-preview.js";
40
+ }
41
+ else {
42
+ const uiPackage = await this.config.getValue(UIPackageConfig);
43
+ pageStopScript = `https://cdn.jsdelivr.net/npm/${uiPackage.package}@${uiPackage.version}/content/preview-video-generator/stop-script.js`;
44
+ }
45
+ let width = 1200;
46
+ let height = 2000;
47
+ let deviceScaleFactor = 1;
48
+ let mobile = false;
49
+ if (/mobile/i.test(this.type)) {
50
+ width = 400;
51
+ height = 1000;
52
+ mobile = true;
53
+ // for preview we will keep device scale same
54
+ deviceScaleFactor = 1;
55
+ }
56
+ const cacheKey = this.cacheKey;
57
+ const output = "webm";
58
+ if (/\.avif$/i.test(fileName)) {
59
+ const url = new URL(this.request.URL);
60
+ url.pathname = url.pathname.replace(/\.avif$/i, ".webm");
61
+ const webmFile = await this.tfs.download(url.toString(), "a.webm");
62
+ return await this.fcs.transform("avif-animated", webmFile, { fileName });
63
+ }
64
+ const url = `https://${this.host}`;
65
+ return await this.puppeteerService.generateVideo({
66
+ width,
67
+ height,
68
+ mobile,
69
+ deviceScaleFactor,
70
+ fileName,
71
+ cacheKey,
72
+ pageStopScript,
73
+ fullPage: true,
74
+ url,
75
+ output,
76
+ timeout: 30000
77
+ });
78
+ }
79
+ }
80
+ __decorate([
81
+ Inject,
82
+ __metadata("design:type", WebSiteContentService)
83
+ ], default_1.prototype, "wcs", void 0);
84
+ __decorate([
85
+ Inject,
86
+ __metadata("design:type", CacheService)
87
+ ], default_1.prototype, "cacheService", void 0);
88
+ __decorate([
89
+ Inject,
90
+ __metadata("design:type", DBConfig)
91
+ ], default_1.prototype, "config", void 0);
92
+ __decorate([
93
+ Inject,
94
+ __metadata("design:type", PuppeteerService)
95
+ ], default_1.prototype, "puppeteerService", void 0);
96
+ __decorate([
97
+ Inject,
98
+ __metadata("design:type", CachedFileConversionService)
99
+ ], default_1.prototype, "cfs", void 0);
100
+ __decorate([
101
+ Route("type"),
102
+ __metadata("design:type", Object)
103
+ ], default_1.prototype, "type", void 0);
104
+ __decorate([
105
+ Route("cacheKey"),
106
+ __metadata("design:type", Object)
107
+ ], default_1.prototype, "cacheKey", void 0);
108
+ __decorate([
109
+ Route("host"),
110
+ __metadata("design:type", Object)
111
+ ], default_1.prototype, "host", void 0);
112
+ __decorate([
113
+ Route("ready"),
114
+ __metadata("design:type", Object)
115
+ ], default_1.prototype, "ready", void 0);
116
+ __decorate([
117
+ Inject,
118
+ __metadata("design:type", FileConversionService)
119
+ ], default_1.prototype, "fcs", void 0);
120
+ __decorate([
121
+ Inject,
122
+ __metadata("design:type", TempFileService)
123
+ ], default_1.prototype, "tfs", void 0);
124
+ //# sourceMappingURL=get.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get.js","sourceRoot":"","sources":["../../../../../../../../../../../src/wwwroot/routes/social-mail/site/v2/preview-video/[host]/[type]/[screen]/[cacheKey]/get.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@social-mail/social-mail-web-server",
3
- "version": "1.16.554",
3
+ "version": "1.16.556",
4
4
  "description": "## Phase 1",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -0,0 +1,32 @@
1
+ import TimeSpan from "@entity-access/entity-access/dist/types/TimeSpan.js";
2
+
3
+ export default class DurationParser {
4
+
5
+ public static parse(text: string) {
6
+ const n = Number.parseFloat(text);
7
+ if(text.endsWith("ms")) {
8
+ return new TimeSpan(n);
9
+ }
10
+ if(text.endsWith("s")) {
11
+ return TimeSpan.fromSeconds(n);
12
+ }
13
+ if(text.endsWith("m")) {
14
+ return TimeSpan.fromMinutes(n);
15
+ }
16
+ if(text.endsWith("us")) {
17
+ return new TimeSpan(n/1000);
18
+ }
19
+ if(text.endsWith("mo")) {
20
+ return TimeSpan.fromDays(n*30);
21
+ }
22
+ if(text.endsWith("d")) {
23
+ return TimeSpan.fromDays(n);
24
+ }
25
+ if(text.endsWith("w")) {
26
+ return TimeSpan.fromDays(n*7);
27
+ }
28
+ if(text.endsWith("y")) {
29
+ return TimeSpan.fromDays(n*365);
30
+ }
31
+ }
32
+ }
@@ -17,7 +17,7 @@ export default async function seedUI(config: DBConfig) {
17
17
  await config.saveVersion(UIPackageConfig, {
18
18
  package: "@social-mail/social-mail-client",
19
19
  view: "dist/web/AppIndex",
20
- version: "1.11.131"
20
+ version: "1.11.132"
21
21
  });
22
22
 
23
23
  await config.saveVersion(WebComponentsPackageConfig, {
@@ -112,7 +112,7 @@ export default class CacheService {
112
112
  }
113
113
 
114
114
 
115
- public async get(ck: { key: string, ttl: TimeSpan, factory: (c?: CachedItem) => Promise<LocalFile> }) {
115
+ public async get(ck: { key: string, ttl: TimeSpan, factory: (c?: CachedItem) => Promise<LocalFile> }): Promise<LocalFile> {
116
116
 
117
117
  const {
118
118
  key,
@@ -207,119 +207,4 @@ export default class CacheService {
207
207
 
208
208
  }
209
209
 
210
-
211
- // private async lockCachedItem(item: CachedItem, max = DateTime.now.addMinutes(1)) {
212
-
213
- // let old = Promise.resolve(0);
214
-
215
- // const renewLock = async (a: CachedItem) => {
216
- // const prev = old;
217
- // return old = (async () => {
218
- // await prev;
219
- // using scope = globalServices.createScope();
220
- // const db = scope.resolve(SocialMailContext);
221
-
222
- // const copy = await db.cachedItems.loadByKeys(a);
223
- // if (!copy || copy.ready) {
224
- // return null;
225
- // }
226
-
227
- // if (copy.lockExpiry && DateTime.from(copy.lockExpiry).msSinceEpoch <= DateTime.now.msSinceEpoch) {
228
- // // we can discard...
229
- // await db.cachedItems.statements.update({
230
- // itemID: a.itemID,
231
- // lockExpiry: null,
232
- // lockToken: null
233
- // });
234
- // }
235
-
236
- // a.lockExpiry = DateTime.now.addSeconds(15);
237
-
238
- // return await db.cachedItems
239
- // .where(a, (x, p) => x.itemID === p.itemID && (x.lockToken === p.lockToken || x.lockToken === null))
240
- // .update(a, (x, p) => ({
241
- // lockExpiry: Sql.cast.asDateTime(p.lockExpiry) as any,
242
- // ready: Sql.cast.asBoolean(p.ready)
243
- // }));
244
- // })();
245
- // };
246
-
247
- // while(true) {
248
-
249
- // if (Date.now() >= max.msSinceEpoch) {
250
- // throw new Error(`Unable to acquire lock on ${item.itemID}`);
251
- // }
252
-
253
- // let locked = await renewLock(item);
254
- // if (locked === null) {
255
- // return;
256
- // }
257
-
258
- // // we have locked the cached item successfully...
259
- // if (locked) {
260
-
261
- // setInterval(async () => {
262
- // try {
263
- // locked = await renewLock(item);
264
- // } catch (error) {
265
- // // eslint-disable-next-line no-console
266
- // console.error(error);
267
- // }
268
- // }, 5000);
269
-
270
- // return {
271
- // async [Symbol.asyncDispose]() {
272
- // item.lockExpiry = null;
273
- // item.lockToken = null;
274
- // item.ready = true;
275
- // await renewLock(item);
276
- // }
277
- // };
278
- // }
279
-
280
- // await sleep(5000);
281
- // }
282
- // }
283
-
284
- // private async getCachedItem(key: string, expiry: DateTime, ttl: TimeSpan) {
285
-
286
- // // New scope is created to isolate
287
- // // database from existing running transaction
288
- // // in case this was called within a transaction scope.
289
-
290
- // using scope = globalServices.createScope();
291
- // const db = scope.resolve(SocialMailContext);
292
-
293
- // const keyHash = await this.cryptoService.hashBuffer(Buffer.from(key, "utf-8"));
294
-
295
- // const cachedItem = await db.cachedItems.statements.selectOrInsert({
296
- // keyHash,
297
- // key,
298
- // expiry,
299
- // ready: false
300
- // }, {
301
- // keyHash,
302
- // key,
303
- // }, 5);
304
-
305
- // if (DateTime.from(cachedItem.expiry).msSinceEpoch < Date.now()) {
306
-
307
- // if (!cachedItem.slide) {
308
-
309
- // // delete this..
310
- // await this.storage.deleteFile(`cache/${cachedItem.itemID}`);
311
- // cachedItem.expiry = expiry;
312
- // await db.cachedItems.where(cachedItem, (x, p) => x.itemID === p.itemID)
313
- // .update(cachedItem, (x, p) => ({ expiry: Sql.cast.asDateTime(p.expiry) as any}));
314
- // }
315
- // }
316
-
317
- // if (cachedItem.slide && cachedItem.ready) {
318
- // await db.cachedItems.where(cachedItem, (x, p) => x.itemID === p.itemID)
319
- // .update(cachedItem, (x, p) => ({ expiry: Sql.date.addDays( x.expiry, 30) as any}));
320
- // }
321
-
322
- // return cachedItem;
323
- // }
324
-
325
210
  }
@@ -0,0 +1,70 @@
1
+ import Inject, { RegisterScoped } from "@entity-access/entity-access/dist/di/di.js";
2
+ import TimeSpan from "@entity-access/entity-access/dist/types/TimeSpan.js";
3
+ import { LocalFile } from "@entity-access/server-pages/dist/core/LocalFile.js";
4
+ import CacheService from "../cache/CacheService.js";
5
+ import FileConversionService from "./FileConversionService.js";
6
+ import { TempFileResult } from "@entity-access/server-pages/dist/Content.js";
7
+ import { CORS } from "../../../common/CORS.js";
8
+
9
+ interface ICacheParams {
10
+ type: string;
11
+ cacheKey: string;
12
+ ttl?: TimeSpan;
13
+ fileName: string;
14
+ senderDomain: string;
15
+ factory:() => Promise<LocalFile>;
16
+ }
17
+
18
+ @RegisterScoped
19
+ export default class CachedFileConversionService {
20
+
21
+ @Inject
22
+ cacheService: CacheService;
23
+
24
+ @Inject
25
+ fc: FileConversionService;
26
+
27
+ async getOrCreateResult(p: ICacheParams, cors = true) {
28
+ const ttl = p.ttl ??= TimeSpan.fromDays(30);
29
+ const file = await this.getOrCreate(p);
30
+ return new TempFileResult(file, {
31
+ headers: {
32
+ ... cors ? CORS.allowAll: {},
33
+ "cache-control": `public,max-age=${ Math.floor(ttl.totalSeconds)}`
34
+ }
35
+ });
36
+ }
37
+
38
+ async getOrCreate(p: ICacheParams) {
39
+
40
+ const { type, cacheKey, ttl = TimeSpan.fromDays(30) } = p;
41
+
42
+ const key = `cached-files-1-${type}-${cacheKey}`;
43
+ return await this.cacheService.get({
44
+ key,
45
+ ttl,
46
+ factory: (c) => this.getFile(p)
47
+ });
48
+ }
49
+
50
+ async getFile(p: ICacheParams) {
51
+ if(/download|view/i.test(p.type)) {
52
+ return await p.factory();
53
+ }
54
+ const key = `cached-files-1-download-${p.cacheKey}`;
55
+ const { ttl = TimeSpan.fromDays(30)} = p;
56
+ return await this.cacheService.get({
57
+ key,
58
+ ttl,
59
+ factory: async() => {
60
+ const { senderDomain, fileName } = p;
61
+ const originalFile = await this.getOrCreate({ ... p, type: "download" });
62
+ return await this.fc.transform(p.type, originalFile, {
63
+ fileName,
64
+ senderDomain
65
+ });
66
+ }
67
+ })
68
+ }
69
+
70
+ }
@@ -155,7 +155,7 @@ export default class FileConversionService {
155
155
  transform = true;
156
156
  }
157
157
 
158
- if(/^webp\-animated$/i.test(type)) {
158
+ if(/^(webp|avif)\-animated$/i.test(type)) {
159
159
  transform = true;
160
160
  }
161
161
 
@@ -0,0 +1,86 @@
1
+ import Inject from "@entity-access/entity-access/dist/di/di.js";
2
+ import Page from "@entity-access/server-pages/dist/Page.js";
3
+ import CacheService from "../../../../../../server/services/cache/CacheService.js";
4
+ import { Route } from "@entity-access/server-pages/dist/core/Route.js";
5
+ import DurationParser from "../../../../../../common/DurationParser.js";
6
+ import { TempFileResult } from "@entity-access/server-pages/dist/Content.js";
7
+ import CachedItem from "../../../../../../server/model/entities/CachedItem.js";
8
+ import { secureFetch } from "../../../../../../server/services/root-certs/RootCertificates.js";
9
+ import { IncomingHttpHeaders } from "node:http";
10
+ import EntityAccessError from "@entity-access/entity-access/dist/common/EntityAccessError.js";
11
+ import TempFileService from "../../../../../../server/storage/TempFileService.js";
12
+ import { parse } from "node:path";
13
+ import { writeFile } from "node:fs/promises";
14
+
15
+ export default class extends Page {
16
+
17
+ @Inject
18
+ cacheService: CacheService;
19
+
20
+ @Inject
21
+ tfs: TempFileService;
22
+
23
+ @Route
24
+ ttl: string;
25
+
26
+ @Route
27
+ type: string;
28
+
29
+ hit = "hit";
30
+
31
+ async run() {
32
+
33
+ if(!/view|download/i.test(this.type)) {
34
+ // go through file conversion...
35
+ }
36
+
37
+ const key = this.request.url;
38
+ const ttl = DurationParser.parse(this.ttl);
39
+ const file = await this.cacheService.get({
40
+ key,
41
+ ttl,
42
+ factory: (c) => this.fetch(c)
43
+ });
44
+
45
+ return new TempFileResult(file, {
46
+ headers: {
47
+ "cache-control": `public,max-age=${ttl.totalSeconds}`,
48
+ "content-type": file.contentType,
49
+ "x-cache-middleware": this.hit,
50
+ }
51
+ });
52
+
53
+ }
54
+
55
+ async fetch(c: CachedItem) {
56
+ this.hit = "miss";
57
+ const url = new URL(this.request.URL);
58
+ url.pathname = "/" + this.childPath.join("/");
59
+ const cookie = this.request.headers.cookie;
60
+ const headers = {} as IncomingHttpHeaders;
61
+ if(cookie) {
62
+ headers.cookie = cookie;
63
+ }
64
+ const response = await secureFetch(url, {
65
+ headers: {
66
+ "cookie": this.request.headers.cookie
67
+ },
68
+ redirect: "follow"
69
+ });
70
+
71
+ const { status, body } = response;
72
+
73
+ if(response.status > 399) {
74
+ throw new EntityAccessError(`${url} failed ${status}\n${await response.text()}`);
75
+ }
76
+
77
+ const lastFileName = this.childPath.at(-1);
78
+ const { ext, base } = parse(lastFileName);
79
+
80
+ const tf = await this.tfs.createTempFile(ext, base);
81
+ await writeFile(tf.path, body);
82
+
83
+ return tf;
84
+
85
+ }
86
+ }
@@ -0,0 +1,3 @@
1
+ import CorsOptionsPage from "../site/list/designs/options.js";
2
+
3
+ export default CorsOptionsPage;
@@ -0,0 +1,121 @@
1
+ import Inject from "@entity-access/entity-access/dist/di/di.js";
2
+ import Page from "@entity-access/server-pages/dist/Page.js";
3
+ import { Route } from "@entity-access/server-pages/dist/core/Route.js";
4
+ import DBConfig, { UIPackageConfig } from "../../../../../../../../../../server/services/DBConfig.js";
5
+ import WebSiteContentService from "../../../../../../../../../../server/services/files/WebSiteContentService.js";
6
+ import PuppeteerService from "../../../../../../../../../../server/services/puppeteer/PuppeteerService.js";
7
+ import CachedFileConversionService from "../../../../../../../../../../server/services/convert/CachedFileConversionService.js";
8
+
9
+ export default class extends Page {
10
+
11
+ @Inject
12
+ wcs: WebSiteContentService;
13
+
14
+ @Inject
15
+ config: DBConfig;
16
+
17
+ @Inject
18
+ puppeteerService: PuppeteerService;
19
+
20
+ @Inject
21
+ cfs: CachedFileConversionService;
22
+
23
+ @Route("type")
24
+ type;
25
+
26
+ @Route("screen")
27
+ screen;
28
+
29
+ @Route("cacheKey")
30
+ cacheKey;
31
+
32
+ @Route("host")
33
+ host;
34
+
35
+ @Route("ready")
36
+ ready;
37
+
38
+ async run() {
39
+
40
+ const key = this.request.url;
41
+ const [ fileName = "preview.webp" ] = this.childPath as any[];
42
+
43
+ return await this.cfs.getOrCreateResult({
44
+ cacheKey: key,
45
+ senderDomain: void 0,
46
+ type: this.type,
47
+ fileName,
48
+ factory: () => this.getFile()
49
+ });
50
+
51
+ }
52
+
53
+ async getFile() {
54
+
55
+ const [ fileName = "preview.webp" ] = this.childPath as any[];
56
+ const snapshot = await this.wcs.getCurrentSnapshotFolder(this.host);
57
+
58
+ let pageReadyScript = "";
59
+
60
+ // check if we have page-ready.js page...
61
+ const hasReadyScript = await this.wcs.getSnapshotFile(snapshot, ["page-ready.js"]);
62
+ if(hasReadyScript) {
63
+ pageReadyScript = "./page-ready.js";
64
+ } else {
65
+ const uiPackage = await this.config.getValue(UIPackageConfig);
66
+ pageReadyScript = `https://cdn.jsdelivr.net/npm/${uiPackage.package}@${uiPackage.version}/content/preview-generator/page-ready.js`;
67
+ }
68
+
69
+
70
+ let width = 1200 as any;
71
+ let height = 1536 as any;
72
+
73
+ let deviceScaleFactor = 1 as any;
74
+
75
+ let mobile = false;
76
+
77
+ if (/mobile/i.test(this.type)) {
78
+ width = 450;
79
+ height = 800;
80
+ mobile = true;
81
+ // for preview we will keep device scale same
82
+ deviceScaleFactor = 1;
83
+ }
84
+
85
+
86
+ const cacheKey = this.cacheKey;
87
+
88
+
89
+ let output = "webp";
90
+ if (/\.png$/i.test(fileName)) {
91
+ output = "png";
92
+ } else if(/\.jpg$/i.test(fileName)) {
93
+ output = "jpg";
94
+ }
95
+
96
+ let testDelay = this.ready || 5000;
97
+
98
+ if(snapshot.pageReadyDelayMS) {
99
+ testDelay = snapshot.pageReadyDelayMS;
100
+ }
101
+
102
+ const url = `https://${this.host}`;
103
+
104
+ return this.puppeteerService.generate({
105
+ width,
106
+ height,
107
+ mobile,
108
+ deviceScaleFactor,
109
+ fileName,
110
+ cacheKey,
111
+ pageReadyScript,
112
+ fullPage: true,
113
+ url,
114
+ output,
115
+ testDelay,
116
+ timeout: 30000
117
+ });
118
+
119
+ }
120
+
121
+ }