5htp-core 0.1.2 → 0.2.0
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/changelog.md +5 -0
- package/doc/TODO.md +71 -0
- package/package.json +5 -4
- package/src/client/{App.tsx → app/component.tsx} +15 -8
- package/src/client/app/index.ts +128 -0
- package/src/client/app/service.ts +34 -0
- package/src/client/app.tsconfig.json +0 -4
- package/src/client/assets/css/medias.less +14 -0
- package/src/client/components/Card/index.tsx +2 -2
- package/src/client/components/Dialog/Manager.tsx +39 -12
- package/src/client/components/Form/index.tsx +1 -1
- package/src/client/components/button.tsx +2 -2
- package/src/client/components/containers/Popover/index.tsx +1 -1
- package/src/client/components/data/spintext/index.tsx +1 -1
- package/src/client/components/dropdown/index.tsx +1 -1
- package/src/client/components/index.ts +8 -0
- package/src/client/components/input/BaseV2/index.tsx +1 -1
- package/src/client/components/input/UploadImage/index.tsx +1 -1
- package/src/client/hooks/index.ts +5 -0
- package/src/client/hooks/useState/index.tsx +2 -2
- package/src/client/hooks.ts +22 -0
- package/src/client/index.ts +5 -0
- package/src/client/pages/_layout/landing/index.tsx +0 -2
- package/src/client/pages/_messages/400.tsx +2 -2
- package/src/client/pages/_messages/401.tsx +2 -2
- package/src/client/pages/_messages/403.tsx +2 -2
- package/src/client/pages/_messages/404.tsx +2 -2
- package/src/client/pages/_messages/500.tsx +2 -2
- package/src/client/pages/bug.tsx +1 -1
- package/src/client/pages/useHeader.tsx +1 -1
- package/src/client/{context/captcha.ts → services/captcha/index.ts} +0 -0
- package/src/client/services/metrics/index.ts +37 -0
- package/src/client/{router → services/router/components}/Link.tsx +1 -1
- package/src/client/services/router/components/Page.tsx +59 -0
- package/src/client/{router/component.tsx → services/router/components/router.tsx} +43 -74
- package/src/client/services/router/index.tsx +448 -0
- package/src/client/services/router/request/api.ts +229 -0
- package/src/client/{router → services/router}/request/history.ts +0 -0
- package/src/client/services/router/request/index.ts +52 -0
- package/src/client/services/router/response/index.tsx +107 -0
- package/src/client/services/router/response/page.ts +95 -0
- package/src/client/{context/socket.ts → services/socket/index.ts} +2 -2
- package/src/client/utils/dom.ts +1 -1
- package/src/common/app/index.ts +9 -0
- package/src/common/data/chaines/index.ts +9 -6
- package/src/common/data/input/validate.ts +3 -166
- package/src/common/data/objets.ts +25 -0
- package/src/common/data/tableaux.ts +8 -0
- package/src/common/errors/index.ts +3 -1
- package/src/common/router/index.ts +67 -88
- package/src/common/router/layouts.ts +50 -0
- package/src/common/router/register.ts +62 -0
- package/src/common/router/request/api.ts +72 -0
- package/src/common/router/request/index.ts +31 -0
- package/src/common/router/{response.ts → response/index.ts} +9 -13
- package/src/common/router/response/page.ts +40 -56
- package/src/common/validation/index.ts +3 -0
- package/src/common/validation/schema.ts +184 -0
- package/src/common/validation/validator.ts +88 -0
- package/src/common/validation/validators.ts +313 -0
- package/src/server/app/config.ts +9 -27
- package/src/server/app/index.ts +81 -124
- package/src/server/app/service.ts +98 -0
- package/src/server/app.tsconfig.json +0 -8
- package/src/server/error/index.ts +13 -0
- package/src/server/index.ts +5 -0
- package/src/server/patch.ts +0 -6
- package/src/server/{data/Cache.ts → services/cache/index.ts} +79 -47
- package/src/server/services/console/bugReporter.ts +26 -16
- package/src/server/services/console/index.ts +59 -51
- package/src/server/services/cron/index.ts +12 -26
- package/src/server/services/database/bucket.ts +40 -0
- package/src/server/services/database/connection.ts +206 -75
- package/src/server/services/database/datatypes.ts +63 -40
- package/src/server/services/database/index.ts +295 -272
- package/src/server/services/database/metas.ts +246 -135
- package/src/server/services/database/stats.ts +151 -126
- package/src/server/services/email/index.ts +28 -52
- package/src/server/services/{router/request/services → metrics}/detect.ts +8 -10
- package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
- package/src/server/services/{http → router/http}/index.ts +28 -70
- package/src/server/services/{http → router/http}/multipart.ts +0 -0
- package/src/server/services/{http → router/http}/session.ts.old +0 -0
- package/src/server/services/router/index.ts +273 -203
- package/src/server/services/router/request/api.ts +73 -0
- package/src/server/services/router/request/index.ts +16 -97
- package/src/server/services/router/request/service.ts +21 -0
- package/src/server/services/router/response/index.ts +125 -64
- package/src/server/services/router/response/{filter → mask}/Filter.ts +0 -0
- package/src/server/services/router/response/{filter → mask}/index.ts +0 -2
- package/src/server/services/router/response/{filter → mask}/selecteurs.ts +0 -0
- package/src/server/services/router/response/page/document.tsx +194 -0
- package/src/server/services/router/response/page/index.tsx +157 -0
- package/src/server/{libs/pages → services/router/response/page}/schemaGenerator.ts +0 -0
- package/src/server/services/router/service.ts +48 -0
- package/src/server/services/schema/index.ts +47 -0
- package/src/server/services/schema/request.ts +55 -0
- package/src/server/services/schema/router.ts +33 -0
- package/src/server/services/socket/index.ts +38 -43
- package/src/server/services/socket/scope.ts +6 -4
- package/src/server/services/users/index.ts +203 -0
- package/src/server/services/{auth/base.ts → users/old.ts} +28 -112
- package/src/server/services/users/router/index.ts +72 -0
- package/src/server/services/users/router/request.ts +49 -0
- package/src/types/aliases.d.ts +43 -2
- package/templates/composant.tsx +1 -1
- package/templates/modal.tsx +1 -1
- package/templates/page.tsx +1 -1
- package/tsconfig.common.json +0 -4
- package/src/client/context/api.ts +0 -92
- package/src/client/context/index.ts +0 -246
- package/src/client/index.tsx +0 -129
- package/src/client/router/index.ts +0 -286
- package/src/client/router/request/index.ts +0 -106
- package/src/client/router/response/index.ts +0 -38
- package/src/client/router/route.ts +0 -75
- package/src/common/data/input/validators/basic.ts +0 -299
- package/src/common/data/input/validators/build.ts +0 -63
- package/src/common/router/request.ts +0 -83
- package/src/server/data/ApiClient.ts +0 -119
- package/src/server/data/input.ts +0 -41
- package/src/server/libs/pages/document.static.tsx +0 -41
- package/src/server/libs/pages/document.tsx +0 -203
- package/src/server/libs/pages/render.tsx +0 -90
- package/src/server/routes/auth.ts +0 -151
- package/src/server/services/redis/index.ts +0 -71
- package/src/server/services/router/request/services/auth.ts +0 -177
|
@@ -0,0 +1,194 @@
|
|
|
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 {
|
|
23
|
+
|
|
24
|
+
public constructor(
|
|
25
|
+
public router: Router,
|
|
26
|
+
public app = router.app
|
|
27
|
+
) {
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public staticDocument() {
|
|
32
|
+
const routesForClient = JSON.stringify( this.router.ssrRoutes );
|
|
33
|
+
return 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="stylesheet" type="text/css" href="/public/icons.css" />
|
|
42
|
+
<link rel="preload" href="/public/client.css" as="style" />
|
|
43
|
+
<link rel="stylesheet" type="text/css" href="/public/client.css" />
|
|
44
|
+
|
|
45
|
+
{/* JS */}
|
|
46
|
+
<script type="text/javascript" dangerouslySetInnerHTML={{
|
|
47
|
+
__html: `window.routes=${routesForClient};` + (this.app.env.profile === 'dev' ? 'window.dev = true;' : '')
|
|
48
|
+
}} />
|
|
49
|
+
<link rel="preload" href="/public/client.js" as="script" />
|
|
50
|
+
<script defer type="text/javascript" src="/public/client.js" />
|
|
51
|
+
|
|
52
|
+
</head>
|
|
53
|
+
<body></body>
|
|
54
|
+
</html>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public async page( html: string, page: Page, response: ServerResponse<Router> ) {
|
|
59
|
+
|
|
60
|
+
const fullUrl = this.router.http.publicUrl + response.request.path;
|
|
61
|
+
|
|
62
|
+
let attrsBody = {
|
|
63
|
+
className: [...page.bodyClass].join(' '),
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return renderToString(
|
|
67
|
+
<html lang="en" {...(page.amp ? { amp: "true" } : {})}>
|
|
68
|
+
<head>
|
|
69
|
+
{/* Format */}
|
|
70
|
+
<meta charSet="utf-8" />
|
|
71
|
+
{page.amp && ( // As a best practice, you should include the script as early as possible in the <head>.
|
|
72
|
+
<script async={true} src="https://cdn.ampproject.org/v0.js"></script>
|
|
73
|
+
)}
|
|
74
|
+
<meta content="IE=edge" httpEquiv="X-UA-Compatible" />
|
|
75
|
+
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
|
|
76
|
+
{!page.amp && page.amp && (
|
|
77
|
+
<link rel="amphtml" href={fullUrl + '/amp'} />
|
|
78
|
+
)}
|
|
79
|
+
|
|
80
|
+
{/* Basique*/}
|
|
81
|
+
<meta content={this.app.identity.web.title} name="apple-mobile-web-app-title" />
|
|
82
|
+
<title>{page.title}</title>
|
|
83
|
+
<meta content={page.description} name="description" />
|
|
84
|
+
<link rel="canonical" href={fullUrl} />
|
|
85
|
+
|
|
86
|
+
{this.metas()}
|
|
87
|
+
|
|
88
|
+
{this.styles( page )}
|
|
89
|
+
|
|
90
|
+
{await this.scripts( response, page )}
|
|
91
|
+
|
|
92
|
+
{/* Rich Snippets: https://schema.org/docs/full.html + https://jsonld.com/ */}
|
|
93
|
+
{/* <script type="application/ld+json" dangerouslySetInnerHTML={{
|
|
94
|
+
__html: JSON.stringify( schemaGenerator(page, route) )
|
|
95
|
+
}}/> */}
|
|
96
|
+
|
|
97
|
+
</head>
|
|
98
|
+
<body {...attrsBody} dangerouslySetInnerHTML={{ __html: html }}></body>
|
|
99
|
+
</html>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private metas() {
|
|
104
|
+
return <>
|
|
105
|
+
{/* Réseaux sociaux */}
|
|
106
|
+
{/*page.metas.metasAdditionnelles && Object.entries(page.metas.metasAdditionnelles).map(
|
|
107
|
+
([ cle, val ]: [ string, string ]) => (
|
|
108
|
+
<meta name={cle} content={val} />
|
|
109
|
+
)
|
|
110
|
+
)*/}
|
|
111
|
+
|
|
112
|
+
{/* Mobile */}
|
|
113
|
+
<meta name="theme-color" content={this.app.identity.maincolor} />
|
|
114
|
+
<meta name="msapplication-TileColor" content={this.app.identity.maincolor} />
|
|
115
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
116
|
+
<meta name="apple-mobile-web-app-title" content={this.app.identity.web.title} />
|
|
117
|
+
|
|
118
|
+
{/* Identité */}
|
|
119
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
120
|
+
<meta name="application-name" content={this.app.identity.web.title} />
|
|
121
|
+
<meta name="type" content="website" />
|
|
122
|
+
{/*app.identity.social?.facebook?.appId && (
|
|
123
|
+
<meta content={app.identity.social?.facebook?.appId} property="fb:appid" />
|
|
124
|
+
)*/}
|
|
125
|
+
|
|
126
|
+
{/* https://stackoverflow.com/questions/48956465/favicon-standard-2019-svg-ico-png-and-dimensions */}
|
|
127
|
+
{/*<link rel="manifest" href={RES['manifest.json']} />*/}
|
|
128
|
+
<link rel="shortcut icon" href="/public/app/favicon.ico" />
|
|
129
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/public/app/favicon-16x16.png" />
|
|
130
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/public/app/favicon-32x32.png" />
|
|
131
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/public/app/apple-touch-icon-180x180.png" />
|
|
132
|
+
<meta name="msapplication-config" content="/public/app/browserconfig.xml" />
|
|
133
|
+
</>
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private styles( page ) {
|
|
137
|
+
return <>
|
|
138
|
+
<link rel="stylesheet" type="text/css" href="/public/icons.css" />
|
|
139
|
+
<link rel="preload" href="/public/client.css" as="style" />
|
|
140
|
+
<link rel="stylesheet" type="text/css" href="/public/client.css" />
|
|
141
|
+
|
|
142
|
+
{page.style.map( style => 'url' in style ? <>
|
|
143
|
+
<link rel="preload" href={style.url} as="style" />
|
|
144
|
+
<link rel="stylesheet" type="text/css" href={style.url} />
|
|
145
|
+
</> : <>
|
|
146
|
+
<style id={style.id} dangerouslySetInnerHTML={{ __html: style.inline }} />
|
|
147
|
+
</>)}
|
|
148
|
+
|
|
149
|
+
{/* Sera remplacé par la chaine exacte après renderToStaticMarkup */}
|
|
150
|
+
{page.amp && (<style amp-boilerplate=""></style>)}
|
|
151
|
+
</>
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private async scripts( response, page ) {
|
|
155
|
+
|
|
156
|
+
const ssrData = safeStringify( await response.forSsr(page) );
|
|
157
|
+
|
|
158
|
+
const routesForClient = JSON.stringify( this.router.ssrRoutes );
|
|
159
|
+
|
|
160
|
+
return <>
|
|
161
|
+
{/* JS */}
|
|
162
|
+
{!page.amp && (
|
|
163
|
+
<script type="text/javascript" dangerouslySetInnerHTML={{
|
|
164
|
+
__html: `window.ssr=${ssrData}; window.routes=${routesForClient};` + (
|
|
165
|
+
this.app.env.profile === 'dev' ? 'window.dev = true;' : ''
|
|
166
|
+
)
|
|
167
|
+
}} />
|
|
168
|
+
)}
|
|
169
|
+
|
|
170
|
+
<link rel="preload" href="/public/client.js" as="script" />
|
|
171
|
+
<script defer type="text/javascript" src="/public/client.js" />
|
|
172
|
+
|
|
173
|
+
{page.scripts.map( script => 'url' in script ? <>
|
|
174
|
+
<link rel="preload" href={script.url} as="script" />
|
|
175
|
+
<script type="text/javascript" src={script.url} {...script.attrs || {}} />
|
|
176
|
+
</> : <>
|
|
177
|
+
<script type="text/javascript" {...script.attrs || {}} id={script.id} dangerouslySetInnerHTML={{ __html: script.inline }} />
|
|
178
|
+
</>)}
|
|
179
|
+
|
|
180
|
+
{/* Initialize GTM & GA for pagechange events */}
|
|
181
|
+
{/* TODO: append via the metrics module */}
|
|
182
|
+
{/*<script async src={"https://www.googletagmanager.com/gtag/js?id=" + this.app.config.tracking.ga.pub}></script>
|
|
183
|
+
<script dangerouslySetInnerHTML={{ __html: `
|
|
184
|
+
window.dataLayer = window.dataLayer || [];
|
|
185
|
+
function gtag(){dataLayer.push(arguments);}
|
|
186
|
+
gtag('js', new Date());
|
|
187
|
+
|
|
188
|
+
gtag('config', '${this.app.config.tracking.ga.pub}', {
|
|
189
|
+
send_page_view: false
|
|
190
|
+
});
|
|
191
|
+
`}} />*/}
|
|
192
|
+
</>
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
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 { default as Router, TRouterContext } from "@server/services/router";
|
|
12
|
+
import type ServerResponse from "@server/services/router/response";
|
|
13
|
+
import type { TRoute } from '@common/router';
|
|
14
|
+
import PageResponse, { TDataProvider, TFrontRenderer } from "@common/router/response/page";
|
|
15
|
+
|
|
16
|
+
// Composants UI
|
|
17
|
+
import App from '@client/app/component';
|
|
18
|
+
|
|
19
|
+
// Caches
|
|
20
|
+
const chunks = require('./chunk-manifest.json');
|
|
21
|
+
|
|
22
|
+
/*----------------------------------
|
|
23
|
+
- TYPES
|
|
24
|
+
----------------------------------*/
|
|
25
|
+
|
|
26
|
+
/*----------------------------------
|
|
27
|
+
- FONCTION
|
|
28
|
+
----------------------------------*/
|
|
29
|
+
|
|
30
|
+
export default class Page<TRouter extends Router = Router> extends PageResponse<TRouter> {
|
|
31
|
+
|
|
32
|
+
public constructor(
|
|
33
|
+
public dataProvider: TDataProvider | null,
|
|
34
|
+
public renderer: TFrontRenderer,
|
|
35
|
+
public context: TRouterContext,
|
|
36
|
+
|
|
37
|
+
public route = context.route,
|
|
38
|
+
public router = context.request.router
|
|
39
|
+
|
|
40
|
+
) {
|
|
41
|
+
|
|
42
|
+
super(dataProvider, renderer, context)
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public render(): Promise<string> {
|
|
47
|
+
|
|
48
|
+
// We render page & document separatly,
|
|
49
|
+
// because document needs to access to runtime assigned values
|
|
50
|
+
// Ex: runtime added scripts, title, metas, ....
|
|
51
|
+
|
|
52
|
+
const html = renderToString(
|
|
53
|
+
<App {...this.context} />
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (html === undefined)
|
|
57
|
+
throw new Error(`Page HTML is empty (undefined)`);
|
|
58
|
+
|
|
59
|
+
// Un chunk peut regrouper plusieurs fihciers css / js
|
|
60
|
+
// L'id du chunk est injecté depuis le plugin babel
|
|
61
|
+
this.addChunks();
|
|
62
|
+
|
|
63
|
+
/*if (page.classeBody)
|
|
64
|
+
attrsBody.className += ' ' + page.classeBody.join(' ');
|
|
65
|
+
|
|
66
|
+
if (page.theme)
|
|
67
|
+
attrsBody.className += ' ' + page.theme;
|
|
68
|
+
|
|
69
|
+
// L'url canonique doit pointer vers la version html
|
|
70
|
+
if (page.amp && fullUrl.endsWith('/amp'))
|
|
71
|
+
fullUrl = fullUrl.substring(0, fullUrl.length - 4);*/
|
|
72
|
+
|
|
73
|
+
return this.router.render.page(html, this, this.context.response);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Define which chunks (script / style) to load
|
|
77
|
+
private addChunks() {
|
|
78
|
+
const pageChunks = [this.route.options["id"]];
|
|
79
|
+
for (const chunk of pageChunks) {
|
|
80
|
+
|
|
81
|
+
if (!chunk) continue;
|
|
82
|
+
|
|
83
|
+
const assets = chunks[chunk];
|
|
84
|
+
if (!assets) {
|
|
85
|
+
console.warn(`Chunk ${chunk} was not found. Indexed chunks: ${Object.keys(chunks).join(', ')}`);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (let i = 0; i < assets.length; i++) {
|
|
90
|
+
const asset = assets[i];
|
|
91
|
+
|
|
92
|
+
if (asset.endsWith('.css'))
|
|
93
|
+
this.style.push({
|
|
94
|
+
id: chunk,
|
|
95
|
+
url: '/public/' + asset
|
|
96
|
+
})
|
|
97
|
+
// Si mode amp, on ne charge pas le JS react (rendu serveur uniquement)
|
|
98
|
+
// Sauf si mode dev, car le hot reload est quand même bien pratique ...
|
|
99
|
+
else if (!this.amp)
|
|
100
|
+
this.scripts.push({
|
|
101
|
+
id: chunk,
|
|
102
|
+
url: '/public/' + asset
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private getSocialMetas() {
|
|
110
|
+
/*if (page.metas.imageUrl)
|
|
111
|
+
page.metas.imageUrl = app.web.url + page.metas.imageUrl;
|
|
112
|
+
|
|
113
|
+
if (!page.metas.metasAdditionnelles)
|
|
114
|
+
page.metas.metasAdditionnelles = {};
|
|
115
|
+
|
|
116
|
+
page.metas.metasAdditionnelles = {
|
|
117
|
+
'og:locale': 'fr_FR',
|
|
118
|
+
'og:site_name': app.identity.web.title,
|
|
119
|
+
'og:title': page.title,
|
|
120
|
+
'og:description': page.description,
|
|
121
|
+
'og:url': fullUrl,
|
|
122
|
+
|
|
123
|
+
...(page.metas.imageUrl ? {
|
|
124
|
+
'og:image': page.metas.imageUrl,
|
|
125
|
+
...(page.metas.imageX ? {
|
|
126
|
+
'og:image:width': page.metas.imageX.toString()
|
|
127
|
+
} : {}),
|
|
128
|
+
...(page.metas.imageY ? {
|
|
129
|
+
'og:image:height': page.metas.imageY.toString()
|
|
130
|
+
} : {})
|
|
131
|
+
} : {}),
|
|
132
|
+
|
|
133
|
+
'twitter:card': 'summary_large_image',
|
|
134
|
+
'twitter:title': page.title,
|
|
135
|
+
'twitter:description': page.description,
|
|
136
|
+
'twitter:url': fullUrl,
|
|
137
|
+
'twitter:text:title': page.title,
|
|
138
|
+
|
|
139
|
+
...(app.identity.social?.twitter? ? {
|
|
140
|
+
'twitter:site': `@${app.identity.social?.twitter?}`,
|
|
141
|
+
'twitter:creator': `@${app.identity.social?.twitter?}`
|
|
142
|
+
} : {}),
|
|
143
|
+
|
|
144
|
+
...(page.metas.imageUrl ? {
|
|
145
|
+
'twitter:image': page.metas.imageUrl,
|
|
146
|
+
...(page.metas.imageX ? {
|
|
147
|
+
'twitter:image:width': page.metas.imageX.toString()
|
|
148
|
+
} : {}),
|
|
149
|
+
...(page.metas.imageY ? {
|
|
150
|
+
'twitter:image:height': page.metas.imageY.toString()
|
|
151
|
+
} : {})
|
|
152
|
+
} : {}),
|
|
153
|
+
|
|
154
|
+
...page.metas.metasAdditionnelles
|
|
155
|
+
};*/
|
|
156
|
+
}
|
|
157
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Specific
|
|
6
|
+
import type Application from '@server/app';
|
|
7
|
+
import type { default as Router } from '.';
|
|
8
|
+
import type ServerRequest from './request';
|
|
9
|
+
import type RequestService from './request/service';
|
|
10
|
+
|
|
11
|
+
/*----------------------------------
|
|
12
|
+
- SERVICE
|
|
13
|
+
----------------------------------*/
|
|
14
|
+
export default abstract class RouterService<TRouter extends Router = Router> {
|
|
15
|
+
|
|
16
|
+
protected router!: TRouter;
|
|
17
|
+
protected app!: Application;
|
|
18
|
+
|
|
19
|
+
public constructor(
|
|
20
|
+
|
|
21
|
+
) {
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
We can't pass the router instance in the routerservice constructor
|
|
27
|
+
Because we instanciate the routerservice in the router instanciation itself
|
|
28
|
+
So if we do:
|
|
29
|
+
public router = new Router(this, {
|
|
30
|
+
...,
|
|
31
|
+
services: () => ({
|
|
32
|
+
|
|
33
|
+
auth: new AuthService(this.router, this.users),
|
|
34
|
+
|
|
35
|
+
}),
|
|
36
|
+
)
|
|
37
|
+
We would have a cicular reference in typings, which will make router typed as any
|
|
38
|
+
*/
|
|
39
|
+
public attach( router: TRouter ) {
|
|
40
|
+
this.router = router;
|
|
41
|
+
this.app = router.app;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public abstract register(): Promise<void>;
|
|
45
|
+
|
|
46
|
+
public abstract requestService( request: ServerRequest<TRouter> ): RequestService;
|
|
47
|
+
|
|
48
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
|
|
7
|
+
// Specific
|
|
8
|
+
import SchemaValidator, { TFileValidator } from '@common/validation/validators';
|
|
9
|
+
|
|
10
|
+
import Validator, { EXCLUDE_VALUE,} from '@common/validation/validator';
|
|
11
|
+
|
|
12
|
+
import NormalizedFile from '@common/data/file';
|
|
13
|
+
|
|
14
|
+
/*----------------------------------
|
|
15
|
+
- TYPES
|
|
16
|
+
----------------------------------*/
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/*----------------------------------
|
|
20
|
+
- SERVICE
|
|
21
|
+
----------------------------------*/
|
|
22
|
+
export default class ServerSchemaValidator extends SchemaValidator {
|
|
23
|
+
|
|
24
|
+
public file = ({ ...opts }: TFileValidator & { sharp: any }) =>
|
|
25
|
+
new Validator<NormalizedFile>('file', (val, input, output) => {
|
|
26
|
+
|
|
27
|
+
// Chaine = url ancien fichier = exclusion de la valeur pour conserver l'ancien fichier
|
|
28
|
+
// NOTE: Si la valeur est présente mais undefined, alors on supprimera le fichier
|
|
29
|
+
if (typeof val === 'string')
|
|
30
|
+
return EXCLUDE_VALUE;
|
|
31
|
+
|
|
32
|
+
// Validation universelle
|
|
33
|
+
const file = this.validateFile(opts, val, input, output);
|
|
34
|
+
|
|
35
|
+
if (file === undefined)
|
|
36
|
+
return file;
|
|
37
|
+
|
|
38
|
+
// Process Image
|
|
39
|
+
if (opts.sharp !== undefined) {
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return file;
|
|
46
|
+
}, opts)
|
|
47
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
import {
|
|
7
|
+
default as Router, RequestService, Request as ServerRequest
|
|
8
|
+
} from '@server/services/router';
|
|
9
|
+
|
|
10
|
+
import ServerSchemaValidator from '.';
|
|
11
|
+
|
|
12
|
+
import Schema, { TSchemaFields, TValidatedData } from '@common/validation/schema';
|
|
13
|
+
|
|
14
|
+
/*----------------------------------
|
|
15
|
+
- TYPES
|
|
16
|
+
----------------------------------*/
|
|
17
|
+
|
|
18
|
+
/*----------------------------------
|
|
19
|
+
- SERVICE
|
|
20
|
+
----------------------------------*/
|
|
21
|
+
export default class RequestValidator extends ServerSchemaValidator implements RequestService {
|
|
22
|
+
|
|
23
|
+
public constructor(
|
|
24
|
+
public request: ServerRequest<Router>,
|
|
25
|
+
public router = request.router,
|
|
26
|
+
public app = router.app
|
|
27
|
+
) {
|
|
28
|
+
|
|
29
|
+
super();
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public validate<TSchemaFieldsA extends TSchemaFields>( fields: TSchemaFieldsA ): TValidatedData<TSchemaFieldsA> {
|
|
34
|
+
|
|
35
|
+
console.log("Validate request data:", this.request.data);
|
|
36
|
+
|
|
37
|
+
const schema = new Schema(fields);
|
|
38
|
+
|
|
39
|
+
// Les InputError seront propagées vers le middleware dédié à la gestion des erreurs
|
|
40
|
+
const { values } = schema.validate(
|
|
41
|
+
this.request.data,
|
|
42
|
+
this.request.data,
|
|
43
|
+
{},
|
|
44
|
+
{
|
|
45
|
+
critique: true,
|
|
46
|
+
validationComplete: true,
|
|
47
|
+
avecDependances: false
|
|
48
|
+
},
|
|
49
|
+
[]
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return values;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
import {
|
|
7
|
+
default as Router, Request as ServerRequest, Config as RouterConfig,
|
|
8
|
+
RouterService
|
|
9
|
+
} from '@server/services/router';
|
|
10
|
+
|
|
11
|
+
import RequestValidator from './request';
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- TYPES
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
|
|
17
|
+
type TRouterWithSchema<TAuthService extends AuthenticationRouterService> = Router<RouterConfig<{ auth: TAuthService }>>
|
|
18
|
+
|
|
19
|
+
/*----------------------------------
|
|
20
|
+
- SERVICE
|
|
21
|
+
----------------------------------*/
|
|
22
|
+
export default class AuthenticationRouterService<
|
|
23
|
+
TUser extends {} = {}
|
|
24
|
+
> extends RouterService {
|
|
25
|
+
|
|
26
|
+
public async register() {
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public requestService( request: ServerRequest< TRouterWithSchema<this>> ): RequestValidator {
|
|
31
|
+
return new RequestValidator( request );
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -3,68 +3,75 @@
|
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
5
|
// Npm
|
|
6
|
-
import { Server as WebSocketServer } from 'ws';
|
|
6
|
+
import { Server as WebSocketServer, ServerOptions } from 'ws';
|
|
7
7
|
import { v4 as uuidv4 } from 'uuid';
|
|
8
8
|
import { IncomingMessage } from 'http';
|
|
9
9
|
import cookie from 'cookie';
|
|
10
10
|
|
|
11
11
|
// Core
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import '@server/services/http';
|
|
12
|
+
import Application, { Service } from '@server/app';
|
|
13
|
+
import SocketScope, { WebSocket } from './scope';
|
|
14
|
+
export type { WebSocket, default as SocketScope } from './scope';
|
|
15
|
+
import type UsersManagementService from '../users';
|
|
17
16
|
|
|
18
17
|
/*----------------------------------
|
|
19
|
-
-
|
|
18
|
+
- TYPES
|
|
20
19
|
----------------------------------*/
|
|
21
20
|
|
|
22
|
-
export type SocketServiceConfig = {
|
|
23
|
-
port: number
|
|
24
|
-
}
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
socket: SocketServiceConfig
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
22
|
+
export type Config<TUser extends {}> = {
|
|
23
|
+
server: ServerOptions["server"],
|
|
24
|
+
users: UsersManagementService<TUser>,
|
|
25
|
+
port: number,
|
|
34
26
|
}
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
- TYPES
|
|
38
|
-
----------------------------------*/
|
|
28
|
+
export type Hooks = {
|
|
39
29
|
|
|
40
|
-
|
|
30
|
+
}
|
|
41
31
|
|
|
42
32
|
/*----------------------------------
|
|
43
33
|
- MANAGER
|
|
44
34
|
----------------------------------*/
|
|
45
|
-
export class WebSocketCommander
|
|
35
|
+
export default class WebSocketCommander<
|
|
36
|
+
TUser extends {},
|
|
37
|
+
TConfig extends Config<TUser>= Config<TUser>
|
|
38
|
+
> extends Service<TConfig, Hooks, Application> {
|
|
46
39
|
|
|
40
|
+
// Services
|
|
47
41
|
public ws!: WebSocketServer;
|
|
42
|
+
public users: UsersManagementService<TUser>;
|
|
43
|
+
|
|
44
|
+
// Context
|
|
45
|
+
public scopes: {[path: string]: SocketScope<TUser>} = {}
|
|
48
46
|
|
|
49
|
-
public
|
|
47
|
+
public constructor( app: Application, config: TConfig ) {
|
|
48
|
+
super(app, config);
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
this.users = config.users;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async register() {
|
|
54
|
+
|
|
55
|
+
this.app.on('cleanup', async () => {
|
|
53
56
|
this.closeAll();
|
|
54
57
|
});
|
|
55
|
-
}
|
|
56
58
|
|
|
57
59
|
|
|
60
|
+
this.users.on('disconnect', async (userId: string) => {
|
|
61
|
+
this.disconnect(userId, 'Logout');
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
58
65
|
public loading: Promise<void> | undefined = undefined;
|
|
59
|
-
public async
|
|
66
|
+
public async start() {
|
|
60
67
|
|
|
61
68
|
console.info(`Loading socket commander`);
|
|
62
|
-
this.ws = new WebSocketServer({ server:
|
|
69
|
+
this.ws = new WebSocketServer({ server: this.config.server })
|
|
63
70
|
.on('connection', (socket: WebSocket, req: IncomingMessage) => {
|
|
64
71
|
|
|
65
72
|
// Resolve scope
|
|
66
73
|
const path = req.url;
|
|
67
|
-
let scope: SocketScope | undefined;
|
|
74
|
+
let scope: SocketScope<TUser> | undefined;
|
|
68
75
|
for (const scopePath in this.scopes)
|
|
69
76
|
if (path === scopePath) {
|
|
70
77
|
scope = this.scopes[path];
|
|
@@ -105,7 +112,7 @@ export class WebSocketCommander {
|
|
|
105
112
|
if (!(path in this.scopes)) {
|
|
106
113
|
|
|
107
114
|
console.info("Registering socket scope:", path);
|
|
108
|
-
this.scopes[path] = new SocketScope(path);
|
|
115
|
+
this.scopes[path] = new SocketScope(path, this);
|
|
109
116
|
|
|
110
117
|
}
|
|
111
118
|
|
|
@@ -135,16 +142,4 @@ export class WebSocketCommander {
|
|
|
135
142
|
for (const path in this.scopes)
|
|
136
143
|
this.scopes[path].close();
|
|
137
144
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/*----------------------------------
|
|
141
|
-
- REGISTER SERVICE
|
|
142
|
-
----------------------------------*/
|
|
143
|
-
app.register('socket', WebSocketCommander);
|
|
144
|
-
declare global {
|
|
145
|
-
namespace Core {
|
|
146
|
-
interface Services {
|
|
147
|
-
socket: WebSocketCommander;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
145
|
}
|