proteum 2.1.0-2 → 2.1.0-3
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 +51 -93
- package/README.md +44 -1
- package/agents/framework/AGENTS.md +155 -788
- package/agents/project/AGENTS.md +81 -110
- package/agents/project/client/AGENTS.md +22 -93
- package/agents/project/client/pages/AGENTS.md +24 -26
- package/agents/project/server/routes/AGENTS.md +10 -8
- package/agents/project/server/services/AGENTS.md +22 -159
- package/agents/project/tests/AGENTS.md +11 -8
- package/cli/commands/dev.ts +1 -0
- package/cli/commands/trace.ts +210 -0
- package/cli/compiler/client/index.ts +30 -8
- package/cli/compiler/server/index.ts +28 -6
- package/cli/paths.ts +16 -1
- package/cli/presentation/commands.ts +23 -1
- package/cli/presentation/devSession.ts +5 -0
- package/cli/runtime/commands.ts +31 -0
- package/common/dev/requestTrace.ts +81 -0
- package/docs/request-tracing.md +115 -0
- package/package.json +1 -1
- package/server/app/container/config.ts +15 -0
- package/server/app/container/index.ts +3 -0
- package/server/app/container/trace/index.ts +284 -0
- package/server/services/prisma/index.ts +61 -5
- package/server/services/router/http/index.ts +40 -0
- package/server/services/router/index.ts +159 -6
- package/server/services/router/response/index.ts +80 -7
- package/server/services/router/response/page/document.tsx +16 -0
- package/server/services/router/response/page/index.tsx +27 -1
- package/Rte.zip +0 -0
- package/agents/project/agents.md.zip +0 -0
- package/doc/TODO.md +0 -71
- package/doc/front/router.md +0 -27
- package/doc/workspace/workspace.png +0 -0
- package/doc/workspace/workspace2.png +0 -0
- package/doc/workspace/workspace_26.01.22.png +0 -0
- package/server/services/router/http/session.ts.old +0 -40
|
@@ -581,14 +581,29 @@ export default class ServerRouter<
|
|
|
581
581
|
this,
|
|
582
582
|
);
|
|
583
583
|
|
|
584
|
+
this.app.container.Trace.startRequest({
|
|
585
|
+
id: request.id,
|
|
586
|
+
method: request.method,
|
|
587
|
+
path: request.path,
|
|
588
|
+
url: request.url,
|
|
589
|
+
headers: request.headers,
|
|
590
|
+
data: request.data,
|
|
591
|
+
});
|
|
592
|
+
|
|
584
593
|
let response: ServerResponse<this>;
|
|
585
594
|
try {
|
|
586
595
|
// Hook
|
|
587
596
|
await this.runHook('request', request);
|
|
597
|
+
this.app.container.Trace.setRequestUser(request.id, request.user?.email);
|
|
588
598
|
|
|
589
599
|
// Bulk API Requests
|
|
590
600
|
if (request.path === '/api' && typeof request.data.fetchers === 'object') {
|
|
591
|
-
|
|
601
|
+
await this.resolveApiBatch(request.data.fetchers, request);
|
|
602
|
+
this.app.container.Trace.finishRequest(request.id, {
|
|
603
|
+
statusCode: request.res.statusCode || 200,
|
|
604
|
+
user: request.user?.email,
|
|
605
|
+
});
|
|
606
|
+
return;
|
|
592
607
|
} else {
|
|
593
608
|
response = await this.resolve(
|
|
594
609
|
request,
|
|
@@ -604,7 +619,17 @@ export default class ServerRouter<
|
|
|
604
619
|
// Static pages
|
|
605
620
|
if (cachedPage) {
|
|
606
621
|
console.log('[router] Get static page from cache', req.path);
|
|
622
|
+
this.app.container.Trace.record(
|
|
623
|
+
request.id,
|
|
624
|
+
'response.send',
|
|
625
|
+
{ cached: true, statusCode: response.statusCode, contentType: 'text/html' },
|
|
626
|
+
'summary',
|
|
627
|
+
);
|
|
607
628
|
res.send(cachedPage.rendered);
|
|
629
|
+
this.app.container.Trace.finishRequest(request.id, {
|
|
630
|
+
statusCode: response.statusCode,
|
|
631
|
+
user: request.user?.email,
|
|
632
|
+
});
|
|
608
633
|
return;
|
|
609
634
|
}
|
|
610
635
|
|
|
@@ -613,9 +638,34 @@ export default class ServerRouter<
|
|
|
613
638
|
// Headers
|
|
614
639
|
res.header(response.headers);
|
|
615
640
|
// Data
|
|
641
|
+
this.app.container.Trace.record(
|
|
642
|
+
request.id,
|
|
643
|
+
'response.send',
|
|
644
|
+
{
|
|
645
|
+
cached: false,
|
|
646
|
+
statusCode: response.statusCode,
|
|
647
|
+
contentType: response.headers['Content-Type'] || '',
|
|
648
|
+
headerKeys: Object.keys(response.headers),
|
|
649
|
+
},
|
|
650
|
+
'summary',
|
|
651
|
+
);
|
|
616
652
|
res.send(response.data);
|
|
653
|
+
this.app.container.Trace.finishRequest(request.id, {
|
|
654
|
+
statusCode: response.statusCode,
|
|
655
|
+
user: request.user?.email,
|
|
656
|
+
});
|
|
617
657
|
} else if (response.data !== 'true') {
|
|
658
|
+
this.app.container.Trace.finishRequest(request.id, {
|
|
659
|
+
statusCode: res.statusCode || response.statusCode,
|
|
660
|
+
user: request.user?.email,
|
|
661
|
+
errorMessage: "Can't return data from the controller since response has already been sent via express.",
|
|
662
|
+
});
|
|
618
663
|
throw new Error("Can't return data from the controller since response has already been sent via express.");
|
|
664
|
+
} else {
|
|
665
|
+
this.app.container.Trace.finishRequest(request.id, {
|
|
666
|
+
statusCode: res.statusCode || response.statusCode,
|
|
667
|
+
user: request.user?.email,
|
|
668
|
+
});
|
|
619
669
|
}
|
|
620
670
|
}
|
|
621
671
|
|
|
@@ -655,6 +705,16 @@ export default class ServerRouter<
|
|
|
655
705
|
},
|
|
656
706
|
async () => {
|
|
657
707
|
const timeStart = Date.now();
|
|
708
|
+
const routeStats = {
|
|
709
|
+
total: this.routes.length,
|
|
710
|
+
staticSkipped: 0,
|
|
711
|
+
methodMismatch: 0,
|
|
712
|
+
acceptMismatch: 0,
|
|
713
|
+
pathMismatch: 0,
|
|
714
|
+
matched: 0,
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
this.app.container.Trace.record(request.id, 'resolve.start', { isStatic: Boolean(isStatic) }, 'summary');
|
|
658
718
|
|
|
659
719
|
if (this.status === 'starting') {
|
|
660
720
|
console.log(LogPrefix, `Waiting for servert to be resdy before resolving request`);
|
|
@@ -669,6 +729,16 @@ export default class ServerRouter<
|
|
|
669
729
|
// Controller route
|
|
670
730
|
const controllerRoute = this.controllers[request.path];
|
|
671
731
|
if (controllerRoute !== undefined) {
|
|
732
|
+
this.app.container.Trace.record(
|
|
733
|
+
request.id,
|
|
734
|
+
'resolve.controller-route',
|
|
735
|
+
{
|
|
736
|
+
path: request.path,
|
|
737
|
+
accept: controllerRoute.options.accept || '',
|
|
738
|
+
filepath: controllerRoute.options.filepath || '',
|
|
739
|
+
},
|
|
740
|
+
'summary',
|
|
741
|
+
);
|
|
672
742
|
// Create response
|
|
673
743
|
await response.runController(controllerRoute);
|
|
674
744
|
if (response.wasProvided) return resolve(response);
|
|
@@ -679,21 +749,81 @@ export default class ServerRouter<
|
|
|
679
749
|
|
|
680
750
|
// Classic routes
|
|
681
751
|
for (const route of this.routes) {
|
|
682
|
-
if (isStatic && !route.options.whenStatic)
|
|
752
|
+
if (isStatic && !route.options.whenStatic) {
|
|
753
|
+
routeStats.staticSkipped++;
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
683
756
|
|
|
684
757
|
// Match Method
|
|
685
|
-
if (request.method !== route.method && route.method !== '*')
|
|
758
|
+
if (request.method !== route.method && route.method !== '*') {
|
|
759
|
+
routeStats.methodMismatch++;
|
|
760
|
+
if (this.app.container.Trace.shouldCapture(request.id, 'deep')) {
|
|
761
|
+
this.app.container.Trace.record(
|
|
762
|
+
request.id,
|
|
763
|
+
'resolve.route-skip',
|
|
764
|
+
{
|
|
765
|
+
reason: 'method',
|
|
766
|
+
routeMethod: route.method,
|
|
767
|
+
requestMethod: request.method,
|
|
768
|
+
routePath: route.path || '',
|
|
769
|
+
routeId: route.options.id || '',
|
|
770
|
+
filepath: route.options.filepath || '',
|
|
771
|
+
},
|
|
772
|
+
'deep',
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
686
777
|
|
|
687
778
|
// Match Response format
|
|
688
|
-
if (!request.accepts(route.options.accept))
|
|
779
|
+
if (!request.accepts(route.options.accept)) {
|
|
780
|
+
routeStats.acceptMismatch++;
|
|
781
|
+
if (this.app.container.Trace.shouldCapture(request.id, 'deep')) {
|
|
782
|
+
this.app.container.Trace.record(
|
|
783
|
+
request.id,
|
|
784
|
+
'resolve.route-skip',
|
|
785
|
+
{
|
|
786
|
+
reason: 'accept',
|
|
787
|
+
routeAccept: route.options.accept || '',
|
|
788
|
+
routePath: route.path || '',
|
|
789
|
+
routeId: route.options.id || '',
|
|
790
|
+
filepath: route.options.filepath || '',
|
|
791
|
+
},
|
|
792
|
+
'deep',
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
689
797
|
|
|
690
798
|
const isMatching = matchRoute(route, request);
|
|
691
|
-
if (!isMatching)
|
|
799
|
+
if (!isMatching) {
|
|
800
|
+
routeStats.pathMismatch++;
|
|
801
|
+
if (this.app.container.Trace.shouldCapture(request.id, 'deep')) {
|
|
802
|
+
this.app.container.Trace.record(
|
|
803
|
+
request.id,
|
|
804
|
+
'resolve.route-skip',
|
|
805
|
+
{
|
|
806
|
+
reason: 'path',
|
|
807
|
+
routePath: route.path || '',
|
|
808
|
+
routeId: route.options.id || '',
|
|
809
|
+
filepath: route.options.filepath || '',
|
|
810
|
+
},
|
|
811
|
+
'deep',
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
692
816
|
|
|
817
|
+
routeStats.matched++;
|
|
693
818
|
await this.resolvedRoute(route, response, timeStart);
|
|
694
|
-
if (response.wasProvided)
|
|
819
|
+
if (response.wasProvided) {
|
|
820
|
+
this.app.container.Trace.record(request.id, 'resolve.routes-evaluated', routeStats, 'resolve');
|
|
821
|
+
return resolve(response);
|
|
822
|
+
}
|
|
695
823
|
}
|
|
696
824
|
|
|
825
|
+
this.app.container.Trace.record(request.id, 'resolve.routes-evaluated', routeStats, 'resolve');
|
|
826
|
+
this.app.container.Trace.record(request.id, 'resolve.not-found', { path: request.path }, 'summary');
|
|
697
827
|
reject(new NotFound());
|
|
698
828
|
} catch (error) {
|
|
699
829
|
const typedError =
|
|
@@ -721,6 +851,19 @@ export default class ServerRouter<
|
|
|
721
851
|
private async resolvedRoute(route: TMatchedRoute, response: ServerResponse<this>, timeStart: number) {
|
|
722
852
|
route = await response.resolveRouteOptions(route);
|
|
723
853
|
|
|
854
|
+
this.app.container.Trace.record(
|
|
855
|
+
response.request.id,
|
|
856
|
+
'resolve.route-match',
|
|
857
|
+
{
|
|
858
|
+
routePath: route.path || '',
|
|
859
|
+
routeId: route.options.id || '',
|
|
860
|
+
filepath: route.options.filepath || '',
|
|
861
|
+
accept: route.options.accept || '',
|
|
862
|
+
method: route.method,
|
|
863
|
+
},
|
|
864
|
+
'summary',
|
|
865
|
+
);
|
|
866
|
+
|
|
724
867
|
// Run on resolution hooks. Ex: authentication check
|
|
725
868
|
await this.runHook('resolved', route, response.request, response);
|
|
726
869
|
|
|
@@ -782,6 +925,16 @@ export default class ServerRouter<
|
|
|
782
925
|
|
|
783
926
|
const response = new ServerResponse(request).status(code);
|
|
784
927
|
|
|
928
|
+
this.app.container.Trace.record(
|
|
929
|
+
request.id,
|
|
930
|
+
'error',
|
|
931
|
+
{
|
|
932
|
+
code,
|
|
933
|
+
error: error instanceof Error ? error : new Error(error.message),
|
|
934
|
+
},
|
|
935
|
+
'summary',
|
|
936
|
+
);
|
|
937
|
+
|
|
785
938
|
// Rapport / debug
|
|
786
939
|
if (code === 500) {
|
|
787
940
|
// Print the error here so the stacktrace appears in the bug report logs
|
|
@@ -88,6 +88,11 @@ export type TRouterContextServices<
|
|
|
88
88
|
|
|
89
89
|
export type TRouterRequestContext<TRouter extends TServerRouter> = TServerRouterCustomContext<TRouter>;
|
|
90
90
|
|
|
91
|
+
const getRouteTraceTarget = (route: TAnyRoute<TRouterContext<TServerRouter>>) => {
|
|
92
|
+
if ('code' in route) return String(route.code);
|
|
93
|
+
return route.path || '';
|
|
94
|
+
};
|
|
95
|
+
|
|
91
96
|
/*----------------------------------
|
|
92
97
|
- CLASSE
|
|
93
98
|
----------------------------------*/
|
|
@@ -129,6 +134,18 @@ export default class ServerResponse<
|
|
|
129
134
|
// Update canonical url
|
|
130
135
|
this.updateCanonicalUrl(route);
|
|
131
136
|
|
|
137
|
+
this.app.container.Trace.record(
|
|
138
|
+
this.request.id,
|
|
139
|
+
'controller.start',
|
|
140
|
+
{
|
|
141
|
+
target: getRouteTraceTarget(route as TAnyRoute<TRouterContext<TServerRouter>>),
|
|
142
|
+
routeId: route.options.id || '',
|
|
143
|
+
filepath: route.options.filepath || '',
|
|
144
|
+
accept: route.options.accept || '',
|
|
145
|
+
},
|
|
146
|
+
'summary',
|
|
147
|
+
);
|
|
148
|
+
|
|
132
149
|
// Create response context for controllers
|
|
133
150
|
const requestContext = await this.createContext(route);
|
|
134
151
|
const contextStore = context.getStore() as
|
|
@@ -141,16 +158,41 @@ export default class ServerResponse<
|
|
|
141
158
|
|
|
142
159
|
// Run controller
|
|
143
160
|
const content = await this.route.controller(requestContext);
|
|
144
|
-
if (content === undefined)
|
|
161
|
+
if (content === undefined) {
|
|
162
|
+
this.app.container.Trace.record(this.request.id, 'controller.result', { kind: 'undefined' }, 'summary');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
145
165
|
|
|
146
166
|
// No need to process the content
|
|
147
|
-
if (content instanceof ServerResponse)
|
|
167
|
+
if (content instanceof ServerResponse) {
|
|
168
|
+
this.app.container.Trace.record(this.request.id, 'controller.result', { kind: 'response' }, 'summary');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
148
171
|
// Render react page to html
|
|
149
|
-
else if (content instanceof Page)
|
|
172
|
+
else if (content instanceof Page) {
|
|
173
|
+
this.app.container.Trace.record(
|
|
174
|
+
this.request.id,
|
|
175
|
+
'controller.result',
|
|
176
|
+
{ kind: 'page', chunkId: content.chunkId || '' },
|
|
177
|
+
'summary',
|
|
178
|
+
);
|
|
179
|
+
await this.render(content, requestContext, additionnalData);
|
|
180
|
+
}
|
|
150
181
|
// Return HTML
|
|
151
|
-
else if (typeof content === 'string' && this.route.options.accept === 'html')
|
|
182
|
+
else if (typeof content === 'string' && this.route.options.accept === 'html') {
|
|
183
|
+
this.app.container.Trace.record(
|
|
184
|
+
this.request.id,
|
|
185
|
+
'controller.result',
|
|
186
|
+
{ kind: 'html', length: content.length },
|
|
187
|
+
'summary',
|
|
188
|
+
);
|
|
189
|
+
await this.html(content);
|
|
190
|
+
}
|
|
152
191
|
// Return JSON
|
|
153
|
-
else
|
|
192
|
+
else {
|
|
193
|
+
this.app.container.Trace.record(this.request.id, 'controller.result', { kind: 'json', data: content }, 'resolve');
|
|
194
|
+
await this.json(content);
|
|
195
|
+
}
|
|
154
196
|
}
|
|
155
197
|
|
|
156
198
|
private updateCanonicalUrl(route: TAnyRoute<TRouterContext<TRouter>>) {
|
|
@@ -175,13 +217,20 @@ export default class ServerResponse<
|
|
|
175
217
|
const requestContext = await this.createContext(route);
|
|
176
218
|
const { options } = splitRouteSetupResult(((setup as any)({ ...requestContext, data: this.request.data }) as {}) || {});
|
|
177
219
|
|
|
220
|
+
this.app.container.Trace.record(
|
|
221
|
+
this.request.id,
|
|
222
|
+
'setup.options',
|
|
223
|
+
{ optionKeys: Object.keys(options) },
|
|
224
|
+
'resolve',
|
|
225
|
+
);
|
|
226
|
+
|
|
178
227
|
return { ...route, options: { ...route.options, ...options } };
|
|
179
228
|
}
|
|
180
229
|
|
|
181
230
|
// Start controller services
|
|
182
231
|
private async createContext(route: TAnyRoute<TRouterContext<TRouter>>): Promise<TRequestContext> {
|
|
183
232
|
const contextServices = this.router.createContextServices(this.request);
|
|
184
|
-
|
|
233
|
+
const controllers = createControllers(this.request.api);
|
|
185
234
|
const customSsrData = this.router.config.context(this.request, this.app) as TRouterRequestContext<TRouter>;
|
|
186
235
|
|
|
187
236
|
// TODO: transmiss safe data (especially for Router), as Router info could be printed on client side
|
|
@@ -196,7 +245,7 @@ export default class ServerResponse<
|
|
|
196
245
|
|
|
197
246
|
Router: this.router,
|
|
198
247
|
...(this.app as {}),
|
|
199
|
-
...
|
|
248
|
+
...controllers,
|
|
200
249
|
|
|
201
250
|
// Router services
|
|
202
251
|
...(contextServices as TRouterContextServices<TRouter>),
|
|
@@ -205,6 +254,19 @@ export default class ServerResponse<
|
|
|
205
254
|
|
|
206
255
|
requestContext.context = requestContext;
|
|
207
256
|
|
|
257
|
+
this.app.container.Trace.record(
|
|
258
|
+
this.request.id,
|
|
259
|
+
'context.create',
|
|
260
|
+
{
|
|
261
|
+
target: getRouteTraceTarget(route as TAnyRoute<TRouterContext<TServerRouter>>),
|
|
262
|
+
routeId: route.options.id || '',
|
|
263
|
+
routerServiceKeys: Object.keys(contextServices),
|
|
264
|
+
controllerKeys: Object.keys(controllers),
|
|
265
|
+
customContextKeys: Object.keys(customSsrData as object),
|
|
266
|
+
},
|
|
267
|
+
'resolve',
|
|
268
|
+
);
|
|
269
|
+
|
|
208
270
|
return requestContext;
|
|
209
271
|
}
|
|
210
272
|
|
|
@@ -249,6 +311,17 @@ export default class ServerResponse<
|
|
|
249
311
|
// Example: error message for error pages
|
|
250
312
|
page.data = { ...page.data, ...additionnalData };
|
|
251
313
|
|
|
314
|
+
this.app.container.Trace.record(
|
|
315
|
+
this.request.id,
|
|
316
|
+
'page.data',
|
|
317
|
+
{
|
|
318
|
+
chunkId: page.chunkId || '',
|
|
319
|
+
dataKeys: Object.keys(page.data || {}),
|
|
320
|
+
data: page.data || {},
|
|
321
|
+
},
|
|
322
|
+
'resolve',
|
|
323
|
+
);
|
|
324
|
+
|
|
252
325
|
// Render page
|
|
253
326
|
await this.router.runHook('render', page);
|
|
254
327
|
const document = await page.render();
|
|
@@ -182,6 +182,22 @@ export default class DocumentRenderer<TRouter extends TServerRouter> {
|
|
|
182
182
|
const ssrData = response.forSsr(page);
|
|
183
183
|
const context = safeStringify(ssrData);
|
|
184
184
|
const routesForClient = JSON.stringify(this.router.ssrRoutes);
|
|
185
|
+
const customContextKeys = Object.keys(ssrData).filter((key) => !['request', 'page', 'user', 'domains'].includes(key));
|
|
186
|
+
|
|
187
|
+
this.app.container.Trace.record(
|
|
188
|
+
response.request.id,
|
|
189
|
+
'ssr.payload',
|
|
190
|
+
{
|
|
191
|
+
chunkId: page.chunkId || '',
|
|
192
|
+
topLevelKeys: Object.keys(ssrData),
|
|
193
|
+
requestDataKeys: Object.keys(ssrData.request.data || {}),
|
|
194
|
+
pageDataKeys: Object.keys(ssrData.page.data || {}),
|
|
195
|
+
customContextKeys,
|
|
196
|
+
serializedBytes: Buffer.byteLength(context, 'utf8'),
|
|
197
|
+
routeCount: this.router.ssrRoutes.length,
|
|
198
|
+
},
|
|
199
|
+
'resolve',
|
|
200
|
+
);
|
|
185
201
|
|
|
186
202
|
return (
|
|
187
203
|
<>
|
|
@@ -62,6 +62,17 @@ export default class ServerPage<TRouter extends TServerRouter = TServerRouter> e
|
|
|
62
62
|
// Ex: runtime added scripts, title, metas, ....
|
|
63
63
|
|
|
64
64
|
const context = this.context as TPageRenderContext & TRouterContext<TRouter>;
|
|
65
|
+
const requestId = context.response.request.id;
|
|
66
|
+
this.app.container.Trace.record(
|
|
67
|
+
requestId,
|
|
68
|
+
'render.start',
|
|
69
|
+
{
|
|
70
|
+
chunkId: this.chunkId || '',
|
|
71
|
+
title: this.title || '',
|
|
72
|
+
routeId: this.route.options['id'] || '',
|
|
73
|
+
},
|
|
74
|
+
'summary',
|
|
75
|
+
);
|
|
65
76
|
const html = renderToString(
|
|
66
77
|
<App context={context as Parameters<typeof App>[0]['context'] & TRouterContext<TRouter>} />,
|
|
67
78
|
);
|
|
@@ -82,7 +93,22 @@ export default class ServerPage<TRouter extends TServerRouter = TServerRouter> e
|
|
|
82
93
|
if (page.theme)
|
|
83
94
|
attrsBody.className += ' ' + page.theme;*/
|
|
84
95
|
|
|
85
|
-
return this.router.render.page(html, this, context.response)
|
|
96
|
+
return this.router.render.page(html, this, context.response).then((document) => {
|
|
97
|
+
this.app.container.Trace.record(
|
|
98
|
+
requestId,
|
|
99
|
+
'render.end',
|
|
100
|
+
{
|
|
101
|
+
chunkId: this.chunkId || '',
|
|
102
|
+
htmlLength: html.length,
|
|
103
|
+
documentLength: document.length,
|
|
104
|
+
styleCount: this.style.length,
|
|
105
|
+
scriptCount: this.scripts.length,
|
|
106
|
+
},
|
|
107
|
+
'summary',
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
return document;
|
|
111
|
+
});
|
|
86
112
|
}
|
|
87
113
|
|
|
88
114
|
/*----------------------------------
|
package/Rte.zip
DELETED
|
Binary file
|
|
Binary file
|
package/doc/TODO.md
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
* Fix erreurs type Client / Server context
|
|
2
|
-
* Server side: ServerContext
|
|
3
|
-
* Client side: ClientContext | ServerContext
|
|
4
|
-
* PageResponse extends Response
|
|
5
|
-
* Toast service
|
|
6
|
-
* ClientApplication hooks
|
|
7
|
-
app.on('bug')
|
|
8
|
-
app.on('error')
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# Dependancies injection
|
|
12
|
-
|
|
13
|
-
# Full stack Pages
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import Router from '@server/services/router';
|
|
17
|
-
import { TRouterContext as ServerServices } from '@server/services/router/response';
|
|
18
|
-
import { TRouterContext as ClientServices } from '@client/services/router/response';
|
|
19
|
-
|
|
20
|
-
abstract class Controller<
|
|
21
|
-
TRouter extends Router,
|
|
22
|
-
TData extends any = any,
|
|
23
|
-
TUserAccess extends string = string
|
|
24
|
-
> {
|
|
25
|
-
|
|
26
|
-
abstract auth: TUserAccess;
|
|
27
|
-
|
|
28
|
-
abstract get( services: ServerServices<TRouter> ): Promise<TData>;
|
|
29
|
-
|
|
30
|
-
abstract render( context: TData, services: ClientServices<TRouter> ): ComponentChild;
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
//? /headhunter/missions/suggested'
|
|
37
|
-
class Missions extends Controller<CrossPath["router"]> {
|
|
38
|
-
|
|
39
|
-
auth = 'USER';
|
|
40
|
-
|
|
41
|
-
async get({ headhunting, response, auth }) {
|
|
42
|
-
|
|
43
|
-
const user = await auth.check('USER');
|
|
44
|
-
|
|
45
|
-
const suggested = await headhunting.missions.Suggest( user );
|
|
46
|
-
|
|
47
|
-
return { suggested }
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
render({ page, api, suggested }) {
|
|
51
|
-
return (
|
|
52
|
-
<Page title="App title here" subtitle="SEO description here">{page.loading || <>
|
|
53
|
-
|
|
54
|
-
<section class="col">
|
|
55
|
-
|
|
56
|
-
<header class="row">
|
|
57
|
-
<h2 class="col-1">Suggested Missions</h2>
|
|
58
|
-
</header>
|
|
59
|
-
|
|
60
|
-
<div class="grid xa3">
|
|
61
|
-
{suggested.map( mission => (
|
|
62
|
-
<MissionCard mission={mission} />
|
|
63
|
-
))}
|
|
64
|
-
</div>
|
|
65
|
-
</section>
|
|
66
|
-
|
|
67
|
-
</>}</Page>
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
```
|
package/doc/front/router.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Disallowed
|
|
2
|
-
|
|
3
|
-
- To destructure page data objects
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
route.page('/withdraw', { bodyId: 'withdraw' }, ({}, { api }) => ({
|
|
7
|
-
|
|
8
|
-
withdraw: api.get('/withdraw')
|
|
9
|
-
|
|
10
|
-
}), ({ withdraw: { history, balance, minimum, fees } }, { api, modal, page, user }) => {
|
|
11
|
-
|
|
12
|
-
...
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Do instead:
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
route.page('/withdraw', { bodyId: 'withdraw' }, ({}, { api }) => ({
|
|
19
|
-
|
|
20
|
-
withdraw: api.get('/withdraw')
|
|
21
|
-
|
|
22
|
-
}), ({ withdraw }, { api, modal, page, user }) => {
|
|
23
|
-
|
|
24
|
-
const { history, balance, minimum, fees } = withdraw;
|
|
25
|
-
|
|
26
|
-
...
|
|
27
|
-
```
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// Npm
|
|
2
|
-
import session from 'express-session';
|
|
3
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
4
|
-
import redisConnector from 'connect-redis';
|
|
5
|
-
const RedisStore = redisConnector(session);
|
|
6
|
-
|
|
7
|
-
// Services
|
|
8
|
-
import type { THttpConfig } from '@server/services/http';
|
|
9
|
-
import Redis from '@server/services/redis';
|
|
10
|
-
|
|
11
|
-
// Middleware
|
|
12
|
-
export const createSessionMiddleware = (httpConfig: THttpConfig) => {
|
|
13
|
-
return session({
|
|
14
|
-
|
|
15
|
-
genid: (req) => {
|
|
16
|
-
return /*req.id;*/uuidv4(); // ID session via UUID
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
name: httpConfig.session.name,
|
|
20
|
-
store: new RedisStore({
|
|
21
|
-
client: Redis.instance,
|
|
22
|
-
ttl: httpConfig.session.duration // secondes
|
|
23
|
-
}),
|
|
24
|
-
secret: httpConfig.session.secret,
|
|
25
|
-
|
|
26
|
-
// Ces deux valeurs sont recommandes avec session filestore
|
|
27
|
-
// https://github.com/valery-barysok/session-file-store/blob/master/examples/express-example/app.js
|
|
28
|
-
resave: true, // Quand false, /admin/console réinitialise la session
|
|
29
|
-
saveUninitialized: true,
|
|
30
|
-
|
|
31
|
-
cookie: {
|
|
32
|
-
maxAge: httpConfig.session.duration * 1000, // millisecondes
|
|
33
|
-
//sameSite: true,
|
|
34
|
-
httpOnly: true,
|
|
35
|
-
// Les variables d'environnement sont des chaines
|
|
36
|
-
secure: httpConfig.ssl,
|
|
37
|
-
path: '/',
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
}
|