proteum 1.0.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/.dockerignore +10 -0
  2. package/Rte.zip +0 -0
  3. package/cli/app/config.ts +54 -0
  4. package/cli/app/index.ts +195 -0
  5. package/cli/bin.js +11 -0
  6. package/cli/commands/build.ts +34 -0
  7. package/cli/commands/deploy/app.ts +29 -0
  8. package/cli/commands/deploy/web.ts +60 -0
  9. package/cli/commands/dev.ts +109 -0
  10. package/cli/commands/init.ts +85 -0
  11. package/cli/compiler/client/identite.ts +72 -0
  12. package/cli/compiler/client/index.ts +334 -0
  13. package/cli/compiler/common/babel/index.ts +170 -0
  14. package/cli/compiler/common/babel/plugins/index.ts +0 -0
  15. package/cli/compiler/common/babel/plugins/services.ts +579 -0
  16. package/cli/compiler/common/babel/routes/imports.ts +127 -0
  17. package/cli/compiler/common/babel/routes/routes.ts +1130 -0
  18. package/cli/compiler/common/files/autres.ts +39 -0
  19. package/cli/compiler/common/files/images.ts +35 -0
  20. package/cli/compiler/common/files/style.ts +78 -0
  21. package/cli/compiler/common/index.ts +154 -0
  22. package/cli/compiler/index.ts +532 -0
  23. package/cli/compiler/server/index.ts +211 -0
  24. package/cli/index.ts +189 -0
  25. package/cli/paths.ts +165 -0
  26. package/cli/print.ts +12 -0
  27. package/cli/tsconfig.json +38 -0
  28. package/cli/utils/index.ts +22 -0
  29. package/cli/utils/keyboard.ts +78 -0
  30. package/client/app/component.tsx +54 -0
  31. package/client/app/index.ts +142 -0
  32. package/client/app/service.ts +34 -0
  33. package/client/app.tsconfig.json +28 -0
  34. package/client/components/Button.tsx +298 -0
  35. package/client/components/Dialog/Manager.tsx +309 -0
  36. package/client/components/Dialog/card.tsx +208 -0
  37. package/client/components/Dialog/index.less +151 -0
  38. package/client/components/Dialog/status.less +176 -0
  39. package/client/components/Dialog/status.tsx +48 -0
  40. package/client/components/index.ts +2 -0
  41. package/client/components/types.d.ts +3 -0
  42. package/client/data/input.ts +32 -0
  43. package/client/global.d.ts +5 -0
  44. package/client/hooks.ts +22 -0
  45. package/client/index.ts +6 -0
  46. package/client/pages/_layout/index.less +6 -0
  47. package/client/pages/_layout/index.tsx +43 -0
  48. package/client/pages/bug.tsx.old +60 -0
  49. package/client/pages/useHeader.tsx +50 -0
  50. package/client/services/captcha/index.ts +67 -0
  51. package/client/services/router/components/Link.tsx +46 -0
  52. package/client/services/router/components/Page.tsx +55 -0
  53. package/client/services/router/components/router.tsx +218 -0
  54. package/client/services/router/index.tsx +521 -0
  55. package/client/services/router/request/api.ts +267 -0
  56. package/client/services/router/request/history.ts +5 -0
  57. package/client/services/router/request/index.ts +53 -0
  58. package/client/services/router/request/multipart.ts +147 -0
  59. package/client/services/router/response/index.tsx +128 -0
  60. package/client/services/router/response/page.ts +86 -0
  61. package/client/services/socket/index.ts +147 -0
  62. package/client/utils/dom.ts +77 -0
  63. package/common/app/index.ts +9 -0
  64. package/common/data/chaines/index.ts +54 -0
  65. package/common/data/dates.ts +179 -0
  66. package/common/data/markdown.ts +73 -0
  67. package/common/data/rte/nodes.ts +83 -0
  68. package/common/data/stats.ts +90 -0
  69. package/common/errors/index.tsx +326 -0
  70. package/common/router/index.ts +213 -0
  71. package/common/router/layouts.ts +93 -0
  72. package/common/router/register.ts +55 -0
  73. package/common/router/request/api.ts +77 -0
  74. package/common/router/request/index.ts +35 -0
  75. package/common/router/response/index.ts +45 -0
  76. package/common/router/response/page.ts +128 -0
  77. package/common/utils/rte.ts +183 -0
  78. package/common/utils.ts +7 -0
  79. package/doc/TODO.md +71 -0
  80. package/doc/front/router.md +27 -0
  81. package/doc/workspace/workspace.png +0 -0
  82. package/doc/workspace/workspace2.png +0 -0
  83. package/doc/workspace/workspace_26.01.22.png +0 -0
  84. package/package.json +171 -0
  85. package/server/app/commands.ts +141 -0
  86. package/server/app/container/config.ts +203 -0
  87. package/server/app/container/console/index.ts +550 -0
  88. package/server/app/container/index.ts +137 -0
  89. package/server/app/index.ts +273 -0
  90. package/server/app/service/container.ts +88 -0
  91. package/server/app/service/index.ts +235 -0
  92. package/server/app.tsconfig.json +28 -0
  93. package/server/context.ts +4 -0
  94. package/server/index.ts +4 -0
  95. package/server/services/auth/index.ts +250 -0
  96. package/server/services/auth/old.ts +277 -0
  97. package/server/services/auth/router/index.ts +95 -0
  98. package/server/services/auth/router/request.ts +54 -0
  99. package/server/services/auth/router/service.json +6 -0
  100. package/server/services/auth/service.json +6 -0
  101. package/server/services/cache/commands.ts +41 -0
  102. package/server/services/cache/index.ts +297 -0
  103. package/server/services/cache/service.json +6 -0
  104. package/server/services/cron/CronTask.ts +86 -0
  105. package/server/services/cron/index.ts +112 -0
  106. package/server/services/cron/service.json +6 -0
  107. package/server/services/disks/driver.ts +103 -0
  108. package/server/services/disks/drivers/local/index.ts +188 -0
  109. package/server/services/disks/drivers/local/service.json +6 -0
  110. package/server/services/disks/drivers/s3/index.ts +301 -0
  111. package/server/services/disks/drivers/s3/service.json +6 -0
  112. package/server/services/disks/index.ts +90 -0
  113. package/server/services/disks/service.json +6 -0
  114. package/server/services/email/index.ts +188 -0
  115. package/server/services/email/utils.ts +53 -0
  116. package/server/services/fetch/index.ts +201 -0
  117. package/server/services/fetch/service.json +7 -0
  118. package/server/services/models.7z +0 -0
  119. package/server/services/prisma/Facet.ts +142 -0
  120. package/server/services/prisma/index.ts +201 -0
  121. package/server/services/prisma/service.json +6 -0
  122. package/server/services/router/http/index.ts +217 -0
  123. package/server/services/router/http/multipart.ts +102 -0
  124. package/server/services/router/http/session.ts.old +40 -0
  125. package/server/services/router/index.ts +801 -0
  126. package/server/services/router/request/api.ts +87 -0
  127. package/server/services/router/request/index.ts +184 -0
  128. package/server/services/router/request/service.ts +21 -0
  129. package/server/services/router/request/validation/zod.ts +180 -0
  130. package/server/services/router/response/index.ts +338 -0
  131. package/server/services/router/response/mask/Filter.ts +323 -0
  132. package/server/services/router/response/mask/index.ts +60 -0
  133. package/server/services/router/response/mask/selecteurs.ts +92 -0
  134. package/server/services/router/response/page/document.tsx +160 -0
  135. package/server/services/router/response/page/index.tsx +196 -0
  136. package/server/services/router/service.json +6 -0
  137. package/server/services/router/service.ts +36 -0
  138. package/server/services/schema/index.ts +44 -0
  139. package/server/services/schema/request.ts +49 -0
  140. package/server/services/schema/router/index.ts +28 -0
  141. package/server/services/schema/router/service.json +6 -0
  142. package/server/services/schema/service.json +6 -0
  143. package/server/services/security/encrypt/aes/index.ts +85 -0
  144. package/server/services/security/encrypt/aes/service.json +6 -0
  145. package/server/services/socket/index.ts +162 -0
  146. package/server/services/socket/scope.ts +226 -0
  147. package/server/services/socket/service.json +6 -0
  148. package/server/services_old/SocketClient.ts +92 -0
  149. package/server/services_old/Token.old.ts +97 -0
  150. package/server/utils/slug.ts +79 -0
  151. package/tsconfig.common.json +45 -0
  152. package/tsconfig.json +3 -0
  153. package/types/aliases.d.ts +54 -0
  154. package/types/global/modules.d.ts +49 -0
  155. package/types/global/utils.d.ts +103 -0
  156. package/types/icons.d.ts +1 -0
