5htp-core 0.5.0 → 0.5.1-2

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 (39) hide show
  1. package/package.json +1 -1
  2. package/src/client/assets/css/components/button.less +0 -1
  3. package/src/client/assets/css/core.less +5 -3
  4. package/src/client/assets/css/text/icons.less +4 -1
  5. package/src/client/assets/css/text/text.less +1 -1
  6. package/src/client/assets/css/text/titres.less +4 -3
  7. package/src/client/assets/css/theme.less +2 -1
  8. package/src/client/assets/css/utils/layouts.less +1 -0
  9. package/src/client/assets/css/utils/sizing.less +15 -15
  10. package/src/client/components/Dialog/Manager.tsx +1 -3
  11. package/src/client/components/Dialog/index.less +6 -9
  12. package/src/client/components/Form.ts +62 -31
  13. package/src/client/components/Select/index.tsx +1 -1
  14. package/src/client/components/Table/index.tsx +40 -6
  15. package/src/client/components/button.tsx +40 -31
  16. package/src/client/components/containers/Popover/getPosition.ts +48 -28
  17. package/src/client/components/containers/Popover/index.tsx +9 -3
  18. package/src/client/components/inputv3/Rte/Editor.tsx +64 -5
  19. package/src/client/components/inputv3/Rte/ToolbarPlugin/BlockFormat.tsx +1 -1
  20. package/src/client/components/inputv3/Rte/index.tsx +11 -76
  21. package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.css +1 -1
  22. package/src/client/components/inputv3/Rte/style.less +1 -25
  23. package/src/client/components/inputv3/base.tsx +1 -1
  24. package/src/client/components/inputv3/index.tsx +7 -1
  25. package/src/client/services/router/components/Link.tsx +9 -5
  26. package/src/client/services/router/components/router.tsx +4 -3
  27. package/src/client/services/router/index.tsx +2 -1
  28. package/src/client/utils/dom.ts +1 -1
  29. package/src/common/errors/index.tsx +19 -7
  30. package/src/common/router/index.ts +2 -0
  31. package/src/common/validation/validators.ts +1 -0
  32. package/src/server/services/auth/index.ts +0 -9
  33. package/src/server/services/database/index.ts +2 -2
  34. package/src/server/services/router/http/index.ts +8 -11
  35. package/src/server/services/router/index.ts +62 -56
  36. package/src/server/services/router/request/index.ts +11 -0
  37. package/src/server/services/router/response/index.ts +21 -0
  38. package/src/server/services/router/response/page/document.tsx +1 -4
  39. package/src/server/services/router/response/page/index.tsx +4 -0
@@ -143,7 +143,7 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
143
143
  query.then = (cb: (data: any) => void) => query().then(cb);
144
144
 
145
145
  query.first = <TRowData extends TObjetDonnees = {}>(opts: TSelectQueryOptions = {}) => this.first<TRowData>(string, opts);
146
- query.firstOrFail = (message?: string, opts: TQueryOptions = {}) => this.firstOrFail<TRowData>(string, message, opts);
146
+ query.firstOrFail = (message: string, opts: TQueryOptions = {}) => this.firstOrFail<TRowData>(string, message, opts);
147
147
 
148
148
  query.value = <TValue extends any = number>(opts: TQueryOptions = {}) => this.selectVal<TValue>(string, opts);
149
149
 
@@ -283,7 +283,7 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
283
283
  return resultatRequetes[0] || null;
284
284
  });
285
285
 
