5htp-core 0.4.9-93 → 0.4.9-95

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "5htp-core",
3
3
  "description": "Convenient TypeScript framework designed for Performance and Productivity.",
4
- "version": "0.4.9-93",
4
+ "version": "0.4.9-95",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -205,7 +205,7 @@ export default ({ value, setValue, props }: {
205
205
 
206
206
  {isMaxLength && <MaxLengthPlugin maxLength={30} />}
207
207
  <DragDropPaste />
208
- <AutoFocusPlugin />
208
+ {props.preview && <AutoFocusPlugin />}
209
209
  <ClearEditorPlugin />
210
210
  <ComponentPickerPlugin />
211
211
  <EmojiPickerPlugin />
@@ -66,10 +66,10 @@ export default (props: Props & InputBaseProps<string>) => {
66
66
  if (RichEditorUtils.active && RichEditorUtils.active?.title !== title)
67
67
  RichEditorUtils.active.close();
68
68
 
69
- // Set active editor
69
+ // Set active Editor
70
70
  RichEditorUtils.active = {
71
71
  title,
72
- close: () => setIsPreview(true)
72
+ close: () => preview ? setIsPreview(true) : null
73
73
  }
74
74
 
75
75
  // Load editor component if not alreayd done
@@ -1,6 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { ComponentChild } from 'preact';
3
3
 
4
+ import type { TJsonLog } from '@server/app/container/console';
5
+ import type ServerRequest from '@server/services/router/request';
6
+ import type { TBasicUser } from '@server/services/auth';
7
+
4
8
  /*----------------------------------
5
9
  - TYPES
6
10
  ----------------------------------*/
@@ -25,19 +29,31 @@ type TDetailsErreur = {
25
29
  ----------------------------------*/
26
30
 
27
31
  export type ServerBug = {
32
+
28
33
  // Context
29
34
  hash: string,
30
35
  date: Date, // Timestamp
31
36
  channelType?: string,
32
37
  channelId?: string,
33
38
 
34
- user: string | null | undefined,
35
- ip: string | null | undefined,
39
+ // User
40
+ user?: TBasicUser | null,
41
+ ip?: string | null,
42
+
43
+ // Request
44
+ request?: {
45
+ method: ServerRequest["method"],
46
+ url: ServerRequest["url"],
47
+ data: ServerRequest["data"],
48
+ validatedData: ServerRequest["validatedData"],
49
+ headers: ServerRequest["headers"],
50
+ cookies: ServerRequest["cookies"],
51
+ },
36
52
 
37
53
  // Error
38
54
  error: Error,
39
55
  stacktrace: string,
40
- logs: string,
56
+ logs: TJsonLog[],
41
57
  }
42
58
 
43
59
  /*----------------------------------
@@ -11,6 +11,9 @@
11
11
  import fs from 'fs-extra';
12
12
  import yaml from 'yaml';
13
13
 
14
+ // Types
15
+ import type { Config as TConsoleConfig } from './console';
16
+
14
17
  /*----------------------------------
15
18
  - TYPES
16
19
  ----------------------------------*/
@@ -31,6 +34,7 @@ export type TEnvConfig = {
31
34
  name: 'local' | 'server',
32
35
  profile: 'dev' | 'prod',
33
36
  version: string,
37
+ console: TConsoleConfig
34
38
  }
35
39
 
36
40
  type AppIdentityConfig = {
@@ -11,19 +11,16 @@ import forTerminal from 'youch-terminal';
11
11
  // Npm
12
12
  import { v4 as uuid } from 'uuid';
13
13
  import { Logger, IMeta, ILogObj, ISettings } from 'tslog';
14
- import { format as formatSql } from 'sql-formatter';
15
14
  import highlight from 'cli-highlight';
15
+ import Ansi2Html from 'ansi-to-html';
16
16
 
17
17
  // Core libs
18
18
  import type ApplicationContainer from '..';
19
19
  import context from '@server/context';
20
- import type { ServerBug } from '@common/errors';
20
+ import type { ServerBug, Anomaly, CoreError } from '@common/errors';
21
21
  import type ServerRequest from '@server/services/router/request';
22
22
  import { SqlError } from '@server/services/database/debug';
23
23
 
24
- // Specific
25
- import logToHTML from './html';
26
-
27
24
  /*----------------------------------
28
25
  - SERVICE CONFIG
29
26
  ----------------------------------*/
@@ -32,13 +29,9 @@ type TLogProfile = 'silly' | 'info' | 'warn' | 'error'
32
29
 
33
30
  export type Config = {
34
31
  debug?: boolean,
32
+ enable: boolean,
35
33
  bufferLimit: number,
36
- dev: {
37
- level: TLogProfile,
38
- },
39
- prod: {
40
- level: TLogProfile
41
- }
34
+ level: TLogProfile,
42
35
  }
43
36
 
44
37
  export type Hooks = {
@@ -113,7 +106,34 @@ const logLevels = {
113
106
  'info': 3,
114
107
  'warn': 4,
115
108
  'error': 5
116
- } as const
109
+ } as const;
110
+
111
+
112
+ var ansi2Html = new Ansi2Html({
113
+ newline: true,
114
+ // Material theme for Tilix
115
+ // https://github.com/gnunn1/tilix/blob/master/data/schemes/material.json
116
+ fg: '#fff',
117
+ bg: '#000',
118
+ colors: [
119
+ "#252525",
120
+ "#FF5252",
121
+ "#C3D82C",
122
+ "#FFC135",
123
+ "#42A5F5",
124
+ "#D81B60",
125
+ "#00ACC1",
126
+ "#F5F5F5",
127
+ "#708284",
128
+ "#FF5252",
129
+ "#C3D82C",
130
+ "#FFC135",
131
+ "#42A5F5",
132
+ "#D81B60",
133
+ "#00ACC1",
134
+ "#F5F5F5"
135
+ ]
136
+ });
117
137
 
118
138
  /*----------------------------------
119
139
  - LOGGER
@@ -150,9 +170,6 @@ export default class Console {
150
170
 
151
171
  const Env = container.Environment;
152
172
 
153
- const envConfig = this.config[ Env.profile === 'prod' ? 'prod' : 'dev' ];
154
- const minLogLevel = logLevels[ envConfig.level ];
155
-
156
173
  this.logger = new Logger({
157
174
  // Use to improve performance in production
158
175
  hideLogPositionForProduction: Env.profile === 'prod',
@@ -168,7 +185,7 @@ export default class Console {
168
185
  meta.path.filePathWithLine = this.shortenFilePath( meta.path.filePathWithLine );
169
186
  }
170
187
 
171
- return this.logger._prettyFormatLogObjMeta( meta );
188
+ return this.logger["_prettyFormatLogObjMeta"]( meta );
172
189
  },
173
190
  transportFormatted: (
174
191
  logMetaMarkup: string,
@@ -183,12 +200,25 @@ export default class Console {
183
200
  }
184
201
  });
185
202
 
186
- if (console["_wrapped"] !== undefined)
203
+ if (!this.config.enable || console["_wrapped"] !== undefined)
187
204
  return;
188
205
 
189
- for (const logLevel in logLevels) {
190
- const levelNumber = logLevels[ logLevel ];
191
- console[ logLevel ] = (...args: any[]) => {
206
+ this.enableLogging(origLog);
207
+ }
208
+
209
+ // Avoid to use lifecycle functions
210
+ protected async start() {}
211
+ public async ready() {}
212
+ public async shutdown() {}
213
+
214
+ private enableLogging( origLog: typeof console.log ) {
215
+
216
+ const minLogLevel = logLevels[this.config.level];
217
+
218
+ let logLevel: TLogLevel;
219
+ for (logLevel in logLevels) {
220
+ const levelNumber = logLevels[logLevel];
221
+ console[logLevel] = (...args: any[]) => {
192
222
 
193
223
  // Dev mode = no care about performance = rich logging
194
224
  if (levelNumber >= minLogLevel)
@@ -206,17 +236,12 @@ export default class Console {
206
236
  });
207
237
  }
208
238
  }
209
-
239
+
210
240
  console["_wrapped"] = true;
211
241
 
212
242
  setInterval(() => this.clean(), 10000);
213
243
  }
214
244
 
215
- // Avoid to use lifecycle functions
216
- protected async start() {}
217
- public async ready() {}
218
- public async shutdown() {}
219
-
220
245
  /*----------------------------------
221
246
  - LOGS FORMATTING
222
247
  ----------------------------------*/
@@ -263,7 +288,7 @@ export default class Console {
263
288
  this.logs = this.logs.slice(bufferOverflow);
264
289
  }
265
290
 
266
- public async createBugReport( error: Error, request?: ServerRequest ) {
291
+ public async createBugReport( error: Error | CoreError | Anomaly, request?: ServerRequest ) {
267
292
 
268
293
  // Print error
269
294
  this.logger.error(LogPrefix, `Sending bug report for the following error:`, error);
@@ -304,7 +329,7 @@ export default class Console {
304
329
  console.error(`Error caused by this query:`, printedQuery);
305
330
  }
306
331
 
307
- if (error.dataForDebugging !== undefined)
332
+ if (('dataForDebugging' in error) && error.dataForDebugging !== undefined)
308
333
  console.error(LogPrefix, `More data about the error:`, error.dataForDebugging);
309
334
 
310
335
  // Prevent spamming the mailbox if infinite loop
@@ -322,24 +347,38 @@ export default class Console {
322
347
 
323
348
  // On envoi l'email avant l'insertion dans bla bdd
324
349
  // Car cette denrière a plus de chances de provoquer une erreur
325
- const logsHtml = this.printHtml(
326
- this.logs.filter( e => e.channel.channelId === channelId).slice(-100),
327
- true
328
- );
350
+ const logs = this.logs.filter(e => e.channel.channelId === channelId).slice(-100);
329
351
 
330
352
  const bugReport: ServerBug = {
353
+
331
354
  // Context
332
355
  hash: hash,
333
356
  date: now,
334
357
  channelType,
335
358
  channelId,
336
- // User
337
- user: request?.user?.email,
338
- ip: request?.ip,
359
+
360
+ ...(request ? {
361
+
362
+ // User
363
+ user: request.user,
364
+ ip: request.ip,
365
+
366
+ // Request
367
+ request: {
368
+ method: request.method,
369
+ url: request.url,
370
+ data: request.data,
371
+ validatedData: request.validatedData,
372
+ headers: request.headers,
373
+ cookies: request.cookies,
374
+ }
375
+
376
+ } : {}),
377
+
339
378
  // Error
340
379
  error,
341
380
  stacktrace: error.stack || error.message,
342
- logs: logsHtml
381
+ logs
343
382
  }
344
383
 
345
384
  await application.reportBug( bugReport );
@@ -353,10 +392,97 @@ export default class Console {
353
392
  }
354
393
 
355
394
  /*----------------------------------
356
- - READ
395
+ - PRINT
357
396
  ----------------------------------*/
358
397
 
359
- public async getLogs( channelType: ChannelInfos["channelType"], channelId?: string ) {
398
+ public bugToHtml( report: ServerBug ) {
399
+ return `
400
+ <b>Channel</b>: ${report.channelType} (${report.channelId})<br />
401
+ <b>User</b>: ${report.user ? (report.user.name + ' (' + report.user.email + ')') : 'Unknown'}<br />
402
+ <b>IP</b>: ${report.ip}<br />
403
+ <b>Error</b>: ${report.error.message}<br />
404
+ ${this.printHtml(report.stacktrace)}<br />
405
+ ${report.request ? `
406
+ <hr />
407
+ <b>Request</b>: ${report.request.method} ${report.request.url}<br />
408
+ <b>Headers</b>: ${this.jsonToHTML(report.request.headers)}<br />
409
+ <b>Cookies</b>: ${this.jsonToHTML(report.request.cookies)}<br />
410
+ <b>Raw Data</b>: ${this.jsonToHTML(report.request.data)}<br />
411
+ <b>Validated Data</b>: ${this.jsonToHTML(report.request.validatedData)}
412
+ ` : ''}
413
+ <hr/>
414
+ Logs: ${this.config.enable ? `<br/>` + this.logsToHTML(report.logs) : 'Logs collection is disabled'}<br />
415
+ `
416
+ }
417
+
418
+ public logsToHTML( logs: TJsonLog[] ): string {
419
+
420
+ let ansi = logs.map( logEntry => this.logToAnsi( logEntry )).join('<br />');
421
+
422
+ // Convert ANSI to HTML
423
+ const html = ansi2Html.toHtml(ansi)
424
+
425
+ return this.printHtml( html );
426
+ }
427
+
428
+ private logToAnsi( log: TJsonLog ) {
429
+
430
+ // Print metas as ANSI
431
+ const logMetaMarkup = this.logger["_prettyFormatLogObjMeta"]({
432
+ date: log.time,
433
+ logLevelId: this.getLogLevelId(log.level),
434
+ logLevelName: log.level,
435
+ // We consider that having the path is useless in this case
436
+ path: undefined,
437
+ });
438
+
439
+ // Print args as ANSI
440
+ const logArgsAndErrorsMarkup = this.logger.runtime.prettyFormatLogObj(log.args, this.logger.settings);
441
+ const logErrors = logArgsAndErrorsMarkup.errors;
442
+ const logArgs = logArgsAndErrorsMarkup.args;
443
+ const logErrorsStr = (logErrors.length > 0 && logArgs.length > 0 ? "\n" : "") + logErrors.join("\n");
444
+ this.logger.settings.prettyInspectOptions.colors = this.logger.settings.stylePrettyLogs;
445
+ let ansi = logMetaMarkup + formatWithOptions(this.logger.settings.prettyInspectOptions, ...logArgs) + logErrorsStr;
446
+
447
+ return ansi;
448
+ }
449
+
450
+ public jsonToHTML( json: unknown ): string {
451
+
452
+ const coloredJson = highlight(
453
+ JSON.stringify(json, null, 4),
454
+ { language: 'json', ignoreIllegals: true }
455
+ );
456
+
457
+ const html = ansi2Html.toHtml(coloredJson)
458
+
459
+ return this.printHtml( html );
460
+ }
461
+
462
+ public printHtml( html: string ): string {
463
+
464
+ if (!html)
465
+ return 'No data';
466
+
467
+ // Preserve spaces
468
+ html = html
469
+ .replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;')
470
+ .replace(/ /g, '&nbsp;')
471
+ .replace(/\n/g, '<br />');
472
+
473
+ // Create console wrapper
474
+ const consoleCss = `background: #000; padding: 20px; font-family: 'Fira Mono', 'monospace', 'Monaco'; font-size: 12px; line-height: 20px;color: #aaa;`
475
+ html = '<div style="' + consoleCss + '">' + html + '</div>';
476
+
477
+ return html;
478
+ }
479
+
480
+ public printSql = (requete: string) => highlight(
481
+ requete,//formatSql(requete, { indent: ' '.repeat(4) }),
482
+ { language: 'sql', ignoreIllegals: true }
483
+ )
484
+
485
+ /*public async getLogs( channelType: ChannelInfos["channelType"], channelId?: string ) {
360
486
 
361
487
  const filters: Partial<TDbQueryLog> = { channelType };
362
488
  if (channelId !== undefined)
@@ -386,23 +512,6 @@ export default class Console {
386
512
  }
387
513
 
388
514
  return this.printHtml( entries );
389
- }
390
-
391
- public printHtml( logs: TJsonLog[], full: boolean = false ): string {
392
-
393
- let html = logs.map( logEntry => logToHTML( logEntry, this )).join('<br />');
394
-
395
- if (full) {
396
- const consoleCss = `background: #000; padding: 20px; font-family: 'Fira Mono', 'monospace', 'Monaco'; font-size: 12px; line-height: 20px;color: #aaa;`
397
- html = '<div style="' + consoleCss + '">' + html + '</div>';
398
- }
399
-
400
- return html;
401
- }
402
-
403
- public printSql = (requete: string) => highlight(
404
- requete,//formatSql(requete, { indent: ' '.repeat(4) }),
405
- { language: 'sql', ignoreIllegals: true }
406
- )
515
+ }*/
407
516
 
408
517
  }
@@ -13,6 +13,7 @@ import type { StartedServicesIndex } from '../service';
13
13
  import Services, { ServicesContainer } from '../service/container';
14
14
  import ConfigParser, { TEnvConfig } from './config';
15
15
  import Console from './console';
16
+ import type ServerRequest from '@server/services/router/request';
16
17
 
17
18
  /*----------------------------------
18
19
  - CLASS
@@ -38,16 +39,7 @@ export class ApplicationContainer<
38
39
  const configParser = new ConfigParser( this.path.root );
39
40
  this.Environment = configParser.env();
40
41
  this.Identity = configParser.identity();
41
- this.Console = new Console(this, {
42
- debug: false,
43
- bufferLimit: 10000,
44
- dev: {
45
- level: 'log'
46
- },
47
- prod: {
48
- level: 'log'
49
- }
50
- });
42
+ this.Console = new Console(this, this.Environment.console);
51
43
  }
52
44
 
53
45
  // Context
@@ -85,11 +77,11 @@ export class ApplicationContainer<
85
77
  }
86
78
  }
87
79
 
88
- public async handleBug( rejection: Error, message: string ) {
80
+ public async handleBug( rejection: Error, message: string, request?: ServerRequest ) {
89
81
  if (this.Console) {
90
82
  try {
91
83
 
92
- this.Console.createBugReport(rejection);
84
+ this.Console.createBugReport(rejection, request);
93
85
 
94
86
  } catch (consoleError) {
95
87
  console.error(
@@ -84,7 +84,7 @@ export class Application<
84
84
  super(self, {}, {}, self);
85
85
 
86
86
  // Handle unhandled crash
87
- this.on('error', e => this.container.handleBug(e, "An error occured in the application"));
87
+ this.on('error', (e, request) => this.container.handleBug(e, "An error occured in the application", request));
88
88
 
89
89
  process.on('unhandledRejection', (error: any, promise: any) => {
90
90
  console.log("unhandledRejection");
@@ -65,6 +65,7 @@ export type TServices = {
65
65
  export type TBasicUser = {
66
66
  type: string,
67
67
  name: string,
68
+ email: string,
68
69
  roles: string[]
69
70
  }
70
71
 
@@ -49,6 +49,7 @@ export default class ServerRequest<
49
49
  public domain: string;
50
50
  public headers: HttpHeaders = {};
51
51
  public cookies: TObjetDonnees = {};
52
+ public validatedData?: TObjetDonnees; // Results from the last schema.validate
52
53
 
53
54
  // reponse
54
55
  public response?: ServerResponse<TRouter>;
@@ -53,6 +53,9 @@ export default class RequestValidator extends ServerSchemaValidator implements R
53
53
  validators: this
54
54
  }, []);
55
55
 
56
+ // For logging
57
+ this.request.validatedData = values;
58
+
56
59
  return values;
57
60
  }
58
61
 
@@ -1,66 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
- // Npm
5
- import Ansi2Html from 'ansi-to-html';
6
- import { formatWithOptions } from 'util';
7
- import type { default as Console, TJsonLog } from '.';;
8
-
9
- var ansi2Html = new Ansi2Html({
10
- newline: true,
11
- // Material theme for Tilix
12
- // https://github.com/gnunn1/tilix/blob/master/data/schemes/material.json
13
- fg: '#fff',
14
- bg: '#000',
15
- colors: [
16
- "#252525",
17
- "#FF5252",
18
- "#C3D82C",
19
- "#FFC135",
20
- "#42A5F5",
21
- "#D81B60",
22
- "#00ACC1",
23
- "#F5F5F5",
24
- "#708284",
25
- "#FF5252",
26
- "#C3D82C",
27
- "#FFC135",
28
- "#42A5F5",
29
- "#D81B60",
30
- "#00ACC1",
31
- "#F5F5F5"
32
- ]
33
- });
34
-
35
- /*----------------------------------
36
- - METHOD
37
- ----------------------------------*/
38
- export default (log: TJsonLog, c: Console) => {
39
-
40
- // Print metas as ANSI
41
- const logMetaMarkup = c.logger._prettyFormatLogObjMeta({
42
- date: log.time,
43
- logLevelId: c.getLogLevelId( log.level ),
44
- logLevelName: log.level,
45
- // We consider that having the path is useless in this case
46
- path: undefined,
47
- });
48
-
49
- // Print args as ANSI
50
- const logArgsAndErrorsMarkup = c.logger.runtime.prettyFormatLogObj( log.args, c.logger.settings);
51
- const logErrors = logArgsAndErrorsMarkup.errors;
52
- const logArgs = logArgsAndErrorsMarkup.args;
53
- const logErrorsStr = (logErrors.length > 0 && logArgs.length > 0 ? "\n" : "") + logErrors.join("\n");
54
- c.logger.settings.prettyInspectOptions.colors = c.logger.settings.stylePrettyLogs;
55
- let ansi = logMetaMarkup + formatWithOptions(c.logger.settings.prettyInspectOptions, ...logArgs) + logErrorsStr;
56
-
57
- // Use HTML spaces
58
- ansi = ansi.replace(/ {2}/g, '&nbsp;');
59
- ansi = ansi.replace(/\t/g, '&nbsp;'.repeat(8));
60
- ansi = ansi.replace(/\n/g, '<br/>');
61
-
62
- // Convert ANSI to HTML
63
- const html = ansi2Html.toHtml(ansi)
64
-
65
- return html;
66
- }