proteum 1.0.2 → 2.0.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/AGENTS.md +101 -0
- package/agents/codex/AGENTS.md +95 -0
- package/agents/codex/CODING_STYLE.md +71 -0
- package/agents/codex/agents.md.zip +0 -0
- package/agents/codex/client/AGENTS.md +102 -0
- package/agents/codex/client/pages/AGENTS.md +35 -0
- package/agents/codex/server/routes/AGENTS.md +12 -0
- package/agents/codex/server/services/AGENTS.md +137 -0
- package/agents/codex/tests/AGENTS.md +8 -0
- package/cli/app/config.ts +13 -11
- package/cli/app/index.ts +74 -82
- package/cli/bin.js +1 -1
- package/cli/commands/build.ts +51 -14
- package/cli/commands/check.ts +19 -0
- package/cli/commands/deploy/app.ts +4 -8
- package/cli/commands/deploy/web.ts +16 -20
- package/cli/commands/dev.ts +189 -64
- package/cli/commands/devEvents.ts +106 -0
- package/cli/commands/init.ts +63 -57
- package/cli/commands/lint.ts +21 -0
- package/cli/commands/refresh.ts +18 -0
- package/cli/commands/typecheck.ts +18 -0
- package/cli/compiler/client/identite.ts +80 -53
- package/cli/compiler/client/index.ts +139 -213
- package/cli/compiler/common/bundleAnalysis.ts +94 -0
- package/cli/compiler/common/clientManifest.ts +67 -0
- package/cli/compiler/common/controllers.ts +288 -0
- package/cli/compiler/common/files/autres.ts +7 -18
- package/cli/compiler/common/files/images.ts +40 -37
- package/cli/compiler/common/files/style.ts +11 -22
- package/cli/compiler/common/generatedRouteModules.ts +368 -0
- package/cli/compiler/common/index.ts +31 -65
- package/cli/compiler/common/loaders/forbid-ssr-import.js +13 -0
- package/cli/compiler/common/rspackAliases.ts +13 -0
- package/cli/compiler/common/scripts.ts +37 -0
- package/cli/compiler/index.ts +781 -230
- package/cli/compiler/server/index.ts +59 -75
- package/cli/compiler/writeIfChanged.ts +21 -0
- package/cli/index.ts +71 -72
- package/cli/paths.ts +51 -57
- package/cli/print.ts +17 -11
- package/cli/tsconfig.json +5 -4
- package/cli/utils/agents.ts +100 -0
- package/cli/utils/check.ts +71 -0
- package/cli/utils/index.ts +1 -3
- package/cli/utils/keyboard.ts +8 -25
- package/cli/utils/runProcess.ts +30 -0
- package/client/app/component.tsx +29 -29
- package/client/app/index.ts +36 -57
- package/client/app/service.ts +7 -12
- package/client/app.tsconfig.json +2 -2
- package/client/components/Dialog/Manager.ssr.tsx +40 -0
- package/client/components/Dialog/Manager.tsx +119 -150
- package/client/components/Dialog/status.tsx +3 -3
- package/client/components/index.ts +1 -1
- package/client/components/types.d.ts +1 -3
- package/client/dev/hmr.ts +65 -0
- package/client/global.d.ts +2 -2
- package/client/hooks.ts +6 -9
- package/client/index.ts +2 -1
- package/client/islands/index.ts +7 -0
- package/client/islands/useDeferredModule.ts +199 -0
- package/client/pages/_layout/index.tsx +4 -12
- package/client/pages/useHeader.tsx +14 -21
- package/client/router.ts +27 -0
- package/client/services/router/components/Link.tsx +34 -27
- package/client/services/router/components/Page.tsx +6 -14
- package/client/services/router/components/router.ssr.tsx +36 -0
- package/client/services/router/components/router.tsx +63 -83
- package/client/services/router/index.tsx +185 -220
- package/client/services/router/request/api.ts +97 -119
- package/client/services/router/request/history.ts +2 -2
- package/client/services/router/request/index.ts +13 -12
- package/client/services/router/request/multipart.ts +72 -62
- package/client/services/router/response/index.tsx +68 -61
- package/client/services/router/response/page.ts +28 -32
- package/client/utils/dom.ts +17 -33
- package/common/app/index.ts +3 -3
- package/common/data/chaines/index.ts +22 -23
- package/common/data/dates.ts +35 -70
- package/common/data/markdown.ts +42 -39
- package/common/dev/serverHotReload.ts +26 -0
- package/common/errors/index.tsx +110 -142
- package/common/router/contracts.ts +29 -0
- package/common/router/index.ts +89 -108
- package/common/router/layouts.ts +34 -47
- package/common/router/pageSetup.ts +50 -0
- package/common/router/register.ts +53 -24
- package/common/router/request/api.ts +30 -36
- package/common/router/request/index.ts +2 -8
- package/common/router/response/index.ts +8 -15
- package/common/router/response/page.ts +70 -58
- package/common/utils.ts +1 -1
- package/doc/TODO.md +1 -1
- package/eslint.js +62 -0
- package/package.json +14 -49
- package/prettier.config.cjs +9 -0
- package/scripts/cleanup-generated-controllers.ts +62 -0
- package/scripts/fix-reference-app-typing.ts +490 -0
- package/scripts/refactor-client-app-imports.ts +244 -0
- package/scripts/refactor-client-pages.ts +587 -0
- package/scripts/refactor-server-controllers.ts +470 -0
- package/scripts/refactor-server-runtime-aliases.ts +360 -0
- package/scripts/restore-client-app-import-files.ts +41 -0
- package/scripts/restore-files-from-git-head.ts +20 -0
- package/scripts/update-codex-agents.ts +35 -0
- package/server/app/commands.ts +35 -64
- package/server/app/container/config.ts +48 -59
- package/server/app/container/console/index.ts +202 -248
- package/server/app/container/index.ts +33 -71
- package/server/app/controller/index.ts +61 -0
- package/server/app/index.ts +39 -105
- package/server/app/service/container.ts +41 -42
- package/server/app/service/index.ts +120 -147
- package/server/context.ts +1 -1
- package/server/index.ts +25 -1
- package/server/services/auth/index.ts +75 -115
- package/server/services/auth/router/index.ts +31 -32
- package/server/services/auth/router/request.ts +14 -16
- package/server/services/cron/CronTask.ts +13 -26
- package/server/services/cron/index.ts +14 -36
- package/server/services/disks/driver.ts +40 -58
- package/server/services/disks/drivers/local/index.ts +79 -90
- package/server/services/disks/drivers/s3/index.ts +116 -163
- package/server/services/disks/index.ts +23 -38
- package/server/services/email/index.ts +45 -104
- package/server/services/email/utils.ts +14 -27
- package/server/services/fetch/index.ts +53 -85
- package/server/services/prisma/Facet.ts +39 -91
- package/server/services/prisma/index.ts +74 -110
- package/server/services/router/generatedRuntime.ts +29 -0
- package/server/services/router/http/index.ts +78 -73
- package/server/services/router/http/multipart.ts +19 -42
- package/server/services/router/index.ts +378 -365
- package/server/services/router/request/api.ts +26 -25
- package/server/services/router/request/index.ts +44 -51
- package/server/services/router/request/service.ts +7 -11
- package/server/services/router/request/validation/zod.ts +111 -148
- package/server/services/router/response/index.ts +110 -125
- package/server/services/router/response/mask/Filter.ts +31 -72
- package/server/services/router/response/mask/index.ts +8 -15
- package/server/services/router/response/mask/selecteurs.ts +11 -25
- package/server/services/router/response/page/clientManifest.ts +25 -0
- package/server/services/router/response/page/document.tsx +199 -127
- package/server/services/router/response/page/index.tsx +89 -94
- package/server/services/router/service.ts +13 -15
- package/server/services/schema/index.ts +17 -26
- package/server/services/schema/request.ts +19 -33
- package/server/services/schema/router/index.ts +8 -11
- package/server/services/security/encrypt/aes/index.ts +15 -35
- package/server/utils/slug.ts +29 -35
- package/skills/clean-project-code/SKILL.md +63 -0
- package/skills/clean-project-code/agents/openai.yaml +4 -0
- package/tsconfig.common.json +4 -3
- package/tsconfig.json +4 -1
- package/types/aliases.d.ts +17 -21
- package/types/controller-input.test.ts +48 -0
- package/types/express-extra.d.ts +6 -0
- package/types/global/constants.d.ts +13 -0
- package/types/global/express-extra.d.ts +6 -0
- package/types/global/modules.d.ts +13 -16
- package/types/global/utils.d.ts +17 -49
- package/types/global/vendors.d.ts +62 -0
- package/types/icons.d.ts +65 -1
- package/types/uuid.d.ts +3 -0
- package/types/vendors.d.ts +62 -0
- package/cli/compiler/common/babel/index.ts +0 -170
- package/cli/compiler/common/babel/plugins/index.ts +0 -0
- package/cli/compiler/common/babel/plugins/services.ts +0 -586
- package/cli/compiler/common/babel/routes/imports.ts +0 -127
- package/cli/compiler/common/babel/routes/routes.ts +0 -1130
- package/client/services/captcha/index.ts +0 -67
- package/client/services/socket/index.ts +0 -147
- package/common/data/rte/nodes.ts +0 -83
- package/common/data/stats.ts +0 -90
- package/common/utils/rte.ts +0 -183
- package/server/services/auth/old.ts +0 -277
- package/server/services/cache/commands.ts +0 -41
- package/server/services/cache/index.ts +0 -297
- package/server/services/cache/service.json +0 -6
- package/server/services/socket/index.ts +0 -162
- package/server/services/socket/scope.ts +0 -226
- package/server/services/socket/service.json +0 -6
- package/server/services_old/SocketClient.ts +0 -92
- package/server/services_old/Token.old.ts +0 -97
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
// INSPIRATION:
|
|
1
|
+
// INSPIRATION:
|
|
3
2
|
// https://adonisjs.com/docs/4.1/routing
|
|
4
3
|
// https://laravel.com/docs/8.x/routing
|
|
5
4
|
// https://github.com/adonisjs/http-server/blob/develop/src/ServerRouter/indexApi.ts
|
|
@@ -11,39 +10,44 @@
|
|
|
11
10
|
|
|
12
11
|
// Node
|
|
13
12
|
// Npm
|
|
13
|
+
const { v4: uuid } = require('uuid') as { v4: () => string };
|
|
14
14
|
import got from 'got';
|
|
15
15
|
import hInterval from 'human-interval';
|
|
16
16
|
import type express from 'express';
|
|
17
17
|
import type { Request, Response, NextFunction } from 'express';
|
|
18
|
-
import { v4 as uuid } from 'uuid';
|
|
19
18
|
import zod, { ZodError } from 'zod';
|
|
20
19
|
export { default as schema } from 'zod';
|
|
21
|
-
import type { GlobImportedWithMetas } from 'babel-plugin-glob-import';
|
|
22
20
|
|
|
23
21
|
// Core
|
|
24
|
-
import type { Application } from '@server/app';
|
|
25
22
|
import Service, { AnyService, TServiceArgs } from '@server/app/service';
|
|
26
23
|
import context from '@server/context';
|
|
27
24
|
import type DisksManager from '@server/services/disks';
|
|
28
25
|
import { CoreError, InputError, NotFound, toJson as errorToJson } from '@common/errors';
|
|
29
26
|
import BaseRouter, {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
TMatchedRoute,
|
|
28
|
+
TRoute,
|
|
29
|
+
TErrorRoute,
|
|
30
|
+
TRouteModule,
|
|
31
|
+
TRouteOptions,
|
|
32
|
+
defaultOptions,
|
|
33
|
+
matchRoute,
|
|
34
|
+
buildUrl,
|
|
35
|
+
TDomainsList,
|
|
33
36
|
} from '@common/router';
|
|
37
|
+
import type { TSsrUnresolvedRoute, TRegisterPageArgs } from '@common/router/contracts';
|
|
34
38
|
import { buildRegex, getRegisterPageArgs } from '@common/router/register';
|
|
35
39
|
import { layoutsList, getLayout } from '@common/router/layouts';
|
|
36
40
|
import { TFetcherList } from '@common/router/request/api';
|
|
37
41
|
import type { TFrontRenderer } from '@common/router/response/page';
|
|
38
|
-
import type { TSsrUnresolvedRoute, TRegisterPageArgs } from '@client/services/router';
|
|
39
42
|
|
|
40
43
|
// Specific
|
|
41
44
|
import { AnyRouterService } from './service';
|
|
42
|
-
import ServerRequest from
|
|
45
|
+
import ServerRequest from './request';
|
|
43
46
|
import ServerResponse, { TRouterContext, TRouterContextServices } from './response';
|
|
44
47
|
import Page from './response/page';
|
|
45
48
|
import HTTP, { Config as HttpServiceConfig } from './http';
|
|
46
49
|
import DocumentRenderer from './response/page/document';
|
|
50
|
+
import { loadGeneratedRuntimeBundle } from './generatedRuntime';
|
|
47
51
|
|
|
48
52
|
/*----------------------------------
|
|
49
53
|
- TYPES
|
|
@@ -51,113 +55,120 @@ import DocumentRenderer from './response/page/document';
|
|
|
51
55
|
|
|
52
56
|
export { type AnyRouterService, default as RouterService } from './service';
|
|
53
57
|
export { default as RequestService } from './request/service';
|
|
54
|
-
export type { default as Request, UploadedFile } from
|
|
55
|
-
export type { default as Response, TRouterContext } from
|
|
58
|
+
export type { default as Request, UploadedFile } from './request';
|
|
59
|
+
export type { default as Response, TRouterContext } from './response';
|
|
56
60
|
export type { TRoute, TAnyRoute } from '@common/router';
|
|
57
61
|
|
|
58
|
-
export type TApiRegisterArgs<TRouter extends TServerRouter> =
|
|
59
|
-
path: string,
|
|
60
|
-
controller: TServerController<TRouter>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
export type TApiRegisterArgs<TRouter extends TServerRouter> =
|
|
63
|
+
| [path: string, controller: TServerController<TRouter>]
|
|
64
|
+
| [path: string, options: Partial<TRouteOptions>, controller: TServerController<TRouter>];
|
|
65
|
+
|
|
66
|
+
type TGeneratedRouteModule = { filepath: string; register?: TRouteModule['__register'] };
|
|
67
|
+
|
|
68
|
+
type TGeneratedControllerDefinition = {
|
|
69
|
+
path: string;
|
|
70
|
+
Controller: new (request: TRouterContext<TServerRouter>) => { [method: string]: () => any };
|
|
71
|
+
method: string;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
type TGeneratedDefinitionsSnapshot = {
|
|
75
|
+
routes: TMatchedRoute[];
|
|
76
|
+
errors: { [code: number]: TErrorRoute<any> };
|
|
77
|
+
controllers: { [path: string]: TRoute };
|
|
78
|
+
ssrRoutes: TSsrUnresolvedRoute[];
|
|
79
|
+
cache: { [pageId: string]: { rendered: any; expire: number | undefined; options: TRouteOptions['static'] } };
|
|
80
|
+
};
|
|
66
81
|
|
|
67
82
|
export type TServerController<TRouter extends TServerRouter> = (context: TRouterContext<TRouter>) => any;
|
|
68
83
|
|
|
69
|
-
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'
|
|
84
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
|
|
70
85
|
export type TRouteHttpMethod = HttpMethod | '*';
|
|
71
86
|
|
|
72
|
-
export type TApiResponseData = {
|
|
73
|
-
data: any,
|
|
74
|
-
triggers?: { [cle: string]: any }
|
|
75
|
-
}
|
|
87
|
+
export type TApiResponseData = { data: any; triggers?: { [cle: string]: any } };
|
|
76
88
|
|
|
77
|
-
export type HttpHeaders = { [cle: string]: string }
|
|
89
|
+
export type HttpHeaders = { [cle: string]: string };
|
|
78
90
|
|
|
79
91
|
/*----------------------------------
|
|
80
92
|
- SERVICE CONFIG
|
|
81
93
|
----------------------------------*/
|
|
82
94
|
|
|
83
|
-
export type TAnyRouter = ServerRouter<
|
|
95
|
+
export type TAnyRouter = ServerRouter<
|
|
96
|
+
AnyService['app'],
|
|
97
|
+
TRouterServicesList,
|
|
98
|
+
Config<TRouterServicesList, AnyService['app']>
|
|
99
|
+
>;
|
|
84
100
|
|
|
85
101
|
const LogPrefix = '[router]';
|
|
86
102
|
|
|
87
103
|
export type Config<
|
|
88
|
-
TServices extends TRouterServicesList
|
|
104
|
+
TServices extends TRouterServicesList,
|
|
105
|
+
TApplication extends AnyService['app'] = AnyService['app'],
|
|
89
106
|
> = {
|
|
107
|
+
debug: boolean;
|
|
90
108
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
disk?: string, // Disk driver ID
|
|
109
|
+
disk?: string; // Disk driver ID
|
|
94
110
|
|
|
95
|
-
domains: TDomainsList
|
|
111
|
+
domains: TDomainsList;
|
|
96
112
|
|
|
97
|
-
http: HttpServiceConfig
|
|
113
|
+
http: HttpServiceConfig;
|
|
98
114
|
|
|
99
|
-
context: (
|
|
100
|
-
request: ServerRequest<TServerRouter>,
|
|
101
|
-
app: Application
|
|
102
|
-
) => {},
|
|
115
|
+
context: (request: ServerRequest<TServerRouter>, app: TApplication) => {};
|
|
103
116
|
|
|
104
|
-
plugins: TServices
|
|
105
|
-
}
|
|
117
|
+
plugins: TServices;
|
|
118
|
+
};
|
|
106
119
|
|
|
107
120
|
// Set it as a function, so when we instanciate the services, we can callthis.router to pass the router instance in roiuter services
|
|
108
|
-
type TRouterServicesList = {
|
|
109
|
-
[serviceName: string]: AnyRouterService
|
|
110
|
-
}
|
|
121
|
+
type TRouterServicesList = { [serviceName: string]: AnyRouterService };
|
|
111
122
|
|
|
112
|
-
export type Hooks = {
|
|
113
|
-
|
|
114
|
-
}
|
|
123
|
+
export type Hooks = {};
|
|
115
124
|
|
|
116
125
|
export type TControllerDefinition = {
|
|
117
|
-
path?: string
|
|
118
|
-
schema?: zod.ZodSchema
|
|
119
|
-
controller: TServerController<TServerRouter
|
|
120
|
-
}
|
|
126
|
+
path?: string;
|
|
127
|
+
schema?: zod.ZodSchema;
|
|
128
|
+
controller: TServerController<TServerRouter>;
|
|
129
|
+
};
|
|
121
130
|
|
|
122
|
-
export type TServerRouter = ServerRouter<
|
|
131
|
+
export type TServerRouter = ServerRouter<
|
|
132
|
+
AnyService['app'],
|
|
133
|
+
TRouterServicesList,
|
|
134
|
+
Config<TRouterServicesList, AnyService['app']>
|
|
135
|
+
>;
|
|
123
136
|
|
|
124
137
|
/*----------------------------------
|
|
125
138
|
- CLASSE
|
|
126
139
|
----------------------------------*/
|
|
127
140
|
export default class ServerRouter<
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
>
|
|
132
|
-
extends Service<TConfig, Hooks, TApplication, TApplication>
|
|
141
|
+
TApplication extends AnyService['app'] = AnyService['app'],
|
|
142
|
+
TServices extends TRouterServicesList = TRouterServicesList,
|
|
143
|
+
TConfig extends Config<TServices, TApplication> = Config<TServices, TApplication>,
|
|
144
|
+
>
|
|
145
|
+
extends Service<TConfig, Hooks, TApplication, TApplication>
|
|
146
|
+
implements BaseRouter
|
|
147
|
+
{
|
|
148
|
+
public disks = this.use<DisksManager<any, any, TApplication>>('Core/Disks', { optional: true });
|
|
133
149
|
|
|
134
|
-
public disks = this.use<DisksManager>('Core/Disks', { optional: true });
|
|
135
|
-
|
|
136
150
|
// Services
|
|
137
151
|
public http: HTTP;
|
|
138
152
|
public render: DocumentRenderer<this>;
|
|
139
153
|
|
|
140
154
|
// Indexed
|
|
141
|
-
public routes:
|
|
155
|
+
public routes: TMatchedRoute[] = []; // API + pages front front
|
|
142
156
|
public errors: { [code: number]: TErrorRoute } = {};
|
|
143
|
-
public controllers: {[path: string]: TRoute} = {};
|
|
157
|
+
public controllers: { [path: string]: TRoute } = {};
|
|
144
158
|
public ssrRoutes: TSsrUnresolvedRoute[] = [];
|
|
145
159
|
|
|
146
160
|
// Cache (ex: for static pages)
|
|
147
161
|
public cache: {
|
|
148
|
-
[pageId: string]: {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
} = {}
|
|
162
|
+
[pageId: string]: { rendered: any; expire: number | undefined; options: TRouteOptions['static'] };
|
|
163
|
+
} = {};
|
|
164
|
+
|
|
165
|
+
private staticRoutesRefreshInterval?: NodeJS.Timeout;
|
|
154
166
|
|
|
155
167
|
/*----------------------------------
|
|
156
168
|
- SERVICE
|
|
157
169
|
----------------------------------*/
|
|
158
170
|
|
|
159
|
-
public constructor(
|
|
160
|
-
|
|
171
|
+
public constructor(...args: TServiceArgs<ServerRouter<TApplication, TServices, TConfig>>) {
|
|
161
172
|
super(...args);
|
|
162
173
|
|
|
163
174
|
this.http = new HTTP(this.config.http, this);
|
|
@@ -169,20 +180,16 @@ export default class ServerRouter<
|
|
|
169
180
|
----------------------------------*/
|
|
170
181
|
|
|
171
182
|
public async ready() {
|
|
172
|
-
|
|
173
183
|
// Detect router services
|
|
174
184
|
for (const serviceName in this.config.plugins) {
|
|
175
185
|
const service = this.config.plugins[serviceName];
|
|
176
186
|
service.parent = this;
|
|
177
|
-
this.app.register(
|
|
187
|
+
this.app.register(service);
|
|
178
188
|
}
|
|
179
189
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
...require("metas:@/client/pages/**/([a-z0-9]*).tsx"),
|
|
184
|
-
//...require("metas:@/client/pages/**/([a-z0-9]*).tsx")
|
|
185
|
-
]);
|
|
190
|
+
this.registerControllers(this.loadGeneratedControllerDefinitions());
|
|
191
|
+
|
|
192
|
+
this.registerRoutes(this.loadGeneratedRouteModules());
|
|
186
193
|
|
|
187
194
|
// Start HTTP server
|
|
188
195
|
await this.http.start();
|
|
@@ -190,71 +197,78 @@ export default class ServerRouter<
|
|
|
190
197
|
// override
|
|
191
198
|
const originalLog = console.log;
|
|
192
199
|
console.log = (...args: any[]) => {
|
|
193
|
-
|
|
194
200
|
// parse stack trace: skip this function and the console.log call
|
|
195
201
|
/*const stackLine = (new Error()).stack?.split('\n')[2] || '';
|
|
196
202
|
const match = stackLine.match(/at (\w+)\.(\w+) /);
|
|
197
203
|
const className = match ? match[1] : '<global>';
|
|
198
204
|
const methodName = match ? match[2] : '<anonymous>';*/
|
|
199
205
|
|
|
200
|
-
const contextData = context.getStore() || {
|
|
201
|
-
channelType: 'master',
|
|
202
|
-
}
|
|
206
|
+
const contextData = context.getStore() || { channelType: 'master' };
|
|
203
207
|
|
|
204
|
-
const requestPrefix =
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
const requestPrefix =
|
|
209
|
+
contextData.channelType === 'request'
|
|
210
|
+
? `[${contextData.user ? contextData.user : 'guest'}] ${contextData.method} ${contextData.path} |`
|
|
211
|
+
: 'master';
|
|
207
212
|
|
|
208
213
|
// prefix and forward
|
|
209
214
|
originalLog.call(
|
|
210
|
-
console,
|
|
215
|
+
console,
|
|
211
216
|
`${requestPrefix}`, // ${className}.${methodName}
|
|
212
|
-
...args
|
|
217
|
+
...args,
|
|
213
218
|
);
|
|
214
219
|
};
|
|
215
220
|
|
|
216
|
-
|
|
217
221
|
// When all the services are ready, initialize static routes
|
|
218
|
-
this.app.on('ready', () => {
|
|
222
|
+
this.app.on('ready', async () => {
|
|
219
223
|
this.initStaticRoutes();
|
|
220
224
|
});
|
|
221
|
-
|
|
222
225
|
}
|
|
223
226
|
|
|
224
|
-
public async shutdown() {
|
|
227
|
+
public async shutdown() {}
|
|
228
|
+
|
|
229
|
+
public async reloadGeneratedDefinitions(changedFiles: string[] = []) {
|
|
230
|
+
const changeSummary = changedFiles.length > 0 ? `\n${changedFiles.join('\n')}` : '';
|
|
231
|
+
console.info(`[router] Hot reloading generated definitions ...${changeSummary}`);
|
|
225
232
|
|
|
233
|
+
const controllerDefinitions = this.loadGeneratedControllerDefinitions();
|
|
234
|
+
const routeModules = this.loadGeneratedRouteModules();
|
|
235
|
+
const previousState = this.snapshotGeneratedDefinitions();
|
|
236
|
+
|
|
237
|
+
this.resetGeneratedDefinitions();
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
this.registerControllers(controllerDefinitions);
|
|
241
|
+
this.registerRoutes(routeModules);
|
|
242
|
+
this.initStaticRoutes();
|
|
243
|
+
|
|
244
|
+
console.info('[router] Generated definitions hot reloaded.');
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.error('[router] Failed to hot reload generated definitions. Restoring previous router state.');
|
|
247
|
+
this.restoreGeneratedDefinitions(previousState);
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
226
250
|
}
|
|
227
251
|
|
|
228
252
|
/*----------------------------------
|
|
229
253
|
- ACTIONS
|
|
230
254
|
----------------------------------*/
|
|
231
255
|
|
|
232
|
-
public async renderStatic(
|
|
233
|
-
url: string,
|
|
234
|
-
options: TRouteOptions["static"],
|
|
235
|
-
rendered?: any
|
|
236
|
-
) {
|
|
237
|
-
|
|
256
|
+
public async renderStatic(url: string, options: TRouteOptions['static'], rendered?: any) {
|
|
238
257
|
// Wildcard: tell that the newly rendered pages should be cached
|
|
239
|
-
if (url === '*' || !url)
|
|
240
|
-
return;
|
|
258
|
+
if (url === '*' || !url) return;
|
|
241
259
|
|
|
242
260
|
if (!rendered) {
|
|
243
|
-
|
|
244
261
|
console.log('[router] renderStatic: url', url);
|
|
245
262
|
|
|
246
263
|
const fullUrl = this.url(url, {}, true);
|
|
247
|
-
const response = await got(
|
|
264
|
+
const response = await got(fullUrl, {
|
|
248
265
|
method: 'GET',
|
|
249
|
-
headers: {
|
|
250
|
-
'Accept': 'text/html',
|
|
251
|
-
'bypasscache': '1'
|
|
252
|
-
},
|
|
266
|
+
headers: { Accept: 'text/html', bypasscache: '1' },
|
|
253
267
|
throwHttpErrors: false,
|
|
254
268
|
});
|
|
255
269
|
|
|
256
270
|
if (response.statusCode !== 200) {
|
|
257
|
-
console.error(
|
|
271
|
+
console.error('[router] renderStatic: page returned code', response.statusCode, fullUrl);
|
|
258
272
|
return;
|
|
259
273
|
}
|
|
260
274
|
|
|
@@ -264,19 +278,15 @@ export default class ServerRouter<
|
|
|
264
278
|
this.cache[url] = {
|
|
265
279
|
rendered: rendered,
|
|
266
280
|
options: options,
|
|
267
|
-
expire: typeof options === 'object'
|
|
268
|
-
? Date.now() + (hInterval(options.refresh) || 3600)
|
|
269
|
-
: undefined
|
|
281
|
+
expire: typeof options === 'object' ? Date.now() + (hInterval(options.refresh) || 3600) : undefined,
|
|
270
282
|
};
|
|
271
|
-
|
|
272
283
|
}
|
|
273
284
|
|
|
274
285
|
private initStaticRoutes() {
|
|
286
|
+
this.clearStaticRoutesRefreshInterval();
|
|
275
287
|
|
|
276
288
|
for (const route of this.routes) {
|
|
277
|
-
|
|
278
|
-
if (!route.options.static)
|
|
279
|
-
continue;
|
|
289
|
+
if (!route.options.static) continue;
|
|
280
290
|
|
|
281
291
|
// Add to static pages
|
|
282
292
|
// Should be a GET oage that don't take any parameter
|
|
@@ -286,36 +296,35 @@ export default class ServerRouter<
|
|
|
286
296
|
}
|
|
287
297
|
|
|
288
298
|
// Every hours, refresh static pages
|
|
289
|
-
setInterval(
|
|
290
|
-
|
|
291
|
-
|
|
299
|
+
this.staticRoutesRefreshInterval = setInterval(
|
|
300
|
+
() => {
|
|
301
|
+
this.refreshStaticPages();
|
|
302
|
+
},
|
|
303
|
+
1000 * 60 * 60,
|
|
304
|
+
);
|
|
292
305
|
}
|
|
293
306
|
|
|
294
307
|
private refreshStaticPages() {
|
|
295
|
-
|
|
296
308
|
console.log('[router] refreshStaticPages');
|
|
297
|
-
|
|
309
|
+
|
|
298
310
|
for (const pageUrl in this.cache) {
|
|
299
311
|
const page = this.cache[pageUrl];
|
|
300
312
|
if (page.expire && page.expire < Date.now()) {
|
|
301
|
-
|
|
302
313
|
this.renderStatic(pageUrl, page.options);
|
|
303
314
|
}
|
|
304
315
|
}
|
|
305
316
|
}
|
|
306
317
|
|
|
307
|
-
private registerRoutes(defModules:
|
|
318
|
+
private registerRoutes(defModules: TGeneratedRouteModule[]) {
|
|
308
319
|
for (const routeModule of defModules) {
|
|
320
|
+
const register = routeModule.register;
|
|
321
|
+
if (!register) continue;
|
|
309
322
|
|
|
310
|
-
|
|
311
|
-
if (!register)
|
|
312
|
-
continue;
|
|
313
|
-
|
|
314
|
-
this.config.debug && console.log(LogPrefix, `Register file:`, routeModule.matches.join('/'));
|
|
323
|
+
this.config.debug && console.log(LogPrefix, `Register file:`, routeModule.filepath);
|
|
315
324
|
try {
|
|
316
325
|
register(this.app);
|
|
317
326
|
} catch (error) {
|
|
318
|
-
console.error(
|
|
327
|
+
console.error('Failed to register route file:', routeModule);
|
|
319
328
|
console.error('Register function:', register.toString());
|
|
320
329
|
throw error;
|
|
321
330
|
}
|
|
@@ -324,20 +333,35 @@ export default class ServerRouter<
|
|
|
324
333
|
this.afterRegister();
|
|
325
334
|
}
|
|
326
335
|
|
|
327
|
-
|
|
336
|
+
private registerControllers(definitions: TGeneratedControllerDefinition[]) {
|
|
337
|
+
for (const definition of definitions) {
|
|
338
|
+
const route: TRoute<TRouterContext<this>> = {
|
|
339
|
+
method: 'POST',
|
|
340
|
+
path: definition.path,
|
|
341
|
+
controller: (requestContext: TRouterContext<this>) => {
|
|
342
|
+
const controller = new definition.Controller(requestContext);
|
|
343
|
+
return controller[definition.method]();
|
|
344
|
+
},
|
|
345
|
+
options: { ...defaultOptions },
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
this.controllers[route.path] = route;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
public url = (path: string, params: {} = {}, absolute: boolean = true) =>
|
|
328
353
|
buildUrl(path, params, this.config.domains, absolute);
|
|
329
354
|
|
|
330
355
|
/*----------------------------------
|
|
331
356
|
- REGISTER
|
|
332
357
|
----------------------------------*/
|
|
333
358
|
|
|
334
|
-
public page(...args: TRegisterPageArgs) {
|
|
335
|
-
|
|
336
|
-
const { path, options, renderer, layout } = getRegisterPageArgs(...args);
|
|
359
|
+
public page(...args: TRegisterPageArgs<any, TRouteOptions>) {
|
|
360
|
+
const { path, options, setup, renderer, layout } = getRegisterPageArgs(...args);
|
|
337
361
|
|
|
338
362
|
const { regex, keys } = buildRegex(path);
|
|
339
363
|
|
|
340
|
-
const route:
|
|
364
|
+
const route: TMatchedRoute<TRouterContext<this>> = {
|
|
341
365
|
method: 'GET',
|
|
342
366
|
path,
|
|
343
367
|
regex,
|
|
@@ -346,29 +370,30 @@ export default class ServerRouter<
|
|
|
346
370
|
options: {
|
|
347
371
|
...defaultOptions,
|
|
348
372
|
accept: 'html', // Les pages retournent forcémment du html
|
|
349
|
-
|
|
373
|
+
setup,
|
|
374
|
+
...options,
|
|
350
375
|
},
|
|
351
|
-
}
|
|
376
|
+
};
|
|
352
377
|
|
|
353
378
|
this.routes.push(route);
|
|
354
379
|
|
|
355
380
|
return this;
|
|
356
|
-
|
|
357
381
|
}
|
|
358
382
|
|
|
359
383
|
public error(
|
|
360
384
|
code: number,
|
|
361
|
-
options:
|
|
362
|
-
renderer: TFrontRenderer<{}, { message: string }
|
|
385
|
+
options: Partial<TRouteOptions>,
|
|
386
|
+
renderer: TFrontRenderer<{}, { message: string }>,
|
|
363
387
|
) {
|
|
388
|
+
const finalOptions = { ...defaultOptions, ...options };
|
|
364
389
|
|
|
365
390
|
// Automatic layout form the nearest _layout folder
|
|
366
|
-
const layout = getLayout('Error ' + code,
|
|
391
|
+
const layout = getLayout('Error ' + code, finalOptions);
|
|
367
392
|
|
|
368
|
-
const route = {
|
|
393
|
+
const route: TErrorRoute<TRouterContext<this>> = {
|
|
369
394
|
code,
|
|
370
395
|
controller: (context: TRouterContext<this>) => new Page(route, renderer, context, layout),
|
|
371
|
-
options
|
|
396
|
+
options: finalOptions,
|
|
372
397
|
};
|
|
373
398
|
|
|
374
399
|
this.errors[code] = route;
|
|
@@ -382,144 +407,160 @@ export default class ServerRouter<
|
|
|
382
407
|
public patch = (...args: TApiRegisterArgs<this>) => this.registerApi('PATCH', ...args);
|
|
383
408
|
public delete = (...args: TApiRegisterArgs<this>) => this.registerApi('DELETE', ...args);
|
|
384
409
|
|
|
385
|
-
public express(
|
|
386
|
-
middleware: (
|
|
387
|
-
req: Request,
|
|
388
|
-
res: Response,
|
|
389
|
-
next: NextFunction,
|
|
390
|
-
requestContext: TRouterContext<this>
|
|
391
|
-
) => void
|
|
410
|
+
public express(
|
|
411
|
+
middleware: (req: Request, res: Response, next: NextFunction, requestContext: TRouterContext<this>) => void,
|
|
392
412
|
) {
|
|
393
|
-
return (context: TRouterContext<this>) =>
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
413
|
+
return (context: TRouterContext<this>) =>
|
|
414
|
+
new Promise((resolve) => {
|
|
415
|
+
context.request.res.on('finish', function () {
|
|
416
|
+
//console.log('the response has been sent', request.res.statusCode);
|
|
417
|
+
resolve(true);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
middleware(
|
|
421
|
+
context.request.req,
|
|
422
|
+
context.request.res,
|
|
423
|
+
() => {
|
|
424
|
+
resolve(true);
|
|
425
|
+
},
|
|
426
|
+
context,
|
|
427
|
+
);
|
|
398
428
|
});
|
|
399
|
-
|
|
400
|
-
middleware(
|
|
401
|
-
context.request.req,
|
|
402
|
-
context.request.res,
|
|
403
|
-
() => { resolve(true); },
|
|
404
|
-
context
|
|
405
|
-
)
|
|
406
|
-
})
|
|
407
429
|
}
|
|
408
430
|
|
|
409
431
|
protected registerApi(method: TRouteHttpMethod, ...args: TApiRegisterArgs<this>): this {
|
|
410
|
-
|
|
411
432
|
let path: string;
|
|
412
433
|
let options: Partial<TRouteOptions> = {};
|
|
413
434
|
let controller: TServerController<this>;
|
|
414
435
|
|
|
415
|
-
if (args.length === 2)
|
|
416
|
-
|
|
417
|
-
else
|
|
418
|
-
([path, options, controller] = args)
|
|
436
|
+
if (args.length === 2) [path, controller] = args;
|
|
437
|
+
else [path, options, controller] = args;
|
|
419
438
|
|
|
420
439
|
const { regex, keys } = buildRegex(path);
|
|
421
440
|
|
|
422
|
-
const route:
|
|
423
|
-
|
|
441
|
+
const route: TMatchedRoute<TRouterContext<this>> = {
|
|
424
442
|
method: method,
|
|
425
443
|
path: path,
|
|
426
444
|
regex,
|
|
427
445
|
keys: keys,
|
|
428
|
-
options: {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
},
|
|
432
|
-
controller
|
|
433
|
-
}
|
|
446
|
+
options: { ...defaultOptions, ...options },
|
|
447
|
+
controller,
|
|
448
|
+
};
|
|
434
449
|
|
|
435
450
|
this.routes.push(route);
|
|
436
451
|
|
|
437
452
|
return this;
|
|
438
453
|
}
|
|
439
454
|
|
|
440
|
-
private
|
|
455
|
+
private clearStaticRoutesRefreshInterval() {
|
|
456
|
+
if (this.staticRoutesRefreshInterval) {
|
|
457
|
+
clearInterval(this.staticRoutesRefreshInterval);
|
|
458
|
+
this.staticRoutesRefreshInterval = undefined;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
private resetGeneratedDefinitions() {
|
|
463
|
+
this.clearStaticRoutesRefreshInterval();
|
|
464
|
+
this.routes = [];
|
|
465
|
+
this.errors = {};
|
|
466
|
+
this.controllers = {};
|
|
467
|
+
this.ssrRoutes = [];
|
|
468
|
+
this.cache = {};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private snapshotGeneratedDefinitions(): TGeneratedDefinitionsSnapshot {
|
|
472
|
+
return {
|
|
473
|
+
routes: [...this.routes],
|
|
474
|
+
errors: { ...this.errors },
|
|
475
|
+
controllers: { ...this.controllers },
|
|
476
|
+
ssrRoutes: [...this.ssrRoutes],
|
|
477
|
+
cache: { ...this.cache },
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
private restoreGeneratedDefinitions(snapshot: TGeneratedDefinitionsSnapshot) {
|
|
482
|
+
this.routes = snapshot.routes;
|
|
483
|
+
this.errors = snapshot.errors;
|
|
484
|
+
this.controllers = snapshot.controllers;
|
|
485
|
+
this.ssrRoutes = snapshot.ssrRoutes;
|
|
486
|
+
this.cache = snapshot.cache;
|
|
487
|
+
this.initStaticRoutes();
|
|
488
|
+
}
|
|
441
489
|
|
|
490
|
+
private loadGeneratedControllerDefinitions() {
|
|
491
|
+
return (
|
|
492
|
+
loadGeneratedRuntimeBundle<TGeneratedControllerDefinition[]>('controllers') ||
|
|
493
|
+
require('@/server/.generated/controllers').default ||
|
|
494
|
+
[]
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
private loadGeneratedRouteModules() {
|
|
499
|
+
return (
|
|
500
|
+
loadGeneratedRuntimeBundle<TGeneratedRouteModule[]>('routes') ||
|
|
501
|
+
require('@/server/.generated/routes').default ||
|
|
502
|
+
[]
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
private async afterRegister() {
|
|
442
507
|
// Ordonne par ordre de priorité
|
|
443
|
-
this.config.debug && console.info(
|
|
508
|
+
this.config.debug && console.info('Loading routes ...');
|
|
444
509
|
this.routes.sort((r1, r2) => {
|
|
445
|
-
|
|
446
510
|
const prioDelta = r2.options.priority - r1.options.priority;
|
|
447
|
-
if (prioDelta !== 0)
|
|
448
|
-
return prioDelta;
|
|
511
|
+
if (prioDelta !== 0) return prioDelta;
|
|
449
512
|
|
|
450
513
|
// HTML avant json
|
|
451
|
-
if (r1.options.accept === 'html' && r2.options.accept !== 'html')
|
|
452
|
-
return -1;
|
|
514
|
+
if (r1.options.accept === 'html' && r2.options.accept !== 'html') return -1;
|
|
453
515
|
|
|
454
516
|
// Unchanged
|
|
455
517
|
return 0;
|
|
456
|
-
})
|
|
518
|
+
});
|
|
457
519
|
// - Génère les définitions de route pour le client
|
|
458
520
|
this.config.debug && console.info(`Registered routes:`);
|
|
459
521
|
for (const route of this.routes) {
|
|
522
|
+
const chunkId = route.options.id;
|
|
460
523
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
this.config.debug && console.info('-',
|
|
464
|
-
route.method,
|
|
465
|
-
route.path,
|
|
466
|
-
' :: ', JSON.stringify(route.options)
|
|
467
|
-
);
|
|
468
|
-
|
|
469
|
-
if (chunkId)
|
|
470
|
-
this.ssrRoutes.push({
|
|
471
|
-
regex: route.regex.source,
|
|
472
|
-
keys: route.keys,
|
|
473
|
-
chunk: chunkId
|
|
474
|
-
});
|
|
524
|
+
this.config.debug && console.info('-', route.method, route.path, ' :: ', JSON.stringify(route.options));
|
|
475
525
|
|
|
526
|
+
if (chunkId) this.ssrRoutes.push({ regex: route.regex.source, keys: route.keys, chunk: chunkId });
|
|
476
527
|
}
|
|
477
528
|
|
|
478
529
|
this.config.debug && console.info(`Registered error pages:`);
|
|
479
530
|
for (const code in this.errors) {
|
|
480
|
-
|
|
481
531
|
const route = this.errors[code];
|
|
482
|
-
const chunkId = route.options
|
|
532
|
+
const chunkId = route.options.id;
|
|
483
533
|
|
|
484
|
-
this.config.debug && console.info('-', code,
|
|
485
|
-
' :: ', JSON.stringify(route.options)
|
|
486
|
-
);
|
|
534
|
+
this.config.debug && console.info('-', code, ' :: ', JSON.stringify(route.options));
|
|
487
535
|
|
|
488
|
-
this.ssrRoutes.push({
|
|
489
|
-
code: parseInt(code),
|
|
490
|
-
chunk: chunkId,
|
|
491
|
-
});
|
|
536
|
+
if (chunkId) this.ssrRoutes.push({ code: parseInt(code), chunk: chunkId });
|
|
492
537
|
}
|
|
493
538
|
|
|
494
539
|
this.config.debug && console.info(`Registered layouts:`);
|
|
495
540
|
for (const layoutId in layoutsList) {
|
|
496
|
-
|
|
497
541
|
const layout = layoutsList[layoutId];
|
|
498
542
|
|
|
499
543
|
this.config.debug && console.info('-', layoutId, layout);
|
|
500
544
|
}
|
|
501
545
|
|
|
502
|
-
this.config.debug && console.info(this.routes.length +
|
|
546
|
+
this.config.debug && console.info(this.routes.length + ' routes where registered.');
|
|
503
547
|
}
|
|
504
548
|
|
|
505
549
|
/*----------------------------------
|
|
506
550
|
- RESOLUTION
|
|
507
551
|
----------------------------------*/
|
|
508
552
|
public async middleware(req: express.Request, res: express.Response) {
|
|
509
|
-
|
|
510
553
|
// Don't cache HTML, because in case of update, assets file name will change (hash.ext)
|
|
511
554
|
// https://github.com/helmetjs/nocache/blob/main/index.ts
|
|
512
|
-
res.setHeader(
|
|
513
|
-
res.setHeader(
|
|
514
|
-
"Cache-Control",
|
|
515
|
-
"no-store, no-cache, must-revalidate, proxy-revalidate"
|
|
516
|
-
);
|
|
555
|
+
res.setHeader('Surrogate-Control', 'no-store');
|
|
556
|
+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
517
557
|
|
|
518
558
|
// Create request
|
|
519
559
|
let requestId = uuid();
|
|
520
|
-
const cachedPage = req.headers['bypasscache']
|
|
521
|
-
|
|
522
|
-
|
|
560
|
+
const cachedPage = req.headers['bypasscache'] ? undefined : this.cache[req.path];
|
|
561
|
+
const headers: HttpHeaders = Object.fromEntries(
|
|
562
|
+
Object.entries(req.headers).map(([key, value]) => [key, Array.isArray(value) ? value.join(', ') : value || '']),
|
|
563
|
+
);
|
|
523
564
|
|
|
524
565
|
const request = new ServerRequest(
|
|
525
566
|
requestId,
|
|
@@ -528,28 +569,25 @@ export default class ServerRouter<
|
|
|
528
569
|
req.path, // url sans params
|
|
529
570
|
// Exclusion de req.files, car le middleware multipart les a normalisé dans req.body
|
|
530
571
|
{ ...req.query, ...req.body },
|
|
531
|
-
|
|
572
|
+
headers,
|
|
532
573
|
|
|
533
574
|
res,
|
|
534
|
-
this
|
|
575
|
+
this,
|
|
535
576
|
);
|
|
536
577
|
|
|
537
578
|
let response: ServerResponse<this>;
|
|
538
579
|
try {
|
|
539
|
-
|
|
540
580
|
// Hook
|
|
541
581
|
await this.runHook('request', request);
|
|
542
582
|
|
|
543
583
|
// Bulk API Requests
|
|
544
|
-
if (request.path === '/api' && typeof request.data.fetchers ===
|
|
545
|
-
|
|
584
|
+
if (request.path === '/api' && typeof request.data.fetchers === 'object') {
|
|
546
585
|
return await this.resolveApiBatch(request.data.fetchers, request);
|
|
547
|
-
|
|
548
586
|
} else {
|
|
549
587
|
response = await this.resolve(
|
|
550
|
-
request,
|
|
588
|
+
request,
|
|
551
589
|
// If cached page, we only run routes with priority >= 10
|
|
552
|
-
cachedPage ? true : false
|
|
590
|
+
cachedPage ? true : false,
|
|
553
591
|
);
|
|
554
592
|
}
|
|
555
593
|
} catch (e) {
|
|
@@ -557,13 +595,12 @@ export default class ServerRouter<
|
|
|
557
595
|
}
|
|
558
596
|
|
|
559
597
|
if (!res.headersSent) {
|
|
560
|
-
|
|
561
598
|
// Static pages
|
|
562
599
|
if (cachedPage) {
|
|
563
600
|
console.log('[router] Get static page from cache', req.path);
|
|
564
|
-
res.send(
|
|
601
|
+
res.send(cachedPage.rendered);
|
|
565
602
|
return;
|
|
566
|
-
}
|
|
603
|
+
}
|
|
567
604
|
|
|
568
605
|
// Status
|
|
569
606
|
res.status(response.statusCode);
|
|
@@ -576,130 +613,117 @@ export default class ServerRouter<
|
|
|
576
613
|
}
|
|
577
614
|
}
|
|
578
615
|
|
|
579
|
-
public createContextServices(
|
|
580
|
-
|
|
581
|
-
const contextServices: Partial<TRouterContextServices<this>> = {}
|
|
616
|
+
public createContextServices(request: ServerRequest<this>) {
|
|
617
|
+
const contextServices: Partial<TRouterContextServices<this>> = {};
|
|
582
618
|
for (const serviceName in this.config.plugins) {
|
|
583
|
-
|
|
584
619
|
const routerService = this.config.plugins[serviceName];
|
|
585
620
|
if (!routerService)
|
|
586
|
-
throw new Error(
|
|
621
|
+
throw new Error(
|
|
622
|
+
`Could not access router service ${serviceName}. Maybe the referenced service is not started yet? Try to reduce its priority.`,
|
|
623
|
+
);
|
|
587
624
|
|
|
588
625
|
if (!routerService.requestService)
|
|
589
|
-
throw new Error(
|
|
626
|
+
throw new Error(
|
|
627
|
+
`Router service ${serviceName} is not implementing the requestService method from the RouterService interface.`,
|
|
628
|
+
);
|
|
590
629
|
|
|
591
|
-
const requestService = routerService.requestService(
|
|
630
|
+
const requestService = routerService.requestService(request);
|
|
592
631
|
if (requestService !== null)
|
|
593
|
-
contextServices[
|
|
594
|
-
|
|
632
|
+
contextServices[serviceName as keyof TRouterContextServices<this>] =
|
|
633
|
+
requestService as TRouterContextServices<this>[keyof TRouterContextServices<this>];
|
|
595
634
|
}
|
|
596
635
|
|
|
597
636
|
return contextServices;
|
|
598
637
|
}
|
|
599
638
|
|
|
600
|
-
public resolve = (
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
error.details.origin = errOrigin;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
this.printTakenTime(timeStart);
|
|
678
|
-
reject( error );
|
|
679
|
-
}
|
|
639
|
+
public resolve = (request: ServerRequest<this>, isStatic?: boolean) =>
|
|
640
|
+
new Promise<ServerResponse<this>>((resolve, reject) => {
|
|
641
|
+
// Create request context so we can access request context across all the request-triggered libs
|
|
642
|
+
context.run(
|
|
643
|
+
{
|
|
644
|
+
// This is for debugging
|
|
645
|
+
channelType: 'request',
|
|
646
|
+
channelId: request.id,
|
|
647
|
+
method: request.method,
|
|
648
|
+
path: request.path,
|
|
649
|
+
},
|
|
650
|
+
async () => {
|
|
651
|
+
const timeStart = Date.now();
|
|
652
|
+
|
|
653
|
+
if (this.status === 'starting') {
|
|
654
|
+
console.log(LogPrefix, `Waiting for servert to be resdy before resolving request`);
|
|
655
|
+
await this.started;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
try {
|
|
659
|
+
const response = new ServerResponse<this>(request);
|
|
660
|
+
|
|
661
|
+
await this.runHook('resolve', request);
|
|
662
|
+
|
|
663
|
+
// Controller route
|
|
664
|
+
const controllerRoute = this.controllers[request.path];
|
|
665
|
+
if (controllerRoute !== undefined) {
|
|
666
|
+
// Create response
|
|
667
|
+
await response.runController(controllerRoute);
|
|
668
|
+
if (response.wasProvided) return resolve(response);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const contextStore = context.getStore();
|
|
672
|
+
if (contextStore) contextStore.user = request.user?.email;
|
|
673
|
+
|
|
674
|
+
// Classic routes
|
|
675
|
+
for (const route of this.routes) {
|
|
676
|
+
if (isStatic && !route.options.whenStatic) continue;
|
|
677
|
+
|
|
678
|
+
// Match Method
|
|
679
|
+
if (request.method !== route.method && route.method !== '*') continue;
|
|
680
|
+
|
|
681
|
+
// Match Response format
|
|
682
|
+
if (!request.accepts(route.options.accept)) continue;
|
|
683
|
+
|
|
684
|
+
const isMatching = matchRoute(route, request);
|
|
685
|
+
if (!isMatching) continue;
|
|
686
|
+
|
|
687
|
+
await this.resolvedRoute(route, response, timeStart);
|
|
688
|
+
if (response.wasProvided) return resolve(response);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
reject(new NotFound());
|
|
692
|
+
} catch (error) {
|
|
693
|
+
const typedError =
|
|
694
|
+
error instanceof Error
|
|
695
|
+
? error
|
|
696
|
+
: new Error(typeof error === 'string' ? error : 'Unknown router error');
|
|
697
|
+
|
|
698
|
+
if (this.app.env.profile === 'dev') {
|
|
699
|
+
console.log('API batch error:', request.method, request.path, typedError);
|
|
700
|
+
const errOrigin = request.method + ' ' + request.path;
|
|
701
|
+
if ('details' in typedError) {
|
|
702
|
+
const routerError = typedError as Error & { details?: { origin?: string } };
|
|
703
|
+
if (routerError.details === undefined) routerError.details = { origin: errOrigin };
|
|
704
|
+
else routerError.details.origin = errOrigin;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
this.printTakenTime(timeStart);
|
|
709
|
+
reject(typedError);
|
|
710
|
+
}
|
|
711
|
+
},
|
|
712
|
+
);
|
|
680
713
|
});
|
|
681
|
-
});
|
|
682
714
|
|
|
683
|
-
private async resolvedRoute(
|
|
684
|
-
route
|
|
685
|
-
response: ServerResponse<this>,
|
|
686
|
-
timeStart: number
|
|
687
|
-
) {
|
|
715
|
+
private async resolvedRoute(route: TMatchedRoute, response: ServerResponse<this>, timeStart: number) {
|
|
716
|
+
route = await response.resolveRouteOptions(route);
|
|
688
717
|
|
|
689
718
|
// Run on resolution hooks. Ex: authentication check
|
|
690
719
|
await this.runHook('resolved', route, response.request, response);
|
|
691
720
|
|
|
692
721
|
// Create response
|
|
693
722
|
await response.runController(route);
|
|
694
|
-
if (!response.wasProvided)
|
|
695
|
-
return;
|
|
723
|
+
if (!response.wasProvided) return;
|
|
696
724
|
|
|
697
725
|
// Set in cache
|
|
698
|
-
if (
|
|
699
|
-
response.request.path
|
|
700
|
-
&& route.options.static
|
|
701
|
-
&& route.options.static.urls.includes('*')
|
|
702
|
-
) {
|
|
726
|
+
if (response.request.path && route.options.static && route.options.static.urls.includes('*')) {
|
|
703
727
|
console.log('[router] Set in cache', response.request.path);
|
|
704
728
|
this.renderStatic(response.request.path, route.options.static, response.data);
|
|
705
729
|
}
|
|
@@ -709,26 +733,26 @@ export default class ServerRouter<
|
|
|
709
733
|
}
|
|
710
734
|
|
|
711
735
|
private printTakenTime = (timeStart: number, timeEndResolving?: number) => {
|
|
712
|
-
|
|
713
736
|
if (this.app.env.name === 'server') return;
|
|
714
737
|
|
|
715
|
-
console.log(
|
|
716
|
-
|
|
738
|
+
console.log(
|
|
739
|
+
Math.round(Date.now() - timeStart) +
|
|
740
|
+
'ms' +
|
|
741
|
+
(timeEndResolving === undefined ? '' : ' | Routing: ' + Math.round(timeEndResolving - timeStart)),
|
|
717
742
|
);
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
private async resolveApiBatch( fetchers: TFetcherList, request: ServerRequest<this> ) {
|
|
743
|
+
};
|
|
721
744
|
|
|
745
|
+
private async resolveApiBatch(fetchers: TFetcherList, request: ServerRequest<this>) {
|
|
722
746
|
// TODO: use api.fetchSync instead
|
|
723
747
|
|
|
724
748
|
const responseData: TObjetDonnees = {};
|
|
725
749
|
for (const id in fetchers) {
|
|
750
|
+
const fetcher = fetchers[id];
|
|
751
|
+
if (!fetcher || !('method' in fetcher)) continue;
|
|
726
752
|
|
|
727
|
-
const { method, path, data } =
|
|
753
|
+
const { method, path, data } = fetcher;
|
|
728
754
|
|
|
729
|
-
const response = await this.resolve(
|
|
730
|
-
request.children(method, path, data)
|
|
731
|
-
);
|
|
755
|
+
const response = await this.resolve(request.children(method, path, data));
|
|
732
756
|
|
|
733
757
|
responseData[id] = response.data;
|
|
734
758
|
|
|
@@ -741,61 +765,50 @@ export default class ServerRouter<
|
|
|
741
765
|
request.res.json(responseData);
|
|
742
766
|
}
|
|
743
767
|
|
|
744
|
-
private async handleError(
|
|
745
|
-
|
|
768
|
+
private async handleError(e: unknown, request: ServerRequest<this>) {
|
|
769
|
+
let error: Error | CoreError;
|
|
746
770
|
if (e instanceof ZodError)
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
771
|
+
error = new InputError(e.issues.map((issue) => issue.path.join('.') + ': ' + issue.message).join(', '));
|
|
772
|
+
else if (e instanceof Error) error = e;
|
|
773
|
+
else error = new Error(typeof e === 'string' ? e : 'Unknown error');
|
|
750
774
|
|
|
751
|
-
const code = 'http' in
|
|
775
|
+
const code = 'http' in error ? error.http : 500;
|
|
752
776
|
|
|
753
|
-
const response = new ServerResponse(request).status(code)
|
|
777
|
+
const response = new ServerResponse(request).status(code);
|
|
754
778
|
|
|
755
779
|
// Rapport / debug
|
|
756
780
|
if (code === 500) {
|
|
757
|
-
|
|
758
781
|
// Print the error here so the stacktrace appears in the bug report logs
|
|
759
|
-
console.log(LogPrefix,
|
|
782
|
+
console.log(LogPrefix, 'Error catched from the router:', error);
|
|
760
783
|
|
|
761
784
|
// Report error
|
|
762
|
-
await this.app.runHook('error',
|
|
785
|
+
await this.app.runHook('error', error, request);
|
|
763
786
|
|
|
764
787
|
// Don't exose technical errors to users
|
|
765
788
|
if (this.app.env.profile === 'prod')
|
|
766
|
-
|
|
767
|
-
|
|
789
|
+
error = new Error(
|
|
790
|
+
'We encountered an internal error, and our team has just been notified. Sorry for the inconvenience.',
|
|
768
791
|
);
|
|
769
|
-
|
|
770
792
|
} else {
|
|
771
|
-
|
|
772
793
|
// For debugging HTTP errors
|
|
773
794
|
/*if (this.app.env.profile === "dev")
|
|
774
795
|
console.warn(e);*/
|
|
775
796
|
|
|
776
|
-
await this.app.runHook('error.' + code,
|
|
797
|
+
await this.app.runHook('error.' + code, error, request);
|
|
777
798
|
}
|
|
778
799
|
|
|
779
800
|
// Return error based on the request format
|
|
780
|
-
if (request.accepts(
|
|
781
|
-
|
|
801
|
+
if (request.accepts('html')) {
|
|
782
802
|
const route = this.errors[code];
|
|
783
|
-
if (route === undefined)
|
|
784
|
-
throw new Error(`No route for error code ${code}`);
|
|
785
|
-
|
|
786
|
-
const jsonError = errorToJson(e);
|
|
787
|
-
await response.setRoute(route).runController(route, {
|
|
788
|
-
error: jsonError
|
|
789
|
-
});
|
|
803
|
+
if (route === undefined) throw new Error(`No route for error code ${code}`);
|
|
790
804
|
|
|
791
|
-
|
|
792
|
-
|
|
805
|
+
const jsonError = errorToJson(error);
|
|
806
|
+
await response.setRoute(route).runController(route, { error: jsonError });
|
|
807
|
+
} else if (request.accepts('json')) {
|
|
808
|
+
const jsonError = errorToJson(error);
|
|
793
809
|
await response.json(jsonError);
|
|
794
|
-
} else
|
|
795
|
-
await response.text(e.message);
|
|
810
|
+
} else await response.text(error.message);
|
|
796
811
|
|
|
797
812
|
return response;
|
|
798
|
-
|
|
799
813
|
}
|
|
800
|
-
|
|
801
|
-
}
|
|
814
|
+
}
|