286
- public firstOrFail = <TRowData extends TObjetDonnees = {}>(query: string, message?: string, opts: TSelectQueryOptions = {}): Promise<TRowData> =>
286
+ public firstOrFail = <TRowData extends TObjetDonnees = {}>(query: string, message: string, opts: TSelectQueryOptions = {}): Promise<TRowData> =>
287
287
  this.select(query, opts).then((resultatRequetes: any) => {
288
288
 
289
289
  if (resultatRequetes.length === 0)
@@ -49,7 +49,8 @@ export type Config = {
49
49
  images?: string[],
50
50
  scripts: string[],
51
51
  },
52
- cors?: CorsOptions
52
+ cors?: CorsOptions,
53
+ helmet?: Parameters<typeof helmet>[0]
53
54
  }
54
55
 
55
56
  export type Hooks = {
@@ -100,7 +101,7 @@ export default class HttpServer {
100
101
  // Config
101
102
  routes.set('trust proxy', 1); // Indique qu'on est sous le proxy apache
102
103
  // Diverses protections (dont le disable x-powered-by)
103
- routes.use(helmet());
104
+ routes.use( helmet(this.config.helmet) );
104
105
 
105
106
  /*----------------------------------
106
107
  - FICHIERS STATIQUES
@@ -162,16 +163,12 @@ export default class HttpServer {
162
163
 
163
164
  // Décodage des données post
164
165
  express.json({
165
-
166
- // NOTE: Encore nécessaire ? Les webhooks stripe & bitgo n'étant plus utilisés
167
- // Because Stripe needs the raw body, we compute it but only when hitting the Stripe callback URL.
168
- /*verify: function (req: Request, res: Response, buf: Buffer) {
169
- if (req.originalUrl.startsWith('/api/paiement/impact/stripe'))
170
- //req.rawBody = buf.toString();
171
- },*/
172
-
173
166
  // TODO: prendre en considération les upload de fichiers
174
- limit: '2mb'
167
+ limit: '2mb',
168
+ verify: (req, res, buf, encoding) => {
169
+ // Store the raw request body so we can access it later
170
+ req.rawBody = buf;
171
+ }
175
172
  }),
176
173
 
177
174
  // Permet de receptionner les données multipart (req.body + req.files)
@@ -12,6 +12,7 @@
12
12
  // Node
13
13
  // Npm
14
14
  import type express from 'express';
15
+ import type { Request, Response, NextFunction } from 'express';
15
16
  import { v4 as uuid } from 'uuid';
16
17
  import type { GlobImportedWithMetas } from 'babel-plugin-glob-import';
17
18
 
@@ -265,7 +266,31 @@ export default class ServerRouter<
265
266
  public post = (...args: TApiRegisterArgs<this>) => this.registerApi('POST', ...args);
266
267
  public put = (...args: TApiRegisterArgs<this>) => this.registerApi('PUT', ...args);
267
268
  public patch = (...args: TApiRegisterArgs<this>) => this.registerApi('PATCH', ...args);
268
- public delete = (...args: TApiRegisterArgs<this>) => this.registerApi('DELETE', ...args)
269
+ public delete = (...args: TApiRegisterArgs<this>) => this.registerApi('DELETE', ...args);
270
+
271
+ public express(
272
+ middleware: (
273
+ req: Request,
274
+ res: Response,
275
+ next: NextFunction,
276
+ requestContext: TRouterContext
277
+ ) => void
278
+ ) {
279
+ return (context: TRouterContext) => new Promise((resolve) => {
280
+
281
+ context.request.res.on('finish', function() {
282
+ //console.log('the response has been sent', request.res.statusCode);
283
+ resolve(true);
284
+ });
285
+
286
+ middleware(
287
+ context.request.req,
288
+ context.request.res,
289
+ () => { resolve(true); },
290
+ context
291
+ )
292
+ })
293
+ }
269
294
 
270
295
  protected registerApi(method: TRouteHttpMethod, ...args: TApiRegisterArgs<this>): this {
271
296
 
@@ -414,22 +439,6 @@ declare type Routes = {
414
439
  // Hook
415
440
  await this.runHook('request', request);
416
441
 
417
- // TODO: move to tracking
418
- /*const now = new Date;
419
-
420
- // Identify Guest & create log entry
421
- const username = request.user?.name;
422
- let clientId: string = request.cookies.clientId;
423
- const newClient = !(typeof clientId === 'string' && clientId.length <= 39)
424
- if (newClient) {
425
- clientId = uuid();
426
- res.cookie('clientId', clientId, { expires: new Date(253402300000000) }); // Never expires
427
- }
428
-
429
- const keepLogs = request.ip !== '86.76.176.80';
430
- if (!keepLogs)
431
- requestId = 'admin';*/
432
-
433
442
  // Create request context so we can access request context across all the request-triggered libs
434
443
  context.run({ channelType: 'request', channelId: requestId }, async () => {
435
444
 
@@ -448,40 +457,17 @@ declare type Routes = {
448
457
  response = await this.handleError(e, request);
449
458
  }
450
459
 
451
- // Status
452
- res.status(response.statusCode);
453
- // Headers
454
- res.header(response.headers);
455
- // Data
456
- res.send(response.data);
457
-
458
- // TODO: move to tracking
459
- /*if (newClient)
460
- console.client({
461
- id: clientId,
462
- ip: request.ip,
463
- user: username,
464
- device: request.deviceString(),
465
- meet: now,
466
- activity: now,
467
- });
468
-
469
- console.request({
470
-
471
- id: requestId,
472
- date: now,
473
-
474
- method: request.method,
475
- url: request.path,
476
- data: request.data,
477
-
478
- ip: request.ip,
479
- user: request.user?.name,
480
- clientId,
460
+ if (!res.headersSent) {
461
+ // Status
462
+ res.status(response.statusCode);
463
+ // Headers
464
+ res.header(response.headers);
465
+ // Data
466
+ res.send(response.data);
467
+ } else if (response.data !== 'true') {
468
+ throw new Error("Can't return data from the controller since response has already been sent via express.");
469
+ }
481
470
 
482
- statusCode: response.statusCode,
483
- time: Date.now() - now.valueOf()
484
- });*/
485
471
  });
486
472
  }
487
473
 
@@ -502,12 +488,15 @@ declare type Routes = {
502
488
 
503
489
  public async resolve(request: ServerRequest<this>): Promise<ServerResponse<this>> {
504
490
 
505
- console.info(LogPrefix, request.ip, request.method, request.domain, request.path);
491
+ const logId = LogPrefix + ' ' + (request.isVirtual ? ' ---- ' : '') + request.ip + ' ' + request.method + ' ' + request.domain + ' ' + request.path;
492
+ console.info(logId);
493
+ const timeStart = Date.now();
506
494
 
507
495
  if (this.status === 'starting') {
508
496
  console.log(LogPrefix, `Waiting for servert to be resdy before resolving request`);
509
497
  await this.started;
510
498
  }
499
+
511
500
  try {
512
501
 
513
502
  const response = new ServerResponse<this>(request);
@@ -530,12 +519,14 @@ declare type Routes = {
530
519
 
531
520
  // Run on resolution hooks. Ex: authentication check
532
521
  await this.runHook('resolved', route);
522
+ const timeEndResolving = Date.now();
533
523
 
534
524
  // Create response
535
525
  await response.runController(route);
536
- if (response.wasProvided)
537
- // On continue l'itération des routes, sauf si des données ont été fournie dans la réponse (.json(), .html(), ...)
526
+ if (response.wasProvided) {
527
+ this.printTakenTime(logId, timeStart, timeEndResolving);
538
528
  return response;
529
+ }
539
530
  }
540
531
 
541
532
  throw new NotFound();
@@ -551,10 +542,20 @@ declare type Routes = {
551
542
  error.details.origin = errOrigin;
552
543
  }
553
544
 
545
+ this.printTakenTime(logId, timeStart);
554
546
  throw error;
555
547
  }
556
548
  }
557
549
 
550
+ private printTakenTime = (logId: string, timeStart: number, timeEndResolving?: number) => {
551
+
552
+ if (this.app.env.name === 'server') return;
553
+
554
+ console.log(logId + ' ' + Math.round(Date.now() - timeStart) + 'ms' +
555
+ (timeEndResolving === undefined ? '' : ' | Routing: ' + Math.round(timeEndResolving - timeStart))
556
+ );
557
+ }
558
+
558
559
  private async resolveApiBatch( fetchers: TFetcherList, request: ServerRequest<this> ) {
559
560
 
560
561
  // TODO: use api.fetchSync instead
@@ -601,9 +602,14 @@ declare type Routes = {
601
602
  if (this.app.env.profile === 'prod')
602
603
  e.message = "We encountered an internal error, and our team has just been notified. Sorry for the inconvenience.";
603
604
 
604
- // Pour déboguer les erreurs HTTP
605
- } else if (code !== 404 && this.app.env.profile === "dev")
606
- console.warn(e);
605
+ } else {
606
+
607
+ // For debugging HTTP errors
608
+ if (this.app.env.profile === "dev")
609
+ console.warn(e);
610
+
611
+ await this.app.runHook('error.' + code, e, request);
612
+ }
607
613
 
608
614
  // Return error based on the request format
609
615
  if (request.accepts("html")) {
@@ -135,6 +135,17 @@ export default class ServerRequest<
135
135
  return locale ? locale.toUpperCase() : 'EN'
136
136
  }
137
137
 
138
+ public cookie( key: string, consume: boolean = false ) {
139
+
140
+ const value = this.req.cookies[ key ];
141
+
142
+ if (consume)
143
+ this.res.clearCookie(key);
144
+
145
+ return value;
146
+
147
+ }
148
+
138
149
  /*----------------------------------
139
150
  - TESTS
140
151
  ----------------------------------*/
@@ -81,6 +81,8 @@ export default class ServerResponse<
81
81
  public statusCode: number = 200;
82
82
  public headers: {[cle: string]: string} = {}
83
83
  public cookie: express.Response["cookie"];
84
+ public clearCookie: express.Response["clearCookie"];
85
+ public canonicalUrl: URL;
84
86
 
85
87
  // If data was provided by at lead one controller
86
88
  public wasProvided = false;
@@ -90,15 +92,22 @@ export default class ServerResponse<
90
92
  super(request);
91
93
 
92
94
  this.cookie = this.request.res.cookie.bind(this.request.res);
95
+ this.clearCookie = this.request.res.clearCookie.bind(this.request.res);
93
96
 
94
97
  this.router = request.router;
95
98
  this.app = this.router.app;
99
+
100
+ this.canonicalUrl = new URL(request.url);
101
+ this.canonicalUrl.search = '';
96
102
  }
97
103
 
98
104
  public async runController( route: TAnyRoute, additionnalData: {} = {} ) {
99
105
 
100
106
  this.route = route;
101
107
 
108
+ // Update canonical url
109
+ this.updateCanonicalUrl(route);
110
+
102
111
  // Create response context for controllers
103
112
  const context = await this.createContext(route);
104
113
 
@@ -138,6 +147,18 @@ export default class ServerResponse<
138
147
  this.router.cache[ chunkId ] = this.data;
139
148
  }
140
149
 
150
+ private updateCanonicalUrl( route: TAnyRoute ) {
151
+
152
+ if (!route.options.canonicalParams)
153
+ return;
154
+
155
+ for (const key of route.options.canonicalParams) {
156
+ const paramValue = this.request.data[ key ];
157
+ if (paramValue !== undefined)
158
+ this.canonicalUrl.searchParams.set(key, paramValue);
159
+ }
160
+ }
161
+
141
162
  /*----------------------------------
142
163
  - INTERNAL
143
164
  ----------------------------------*/
@@ -57,9 +57,6 @@ export default class DocumentRenderer<TRouter extends Router> {
57
57
 
58
58
  public async page( html: string, page: Page, response: ServerResponse<TRouter> ) {
59
59
 
60
- // TODO: can be customized via page / route config
61
- const canonicalUrl = response.request.req.url;
62
-
63
60
  let attrsBody = {
64
61
  className: [...page.bodyClass].join(' '),
65
62
  };
@@ -94,7 +91,7 @@ export default class DocumentRenderer<TRouter extends Router> {
94
91
  {/* Page */}
95
92
  <title>{page.title}</title>
96
93
  <meta content={page.description} name="description" />
97
- <link rel="canonical" href={canonicalUrl} />
94
+ <link rel="canonical" href={response.canonicalUrl} />
98
95
 
99
96
  {/* SEO, social medias, OG tags, ... */}
100
97
  {page.head.map(({ $, ...attrs }) => (
@@ -120,8 +120,12 @@ export default class ServerPage<TRouter extends Router = Router> extends PageRes
120
120
 
121
121
  private buildMetas() {
122
122
 
123
+ const shouldIndex = this.context.response.statusCode < 300;
124
+
123
125
  const metas = {
124
126
 
127
+ robots: shouldIndex ? 'index' : 'noindex',
128
+
125
129
  'og:type': 'website',
126
130
  'og:locale': this.app.identity.locale,
127
131
  'og:site_name': this.app.identity.web.title,