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.
Files changed (127) hide show
  1. package/changelog.md +5 -0
  2. package/doc/TODO.md +71 -0
  3. package/package.json +5 -4
  4. package/src/client/{App.tsx → app/component.tsx} +15 -8
  5. package/src/client/app/index.ts +128 -0
  6. package/src/client/app/service.ts +34 -0
  7. package/src/client/app.tsconfig.json +0 -4
  8. package/src/client/assets/css/medias.less +14 -0
  9. package/src/client/components/Card/index.tsx +2 -2
  10. package/src/client/components/Dialog/Manager.tsx +39 -12
  11. package/src/client/components/Form/index.tsx +1 -1
  12. package/src/client/components/button.tsx +2 -2
  13. package/src/client/components/containers/Popover/index.tsx +1 -1
  14. package/src/client/components/data/spintext/index.tsx +1 -1
  15. package/src/client/components/dropdown/index.tsx +1 -1
  16. package/src/client/components/index.ts +8 -0
  17. package/src/client/components/input/BaseV2/index.tsx +1 -1
  18. package/src/client/components/input/UploadImage/index.tsx +1 -1
  19. package/src/client/hooks/index.ts +5 -0
  20. package/src/client/hooks/useState/index.tsx +2 -2
  21. package/src/client/hooks.ts +22 -0
  22. package/src/client/index.ts +5 -0
  23. package/src/client/pages/_layout/landing/index.tsx +0 -2
  24. package/src/client/pages/_messages/400.tsx +2 -2
  25. package/src/client/pages/_messages/401.tsx +2 -2
  26. package/src/client/pages/_messages/403.tsx +2 -2
  27. package/src/client/pages/_messages/404.tsx +2 -2
  28. package/src/client/pages/_messages/500.tsx +2 -2
  29. package/src/client/pages/bug.tsx +1 -1
  30. package/src/client/pages/useHeader.tsx +1 -1
  31. package/src/client/{context/captcha.ts → services/captcha/index.ts} +0 -0
  32. package/src/client/services/metrics/index.ts +37 -0
  33. package/src/client/{router → services/router/components}/Link.tsx +1 -1
  34. package/src/client/services/router/components/Page.tsx +59 -0
  35. package/src/client/{router/component.tsx → services/router/components/router.tsx} +43 -74
  36. package/src/client/services/router/index.tsx +448 -0
  37. package/src/client/services/router/request/api.ts +229 -0
  38. package/src/client/{router → services/router}/request/history.ts +0 -0
  39. package/src/client/services/router/request/index.ts +52 -0
  40. package/src/client/services/router/response/index.tsx +107 -0
  41. package/src/client/services/router/response/page.ts +95 -0
  42. package/src/client/{context/socket.ts → services/socket/index.ts} +2 -2
  43. package/src/client/utils/dom.ts +1 -1
  44. package/src/common/app/index.ts +9 -0
  45. package/src/common/data/chaines/index.ts +9 -6
  46. package/src/common/data/input/validate.ts +3 -166
  47. package/src/common/data/objets.ts +25 -0
  48. package/src/common/data/tableaux.ts +8 -0
  49. package/src/common/errors/index.ts +3 -1
  50. package/src/common/router/index.ts +67 -88
  51. package/src/common/router/layouts.ts +50 -0
  52. package/src/common/router/register.ts +62 -0
  53. package/src/common/router/request/api.ts +72 -0
  54. package/src/common/router/request/index.ts +31 -0
  55. package/src/common/router/{response.ts → response/index.ts} +9 -13
  56. package/src/common/router/response/page.ts +40 -56
  57. package/src/common/validation/index.ts +3 -0
  58. package/src/common/validation/schema.ts +184 -0
  59. package/src/common/validation/validator.ts +88 -0
  60. package/src/common/validation/validators.ts +313 -0
  61. package/src/server/app/config.ts +9 -27
  62. package/src/server/app/index.ts +81 -124
  63. package/src/server/app/service.ts +98 -0
  64. package/src/server/app.tsconfig.json +0 -8
  65. package/src/server/error/index.ts +13 -0
  66. package/src/server/index.ts +5 -0
  67. package/src/server/patch.ts +0 -6
  68. package/src/server/{data/Cache.ts → services/cache/index.ts} +79 -47
  69. package/src/server/services/console/bugReporter.ts +26 -16
  70. package/src/server/services/console/index.ts +59 -51
  71. package/src/server/services/cron/index.ts +12 -26
  72. package/src/server/services/database/bucket.ts +40 -0
  73. package/src/server/services/database/connection.ts +206 -75
  74. package/src/server/services/database/datatypes.ts +63 -40
  75. package/src/server/services/database/index.ts +295 -272
  76. package/src/server/services/database/metas.ts +246 -135
  77. package/src/server/services/database/stats.ts +151 -126
  78. package/src/server/services/email/index.ts +28 -52
  79. package/src/server/services/{router/request/services → metrics}/detect.ts +8 -10
  80. package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
  81. package/src/server/services/{http → router/http}/index.ts +28 -70
  82. package/src/server/services/{http → router/http}/multipart.ts +0 -0
  83. package/src/server/services/{http → router/http}/session.ts.old +0 -0
  84. package/src/server/services/router/index.ts +273 -203
  85. package/src/server/services/router/request/api.ts +73 -0
  86. package/src/server/services/router/request/index.ts +16 -97
  87. package/src/server/services/router/request/service.ts +21 -0
  88. package/src/server/services/router/response/index.ts +125 -64
  89. package/src/server/services/router/response/{filter → mask}/Filter.ts +0 -0
  90. package/src/server/services/router/response/{filter → mask}/index.ts +0 -2
  91. package/src/server/services/router/response/{filter → mask}/selecteurs.ts +0 -0
  92. package/src/server/services/router/response/page/document.tsx +194 -0
  93. package/src/server/services/router/response/page/index.tsx +157 -0
  94. package/src/server/{libs/pages → services/router/response/page}/schemaGenerator.ts +0 -0
  95. package/src/server/services/router/service.ts +48 -0
  96. package/src/server/services/schema/index.ts +47 -0
  97. package/src/server/services/schema/request.ts +55 -0
  98. package/src/server/services/schema/router.ts +33 -0
  99. package/src/server/services/socket/index.ts +38 -43
  100. package/src/server/services/socket/scope.ts +6 -4
  101. package/src/server/services/users/index.ts +203 -0
  102. package/src/server/services/{auth/base.ts → users/old.ts} +28 -112
  103. package/src/server/services/users/router/index.ts +72 -0
  104. package/src/server/services/users/router/request.ts +49 -0
  105. package/src/types/aliases.d.ts +43 -2
  106. package/templates/composant.tsx +1 -1
  107. package/templates/modal.tsx +1 -1
  108. package/templates/page.tsx +1 -1
  109. package/tsconfig.common.json +0 -4
  110. package/src/client/context/api.ts +0 -92
  111. package/src/client/context/index.ts +0 -246
  112. package/src/client/index.tsx +0 -129
  113. package/src/client/router/index.ts +0 -286
  114. package/src/client/router/request/index.ts +0 -106
  115. package/src/client/router/response/index.ts +0 -38
  116. package/src/client/router/route.ts +0 -75
  117. package/src/common/data/input/validators/basic.ts +0 -299
  118. package/src/common/data/input/validators/build.ts +0 -63
  119. package/src/common/router/request.ts +0 -83
  120. package/src/server/data/ApiClient.ts +0 -119
  121. package/src/server/data/input.ts +0 -41
  122. package/src/server/libs/pages/document.static.tsx +0 -41
  123. package/src/server/libs/pages/document.tsx +0 -203
  124. package/src/server/libs/pages/render.tsx +0 -90
  125. package/src/server/routes/auth.ts +0 -151
  126. package/src/server/services/redis/index.ts +0 -71
  127. 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
- }