@@ -0,0 +1,338 @@
1
+ /* INSPIRATION:
2
+ https://laravel.com/docs/8.x/responses
3
+ https://docs.adonisjs.com/guides/response
4
+ */
5
+
6
+ /*----------------------------------
7
+ - DEPENDANCES
8
+ ----------------------------------*/
9
+
10
+ // Npm
11
+ import express from 'express';
12
+
13
+ // Core
14
+ import { Application } from '@server/app';
15
+ import type { AnyRouterService, default as ServerRouter, TServerRouter, TAnyRouter } from '@server/services/router';
16
+ import ServerRequest from '@server/services/router/request';
17
+ import { TRoute, TAnyRoute, TDomainsList } from '@common/router';
18
+ import { NotFound, Forbidden, Anomaly } from '@common/errors';
19
+ import BaseResponse, { TResponseData } from '@common/router/response';
20
+ import Page from './page';
21
+
22
+ // To move into a new npm module: json-mask
23
+ import jsonMask from './mask';
24
+
25
+ // Types
26
+ import type { TBasicUser } from '@server/services/auth';
27
+
28
+ /*----------------------------------
29
+ - TYPES
30
+ ----------------------------------*/
31
+
32
+ const debug = true;
33
+
34
+ export type TBasicSSrData = {
35
+ request: { data: TObjetDonnees, id: string },
36
+ page: { chunkId: string, data?: TObjetDonnees },
37
+ user: TBasicUser | null,
38
+ domains: TDomainsList
39
+ }
40
+
41
+ export type TRouterContext<TRouter extends TServerRouter> = (
42
+ // Request context
43
+ {
44
+ app: TRouter["app"],
45
+ context: TRouterContext<TRouter>, // = this
46
+ request: ServerRequest<TRouter>,
47
+ api: ServerRequest<TRouter>["api"],
48
+ response: ServerResponse<TRouter>,
49
+ route: TRoute,
50
+ page?: Page,
51
+
52
+ Router: TRouter,
53
+ }
54
+ & TRouterContextServices<TRouter>
55
+ //& TRouterRequestContext<TRouter>
56
+ )
57
+
58
+ export type TRouterContextServices<
59
+ TRouter extends TServerRouter,
60
+ TPlugins = TRouter["config"]["plugins"]
61
+ > = (
62
+ // Custom context via servuces
63
+ // For each roiuter service, return the request service (returned by roiuterService.requestService() )
64
+ {
65
+ [serviceName in keyof TPlugins]: TPlugins[serviceName] extends AnyRouterService
66
+ ? ReturnType<TPlugins[serviceName]["requestService"]>
67
+ : TPlugins[serviceName]
68
+ }
69
+ )
70
+
71
+ export type TRouterRequestContext<
72
+ TRouter extends TServerRouter
73
+ > = ReturnType<TRouter["config"]["context"]>
74
+
75
+
76
+ /*----------------------------------
77
+ - CLASSE
78
+ ----------------------------------*/
79
+ export default class ServerResponse<
80
+ TRouter extends TAnyRouter,
81
+ TRequestContext = TRouterContext<TAnyRouter>,
82
+ TData extends TResponseData = TResponseData
83
+ > extends BaseResponse<TData, ServerRequest<TRouter>> {
84
+
85
+ // Services
86
+ public app: Application;
87
+ public router: TRouter;
88
+
89
+ // Response metadata
90
+ public statusCode: number = 200;
91
+ public headers: {[cle: string]: string} = {}
92
+ public cookie: express.Response["cookie"];
93
+ public clearCookie: express.Response["clearCookie"];
94
+ public canonicalUrl: URL;
95
+
96
+ // If data was provided by at lead one controller
97
+ public wasProvided = false;
98
+
99
+ public constructor( request: ServerRequest<TRouter> ) {
100
+
101
+ super(request);
102
+
103
+ this.cookie = this.request.res.cookie.bind(this.request.res);
104
+ this.clearCookie = this.request.res.clearCookie.bind(this.request.res);
105
+
106
+ this.router = request.router;
107
+ this.app = this.router.app;
108
+
109
+ this.canonicalUrl = new URL(request.url);
110
+ this.canonicalUrl.search = '';
111
+ }
112
+
113
+ public async runController( route: TAnyRoute, additionnalData: {} = {} ) {
114
+
115
+ this.route = route;
116
+
117
+ // Update canonical url
118
+ this.updateCanonicalUrl(route);
119
+
120
+ // Create response context for controllers
121
+ const context = await this.createContext(route);
122
+
123
+ // Run controller
124
+ const content = await this.route.controller( context );
125
+ if (content === undefined)
126
+ return;
127
+
128
+ // No need to process the content
129
+ if (content instanceof ServerResponse)
130
+ return;
131
+ // Render react page to html
132
+ else if (content instanceof Page)
133
+ await this.render(content, context, additionnalData);
134
+ // Return HTML
135
+ else if (typeof content === 'string' && this.route.options.accept === 'html')
136
+ await this.html(content);
137
+ // Return JSON
138
+ else
139
+ await this.json(content);
140
+ }
141
+
142
+ private updateCanonicalUrl( route: TAnyRoute ) {
143
+
144
+ if (!route.options.canonicalParams)
145
+ return;
146
+
147
+ for (const key of route.options.canonicalParams) {
148
+ const paramValue = this.request.data[ key ];
149
+ if (paramValue !== undefined)
150
+ this.canonicalUrl.searchParams.set(key, paramValue);
151
+ }
152
+ }
153
+
154
+ /*----------------------------------
155
+ - INTERNAL
156
+ ----------------------------------*/
157
+
158
+ // Start controller services
159
+ private async createContext( route: TRoute ): Promise<TRequestContext> {
160
+
161
+ const contextServices = this.router.createContextServices(this.request);
162
+
163
+ const customSsrData = this.router.config.context(this.request, this.app);
164
+
165
+ // TODO: transmiss safe data (especially for Router), as Router info could be printed on client side
166
+ const context: TRequestContext = {
167
+ // Router context
168
+ app: this.app,
169
+ context: undefined as TRequestContext,
170
+ request: this.request,
171
+ response: this,
172
+ route: route,
173
+ api: this.request.api,
174
+
175
+ Router: this.router,
176
+
177
+ // Router services
178
+ ...(contextServices as TRouterContextServices<TRouter>),
179
+ ...customSsrData
180
+ }
181
+
182
+ context.context = context;
183
+
184
+ return context;
185
+ }
186
+
187
+ public forSsr( page: Page<TRouter> ): TBasicSSrData {
188
+
189
+ const customSsrData = this.router.config.context(this.request, this.app);
190
+
191
+ return {
192
+ request: {
193
+ id: this.request.id,
194
+ data: this.request.data,
195
+ },
196
+ page: {
197
+ chunkId: page.chunkId,
198
+ data: page.data
199
+ },
200
+ domains: this.router.config.domains,
201
+ ...customSsrData
202
+ }
203
+ }
204
+
205
+ public status(code: number) {
206
+ this.statusCode = code;
207
+ return this;
208
+ }
209
+
210
+ public setHeaders( headers: {[cle: string]: string} ) {
211
+ this.headers = { ...this.headers, ...headers };
212
+ return this;
213
+ }
214
+
215
+ /*----------------------------------
216
+ - DATA RESPONSE
217
+ ----------------------------------*/
218
+
219
+ public type( mimetype: string ) {
220
+ this.headers['Content-Type'] = mimetype;
221
+ return this;
222
+ }
223
+
224
+ public async render( page: Page, context: TRouterContext<TRouter>, additionnalData: {} ) {
225
+
226
+ // Set page in context for the client side
227
+ context.page = page;
228
+
229
+ // Prepare page & fetch data
230
+ page.data = await page.fetchData();
231
+ if (additionnalData !== undefined) // Example: error message for error pages
232
+ page.data = { ...page.data, ...additionnalData }
233
+
234
+ // Render page
235
+ await this.router.runHook('render', page);
236
+ const document = await page.render();
237
+ this.html(document);
238
+
239
+ // Never put html in the cache
240
+ // Because assets urls need to be updated when their hash has been changed by a release
241
+ this.request.res.setHeader("Expires", "0");
242
+
243
+ }
244
+
245
+ public async json(data?: any, mask?: string) {
246
+
247
+ // RAPPEL: On jsonMask aussi les requetes internes, car leurs données seront imprimées au SSR pour le contexte client
248
+ // filtreApi vérifie systèmatiquement si la donnée a été filtrée
249
+ // NOTE: On évite le filtrage sans masque spécifié (performances + risques erreurs)
250
+ if (mask !== undefined)
251
+ data = await jsonMask(data, mask, this.request.user);
252
+
253
+ this.headers['Content-Type'] = 'application/json';
254
+ this.data = this.request.isVirtual ? data : JSON.stringify(data);
255
+ return this.end();
256
+ }
257
+
258
+ public html(html: string) {
259
+
260
+ this.headers['Content-Type'] = 'text/html';
261
+ this.data = html;
262
+ return this.end();
263
+
264
+ }
265
+
266
+ public xml(xml: string) {
267
+
268
+ this.headers['Content-Type'] = 'text/xml';
269
+ this.data = xml;
270
+ return this.end();
271
+ }
272
+
273
+ public text(text: string, mimetype: string = 'text/plain') {
274
+
275
+ this.headers['Content-Type'] = mimetype;
276
+ this.data = text;
277
+ return this.end();
278
+ }
279
+
280
+ // TODO: https://github.com/adonisjs/http-server/blob/develop/src/Response/index.ts#L430
281
+ public async file( filename: string, mimetype?: string ) {
282
+
283
+ // Securité
284
+ if (filename.includes('..'))
285
+ throw new Forbidden("Disallowed");
286
+
287
+ // // Force absolute path
288
+ // if (!filename.startsWith( this.app.path.root ))
289
+ // filename = filename[0] === '/'
290
+ // ? this.app.path.root + '/bin' + filename
291
+ // : this.app.path.data + '/' + filename;
292
+ // Disk not provided = file response disabled
293
+ if (this.router.disks === undefined)
294
+ throw new Anomaly("Router: Unable to return file response in router, because no disk has been given in the router config.");
295
+
296
+ // Retirve disk driver
297
+ const disk = this.router.disks.get('default');
298
+
299
+ // Verif existance
300
+ const fileExists = await disk.exists('data', filename);
301
+ if (!fileExists) {
302
+ console.log("File " + filename + " was not found.");
303
+ throw new NotFound();
304
+ }
305
+
306
+ // envoi filename
307
+ const file = await disk.readFile('data', filename, {
308
+ encoding: 'buffer'
309
+ });
310
+ this.data = file;
311
+
312
+
313
+ // Mimetype
314
+ if (mimetype !== undefined)
315
+ this.headers['Content-Type'] = mimetype;
316
+
317
+ return this.end();
318
+ }
319
+
320
+ public redirect(url: string, code: number = 302, absolute: boolean = false) {
321
+
322
+ debug && console.log("[routeur][response] Redirect", url);
323
+ this.statusCode = code;
324
+ this.headers['Location'] = this.router.url( url, {}, absolute );
325
+ return this.end();
326
+ }
327
+
328
+ public end() {
329
+ this.wasProvided = true;
330
+ return this;
331
+ }
332
+
333
+ public next() {
334
+ this.wasProvided = false;
335
+ return this;
336
+ }
337
+
338
+ }
@@ -0,0 +1,323 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Libs
6
+ import { TSelecteur } from './selecteurs';
7
+
8
+ /*----------------------------------
9
+ - TYPES
10
+ ----------------------------------*/
11
+
12
+ type TObjet = { [cle: string]: TObjet | any };
13
+
14
+ /*----------------------------------
15
+ - CONFIG
16
+ ----------------------------------*/
17
+
18
+ const debug = false;
19
+
20
+ const maxLevel = 10; // Prévention contre les références circulaires
21
+
22
+ /*----------------------------------
23
+ - CLASS
24
+ ----------------------------------*/
25
+ export default class Filtre {
26
+
27
+ public constructor() {
28
+
29
+
30
+ }
31
+
32
+ public filtrer(
33
+ donnee: TObjet | Array<unknown> | Date,
34
+ schema?: TSelecteur,
35
+ chemin: string[] = [],
36
+ schemaParent?: TObjet
37
+ ): any {
38
+
39
+ // Prévention contre les références circulaires
40
+ if (chemin.length > maxLevel)
41
+ throw new Error(`Erreur: Niveau max (${maxLevel}) atteint via la branche ${chemin.join('.')}. Vérifier s'il n'existe pas une référence circulaire dans l'objet à filtrer.`)
42
+
43
+ try {
44
+
45
+ // Tableau: on itère chaque élement de celui-ci
46
+ if (Array.isArray(donnee)) {
47
+
48
+ debug && console.log(`[requete][reponse][filtre]`, chemin.join('.'), ': Tableau');
49
+
50
+ return this.tableau(donnee, schema, chemin, schemaParent);
51
+
52
+ // Valeur: Chaque true doit être remplacé par la donnee[ nomBranche ] correspondante
53
+ // Si la donnée est une chaine, un nombre, etc ... On la traite comme s'il y avait un true
54
+ } else if (
55
+ // Extrémité de la branche
56
+ schema === true
57
+ ||
58
+ // Valeur non-itérable
59
+ !donnee
60
+ ||
61
+ typeof donnee !== 'object'
62
+ ||
63
+ donnee instanceof Date
64
+ ) {
65
+
66
+ debug && console.log(`[requete][reponse][filtre]`, chemin.join('.'), ': Valeur');
67
+
68
+ return this.valeur(
69
+ donnee,
70
+ schema,
71
+ chemin
72
+ );
73
+
74
+ // Objet
75
+ } else {
76
+
77
+ debug && console.log(`[requete][reponse][filtre]`, chemin.join('.'), ': Objet');
78
+
79
+ return this.objet(donnee, schema, chemin, schemaParent);
80
+
81
+ }
82
+
83
+ } catch (error) {
84
+
85
+ console.error('Erreur =', error, '|| données =', donnee, '|| chemin =', chemin, '|| schema =', schema);
86
+ throw new Error(`Erreur lors du filtrage. Infos ci-dessus.`);
87
+
88
+ }
89
+ }
90
+
91
+ private tableau(
92
+ donnee: any[],
93
+ schema: TSelecteur | undefined,
94
+
95
+ chemin: string[],
96
+ schemaParent?: TObjet
97
+ ) {
98
+
99
+ let retour: any[] = [];
100
+
101
+ for (const iElem in donnee) {
102
+ retour.push(
103
+ this.filtrer(
104
+ donnee[iElem],
105
+ schema,
106
+
107
+ [...chemin, iElem],
108
+ schemaParent
109
+ )
110
+ )
111
+ }
112
+
113
+ return retour;
114
+ }
115
+
116
+ private objet(
117
+ donnee: TObjet,
118
+ schema: TSelecteur | undefined,
119
+
120
+ chemin: string[],
121
+ schemaParent?: TObjet
122
+ ) {
123
+
124
+ if (typeof schema === 'object') {
125
+
126
+ // Exemple: article ( titre enfant( @ ) )
127
+ // Copie du schéma parent
128
+ if (schema['@'] === true) {
129
+
130
+ if (schemaParent === undefined)
131
+ throw new Error(`Référence au schéma parent trouvée, mais impossible d'accéder au schéma parent (schemaParent = undefined)`);
132
+
133
+ schema = schemaParent;
134
+
135
+ // Exemple: *
136
+ // Parcours et filtre toutes les entrées
137
+ } else if (schema['*'] === true) {
138
+
139
+ // L'itération se fera directement sur les données fournies
140
+ schema = undefined;
141
+
142
+ // Exemple: * ( nom symbole )
143
+ // Applique un selecteurs à toutes les entrées
144
+ } else if (schema['*'] !== undefined) {
145
+
146
+ const schemaBranches = schema['*'];
147
+ schema = {};
148
+
149
+ // Applique le schema du wildcard à toutes les entrées de l'objet
150
+ // TODO: alternative plus performante
151
+ for (const cle in donnee)
152
+ schema[cle] = schemaBranches;
153
+
154
+ }
155
+ }
156
+
157
+ let retour: TObjet = {};
158
+
159
+ // Liste des clés à itérer
160
+ let clesAiterer: string[];
161
+ if (schema !== undefined)
162
+ clesAiterer = Object.keys(schema as object);
163
+ else // En dernier recours, on itère tout simplement les données de l'objet
164
+ clesAiterer = Object.keys(donnee);
165
+
166
+ // Objet
167
+ for (const nomBranche of clesAiterer) {
168
+
169
+ const cheminA = [...chemin, nomBranche];
170
+ let donneeBranche = undefined;
171
+
172
+ // Extraction de la valeur de la propriété
173
+ if (donneeBranche === undefined)
174
+ donneeBranche = donnee[nomBranche];
175
+
176
+ // Filtrage de la valeur de la propriété
177
+ retour[nomBranche] = this.filtrer(
178
+ donneeBranche,
179
+ schema !== undefined ? schema[nomBranche] : undefined,
180
+
181
+ cheminA,
182
+
183
+ schema as TObjet
184
+ );
185
+ }
186
+
187
+ return retour;
188
+ }
189
+
190
+ // Traitement des données aux extrémités (auxquelles font référence les true)
191
+ private valeur(
192
+ donnee: any,
193
+ schema: TSelecteur,
194
+ chemin: string[]
195
+ ) {
196
+
197
+ // Si sélecteur wildcard
198
+ const wildcard = typeof schema === 'object' && schema['*'] === true
199
+
200
+ // Traitement des objets
201
+ if (donnee && typeof donnee === 'object') {
202
+
203
+ // Promise
204
+ if (typeof donnee.then === 'function')
205
+ throw new Error(chemin.join('.') + ": Les promises ne sont pas autorisées en retour api, sauf via un getter de modèle.");
206
+
207
+ // Sinon, wildcard obliatoire si on souhaite conserver l'objet entier
208
+ else if (wildcard === false) {
209
+
210
+ if (donnee instanceof Date)
211
+ return donnee.toISOString();
212
+ // Mauvaise idée: Les instances de modèle Sequelize possèdent une méthode toString()
213
+ /*else if (typeof donnee.toString === 'function')
214
+ return donnee.toString();*/
215
+
216
+ }
217
+
218
+ }
219
+
220
+ return donnee;
221
+ }
222
+
223
+ /*private propriete<TModele extends Modele>(
224
+ nom: keyof TModele,
225
+ modele: TModele,
226
+ metasClasse: TModelMetas,
227
+ cheminA: string[]
228
+ ): any | undefined {
229
+
230
+ const metasProp = metasClasse.attributes[nom];
231
+
232
+ if (metasProp === undefined)
233
+ console.warn(`ATTENTION: La propriété « ${nom} » a été demandée via le sélecteur, mais cette dernière n'a pas été référencée dans le modèle ${metasClasse.nom}.`);
234
+
235
+ // Exposé publiquement
236
+ // @ts-ignore: 'string' can't be used to index type '{ "Post": string[]; "Question": string[], ...
237
+ if (metasProp?.api === undefined) {
238
+ //debug && console.log(`Elimination de la donnée ${cheminA.join('.')} (propriété de classe ${metasClasse.nom}.${nom}) (non-exposé à l'API)`);
239
+ return undefined;
240
+ }
241
+
242
+ let valeur: any;
243
+ // Si promise, on lui rattache un catch le plus tôt possible, avant qu'on ne tente d'acceder à sa valeur
244
+ // (un simple acces lançant directement la promise)
245
+ valeur = modele[nom];
246
+
247
+ // Permissions
248
+ if (!this.controleAcces(modele, metasProp.api.auth, `Elimination de la donnée ${cheminA.join('.')} (${metasClasse.nom}.${nom})`)) {
249
+ return undefined;
250
+ }
251
+
252
+ if (valeur === undefined || valeur === null)
253
+ return undefined;
254
+
255
+ // Filtre spécifique à la propriété
256
+ if (metasProp.api.sortie)
257
+ valeur = metasProp.api.sortie(valeur);
258
+
259
+ if (valeur === undefined || valeur === null)
260
+ return undefined;
261
+
262
+ // Dernier traitement des valeurs
263
+ //valeur = filtresProps(valeur, metasProp);
264
+ return valeur;
265
+ }
266
+
267
+ private controleAcces(
268
+ donneesCompletes: Modele,
269
+ roleRequis: TControleAcces | undefined,
270
+ logElimination: string
271
+ ): boolean {
272
+
273
+ if (roleRequis === undefined)
274
+ return true;
275
+ else if (!this.user) {
276
+ //debug && console.log(logElimination + ` (Non-connecté)`);
277
+ return false;
278
+ } else {
279
+
280
+ // L'admin peut tout voir depuis l'interface d'amdin
281
+ if (this.user && this.user.roles.includes('ADMIN'))
282
+ return true;
283
+
284
+ // Fonction personnalisée
285
+ if (typeof roleRequis === 'function') {
286
+ const retour = roleRequis(donneesCompletes, this.user);
287
+ //debug && console.log(logElimination + ` (Via fonction custom)`);
288
+ return retour;
289
+ } else {
290
+
291
+ // Force le format tableau
292
+ if (typeof roleRequis === 'string')
293
+ roleRequis = [roleRequis];
294
+
295
+ // Vérification si l'un des role requis correspond à l'utilisateur actuel
296
+ for (const role of roleRequis) {
297
+
298
+ // Role simple
299
+ if (this.user.roles.includes(role))
300
+ return true;
301
+ // Correspondance id utilisateur avec valeur d'une colonne (ex: auteur)
302
+ else {
303
+ const nomCol = role === 'id' || role.endsWith('_id')
304
+ ? role
305
+ : role + '_id';
306
+ const valeurCol = donneesCompletes[nomCol];
307
+
308
+ // TODO: Vérif si nom colonne existante dans modèle
309
+
310
+ if (valeurCol === this.user.id)
311
+ return true;
312
+ }
313
+ }
314
+
315
+
316
+ //debug && console.log(logElimination + ` (User actuel: ${this.user.id} ${this.user.roles.join(', ')} ; Requis: ${roleRequis.join(', ')})`);
317
+ }
318
+
319
+ return false;
320
+ }
321
+ }*/
322
+
323
+ }