proteum 1.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/.dockerignore +10 -0
- package/Rte.zip +0 -0
- package/cli/app/config.ts +54 -0
- package/cli/app/index.ts +195 -0
- package/cli/bin.js +11 -0
- package/cli/commands/build.ts +34 -0
- package/cli/commands/deploy/app.ts +29 -0
- package/cli/commands/deploy/web.ts +60 -0
- package/cli/commands/dev.ts +109 -0
- package/cli/commands/init.ts +85 -0
- package/cli/compiler/client/identite.ts +72 -0
- package/cli/compiler/client/index.ts +334 -0
- package/cli/compiler/common/babel/index.ts +170 -0
- package/cli/compiler/common/babel/plugins/index.ts +0 -0
- package/cli/compiler/common/babel/plugins/services.ts +579 -0
- package/cli/compiler/common/babel/routes/imports.ts +127 -0
- package/cli/compiler/common/babel/routes/routes.ts +1130 -0
- package/cli/compiler/common/files/autres.ts +39 -0
- package/cli/compiler/common/files/images.ts +35 -0
- package/cli/compiler/common/files/style.ts +78 -0
- package/cli/compiler/common/index.ts +154 -0
- package/cli/compiler/index.ts +532 -0
- package/cli/compiler/server/index.ts +211 -0
- package/cli/index.ts +189 -0
- package/cli/paths.ts +165 -0
- package/cli/print.ts +12 -0
- package/cli/tsconfig.json +38 -0
- package/cli/utils/index.ts +22 -0
- package/cli/utils/keyboard.ts +78 -0
- package/client/app/component.tsx +54 -0
- package/client/app/index.ts +142 -0
- package/client/app/service.ts +34 -0
- package/client/app.tsconfig.json +28 -0
- package/client/components/Button.tsx +298 -0
- package/client/components/Dialog/Manager.tsx +309 -0
- package/client/components/Dialog/card.tsx +208 -0
- package/client/components/Dialog/index.less +151 -0
- package/client/components/Dialog/status.less +176 -0
- package/client/components/Dialog/status.tsx +48 -0
- package/client/components/index.ts +2 -0
- package/client/components/types.d.ts +3 -0
- package/client/data/input.ts +32 -0
- package/client/global.d.ts +5 -0
- package/client/hooks.ts +22 -0
- package/client/index.ts +6 -0
- package/client/pages/_layout/index.less +6 -0
- package/client/pages/_layout/index.tsx +43 -0
- package/client/pages/bug.tsx.old +60 -0
- package/client/pages/useHeader.tsx +50 -0
- package/client/services/captcha/index.ts +67 -0
- package/client/services/router/components/Link.tsx +46 -0
- package/client/services/router/components/Page.tsx +55 -0
- package/client/services/router/components/router.tsx +218 -0
- package/client/services/router/index.tsx +521 -0
- package/client/services/router/request/api.ts +267 -0
- package/client/services/router/request/history.ts +5 -0
- package/client/services/router/request/index.ts +53 -0
- package/client/services/router/request/multipart.ts +147 -0
- package/client/services/router/response/index.tsx +128 -0
- package/client/services/router/response/page.ts +86 -0
- package/client/services/socket/index.ts +147 -0
- package/client/utils/dom.ts +77 -0
- package/common/app/index.ts +9 -0
- package/common/data/chaines/index.ts +54 -0
- package/common/data/dates.ts +179 -0
- package/common/data/markdown.ts +73 -0
- package/common/data/rte/nodes.ts +83 -0
- package/common/data/stats.ts +90 -0
- package/common/errors/index.tsx +326 -0
- package/common/router/index.ts +213 -0
- package/common/router/layouts.ts +93 -0
- package/common/router/register.ts +55 -0
- package/common/router/request/api.ts +77 -0
- package/common/router/request/index.ts +35 -0
- package/common/router/response/index.ts +45 -0
- package/common/router/response/page.ts +128 -0
- package/common/utils/rte.ts +183 -0
- package/common/utils.ts +7 -0
- package/doc/TODO.md +71 -0
- package/doc/front/router.md +27 -0
- 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/package.json +171 -0
- package/server/app/commands.ts +141 -0
- package/server/app/container/config.ts +203 -0
- package/server/app/container/console/index.ts +550 -0
- package/server/app/container/index.ts +137 -0
- package/server/app/index.ts +273 -0
- package/server/app/service/container.ts +88 -0
- package/server/app/service/index.ts +235 -0
- package/server/app.tsconfig.json +28 -0
- package/server/context.ts +4 -0
- package/server/index.ts +4 -0
- package/server/services/auth/index.ts +250 -0
- package/server/services/auth/old.ts +277 -0
- package/server/services/auth/router/index.ts +95 -0
- package/server/services/auth/router/request.ts +54 -0
- package/server/services/auth/router/service.json +6 -0
- package/server/services/auth/service.json +6 -0
- package/server/services/cache/commands.ts +41 -0
- package/server/services/cache/index.ts +297 -0
- package/server/services/cache/service.json +6 -0
- package/server/services/cron/CronTask.ts +86 -0
- package/server/services/cron/index.ts +112 -0
- package/server/services/cron/service.json +6 -0
- package/server/services/disks/driver.ts +103 -0
- package/server/services/disks/drivers/local/index.ts +188 -0
- package/server/services/disks/drivers/local/service.json +6 -0
- package/server/services/disks/drivers/s3/index.ts +301 -0
- package/server/services/disks/drivers/s3/service.json +6 -0
- package/server/services/disks/index.ts +90 -0
- package/server/services/disks/service.json +6 -0
- package/server/services/email/index.ts +188 -0
- package/server/services/email/utils.ts +53 -0
- package/server/services/fetch/index.ts +201 -0
- package/server/services/fetch/service.json +7 -0
- package/server/services/models.7z +0 -0
- package/server/services/prisma/Facet.ts +142 -0
- package/server/services/prisma/index.ts +201 -0
- package/server/services/prisma/service.json +6 -0
- package/server/services/router/http/index.ts +217 -0
- package/server/services/router/http/multipart.ts +102 -0
- package/server/services/router/http/session.ts.old +40 -0
- package/server/services/router/index.ts +801 -0
- package/server/services/router/request/api.ts +87 -0
- package/server/services/router/request/index.ts +184 -0
- package/server/services/router/request/service.ts +21 -0
- package/server/services/router/request/validation/zod.ts +180 -0
- package/server/services/router/response/index.ts +338 -0
- package/server/services/router/response/mask/Filter.ts +323 -0
- package/server/services/router/response/mask/index.ts +60 -0
- package/server/services/router/response/mask/selecteurs.ts +92 -0
- package/server/services/router/response/page/document.tsx +160 -0
- package/server/services/router/response/page/index.tsx +196 -0
- package/server/services/router/service.json +6 -0
- package/server/services/router/service.ts +36 -0
- package/server/services/schema/index.ts +44 -0
- package/server/services/schema/request.ts +49 -0
- package/server/services/schema/router/index.ts +28 -0
- package/server/services/schema/router/service.json +6 -0
- package/server/services/schema/service.json +6 -0
- package/server/services/security/encrypt/aes/index.ts +85 -0
- package/server/services/security/encrypt/aes/service.json +6 -0
- package/server/services/socket/index.ts +162 -0
- package/server/services/socket/scope.ts +226 -0
- package/server/services/socket/service.json +6 -0
- package/server/services_old/SocketClient.ts +92 -0
- package/server/services_old/Token.old.ts +97 -0
- package/server/utils/slug.ts +79 -0
- package/tsconfig.common.json +45 -0
- package/tsconfig.json +3 -0
- package/types/aliases.d.ts +54 -0
- package/types/global/modules.d.ts +49 -0
- package/types/global/utils.d.ts +103 -0
- package/types/icons.d.ts +1 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Libs
|
|
6
|
+
import compilerSelecteurs, { TObjetSelecteurs } from './selecteurs';
|
|
7
|
+
import Filter from './Filter';
|
|
8
|
+
|
|
9
|
+
// Filtres spécifiques métier
|
|
10
|
+
//import filtresProps from '@/general/serveur/filtresApi';
|
|
11
|
+
|
|
12
|
+
/*----------------------------------
|
|
13
|
+
- TYPES
|
|
14
|
+
----------------------------------*/
|
|
15
|
+
|
|
16
|
+
/*----------------------------------
|
|
17
|
+
- CONFIG
|
|
18
|
+
----------------------------------*/
|
|
19
|
+
|
|
20
|
+
const debug = false;
|
|
21
|
+
|
|
22
|
+
/*----------------------------------
|
|
23
|
+
- MODULE
|
|
24
|
+
----------------------------------*/
|
|
25
|
+
|
|
26
|
+
// proprieteModele = Si l'objet actuellement traité fait partie des valeurs d'un modèle
|
|
27
|
+
export default (
|
|
28
|
+
donnee: any,
|
|
29
|
+
selecteurs?: string | TObjetSelecteurs
|
|
30
|
+
) => {
|
|
31
|
+
|
|
32
|
+
// Pas besoin de filtrer
|
|
33
|
+
if (donnee === null || typeof donnee !== 'object'/* || donnee._filtered === true */) {
|
|
34
|
+
return donnee;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Correction sélecteurs
|
|
38
|
+
let selecteursCompiles: TObjetSelecteurs | undefined;
|
|
39
|
+
if (selecteurs === undefined)
|
|
40
|
+
selecteursCompiles = undefined;
|
|
41
|
+
else if (typeof selecteurs === 'string')
|
|
42
|
+
selecteursCompiles = compilerSelecteurs(selecteurs);
|
|
43
|
+
else // Déjà un objet, pas besoin de compiler
|
|
44
|
+
selecteursCompiles = selecteurs;
|
|
45
|
+
|
|
46
|
+
debug && console.log('Avant filtrage', donnee);
|
|
47
|
+
|
|
48
|
+
const filtre = new Filter();
|
|
49
|
+
const retour = filtre.filtrer(donnee, selecteursCompiles);
|
|
50
|
+
|
|
51
|
+
// Empêche un nouveau filtrage
|
|
52
|
+
// OBSOLETE: éviter d emodifier le retour des api, car cela peut poser probleme lors du traitement de ces données (ex: itération clés objet)
|
|
53
|
+
/*if (typeof retour === 'object' && retour !== null)
|
|
54
|
+
retour._filtered = true;*/
|
|
55
|
+
|
|
56
|
+
debug && console.log('Apres filtrage', retour);
|
|
57
|
+
|
|
58
|
+
return retour;
|
|
59
|
+
|
|
60
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
/*----------------------------------
|
|
6
|
+
- TYPES
|
|
7
|
+
----------------------------------*/
|
|
8
|
+
export type TSelecteur = true | "*" | TObjetSelecteurs
|
|
9
|
+
// Wildcard en clé = selectionner toutes les clés
|
|
10
|
+
// Wildcard en valeur = sélectionner absolument toutes les valeur descendantes
|
|
11
|
+
export type TObjetSelecteurs = {[cle: string]: TSelecteur}
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- CONSTANTES
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
const reSelecteurs = /((([a-z0-9\*\@\.\_\#]+)|(\{[^\}]+\}))\s*(\(?))|\)/gi; // <branche>( <elem1> <elem2> )
|
|
17
|
+
|
|
18
|
+
const cache: TObjetSelecteurs = {};
|
|
19
|
+
|
|
20
|
+
/*----------------------------------
|
|
21
|
+
- FONCTION
|
|
22
|
+
----------------------------------*/
|
|
23
|
+
export default (selecteurs: string) => {
|
|
24
|
+
|
|
25
|
+
if (cache[selecteurs] === undefined)
|
|
26
|
+
cache[selecteurs] = compiler(selecteurs);
|
|
27
|
+
|
|
28
|
+
return cache[selecteurs];
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const compiler = (selecteurs: string): TObjetSelecteurs => {
|
|
33
|
+
|
|
34
|
+
let chemins: TObjetSelecteurs = {};
|
|
35
|
+
let cheminA: string[] = []; // Chemin en construction
|
|
36
|
+
|
|
37
|
+
let branche;
|
|
38
|
+
while (branche = reSelecteurs.exec(selecteurs)) {
|
|
39
|
+
|
|
40
|
+
if (branche[0] === ')') { // fermeture
|
|
41
|
+
|
|
42
|
+
// Niveau précédent: docs.titre => docs
|
|
43
|
+
cheminA.pop();
|
|
44
|
+
|
|
45
|
+
} else {
|
|
46
|
+
|
|
47
|
+
const nomBranche = branche[2];
|
|
48
|
+
const ouverture = branche[5] === '(';
|
|
49
|
+
|
|
50
|
+
// Ligne débutant par un # = commentaire
|
|
51
|
+
if (nomBranche.startsWith("#"))
|
|
52
|
+
continue;
|
|
53
|
+
|
|
54
|
+
if (ouverture)
|
|
55
|
+
// Ouverture de la branche: docs => docs.titre
|
|
56
|
+
cheminA.push(nomBranche);
|
|
57
|
+
else {
|
|
58
|
+
|
|
59
|
+
const chemin = [...cheminA, nomBranche];
|
|
60
|
+
|
|
61
|
+
// Construction du chemin
|
|
62
|
+
let brancheA = chemins;
|
|
63
|
+
const nbBranches = chemin.length;
|
|
64
|
+
for (let iBranche = 0; iBranche < nbBranches; iBranche++) {
|
|
65
|
+
|
|
66
|
+
const nomBranche = chemin[iBranche];
|
|
67
|
+
|
|
68
|
+
const extremite = iBranche === nbBranches - 1
|
|
69
|
+
if (extremite) {
|
|
70
|
+
|
|
71
|
+
// Dernière branche = extremité = true
|
|
72
|
+
brancheA[nomBranche] = true;
|
|
73
|
+
|
|
74
|
+
} else {
|
|
75
|
+
|
|
76
|
+
// Pas encore définie
|
|
77
|
+
if (brancheA[nomBranche] === undefined)
|
|
78
|
+
// Sinon, initialisation
|
|
79
|
+
brancheA[nomBranche] = {};
|
|
80
|
+
|
|
81
|
+
// Rférnce pour la prochaine itération
|
|
82
|
+
brancheA = brancheA[nomBranche];
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return chemins;
|
|
91
|
+
|
|
92
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import renderToString from "preact-render-to-string";
|
|
8
|
+
const safeStringify = require('fast-safe-stringify'); // remplace les références circulairs par un [Circular]
|
|
9
|
+
|
|
10
|
+
// Core
|
|
11
|
+
import type { default as Router, Response as ServerResponse } from "@server/services/router";
|
|
12
|
+
import type Page from '.';
|
|
13
|
+
|
|
14
|
+
/*----------------------------------
|
|
15
|
+
- TYPES
|
|
16
|
+
----------------------------------*/
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/*----------------------------------
|
|
20
|
+
- SERVICE
|
|
21
|
+
----------------------------------*/
|
|
22
|
+
export default class DocumentRenderer<TRouter extends Router> {
|
|
23
|
+
|
|
24
|
+
public constructor(
|
|
25
|
+
public router: TRouter,
|
|
26
|
+
public app = router.app
|
|
27
|
+
) {
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public staticDocument() {
|
|
32
|
+
const routesForClient = JSON.stringify( this.router.ssrRoutes );
|
|
33
|
+
return '<!doctype html>' + renderToString(
|
|
34
|
+
<html lang="en">
|
|
35
|
+
<head>
|
|
36
|
+
{/* Format */}
|
|
37
|
+
<meta charSet="utf-8" />
|
|
38
|
+
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
|
|
39
|
+
|
|
40
|
+
{/* CSS */}
|
|
41
|
+
<link rel="preload" href="/public/client.css" as="style" />
|
|
42
|
+
<link rel="preload" as="font" href={"/public/icons.woff2?v=" + BUILD_ID} type="font/woff2" />
|
|
43
|
+
<link rel="stylesheet" type="text/css" href="/public/icons.css" />
|
|
44
|
+
<link rel="stylesheet" type="text/css" href="/public/client.css" />
|
|
45
|
+
|
|
46
|
+
{/* JS */}
|
|
47
|
+
<script type="text/javascript" dangerouslySetInnerHTML={{
|
|
48
|
+
__html: `window.routes=${routesForClient};` + (this.app.env.profile === 'dev' ? 'window.dev = true;' : '')
|
|
49
|
+
}} />
|
|
50
|
+
<link rel="preload" href="/public/client.js" as="script" />
|
|
51
|
+
<script defer type="text/javascript" src="/public/client.js" />
|
|
52
|
+
|
|
53
|
+
</head>
|
|
54
|
+
<body></body>
|
|
55
|
+
</html>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public async page( html: string, page: Page, response: ServerResponse<TRouter> ) {
|
|
60
|
+
|
|
61
|
+
let attrsBody = {
|
|
62
|
+
className: [...page.bodyClass].join(' '),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return '<!doctype html>' + renderToString(
|
|
66
|
+
<html lang="en">
|
|
67
|
+
<head>
|
|
68
|
+
{/* Format */}
|
|
69
|
+
<meta charSet="utf-8" />
|
|
70
|
+
<meta content="IE=edge" httpEquiv="X-UA-Compatible" />
|
|
71
|
+
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
|
|
72
|
+
|
|
73
|
+
{/* Mobile */}
|
|
74
|
+
<meta name="application-name" content={this.app.identity.web.title} />
|
|
75
|
+
<meta name="apple-mobile-web-app-title" content={this.app.identity.web.title} />
|
|
76
|
+
<meta name="apple-mobile-web-app-title" content={this.app.identity.web.title} />
|
|
77
|
+
<meta content={this.app.identity.author.name} name="author" />
|
|
78
|
+
<meta name="theme-color" content={this.app.identity.maincolor} />
|
|
79
|
+
<meta name="msapplication-TileColor" content={this.app.identity.maincolor} />
|
|
80
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
81
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
82
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
|
|
83
|
+
|
|
84
|
+
{/* https://stackoverflow.com/questions/48956465/favicon-standard-2019-svg-ico-png-and-dimensions */}
|
|
85
|
+
{/*<link rel="manifest" href={RES['manifest.json']} />*/}
|
|
86
|
+
<link rel="shortcut icon" href="/public/app/favicon.ico" />
|
|
87
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/public/app/favicon-16x16.png" />
|
|
88
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/public/app/favicon-32x32.png" />
|
|
89
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/public/app/apple-touch-icon-180x180.png" />
|
|
90
|
+
<meta name="msapplication-config" content="/public/app/browserconfig.xml" />
|
|
91
|
+
|
|
92
|
+
{/* Page */}
|
|
93
|
+
<title>{page.title}</title>
|
|
94
|
+
<meta content={page.description} name="description" />
|
|
95
|
+
<link rel="canonical" href={response.canonicalUrl} />
|
|
96
|
+
|
|
97
|
+
{/* SEO, social medias, OG tags, ... */}
|
|
98
|
+
{page.head.map(({ $, ...attrs }) => (
|
|
99
|
+
React.createElement($, attrs)
|
|
100
|
+
))}
|
|
101
|
+
|
|
102
|
+
{this.styles( page )}
|
|
103
|
+
|
|
104
|
+
{await this.scripts( response, page )}
|
|
105
|
+
|
|
106
|
+
{/* Rich Snippets: https://schema.org/docs/full.html + https://jsonld.com/ */}
|
|
107
|
+
<script type="application/ld+json" dangerouslySetInnerHTML={{
|
|
108
|
+
__html: JSON.stringify({
|
|
109
|
+
'@context': 'http://schema.org',
|
|
110
|
+
'@graph': page.jsonld
|
|
111
|
+
})
|
|
112
|
+
}}/>
|
|
113
|
+
|
|
114
|
+
</head>
|
|
115
|
+
<body {...attrsBody} dangerouslySetInnerHTML={{ __html: html }}></body>
|
|
116
|
+
</html>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private styles( page: Page ) {
|
|
121
|
+
return <>
|
|
122
|
+
<link rel="preload" href="/public/client.css" as="style" />
|
|
123
|
+
<link rel="stylesheet" type="text/css" href="/public/client.css" />
|
|
124
|
+
|
|
125
|
+
{page.style.map( style => 'url' in style ? <>
|
|
126
|
+
<link rel="preload" href={style.url} as="style" />
|
|
127
|
+
<link rel="stylesheet" type="text/css" href={style.url} />
|
|
128
|
+
</> : <>
|
|
129
|
+
<style id={style.id} dangerouslySetInnerHTML={{ __html: style.inline }} />
|
|
130
|
+
</>)}
|
|
131
|
+
</>
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private async scripts( response: ServerResponse<TRouter>, page: Page ) {
|
|
135
|
+
|
|
136
|
+
const ssrData = response.forSsr(page);
|
|
137
|
+
const context = safeStringify( ssrData );
|
|
138
|
+
const routesForClient = JSON.stringify( this.router.ssrRoutes );
|
|
139
|
+
|
|
140
|
+
return <>
|
|
141
|
+
{/* JS */}
|
|
142
|
+
<script type="text/javascript" dangerouslySetInnerHTML={{
|
|
143
|
+
__html: `window.ssr=${context}; window.routes=${routesForClient};` + (
|
|
144
|
+
this.app.env.profile === 'dev' ? 'window.dev = true;' : ''
|
|
145
|
+
)
|
|
146
|
+
}} />
|
|
147
|
+
|
|
148
|
+
<link rel="preload" href={"/public/client.js?v=" + BUILD_ID} as="script" />
|
|
149
|
+
<script defer type="text/javascript" src={"/public/client.js?v=" + BUILD_ID} />
|
|
150
|
+
|
|
151
|
+
{page.scripts.map( script => 'url' in script ? <>
|
|
152
|
+
<link rel="preload" href={script.url} as="script" />
|
|
153
|
+
<script type="text/javascript" src={script.url} {...script.attrs || {}} />
|
|
154
|
+
</> : <>
|
|
155
|
+
<script type="text/javascript" {...script.attrs || {}} id={script.id}
|
|
156
|
+
dangerouslySetInnerHTML={{ __html: script.inline }} />
|
|
157
|
+
</>)}
|
|
158
|
+
</>
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import renderToString from "preact-render-to-string";
|
|
8
|
+
|
|
9
|
+
// Core
|
|
10
|
+
import { default as Router, TRouterContext } from "@server/services/router";
|
|
11
|
+
import type { Layout, TRoute, TErrorRoute, TClientOrServerContext } from '@common/router';
|
|
12
|
+
import PageResponse, { TFrontRenderer } from "@common/router/response/page";
|
|
13
|
+
|
|
14
|
+
// Composants UI
|
|
15
|
+
import App from '@client/app/component';
|
|
16
|
+
|
|
17
|
+
// Caches
|
|
18
|
+
const chunks = require('./chunk-manifest.json');
|
|
19
|
+
|
|
20
|
+
/*----------------------------------
|
|
21
|
+
- TYPES
|
|
22
|
+
----------------------------------*/
|
|
23
|
+
|
|
24
|
+
const seoLimits = {
|
|
25
|
+
title: 70,
|
|
26
|
+
description: 255
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/*----------------------------------
|
|
30
|
+
- FONCTION
|
|
31
|
+
----------------------------------*/
|
|
32
|
+
|
|
33
|
+
export default class ServerPage<TRouter extends Router = Router> extends PageResponse<TRouter> {
|
|
34
|
+
|
|
35
|
+
public constructor(
|
|
36
|
+
public route: TRoute | TErrorRoute,
|
|
37
|
+
public renderer: TFrontRenderer,
|
|
38
|
+
public context: TRouterContext,
|
|
39
|
+
public layout?: Layout,
|
|
40
|
+
|
|
41
|
+
public app = context.app,
|
|
42
|
+
public router = context.request.router,
|
|
43
|
+
|
|
44
|
+
) {
|
|
45
|
+
|
|
46
|
+
super(route, renderer, context)
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public render(): Promise<string> {
|
|
51
|
+
|
|
52
|
+
// Complete SEO metadatas
|
|
53
|
+
const titleSuffix = ' | ' + this.app.identity.web.titleSuffix
|
|
54
|
+
if (this.title === undefined)
|
|
55
|
+
this.title = this.app.identity.web.fullTitle;
|
|
56
|
+
else if (this.title.length < seoLimits.title - titleSuffix.length)
|
|
57
|
+
this.title += titleSuffix;
|
|
58
|
+
|
|
59
|
+
if (this.description === undefined)
|
|
60
|
+
this.description = this.app.identity.web.description;
|
|
61
|
+
|
|
62
|
+
// We render page & document separatly,
|
|
63
|
+
// because document needs to access to runtime assigned values
|
|
64
|
+
// Ex: runtime added scripts, title, metas, ....
|
|
65
|
+
|
|
66
|
+
const html = renderToString(
|
|
67
|
+
<App {...this.context} />
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (html === undefined)
|
|
71
|
+
throw new Error(`Page HTML is empty (undefined)`);
|
|
72
|
+
|
|
73
|
+
// Metas
|
|
74
|
+
this.buildMetas();
|
|
75
|
+
this.buildJsonLd();
|
|
76
|
+
|
|
77
|
+
// Un chunk peut regrouper plusieurs fihciers css / js
|
|
78
|
+
// L'id du chunk est injecté depuis le plugin babel
|
|
79
|
+
this.addChunks();
|
|
80
|
+
|
|
81
|
+
/*if (page.classeBody)
|
|
82
|
+
attrsBody.className += ' ' + page.classeBody.join(' ');
|
|
83
|
+
|
|
84
|
+
if (page.theme)
|
|
85
|
+
attrsBody.className += ' ' + page.theme;*/
|
|
86
|
+
|
|
87
|
+
return this.router.render.page(html, this, this.context.response);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Define which chunks (script / style) to load
|
|
91
|
+
private addChunks() {
|
|
92
|
+
const pageChunks = [this.route.options["id"]];
|
|
93
|
+
for (const chunk of pageChunks) {
|
|
94
|
+
|
|
95
|
+
if (!chunk) continue;
|
|
96
|
+
|
|
97
|
+
const assets = chunks[chunk];
|
|
98
|
+
if (!assets) {
|
|
99
|
+
console.warn(`Chunk ${chunk} was not found. Indexed chunks: ${Object.keys(chunks).join(', ')}`);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < assets.length; i++) {
|
|
104
|
+
const asset = assets[i];
|
|
105
|
+
|
|
106
|
+
if (asset.endsWith('.css'))
|
|
107
|
+
this.style.push({
|
|
108
|
+
id: chunk,
|
|
109
|
+
url: '/public/' + asset
|
|
110
|
+
})
|
|
111
|
+
else
|
|
112
|
+
this.scripts.push({
|
|
113
|
+
id: chunk,
|
|
114
|
+
url: '/public/' + asset
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private buildMetas() {
|
|
122
|
+
|
|
123
|
+
const shouldIndex = this.context.response.statusCode < 300;
|
|
124
|
+
|
|
125
|
+
const metas = {
|
|
126
|
+
|
|
127
|
+
robots: shouldIndex ? 'index' : 'noindex',
|
|
128
|
+
|
|
129
|
+
'og:type': 'website',
|
|
130
|
+
'og:locale': this.app.identity.locale,
|
|
131
|
+
'og:site_name': this.app.identity.web.title,
|
|
132
|
+
'og:url': this.url,
|
|
133
|
+
|
|
134
|
+
'og:title': this.title,
|
|
135
|
+
'og:description': this.description,
|
|
136
|
+
|
|
137
|
+
'twitter:url': this.url,
|
|
138
|
+
'twitter:card': 'summary_large_image',
|
|
139
|
+
'twitter:title': this.title,
|
|
140
|
+
'twitter:description': this.description,
|
|
141
|
+
|
|
142
|
+
...(this.app.identity.web.metas || {}),
|
|
143
|
+
|
|
144
|
+
...this.metas
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
for (const key in metas) {
|
|
148
|
+
const value = metas[key];
|
|
149
|
+
if (value === "") continue;
|
|
150
|
+
this.head.push({ $: 'meta', property: key, content: value });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private buildJsonLd() {
|
|
155
|
+
this.jsonld.push({
|
|
156
|
+
'@type': 'Organization',
|
|
157
|
+
'@id': this.router.url('/#organization'),
|
|
158
|
+
name: this.app.identity.author.name,
|
|
159
|
+
url: this.app.identity.author.url,
|
|
160
|
+
logo: {
|
|
161
|
+
'@type': 'ImageObject',
|
|
162
|
+
'@id': this.router.url('/#logo'),
|
|
163
|
+
url: this.router.url('/public/brand/1024.png'),
|
|
164
|
+
width: "1024px",
|
|
165
|
+
height: "1024px",
|
|
166
|
+
caption: this.app.identity.name
|
|
167
|
+
},
|
|
168
|
+
sameAs: []
|
|
169
|
+
}, {
|
|
170
|
+
'@type': 'WebSite',
|
|
171
|
+
'@id': this.router.url('/#website'),
|
|
172
|
+
url: this.router.url('/'),
|
|
173
|
+
name: this.app.identity.name,
|
|
174
|
+
description: this.app.identity.description,
|
|
175
|
+
"publisher": {
|
|
176
|
+
"@id": this.router.url('/#organization'),
|
|
177
|
+
},
|
|
178
|
+
inLanguage: this.app.identity.locale,
|
|
179
|
+
potentialAction: [],
|
|
180
|
+
|
|
181
|
+
...(this.app.identity.web.jsonld || {}),
|
|
182
|
+
}, {
|
|
183
|
+
'@type': "WebPage",
|
|
184
|
+
'@id': this.url,
|
|
185
|
+
url: this.url,
|
|
186
|
+
|
|
187
|
+
"isPartOf": {
|
|
188
|
+
"@id": this.router.url('/#website'),
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
name: this.title,
|
|
192
|
+
description: this.description,
|
|
193
|
+
inLanguage: this.app.identity.locale,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
import type { Application } from '@server/app';
|
|
7
|
+
import Service, { TRegisteredServicesIndex, TServiceArgs } from '@server/app/service';
|
|
8
|
+
|
|
9
|
+
// Specific
|
|
10
|
+
import type { default as Router } from '.';
|
|
11
|
+
import type ServerRequest from './request';
|
|
12
|
+
import type RequestService from './request/service';
|
|
13
|
+
import type { TAnyRouter } from '.';
|
|
14
|
+
|
|
15
|
+
export type AnyRouterService = RouterService<any, TAnyRouter>;
|
|
16
|
+
|
|
17
|
+
export type TRouterServiceArgs = [
|
|
18
|
+
getConfig: TServiceArgs<AnyRouterService>[1],
|
|
19
|
+
app: Application,
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/*----------------------------------
|
|
23
|
+
- SERVICE
|
|
24
|
+
----------------------------------*/
|
|
25
|
+
export default abstract class RouterService<
|
|
26
|
+
TConfig extends {},
|
|
27
|
+
TRouter extends TAnyRouter
|
|
28
|
+
> extends Service<TConfig, {}, Application, TRouter> {
|
|
29
|
+
|
|
30
|
+
public constructor( ...[config, app]: TRouterServiceArgs) {
|
|
31
|
+
super(app, config, app);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public abstract requestService( request: ServerRequest<TRouter> ): RequestService | {} | null;
|
|
35
|
+
|
|
36
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
import type { Application } from '@server/app';
|
|
7
|
+
|
|
8
|
+
// Specific
|
|
9
|
+
import { SchemaValidators, TFileValidator } from '@server/services/router/request/validation/validators';
|
|
10
|
+
import Validator, { TValidatorOptions } from '@server/services/router/request/validation/validator';
|
|
11
|
+
|
|
12
|
+
/*----------------------------------
|
|
13
|
+
- TYPES
|
|
14
|
+
----------------------------------*/
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/*----------------------------------
|
|
18
|
+
- SERVICE
|
|
19
|
+
----------------------------------*/
|
|
20
|
+
export default class ServerSchemaValidator extends SchemaValidators {
|
|
21
|
+
|
|
22
|
+
public constructor( public app: Application ) {
|
|
23
|
+
super();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public richText = (opts: TValidatorOptions<string> & {
|
|
27
|
+
attachements?: TFileValidator
|
|
28
|
+
} = {}) => new Validator<string>('richText', (val, options, path) => {
|
|
29
|
+
|
|
30
|
+
// Default validation
|
|
31
|
+
val = super.richText(opts).validate(val, options, path);
|
|
32
|
+
|
|
33
|
+
// Uploads are done in the business code since the process is specific to every case:
|
|
34
|
+
// - ID in the destination directory
|
|
35
|
+
// - Cleanup before upload
|
|
36
|
+
|
|
37
|
+
return val;
|
|
38
|
+
|
|
39
|
+
}, {
|
|
40
|
+
//defaut: new Date,
|
|
41
|
+
...opts,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import zod from 'zod';
|
|
7
|
+
import { SomeType } from 'zod/v4/core';
|
|
8
|
+
|
|
9
|
+
// Core
|
|
10
|
+
import {
|
|
11
|
+
default as Router, TServerRouter, Request as ServerRequest
|
|
12
|
+
} from '@server/services/router';
|
|
13
|
+
|
|
14
|
+
// Ap
|
|
15
|
+
import { preprocessSchema, schema } from '@server/services/router/request/validation/zod';
|
|
16
|
+
|
|
17
|
+
/*----------------------------------
|
|
18
|
+
- SERVICE CONFIG
|
|
19
|
+
----------------------------------*/
|
|
20
|
+
|
|
21
|
+
const LogPrefix = `[router][validation]`;
|
|
22
|
+
|
|
23
|
+
export type TConfig = {
|
|
24
|
+
debug?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/*----------------------------------
|
|
28
|
+
- SERVICE
|
|
29
|
+
----------------------------------*/
|
|
30
|
+
export default(
|
|
31
|
+
request: ServerRequest< TServerRouter >,
|
|
32
|
+
config: TConfig,
|
|
33
|
+
router = request.router,
|
|
34
|
+
app = router.app
|
|
35
|
+
) => ({
|
|
36
|
+
|
|
37
|
+
...schema,
|
|
38
|
+
|
|
39
|
+
validate( fields: zod.ZodSchema | { [key: string]: zod.ZodSchema } ) {
|
|
40
|
+
|
|
41
|
+
config.debug && console.log(LogPrefix, "Validate request data:", request.data);
|
|
42
|
+
|
|
43
|
+
const schema = typeof fields === 'object' ? zod.object(fields) : fields;
|
|
44
|
+
|
|
45
|
+
const preprocessedSchema = preprocessSchema(schema);
|
|
46
|
+
|
|
47
|
+
return preprocessedSchema.parse(request.data);
|
|
48
|
+
},
|
|
49
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
import {
|
|
7
|
+
default as Router, Request as ServerRequest, Config as RouterConfig,
|
|
8
|
+
RouterService, TAnyRouter
|
|
9
|
+
} from '@server/services/router';
|
|
10
|
+
|
|
11
|
+
import makeRequestValidators from '../request';
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- TYPES
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/*----------------------------------
|
|
19
|
+
- SERVICE
|
|
20
|
+
----------------------------------*/
|
|
21
|
+
export default class SchemaRouterService<
|
|
22
|
+
TUser extends {} = {}
|
|
23
|
+
> extends RouterService<{}, TAnyRouter> {
|
|
24
|
+
|
|
25
|
+
public requestService( request: ServerRequest ) {
|
|
26
|
+
return makeRequestValidators( request, this.config );
|
|
27
|
+
}
|
|
28
|
+
}
|