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
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
/*----------------------------------
|
|
2
|
-
- DEPENDANCES
|
|
3
|
-
----------------------------------*/
|
|
4
|
-
|
|
5
|
-
// Npm
|
|
6
|
-
import React from 'react';
|
|
7
|
-
import { VNode } from 'preact';
|
|
8
|
-
|
|
9
|
-
// Core
|
|
10
|
-
import app, { services } from '@server/app';
|
|
11
|
-
|
|
12
|
-
/*----------------------------------
|
|
13
|
-
- TYPES
|
|
14
|
-
----------------------------------*/
|
|
15
|
-
|
|
16
|
-
import ServerRequest from '../../services/router/request';
|
|
17
|
-
import { TSsrData } from '../../services/router/response';
|
|
18
|
-
import PageResponse from '@common/router/response/page';
|
|
19
|
-
|
|
20
|
-
/*----------------------------------
|
|
21
|
-
- RESSOURCES
|
|
22
|
-
----------------------------------*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/*----------------------------------
|
|
26
|
-
- COMPOSANT
|
|
27
|
-
----------------------------------*/
|
|
28
|
-
export default ({ page, children: html, request, ssrData }: {
|
|
29
|
-
page: PageResponse,
|
|
30
|
-
children: string,
|
|
31
|
-
request: ServerRequest,
|
|
32
|
-
ssrData: TSsrData
|
|
33
|
-
}) => {
|
|
34
|
-
|
|
35
|
-
const routesForClient = JSON.stringify( services.router.ssrRoutes );
|
|
36
|
-
|
|
37
|
-
const fullUrl = services.http.url + request.path;
|
|
38
|
-
|
|
39
|
-
let attrsBody = {
|
|
40
|
-
className: [...page.bodyClass].join(' '),
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/*if (page.classeBody)
|
|
44
|
-
attrsBody.className += ' ' + page.classeBody.join(' ');
|
|
45
|
-
|
|
46
|
-
if (page.theme)
|
|
47
|
-
attrsBody.className += ' ' + page.theme;
|
|
48
|
-
|
|
49
|
-
// L'url canonique doit pointer vers la version html
|
|
50
|
-
if (page.amp && fullUrl.endsWith('/amp'))
|
|
51
|
-
fullUrl = fullUrl.substring(0, fullUrl.length - 4);*/
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
/*if (page.metas.imageUrl)
|
|
55
|
-
page.metas.imageUrl = app.web.url + page.metas.imageUrl;
|
|
56
|
-
|
|
57
|
-
if (!page.metas.metasAdditionnelles)
|
|
58
|
-
page.metas.metasAdditionnelles = {};
|
|
59
|
-
|
|
60
|
-
page.metas.metasAdditionnelles = {
|
|
61
|
-
'og:locale': 'fr_FR',
|
|
62
|
-
'og:site_name': app.identity.web.title,
|
|
63
|
-
'og:title': page.title,
|
|
64
|
-
'og:description': page.description,
|
|
65
|
-
'og:url': fullUrl,
|
|
66
|
-
|
|
67
|
-
...(page.metas.imageUrl ? {
|
|
68
|
-
'og:image': page.metas.imageUrl,
|
|
69
|
-
...(page.metas.imageX ? {
|
|
70
|
-
'og:image:width': page.metas.imageX.toString()
|
|
71
|
-
} : {}),
|
|
72
|
-
...(page.metas.imageY ? {
|
|
73
|
-
'og:image:height': page.metas.imageY.toString()
|
|
74
|
-
} : {})
|
|
75
|
-
} : {}),
|
|
76
|
-
|
|
77
|
-
'twitter:card': 'summary_large_image',
|
|
78
|
-
'twitter:title': page.title,
|
|
79
|
-
'twitter:description': page.description,
|
|
80
|
-
'twitter:url': fullUrl,
|
|
81
|
-
'twitter:text:title': page.title,
|
|
82
|
-
|
|
83
|
-
...(app.identity.social?.twitter? ? {
|
|
84
|
-
'twitter:site': `@${app.identity.social?.twitter?}`,
|
|
85
|
-
'twitter:creator': `@${app.identity.social?.twitter?}`
|
|
86
|
-
} : {}),
|
|
87
|
-
|
|
88
|
-
...(page.metas.imageUrl ? {
|
|
89
|
-
'twitter:image': page.metas.imageUrl,
|
|
90
|
-
...(page.metas.imageX ? {
|
|
91
|
-
'twitter:image:width': page.metas.imageX.toString()
|
|
92
|
-
} : {}),
|
|
93
|
-
...(page.metas.imageY ? {
|
|
94
|
-
'twitter:image:height': page.metas.imageY.toString()
|
|
95
|
-
} : {})
|
|
96
|
-
} : {}),
|
|
97
|
-
|
|
98
|
-
...page.metas.metasAdditionnelles
|
|
99
|
-
};*/
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<html lang="en" {...(page.amp ? { amp: "true" } : {})}>
|
|
103
|
-
<head>
|
|
104
|
-
{/* Format */}
|
|
105
|
-
<meta charSet="utf-8" />
|
|
106
|
-
{page.amp && ( // As a best practice, you should include the script as early as possible in the <head>.
|
|
107
|
-
<script async={true} src="https://cdn.ampproject.org/v0.js"></script>
|
|
108
|
-
)}
|
|
109
|
-
<meta content="IE=edge" httpEquiv="X-UA-Compatible" />
|
|
110
|
-
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
|
|
111
|
-
{!page.amp && page.amp && (
|
|
112
|
-
<link rel="amphtml" href={fullUrl + '/amp'} />
|
|
113
|
-
)}
|
|
114
|
-
|
|
115
|
-
{/* Basique*/}
|
|
116
|
-
<meta content={app.identity.web.title} name="apple-mobile-web-app-title" />
|
|
117
|
-
<title>{page.title}</title>
|
|
118
|
-
<meta content={page.description} name="description" />
|
|
119
|
-
<link rel="canonical" href={fullUrl} />
|
|
120
|
-
|
|
121
|
-
{/* Réseaux sociaux */}
|
|
122
|
-
{/*page.metas.metasAdditionnelles && Object.entries(page.metas.metasAdditionnelles).map(
|
|
123
|
-
([ cle, val ]: [ string, string ]) => (
|
|
124
|
-
<meta name={cle} content={val} />
|
|
125
|
-
)
|
|
126
|
-
)*/}
|
|
127
|
-
|
|
128
|
-
{/* Mobile */}
|
|
129
|
-
<meta name="theme-color" content={app.identity.maincolor} />
|
|
130
|
-
<meta name="msapplication-TileColor" content={app.identity.maincolor} />
|
|
131
|
-
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
132
|
-
<meta name="apple-mobile-web-app-title" content={app.identity.web.title} />
|
|
133
|
-
|
|
134
|
-
{/* Identité */}
|
|
135
|
-
<meta name="mobile-web-app-capable" content="yes" />
|
|
136
|
-
<meta name="application-name" content={app.identity.web.title} />
|
|
137
|
-
<meta name="type" content="website" />
|
|
138
|
-
{/*app.identity.social?.facebook?.appId && (
|
|
139
|
-
<meta content={app.identity.social?.facebook?.appId} property="fb:appid" />
|
|
140
|
-
)*/}
|
|
141
|
-
|
|
142
|
-
{/* https://stackoverflow.com/questions/48956465/favicon-standard-2019-svg-ico-png-and-dimensions */}
|
|
143
|
-
{/*<link rel="manifest" href={RES['manifest.json']} />*/}
|
|
144
|
-
<link rel="shortcut icon" href="/public/app/favicon.ico" />
|
|
145
|
-
<link rel="icon" type="image/png" sizes="16x16" href="/public/app/favicon-16x16.png" />
|
|
146
|
-
<link rel="icon" type="image/png" sizes="32x32" href="/public/app/favicon-32x32.png" />
|
|
147
|
-
<link rel="apple-touch-icon" sizes="180x180" href="/public/app/apple-touch-icon-180x180.png" />
|
|
148
|
-
<meta name="msapplication-config" content="/public/app/browserconfig.xml" />
|
|
149
|
-
|
|
150
|
-
{/* CSS */}
|
|
151
|
-
<link rel="stylesheet" type="text/css" href="/public/icons.css" />
|
|
152
|
-
<link rel="preload" href="/public/client.css" as="style" />
|
|
153
|
-
<link rel="stylesheet" type="text/css" href="/public/client.css" />
|
|
154
|
-
|
|
155
|
-
{page.style.map( style => 'url' in style ? <>
|
|
156
|
-
<link rel="preload" href={style.url} as="style" />
|
|
157
|
-
<link rel="stylesheet" type="text/css" href={style.url} />
|
|
158
|
-
</> : <>
|
|
159
|
-
<style id={style.id} dangerouslySetInnerHTML={{ __html: style.inline }} />
|
|
160
|
-
</>)}
|
|
161
|
-
|
|
162
|
-
{/* Sera remplacé par la chaine exacte après renderToStaticMarkup */}
|
|
163
|
-
{page.amp && (<style amp-boilerplate=""></style>)}
|
|
164
|
-
|
|
165
|
-
{/* JS */}
|
|
166
|
-
{!page.amp && (
|
|
167
|
-
<script type="text/javascript" dangerouslySetInnerHTML={{
|
|
168
|
-
__html: `window.ssr=${ssrData}; window.routes=${routesForClient};` + (app.env.profile === 'dev' ? 'window.dev = true;' : '')
|
|
169
|
-
}} />
|
|
170
|
-
)}
|
|
171
|
-
|
|
172
|
-
<link rel="preload" href="/public/client.js" as="script" />
|
|
173
|
-
<script defer type="text/javascript" src="/public/client.js" />
|
|
174
|
-
|
|
175
|
-
{page.scripts.map( script => 'url' in script ? <>
|
|
176
|
-
<link rel="preload" href={script.url} as="script" />
|
|
177
|
-
<script type="text/javascript" src={script.url} {...script.attrs || {}} />
|
|
178
|
-
</> : <>
|
|
179
|
-
<script type="text/javascript" {...script.attrs || {}} id={script.id} dangerouslySetInnerHTML={{ __html: script.inline }} />
|
|
180
|
-
</>)}
|
|
181
|
-
|
|
182
|
-
<script async src={"https://www.googletagmanager.com/gtag/js?id=" + 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', '${app.config.tracking.ga.pub}', {
|
|
189
|
-
send_page_view: false
|
|
190
|
-
});
|
|
191
|
-
`}} />
|
|
192
|
-
|
|
193
|
-
{/* Rich Snippets: https://schema.org/docs/full.html + https://jsonld.com/ */}
|
|
194
|
-
{/* <script type="application/ld+json" dangerouslySetInnerHTML={{
|
|
195
|
-
__html: JSON.stringify( schemaGenerator(page, route) )
|
|
196
|
-
}}/> */}
|
|
197
|
-
|
|
198
|
-
</head>
|
|
199
|
-
<body {...attrsBody} dangerouslySetInnerHTML={{ __html: html }}></body>
|
|
200
|
-
</html>
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/*----------------------------------
|
|
2
|
-
- DEPENDANCES
|
|
3
|
-
----------------------------------*/
|
|
4
|
-
|
|
5
|
-
// Npm
|
|
6
|
-
import renderToString from "preact-render-to-string";
|
|
7
|
-
const safeStringify = require('fast-safe-stringify'); // remplace les références circulairs par un [Circular]
|
|
8
|
-
import React from 'react';
|
|
9
|
-
|
|
10
|
-
// Core
|
|
11
|
-
import ServerResponse from "@server/services/router/response";
|
|
12
|
-
import { PageResponse } from "@common/router/response";
|
|
13
|
-
import { ClientContext } from '@client/context';
|
|
14
|
-
|
|
15
|
-
// Composants UI
|
|
16
|
-
import Html from './document';
|
|
17
|
-
import App from '@client/App';
|
|
18
|
-
import DocumentStatic from "./document.static";
|
|
19
|
-
|
|
20
|
-
// Caches
|
|
21
|
-
const chunks = require('./chunk-manifest.json');
|
|
22
|
-
|
|
23
|
-
/*----------------------------------
|
|
24
|
-
- TYPES
|
|
25
|
-
----------------------------------*/
|
|
26
|
-
|
|
27
|
-
/*----------------------------------
|
|
28
|
-
- FONCTION
|
|
29
|
-
----------------------------------*/
|
|
30
|
-
|
|
31
|
-
export const staticDocument = () => renderToString(<DocumentStatic />);
|
|
32
|
-
|
|
33
|
-
export const page = async (
|
|
34
|
-
page: PageResponse,
|
|
35
|
-
response: ServerResponse,
|
|
36
|
-
context: ClientContext
|
|
37
|
-
): Promise<string> => {
|
|
38
|
-
|
|
39
|
-
// We render page & document separatly,
|
|
40
|
-
// because document needs to access to runtime assigned values
|
|
41
|
-
// Ex: runtime added scripts, title, metas, ....
|
|
42
|
-
|
|
43
|
-
const html = renderToString(
|
|
44
|
-
<App context={context} />
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
// 1 chunk peut regrouper plusieurs fihciers css / js
|
|
48
|
-
// L'id du chunk est injecté depuis le plugin babel
|
|
49
|
-
const pageChunks = [response.route.options["id"]];
|
|
50
|
-
for (const chunk of pageChunks) {
|
|
51
|
-
|
|
52
|
-
if (!chunk) continue;
|
|
53
|
-
|
|
54
|
-
const assets = chunks[chunk];
|
|
55
|
-
if (!assets) {
|
|
56
|
-
console.warn(`Chunk ${chunk} was not found. Indexed chunks: ${Object.keys(chunks).join(', ')}`);
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
for (let i = 0; i < assets.length; i++) {
|
|
61
|
-
const asset = assets[i];
|
|
62
|
-
|
|
63
|
-
if (asset.endsWith('.css'))
|
|
64
|
-
page.style.push({
|
|
65
|
-
id: chunk,
|
|
66
|
-
url: '/public/' + asset
|
|
67
|
-
})
|
|
68
|
-
// Si mode amp, on ne charge pas le JS react (rendu serveur uniquement)
|
|
69
|
-
// Sauf si mode dev, car le hot reload est quand même bien pratique ...
|
|
70
|
-
else if (!page.amp)
|
|
71
|
-
page.scripts.push({
|
|
72
|
-
id: chunk,
|
|
73
|
-
url: '/public/' + asset
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (html === undefined)
|
|
80
|
-
throw new Error(`Page HTML is empty (undefined)`);
|
|
81
|
-
|
|
82
|
-
const ssrData = safeStringify(await response.forSsr(page));
|
|
83
|
-
|
|
84
|
-
return renderToString(
|
|
85
|
-
<Html page={page} request={response.request} ssrData={ssrData}>
|
|
86
|
-
{html}
|
|
87
|
-
</Html>
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
/*----------------------------------
|
|
2
|
-
- DEPENDANCES
|
|
3
|
-
----------------------------------*/
|
|
4
|
-
|
|
5
|
-
// Npm
|
|
6
|
-
|
|
7
|
-
// Core
|
|
8
|
-
import route from '@router';
|
|
9
|
-
import app, { $ } from '@server/app';
|
|
10
|
-
import getStats from '@server/services/database/stats';
|
|
11
|
-
|
|
12
|
-
/*----------------------------------
|
|
13
|
-
- ROUTES
|
|
14
|
-
----------------------------------*/
|
|
15
|
-
|
|
16
|
-
route.get('*', { priority: 10 }, async ({ request, user, auth, response }) => {
|
|
17
|
-
|
|
18
|
-
if (!request.accepts('html'))
|
|
19
|
-
return undefined;
|
|
20
|
-
|
|
21
|
-
// User activity
|
|
22
|
-
if (user) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
} else {
|
|
27
|
-
|
|
28
|
-
// Referrer Tracking: <url>?r=<referrer>
|
|
29
|
-
const referrer = request.req.query.r;
|
|
30
|
-
await $.auth.setReferrer(referrer, request);
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
route.get('/r/:referrer', { priority: 10 }, async ({ response, request }) => {
|
|
36
|
-
|
|
37
|
-
const { referrer } = request.data;
|
|
38
|
-
|
|
39
|
-
await $.auth.setReferrer(referrer, request);
|
|
40
|
-
|
|
41
|
-
return response.redirect('/');
|
|
42
|
-
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
route.post('/auth/email', async ({ schema, auth, request, response, detect }) => {
|
|
46
|
-
|
|
47
|
-
const { email } = await schema.validate({
|
|
48
|
-
email: schema.email(),
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
return await $.auth.Auth(email, request);
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
route.get('/auth/google', async ({ auth, response, request }) => {
|
|
56
|
-
|
|
57
|
-
return response.redirect( await $.auth.FromGoogle(request) );
|
|
58
|
-
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
route.get('/auth/google/response', async ({ auth, response, request, detect }) => {
|
|
62
|
-
|
|
63
|
-
// Pas besoin de passer par schema, car le code est seulement et diectement passé à l'api de google
|
|
64
|
-
let { code } = request.data;
|
|
65
|
-
code = decodeURIComponent(code);
|
|
66
|
-
|
|
67
|
-
const { redirect } = await $.auth.GoogleResponse('code', code, request);
|
|
68
|
-
|
|
69
|
-
return response.redirect( redirect );
|
|
70
|
-
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Android app: One tap UI
|
|
74
|
-
route.get('/auth/google/onetap/:token', async ({ request, auth, response, detect }) => {
|
|
75
|
-
|
|
76
|
-
// Pas besoin de passer par schema, car le token est seulement et diectement passé à l'api de google
|
|
77
|
-
const { token } = request.data;
|
|
78
|
-
|
|
79
|
-
const authRes = await $.auth.GoogleResponse('token', token, request);
|
|
80
|
-
|
|
81
|
-
return response.text( authRes.token );
|
|
82
|
-
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
route.post('/auth/logout', async ({ auth }) => {
|
|
86
|
-
|
|
87
|
-
auth.logout();
|
|
88
|
-
|
|
89
|
-
return true;
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// Too specific to project
|
|
99
|
-
route.get('/invite', async ({ auth, schema }) => {
|
|
100
|
-
|
|
101
|
-
const user = await auth.check("USER");
|
|
102
|
-
|
|
103
|
-
const referrals = await app.services.sql`
|
|
104
|
-
SELECT
|
|
105
|
-
name,
|
|
106
|
-
country,
|
|
107
|
-
meet,
|
|
108
|
-
activity
|
|
109
|
-
FROM User u
|
|
110
|
-
WHERE referrer = ${user.email}
|
|
111
|
-
`.all();
|
|
112
|
-
|
|
113
|
-
const stats = await getStats('UserStats', [
|
|
114
|
-
'refClics',
|
|
115
|
-
'refSignups',
|
|
116
|
-
'refCommission'
|
|
117
|
-
], {
|
|
118
|
-
relative: true,
|
|
119
|
-
period: '12 hours',
|
|
120
|
-
interval: '1 hour',
|
|
121
|
-
where: user ? (`user = ` + app.services.sql.esc(user.email)) : '0',
|
|
122
|
-
cache: user ? {
|
|
123
|
-
id: 'user.' + user.email + '.referrals'
|
|
124
|
-
} : undefined
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
return { ...stats, referrals };
|
|
128
|
-
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
route.get('/@:name/avatar.webp', {
|
|
132
|
-
logging: false,
|
|
133
|
-
priority: 2, // Before /@:username/:threadslug
|
|
134
|
-
}, async ({ schema, response }) => {
|
|
135
|
-
|
|
136
|
-
const { name } = await schema.validate({
|
|
137
|
-
name: schema.string()
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
if (name === "null")
|
|
141
|
-
return response.file(Logo);
|
|
142
|
-
|
|
143
|
-
const { emailHash } = await app.services.sql`
|
|
144
|
-
SELECT emailHash
|
|
145
|
-
FROM User
|
|
146
|
-
WHERE name = ${name}
|
|
147
|
-
`.firstOrFail();
|
|
148
|
-
|
|
149
|
-
return response.redirect("https://www.gravatar.com/avatar/" + emailHash + "?s=64&d=mp");
|
|
150
|
-
|
|
151
|
-
});
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/*----------------------------------
|
|
2
|
-
- DEPENDANCES
|
|
3
|
-
----------------------------------*/
|
|
4
|
-
|
|
5
|
-
// Npm
|
|
6
|
-
import redis, { RedisClient } from 'redis';
|
|
7
|
-
|
|
8
|
-
// Core
|
|
9
|
-
import app from '@server/app';
|
|
10
|
-
|
|
11
|
-
/*----------------------------------
|
|
12
|
-
- TYPES
|
|
13
|
-
----------------------------------*/
|
|
14
|
-
|
|
15
|
-
/*----------------------------------
|
|
16
|
-
- SERVICE CONFIG
|
|
17
|
-
----------------------------------*/
|
|
18
|
-
|
|
19
|
-
export type RedisServiceConfig = {
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
declare global {
|
|
24
|
-
namespace Core {
|
|
25
|
-
namespace Config {
|
|
26
|
-
interface Services {
|
|
27
|
-
redis: RedisServiceConfig
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/*----------------------------------
|
|
34
|
-
- SERVICE
|
|
35
|
-
----------------------------------*/
|
|
36
|
-
export class RedisService {
|
|
37
|
-
|
|
38
|
-
public instance!: RedisClient;
|
|
39
|
-
|
|
40
|
-
/*----------------------------------
|
|
41
|
-
- HOOKS
|
|
42
|
-
----------------------------------*/
|
|
43
|
-
public constructor() {
|
|
44
|
-
this.instance = redis.createClient()
|
|
45
|
-
.on('connect', () => {
|
|
46
|
-
console.log('Connecté au serveur Redis');
|
|
47
|
-
})
|
|
48
|
-
.on('error', (e) => {
|
|
49
|
-
//Probleme.Signaler(`Serveur Redis`, e, undefined);
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public load() {
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/*----------------------------------
|
|
60
|
-
- REGISTER SERVICE
|
|
61
|
-
----------------------------------*/
|
|
62
|
-
app.register('redis', RedisService);
|
|
63
|
-
declare global {
|
|
64
|
-
namespace Core {
|
|
65
|
-
interface Services {
|
|
66
|
-
redis: RedisService;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export default new RedisService
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/*----------------------------------
|
|
2
|
-
- DEPENDANCES
|
|
3
|
-
----------------------------------*/
|
|
4
|
-
|
|
5
|
-
// Npm
|
|
6
|
-
import type express from 'express';
|
|
7
|
-
import type http from 'http';
|
|
8
|
-
import jwt from 'jsonwebtoken';
|
|
9
|
-
|
|
10
|
-
// Cre
|
|
11
|
-
import app, { $ } from '@server/app';
|
|
12
|
-
import { InputError, AuthRequired, Forbidden } from '@common/errors';
|
|
13
|
-
import type { TUserRole } from '@server/services/auth/base';
|
|
14
|
-
|
|
15
|
-
/*----------------------------------
|
|
16
|
-
- TYPES
|
|
17
|
-
----------------------------------*/
|
|
18
|
-
|
|
19
|
-
import type ServerRequest from '@server/services/router/request'
|
|
20
|
-
|
|
21
|
-
type TJwtSession = { email: string }
|
|
22
|
-
|
|
23
|
-
type TRequest = express.Request | http.IncomingMessage;
|
|
24
|
-
|
|
25
|
-
/*----------------------------------
|
|
26
|
-
- CONFIG
|
|
27
|
-
----------------------------------*/
|
|
28
|
-
|
|
29
|
-
const config = app.config.auth;
|
|
30
|
-
|
|
31
|
-
const LogPrefix = '[router][auth]';
|
|
32
|
-
|
|
33
|
-
/*----------------------------------
|
|
34
|
-
- MODULE
|
|
35
|
-
----------------------------------*/
|
|
36
|
-
export default class AuthService {
|
|
37
|
-
|
|
38
|
-
protected request: ServerRequest;
|
|
39
|
-
|
|
40
|
-
public constructor( request: ServerRequest ) {
|
|
41
|
-
this.request = request;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
public static async decode( req: TRequest, withData: true ): Promise<User | null>;
|
|
45
|
-
public static async decode( req: TRequest, withData?: false ): Promise<string | null>;
|
|
46
|
-
public static async decode( req: TRequest, withData: boolean = false ): Promise<string | User | null> {
|
|
47
|
-
|
|
48
|
-
config.debug && console.log(LogPrefix, 'Decode:', { cookie: req.cookies['authorization'] });
|
|
49
|
-
|
|
50
|
-
let token: string | undefined;
|
|
51
|
-
if (('cookies' in req) && typeof req.cookies['authorization'] === 'string')
|
|
52
|
-
token = req.cookies['authorization'];
|
|
53
|
-
// Desktop app webview do not support cookie config, so wwe retrieve it from headers
|
|
54
|
-
else if (typeof req.headers['authorization'] === 'string')
|
|
55
|
-
token = req.headers['authorization'];
|
|
56
|
-
|
|
57
|
-
if (token === undefined)
|
|
58
|
-
return this.authFailed(req);
|
|
59
|
-
|
|
60
|
-
let session: TJwtSession;
|
|
61
|
-
try {
|
|
62
|
-
session = jwt.verify(token, config.jwt.key, { maxAge: config.jwt.expiration });
|
|
63
|
-
} catch (error) {
|
|
64
|
-
console.warn(LogPrefix, "Failed to decode jwt token:", token);
|
|
65
|
-
return this.authFailed(req);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Not normal (could assume the JWT session is incompatible ??)
|
|
69
|
-
if (!session.email) {
|
|
70
|
-
console.warn(LogPrefix, "No email provided in JWT decrypted session:", session);
|
|
71
|
-
return this.authFailed(req);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Return email only
|
|
75
|
-
if (!withData) {
|
|
76
|
-
config.debug && console.log(LogPrefix, `Auth user ${session.email} successfull. Return email only`);
|
|
77
|
-
return session.email;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Deserialize full user data
|
|
81
|
-
config.debug && console.log(LogPrefix, `Deserialize user ${session.email}`);
|
|
82
|
-
const user = await $.auth.getData('email = ' + $.sql.esc(session.email));
|
|
83
|
-
if (user) {
|
|
84
|
-
|
|
85
|
-
config.debug && console.log(LogPrefix, `Deserialized user ${session.email}:`, user);
|
|
86
|
-
|
|
87
|
-
// Banni = déconnexion
|
|
88
|
-
// Une erreur s'affichera à chaque tentatove de login
|
|
89
|
-
if (user.banned) {
|
|
90
|
-
|
|
91
|
-
this.authFailed(req);
|
|
92
|
-
throw new Forbidden("Your account has been suspended. If you think it's a mistake, please contact me: contact@gaetan-legac.fr.");
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return user;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private static authFailed( req: TRequest ) {
|
|
101
|
-
|
|
102
|
-
if ('res' in req) {
|
|
103
|
-
// If use auth failed, we remove the jwt token so we avoid to trigger the same auth error in the next request
|
|
104
|
-
console.warn(LogPrefix, "Auth failed: remove authorization cookie");
|
|
105
|
-
req.res?.clearCookie('authorization');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
public login( email: string ): string {
|
|
112
|
-
|
|
113
|
-
config.debug && console.info(LogPrefix, `Authentification de ` + email);
|
|
114
|
-
|
|
115
|
-
const token = jwt.sign({ email }, config.jwt.key);
|
|
116
|
-
|
|
117
|
-
config.debug && console.info(LogPrefix, `Generated JWT token: ` + token);
|
|
118
|
-
|
|
119
|
-
this.request.res.cookie('authorization', token);
|
|
120
|
-
|
|
121
|
-
return token;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
public logout() {
|
|
125
|
-
|
|
126
|
-
const user = this.request.user;
|
|
127
|
-
if (!user) return;
|
|
128
|
-
|
|
129
|
-
config.debug && console.info(LogPrefix, `Logout ${user.email}`);
|
|
130
|
-
this.request.res.clearCookie('authorization');
|
|
131
|
-
$.socket.disconnect(user.email, 'Logout');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public check(role: TUserRole, motivation?: string): User;
|
|
135
|
-
public check(role: false, motivation?: string): null;
|
|
136
|
-
public check(role: TUserRole | boolean = 'USER', motivation?: string): User | null {
|
|
137
|
-
|
|
138
|
-
const user = this.request.user;
|
|
139
|
-
|
|
140
|
-
if (role === true)
|
|
141
|
-
role = 'USER';
|
|
142
|
-
|
|
143
|
-
// First layer control
|
|
144
|
-
if (role === false) {
|
|
145
|
-
|
|
146
|
-
if (user !== null)
|
|
147
|
-
throw new InputError("You're already logged in.");
|
|
148
|
-
|
|
149
|
-
} else if (role === 'DEV' && (process.env.environnement === 'local' || (user && user.roles.includes('ADMIN')))) {
|
|
150
|
-
|
|
151
|
-
// It's a bypass
|
|
152
|
-
return user;
|
|
153
|
-
|
|
154
|
-
} else if (user === null) {
|
|
155
|
-
|
|
156
|
-
config.debug && console.warn(LogPrefix, "Refusé pour anonyme (" + this.request.ip + ")");
|
|
157
|
-
throw new AuthRequired(motivation);
|
|
158
|
-
|
|
159
|
-
} else {
|
|
160
|
-
|
|
161
|
-
// Second layer control
|
|
162
|
-
if (!user.roles.includes(role)) {
|
|
163
|
-
|
|
164
|
-
console.warn(LogPrefix, "Refusé: " + role + " pour " + user.email + " (" + (user.roles ? user.roles.join(', ') : 'role inconnu') + ")");
|
|
165
|
-
|
|
166
|
-
throw new Forbidden("You do not have sufficient permissions to access this resource.");
|
|
167
|
-
|
|
168
|
-
} else {
|
|
169
|
-
|
|
170
|
-
console.warn(LogPrefix, "Autorisé " + role + " pour " + user.email + " (" + user.roles.join(', ') + ")");
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return user;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|