@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/common/DurationParser.d.ts +5 -0
- package/dist/common/DurationParser.d.ts.map +1 -0
- package/dist/common/DurationParser.js +31 -0
- package/dist/common/DurationParser.js.map +1 -0
- package/dist/server/seed/ui/seed-ui.js +1 -1
- package/dist/server/services/cache/CacheService.d.ts +2 -2
- package/dist/server/services/cache/CacheService.d.ts.map +1 -1
- package/dist/server/services/cache/CacheService.js.map +1 -1
- package/dist/server/services/convert/CachedFileConversionService.d.ts +22 -0
- package/dist/server/services/convert/CachedFileConversionService.d.ts.map +1 -0
- package/dist/server/services/convert/CachedFileConversionService.js +68 -0
- package/dist/server/services/convert/CachedFileConversionService.js.map +1 -0
- package/dist/server/services/convert/FileConversionService.js +1 -1
- package/dist/server/services/convert/FileConversionService.js.map +1 -1
- package/dist/server/services/ssr/SSRService.d.ts +1 -1
- package/dist/server/services/ssr/SSRService.d.ts.map +1 -1
- package/dist/server/smtp/services/CachedEmailService.d.ts +3 -3
- package/dist/server/smtp/services/CachedEmailService.d.ts.map +1 -1
- package/dist/wwwroot/routes/social-mail/cached/[ttl]/[type]/get.d.ts +15 -0
- package/dist/wwwroot/routes/social-mail/cached/[ttl]/[type]/get.d.ts.map +1 -0
- package/dist/wwwroot/routes/social-mail/cached/[ttl]/[type]/get.js +87 -0
- package/dist/wwwroot/routes/social-mail/cached/[ttl]/[type]/get.js.map +1 -0
- package/dist/wwwroot/routes/social-mail/cached/options.d.ts +3 -0
- package/dist/wwwroot/routes/social-mail/cached/options.d.ts.map +1 -0
- package/dist/wwwroot/routes/social-mail/cached/options.js +3 -0
- package/dist/wwwroot/routes/social-mail/cached/options.js.map +1 -0
- package/dist/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.d.ts +19 -0
- package/dist/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.d.ts.map +1 -0
- package/dist/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.js +118 -0
- package/dist/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.js.map +1 -0
- package/dist/wwwroot/routes/social-mail/site/v2/preview-video/[host]/[type]/[screen]/[cacheKey]/get.d.ts +22 -0
- package/dist/wwwroot/routes/social-mail/site/v2/preview-video/[host]/[type]/[screen]/[cacheKey]/get.d.ts.map +1 -0
- package/dist/wwwroot/routes/social-mail/site/v2/preview-video/[host]/[type]/[screen]/[cacheKey]/get.js +124 -0
- package/dist/wwwroot/routes/social-mail/site/v2/preview-video/[host]/[type]/[screen]/[cacheKey]/get.js.map +1 -0
- package/package.json +1 -1
- package/src/common/DurationParser.ts +32 -0
- package/src/server/seed/ui/seed-ui.ts +1 -1
- package/src/server/services/cache/CacheService.ts +1 -116
- package/src/server/services/convert/CachedFileConversionService.ts +70 -0
- package/src/server/services/convert/FileConversionService.ts +1 -1
- package/src/wwwroot/routes/social-mail/cached/[ttl]/[type]/get.tsx +86 -0
- package/src/wwwroot/routes/social-mail/cached/options.tsx +3 -0
- package/src/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.tsx +121 -0
- 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
|
@@ -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.
|
|
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
|
+
}
|
|
@@ -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
|
+
}
|
package/src/wwwroot/routes/social-mail/site/v2/preview/[host]/[type]/[screen]/[cacheKey]/get.tsx
ADDED
|
@@ -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
|
+
}
|