@entity-access/server-pages 1.0.28 → 1.0.30
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/.vscode/settings.json +1 -0
- package/dist/Content.d.ts +6 -6
- package/dist/Content.d.ts.map +1 -1
- package/dist/Content.js +21 -37
- package/dist/Content.js.map +1 -1
- package/dist/Page.d.ts +11 -40
- package/dist/Page.d.ts.map +1 -1
- package/dist/Page.js +5 -3
- package/dist/Page.js.map +1 -1
- package/dist/ServerPages.d.ts +11 -5
- package/dist/ServerPages.d.ts.map +1 -1
- package/dist/ServerPages.js +102 -81
- package/dist/ServerPages.js.map +1 -1
- package/dist/core/LocalFile.d.ts +2 -1
- package/dist/core/LocalFile.d.ts.map +1 -1
- package/dist/core/LocalFile.js +8 -0
- package/dist/core/LocalFile.js.map +1 -1
- package/dist/core/RouteTree.js +1 -1
- package/dist/core/RouteTree.js.map +1 -1
- package/dist/core/SessionUser.d.ts +2 -2
- package/dist/core/SessionUser.d.ts.map +1 -1
- package/dist/core/SessionUser.js.map +1 -1
- package/dist/core/Wrapped.d.ts +70 -0
- package/dist/core/Wrapped.d.ts.map +1 -0
- package/dist/core/Wrapped.js +253 -0
- package/dist/core/Wrapped.js.map +1 -0
- package/dist/core/cached.d.ts +2 -0
- package/dist/core/cached.d.ts.map +1 -0
- package/dist/core/cached.js +3 -0
- package/dist/core/cached.js.map +1 -0
- package/dist/decorators/Authorize.d.ts +2 -0
- package/dist/decorators/Authorize.d.ts.map +1 -0
- package/dist/decorators/Authorize.js +3 -0
- package/dist/decorators/Authorize.js.map +1 -0
- package/dist/parsers/json/jsonParser.d.ts +2 -0
- package/dist/parsers/json/jsonParser.d.ts.map +1 -0
- package/dist/parsers/json/jsonParser.js +3 -0
- package/dist/parsers/json/jsonParser.js.map +1 -0
- package/dist/services/CookieService.d.ts +3 -3
- package/dist/services/CookieService.d.ts.map +1 -1
- package/dist/services/CookieService.js +5 -7
- package/dist/services/CookieService.js.map +1 -1
- package/dist/services/TokenService.d.ts +1 -0
- package/dist/services/TokenService.d.ts.map +1 -1
- package/dist/services/TokenService.js.map +1 -1
- package/dist/services/UserSessionProvider.d.ts +2 -1
- package/dist/services/UserSessionProvider.d.ts.map +1 -1
- package/dist/services/UserSessionProvider.js +3 -3
- package/dist/services/UserSessionProvider.js.map +1 -1
- package/dist/ssl/SelfSigned.d.ts +7 -0
- package/dist/ssl/SelfSigned.d.ts.map +1 -0
- package/dist/ssl/SelfSigned.js +62 -0
- package/dist/ssl/SelfSigned.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -6
- package/self-signed/cert.crt +22 -0
- package/self-signed/key.pem +27 -0
- package/src/Content.tsx +28 -43
- package/src/Page.tsx +18 -62
- package/src/ServerPages.ts +114 -76
- package/src/core/LocalFile.ts +11 -2
- package/src/core/RouteTree.ts +1 -1
- package/src/core/SessionUser.ts +2 -2
- package/src/core/Wrapped.ts +359 -0
- package/src/core/cached.ts +3 -0
- package/src/decorators/Authorize.ts +3 -0
- package/src/parsers/json/jsonParser.ts +3 -0
- package/src/services/CookieService.ts +7 -9
- package/src/services/TokenService.ts +1 -0
- package/src/services/UserSessionProvider.ts +4 -2
- package/src/ssl/SelfSigned.ts +78 -0
- package/test.js +1 -3
package/src/Content.tsx
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import { File } from "buffer";
|
|
3
|
-
import { Response } from "express";
|
|
4
3
|
import XNode from "./html/XNode.js";
|
|
5
4
|
import { parse } from "path";
|
|
6
5
|
import { LocalFile } from "./core/LocalFile.js";
|
|
7
6
|
import SessionUser from "./core/SessionUser.js";
|
|
7
|
+
import { WrappedResponse } from "./core/Wrapped.js";
|
|
8
8
|
|
|
9
9
|
export interface IPageResult {
|
|
10
|
-
send(res:
|
|
10
|
+
send(res: WrappedResponse): Promise<any>;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export class TempFileResult implements IPageResult {
|
|
@@ -34,26 +34,18 @@ export class TempFileResult implements IPageResult {
|
|
|
34
34
|
this.immutable = immutable;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
send(res:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
lastModified: false
|
|
50
|
-
}, (error) => {
|
|
51
|
-
if(error) {
|
|
52
|
-
reject(error);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
resolve();
|
|
56
|
-
});
|
|
37
|
+
send(res: WrappedResponse) {
|
|
38
|
+
res.setHeader("content-disposition", `${this.contentDisposition};filename=${encodeURIComponent(this.file.fileName)}`);
|
|
39
|
+
return res.sendFile(this.file.path,{
|
|
40
|
+
headers: {
|
|
41
|
+
"content-type": this.file.contentType
|
|
42
|
+
},
|
|
43
|
+
acceptRanges: true,
|
|
44
|
+
cacheControl: this.cacheControl,
|
|
45
|
+
maxAge: this.maxAge,
|
|
46
|
+
etag: this.etag,
|
|
47
|
+
immutable: this.immutable,
|
|
48
|
+
lastModified: false
|
|
57
49
|
});
|
|
58
50
|
}
|
|
59
51
|
|
|
@@ -86,22 +78,15 @@ export class FileResult implements IPageResult {
|
|
|
86
78
|
this.fileName = parsed.base;
|
|
87
79
|
}
|
|
88
80
|
|
|
89
|
-
send(res:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}, (error) => {
|
|
99
|
-
if(error) {
|
|
100
|
-
reject(error);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
resolve();
|
|
104
|
-
});
|
|
81
|
+
send(res: WrappedResponse) {
|
|
82
|
+
|
|
83
|
+
res.setHeader("content-disposition", `${this.contentDisposition};filename=${encodeURIComponent(this.fileName)}`);
|
|
84
|
+
return res.sendFile(this.filePath,{
|
|
85
|
+
acceptRanges: true,
|
|
86
|
+
cacheControl: this.cacheControl,
|
|
87
|
+
maxAge: this.maxAge,
|
|
88
|
+
etag: this.etag,
|
|
89
|
+
immutable: this.immutable
|
|
105
90
|
});
|
|
106
91
|
}
|
|
107
92
|
|
|
@@ -113,8 +98,8 @@ export class Redirect implements IPageResult {
|
|
|
113
98
|
|
|
114
99
|
}
|
|
115
100
|
|
|
116
|
-
async send(res:
|
|
117
|
-
res.
|
|
101
|
+
async send(res: WrappedResponse) {
|
|
102
|
+
return res.sendRedirect(this.location);
|
|
118
103
|
}
|
|
119
104
|
|
|
120
105
|
}
|
|
@@ -157,10 +142,10 @@ export default class Content implements IPageResult {
|
|
|
157
142
|
return p as Content;
|
|
158
143
|
}
|
|
159
144
|
|
|
160
|
-
public async send(res:
|
|
145
|
+
public async send(res: WrappedResponse, user?: SessionUser) {
|
|
161
146
|
const { status, body, contentType } = this;
|
|
162
|
-
|
|
163
|
-
|
|
147
|
+
res.setHeader("content-type", contentType);
|
|
148
|
+
res.statusCode = status;
|
|
164
149
|
if (typeof body === "string") {
|
|
165
150
|
if (status >= 300) {
|
|
166
151
|
const u = user ? `User: ${user.userID},${user.userName}` : "User: Anonymous";
|
package/src/Page.tsx
CHANGED
|
@@ -2,73 +2,21 @@ import busboy from "busboy";
|
|
|
2
2
|
import HtmlDocument from "./html/HtmlDocument.js";
|
|
3
3
|
import XNode from "./html/XNode.js";
|
|
4
4
|
import Content, { IPageResult, Redirect } from "./Content.js";
|
|
5
|
-
import { ServiceProvider } from "@entity-access/entity-access/dist/di/di.js";
|
|
6
|
-
import { Request } from "express";
|
|
7
5
|
import { LocalFile } from "./core/LocalFile.js";
|
|
8
6
|
import TempFolder from "./core/TempFolder.js";
|
|
9
7
|
import SessionUser from "./core/SessionUser.js";
|
|
8
|
+
import { WrappedRequest, WrappedResponse } from "./core/Wrapped.js";
|
|
9
|
+
import { ServiceProvider } from "@entity-access/entity-access/dist/di/di.js";
|
|
10
10
|
|
|
11
11
|
export const isPage = Symbol("isPage");
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
export interface IRouteCheck {
|
|
15
|
+
scope: ServiceProvider;
|
|
15
16
|
method: string;
|
|
16
17
|
current: string;
|
|
17
18
|
path: string[];
|
|
18
|
-
|
|
19
|
-
params: any;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface IPageContext {
|
|
23
|
-
/**
|
|
24
|
-
* path till the current folder where this page is located, including the name of current folder itself.
|
|
25
|
-
*/
|
|
26
|
-
currentPath: string[];
|
|
27
|
-
/**
|
|
28
|
-
* Path to the next children to be precessed.
|
|
29
|
-
*/
|
|
30
|
-
childPath: string[];
|
|
31
|
-
|
|
32
|
-
// /**
|
|
33
|
-
// * List of all paths that were tried before executing this page.
|
|
34
|
-
// */
|
|
35
|
-
// notFoundPath: string[];
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Query string if associated, empty object is always present.
|
|
39
|
-
*/
|
|
40
|
-
query: any;
|
|
41
|
-
|
|
42
|
-
body: any;
|
|
43
|
-
|
|
44
|
-
url: string;
|
|
45
|
-
|
|
46
|
-
signal:AbortSignal;
|
|
47
|
-
/**
|
|
48
|
-
* Request
|
|
49
|
-
*/
|
|
50
|
-
// request: Request;
|
|
51
|
-
/**
|
|
52
|
-
* Response
|
|
53
|
-
*/
|
|
54
|
-
// response: Response;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Request method
|
|
58
|
-
*/
|
|
59
|
-
method: string;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Currently logged in user
|
|
63
|
-
*/
|
|
64
|
-
sessionUser: SessionUser;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Actual file path of the page
|
|
68
|
-
*/
|
|
69
|
-
filePath: string;
|
|
70
|
-
|
|
71
|
-
disposables: Disposable[];
|
|
19
|
+
request: WrappedRequest;
|
|
72
20
|
}
|
|
73
21
|
|
|
74
22
|
export interface IFormData {
|
|
@@ -79,7 +27,7 @@ export interface IFormData {
|
|
|
79
27
|
/**
|
|
80
28
|
* Page should not contain any reference to underlying request/response objects.
|
|
81
29
|
*/
|
|
82
|
-
export default class Page
|
|
30
|
+
export default class Page {
|
|
83
31
|
|
|
84
32
|
static [isPage] = true;
|
|
85
33
|
|
|
@@ -89,10 +37,18 @@ export default class Page implements IPageContext {
|
|
|
89
37
|
* @param pageContext page related items
|
|
90
38
|
* @returns true if it can handle the path, default is true
|
|
91
39
|
*/
|
|
92
|
-
static canHandle(pageContext: IRouteCheck) {
|
|
40
|
+
static canHandle(pageContext: IRouteCheck) : boolean | Promise<boolean> {
|
|
93
41
|
return true;
|
|
94
42
|
}
|
|
95
43
|
|
|
44
|
+
request: WrappedRequest;
|
|
45
|
+
|
|
46
|
+
response: WrappedResponse;
|
|
47
|
+
|
|
48
|
+
get params() {
|
|
49
|
+
return this.request?.query;
|
|
50
|
+
}
|
|
51
|
+
|
|
96
52
|
signal: AbortSignal;
|
|
97
53
|
|
|
98
54
|
currentPath: string[];
|
|
@@ -124,7 +80,7 @@ export default class Page implements IPageContext {
|
|
|
124
80
|
|
|
125
81
|
disposables: Disposable[] = [];
|
|
126
82
|
|
|
127
|
-
private formDataPromise
|
|
83
|
+
private formDataPromise: Promise<IFormData>;
|
|
128
84
|
|
|
129
85
|
constructor() {
|
|
130
86
|
this.cacheControl = "no-cache, no-store, max-age=0";
|
|
@@ -135,13 +91,14 @@ export default class Page implements IPageContext {
|
|
|
135
91
|
}
|
|
136
92
|
|
|
137
93
|
readFormData(): Promise<IFormData> {
|
|
138
|
-
|
|
94
|
+
|
|
95
|
+
return this.formDataPromise ??= (async () => {
|
|
139
96
|
let tempFolder: TempFolder;
|
|
140
97
|
const result: IFormData = {
|
|
141
98
|
fields: {},
|
|
142
99
|
files: []
|
|
143
100
|
};
|
|
144
|
-
const req =
|
|
101
|
+
const req = this.request;
|
|
145
102
|
const bb = busboy({ headers: req.headers , defParamCharset: "utf8" });
|
|
146
103
|
const tasks = [];
|
|
147
104
|
await new Promise((resolve, reject) => {
|
|
@@ -167,7 +124,6 @@ export default class Page implements IPageContext {
|
|
|
167
124
|
await Promise.all(tasks);
|
|
168
125
|
return result;
|
|
169
126
|
})();
|
|
170
|
-
return this.formDataPromise;
|
|
171
127
|
}
|
|
172
128
|
|
|
173
129
|
|
package/src/ServerPages.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import { RegisterSingleton, ServiceProvider } from "@entity-access/entity-access/dist/di/di.js";
|
|
3
|
-
import express, { Request, Response } from "express";
|
|
4
3
|
import Page from "./Page.js";
|
|
5
4
|
import Content from "./Content.js";
|
|
6
|
-
import SessionUser from "./core/SessionUser.js";
|
|
7
5
|
import RouteTree from "./core/RouteTree.js";
|
|
8
|
-
import CookieService from "./services/CookieService.js";
|
|
9
|
-
import cookieParser from "cookie-parser";
|
|
10
|
-
import bodyParser from "body-parser";
|
|
11
6
|
import { fileURLToPath } from "node:url";
|
|
12
7
|
import { dirname, join } from "node:path";
|
|
13
|
-
import { Server
|
|
8
|
+
import { Server } from "socket.io";
|
|
14
9
|
import * as http from "http";
|
|
10
|
+
import * as http2 from "http2";
|
|
15
11
|
import SocketService from "./socket/SocketService.js";
|
|
12
|
+
import { Wrapped, WrappedRequest, WrappedResponse } from "./core/Wrapped.js";
|
|
13
|
+
import { SecureContext, createSecureContext } from "node:tls";
|
|
14
|
+
import { SelfSigned } from "./ssl/SelfSigned.js";
|
|
16
15
|
|
|
17
16
|
RegisterSingleton
|
|
18
17
|
export default class ServerPages {
|
|
@@ -46,13 +45,22 @@ export default class ServerPages {
|
|
|
46
45
|
* All services should be registered before calling build
|
|
47
46
|
* @param app Express App
|
|
48
47
|
*/
|
|
49
|
-
public async build(
|
|
48
|
+
public async build({
|
|
49
|
+
createSocketService = true,
|
|
50
|
+
port = 8080,
|
|
51
|
+
protocol = "http",
|
|
52
|
+
disableHttp2Warning = false,
|
|
53
|
+
SNICallback
|
|
54
|
+
}:{
|
|
55
|
+
createSocketService?: boolean,
|
|
56
|
+
port: number,
|
|
57
|
+
disableHttp2Warning?: boolean,
|
|
58
|
+
protocol: "http" | "http2" | "https2",
|
|
59
|
+
SNICallback: (servername: string, cb: (err: Error | null, ctx?: SecureContext) => void) => void
|
|
60
|
+
}) {
|
|
50
61
|
try {
|
|
51
|
-
// etag must be set by individual request processors if needed.
|
|
52
|
-
app.set("etag", false);
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
app.use(bodyParser.json());
|
|
63
|
+
let httpServer = null as http.Server | http2.Http2Server | http2.Http2SecureServer;
|
|
56
64
|
|
|
57
65
|
let socketServer = null as Server;
|
|
58
66
|
if (createSocketService) {
|
|
@@ -61,95 +69,125 @@ export default class ServerPages {
|
|
|
61
69
|
(ss as any).attach(socketServer);
|
|
62
70
|
await (ss as any).init();
|
|
63
71
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
|
|
73
|
+
switch(protocol) {
|
|
74
|
+
case "http":
|
|
75
|
+
httpServer = http.createServer((req, res) => this.process(req, res))
|
|
76
|
+
break;
|
|
77
|
+
case "https2":
|
|
78
|
+
let sc = null;
|
|
79
|
+
SNICallback ??= (name, cb) => {
|
|
80
|
+
if (sc) {
|
|
81
|
+
return cb(null, sc);
|
|
82
|
+
}
|
|
83
|
+
const { key, cert } = SelfSigned.setupSelfSigned();
|
|
84
|
+
sc = createSecureContext({ key, cert });
|
|
85
|
+
cb(null, sc);
|
|
86
|
+
};
|
|
87
|
+
httpServer = http2.createSecureServer({
|
|
88
|
+
SNICallback
|
|
89
|
+
}, (req, res) => this.process(req, res))
|
|
90
|
+
break;
|
|
91
|
+
case "http2":
|
|
92
|
+
httpServer = http2.createSecureServer({
|
|
93
|
+
},(req, res) => this.process(req, res))
|
|
94
|
+
if (!disableHttp2Warning) {
|
|
95
|
+
console.warn("Http2 without SSL should not be used in production");
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
await new Promise<void>((resolve, reject) => {
|
|
102
|
+
const server = httpServer.listen(port, () => {
|
|
103
|
+
resolve();
|
|
68
104
|
});
|
|
69
105
|
socketServer?.attach(server);
|
|
70
106
|
});
|
|
107
|
+
return httpServer;
|
|
71
108
|
} catch (error) {
|
|
72
109
|
console.error(error);
|
|
73
110
|
}
|
|
74
111
|
return null;
|
|
75
112
|
}
|
|
76
113
|
|
|
77
|
-
protected async process(req:
|
|
114
|
+
protected async process(req: any, resp: any) {
|
|
115
|
+
|
|
116
|
+
req = Wrapped.request(req);
|
|
117
|
+
resp = Wrapped.response(req, resp);
|
|
118
|
+
|
|
119
|
+
req.response = resp;
|
|
78
120
|
|
|
79
121
|
if((req as any).processed) {
|
|
80
122
|
return;
|
|
81
123
|
}
|
|
82
124
|
(req as any).processed = true;
|
|
83
125
|
|
|
84
|
-
// defaulting to no cache
|
|
85
|
-
// static content delivery should override this
|
|
86
|
-
resp.setHeader("cache-control", "no-cache");
|
|
87
|
-
|
|
88
|
-
using scope = ServiceProvider.createScope(this);
|
|
89
|
-
let sent = false;
|
|
90
|
-
const acceptJson = req.accepts().some((s) => /\/json$/i.test(s));
|
|
91
126
|
try {
|
|
92
127
|
|
|
93
|
-
|
|
128
|
+
// defaulting to no cache
|
|
129
|
+
// static content delivery should override this
|
|
130
|
+
resp.setHeader("cache-control", "no-cache");
|
|
131
|
+
|
|
132
|
+
using scope = ServiceProvider.createScope(this);
|
|
133
|
+
let sent = false;
|
|
134
|
+
const acceptJson = req.accepts().some((s) => /\/json$/i.test(s));
|
|
135
|
+
|
|
94
136
|
|
|
95
137
|
try {
|
|
96
|
-
|
|
138
|
+
const path = req.path.split("/").filter((x) => x);
|
|
139
|
+
const method = req.method;
|
|
140
|
+
const { pageClass, childPath } = (await this.root.getRoute({
|
|
141
|
+
scope,
|
|
142
|
+
method,
|
|
143
|
+
current: "",
|
|
144
|
+
path,
|
|
145
|
+
request: req
|
|
146
|
+
})) ?? {
|
|
147
|
+
pageClass: Page,
|
|
148
|
+
childPath: path
|
|
149
|
+
};
|
|
150
|
+
const page = scope.create(pageClass);
|
|
151
|
+
page.method = method;
|
|
152
|
+
page.childPath = childPath;
|
|
153
|
+
page.request = req;
|
|
154
|
+
page.response = resp;
|
|
155
|
+
const content = await page.all(page.params);
|
|
156
|
+
resp.setHeader("cache-control", page.cacheControl);
|
|
157
|
+
resp.removeHeader("etag");
|
|
158
|
+
sent = true;
|
|
159
|
+
await content.send(resp);
|
|
97
160
|
} catch (error) {
|
|
161
|
+
if (!sent) {
|
|
162
|
+
try {
|
|
163
|
+
|
|
164
|
+
if (acceptJson || error.errorModel) {
|
|
165
|
+
await Content.json(
|
|
166
|
+
{
|
|
167
|
+
... error.errorModel ?? {},
|
|
168
|
+
message: error.message ?? error,
|
|
169
|
+
detail: error.stack ?? error,
|
|
170
|
+
}
|
|
171
|
+
, 500).send(resp);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const content = Content.html(`<!DOCTYPE html>\n<html><body><pre>Server Error for ${req.url}\r\n${error?.stack ?? error}</pre></body></html>`, 500);
|
|
176
|
+
await content.send(resp);
|
|
177
|
+
} catch (e1) {
|
|
178
|
+
resp.send(e1.stack ?? e1, 500);
|
|
179
|
+
console.error(e1);
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
98
183
|
console.error(error);
|
|
99
184
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const method = req.method;
|
|
105
|
-
const params = { ... req.params, ... req.query, ... req.body ?? {} };
|
|
106
|
-
const { pageClass, childPath } = (await this.root.getRoute({
|
|
107
|
-
method,
|
|
108
|
-
current: "",
|
|
109
|
-
path,
|
|
110
|
-
params,
|
|
111
|
-
sessionUser
|
|
112
|
-
})) ?? {
|
|
113
|
-
pageClass: Page,
|
|
114
|
-
childPath: path
|
|
115
|
-
};
|
|
116
|
-
const page = scope.create(pageClass);
|
|
117
|
-
page.method = method;
|
|
118
|
-
page.childPath = childPath;
|
|
119
|
-
page.body = req.body;
|
|
120
|
-
page.query = req.query;
|
|
121
|
-
page.sessionUser = sessionUser;
|
|
122
|
-
(page as any).req = req;
|
|
123
|
-
(page as any).res = resp;
|
|
124
|
-
const content = await page.all(params);
|
|
125
|
-
resp.setHeader("cache-control", page.cacheControl);
|
|
126
|
-
resp.removeHeader("etag");
|
|
127
|
-
sent = true;
|
|
128
|
-
await content.send(resp);
|
|
129
|
-
} catch (error) {
|
|
130
|
-
if (!sent) {
|
|
131
|
-
try {
|
|
132
|
-
|
|
133
|
-
if (acceptJson || error.errorModel) {
|
|
134
|
-
await Content.json(
|
|
135
|
-
{
|
|
136
|
-
... error.errorModel ?? {},
|
|
137
|
-
message: error.message ?? error,
|
|
138
|
-
detail: error.stack ?? error,
|
|
139
|
-
}
|
|
140
|
-
, 500).send(resp);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const content = Content.html(`<!DOCTYPE html>\n<html><body><pre>Server Error for ${req.url}\r\n${error?.stack ?? error}</pre></body></html>`, 500);
|
|
145
|
-
await content.send(resp);
|
|
146
|
-
} catch (e1) {
|
|
147
|
-
resp.send(e1.stack ?? e1);
|
|
148
|
-
console.error(e1);
|
|
185
|
+
} finally {
|
|
186
|
+
if(Array.isArray(req.disposables)) {
|
|
187
|
+
for (const iterator of req.disposables) {
|
|
188
|
+
iterator[Symbol.dispose]?.();
|
|
149
189
|
}
|
|
150
|
-
return;
|
|
151
190
|
}
|
|
152
|
-
console.error(error);
|
|
153
191
|
}
|
|
154
192
|
}
|
|
155
193
|
|
package/src/core/LocalFile.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createReadStream, createWriteStream, existsSync, statSync } from "fs";
|
|
1
|
+
import { createReadStream, createWriteStream, existsSync, read, statSync } from "fs";
|
|
2
2
|
import { basename } from "path";
|
|
3
3
|
import mime from "mime-types";
|
|
4
|
-
import internal, { Stream } from "stream";
|
|
4
|
+
import internal, { Stream, Writable } from "stream";
|
|
5
5
|
import { appendFile, open, readFile, writeFile } from "fs/promises";
|
|
6
6
|
|
|
7
7
|
|
|
@@ -54,6 +54,15 @@ export class LocalFile {
|
|
|
54
54
|
return await readFile(this.path, { flag: "r" });
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
public async writeTo(writable: Writable, start?: number, end?: number) {
|
|
58
|
+
const readable = createReadStream(this.path, { start, end });
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
readable.pipe(writable, { end: true })
|
|
61
|
+
.on("end", resolve)
|
|
62
|
+
.on("error", reject);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
public async delete() {
|
|
58
67
|
return this.onDispose?.();
|
|
59
68
|
}
|
package/src/core/RouteTree.ts
CHANGED
|
@@ -60,7 +60,7 @@ export default class RouteTree {
|
|
|
60
60
|
const pageClassPromise = (this.handler[method] ??= this.handler[method.toLowerCase()]) ?? this.handler["index"];
|
|
61
61
|
if (pageClassPromise) {
|
|
62
62
|
const pageClass = await pageClassPromise;
|
|
63
|
-
if(pageClass.canHandle(rc)) {
|
|
63
|
+
if(await pageClass.canHandle(rc)) {
|
|
64
64
|
return { pageClass, childPath: rc.path };
|
|
65
65
|
}
|
|
66
66
|
}
|
package/src/core/SessionUser.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Request, Response } from "express";
|
|
2
1
|
import EntityAccessError from "@entity-access/entity-access/dist/common/EntityAccessError.js";
|
|
3
2
|
import { RegisterScoped } from "@entity-access/entity-access/dist/di/di.js";
|
|
4
3
|
import DateTime from "@entity-access/entity-access/dist/types/DateTime.js";
|
|
5
4
|
import TokenService, { IAuthCookie } from "../services/TokenService.js";
|
|
5
|
+
import { WrappedResponse } from "./Wrapped.js";
|
|
6
6
|
|
|
7
7
|
const secure = (process.env["SOCIAL_MAIL_AUTH_COOKIE_SECURE"] ?? "true") === "true";
|
|
8
8
|
|
|
@@ -48,7 +48,7 @@ export default class SessionUser {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
constructor(
|
|
51
|
-
private resp:
|
|
51
|
+
private resp: WrappedResponse,
|
|
52
52
|
private cookieName: string,
|
|
53
53
|
private tokenService: TokenService
|
|
54
54
|
) {}
|