5htp-core 0.3.7 → 0.3.9

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 (46) hide show
  1. package/package.json +5 -3
  2. package/src/client/assets/css/components/button.less +6 -10
  3. package/src/client/assets/css/components/card.less +1 -7
  4. package/src/client/assets/css/text/icons.less +8 -5
  5. package/src/client/assets/css/text/text.less +2 -3
  6. package/src/client/assets/css/theme.less +1 -1
  7. package/src/client/assets/css/utils/layouts.less +4 -0
  8. package/src/client/components/Form.ts +2 -1
  9. package/src/client/components/Select/ChoiceElement.tsx +20 -6
  10. package/src/client/components/Select/ChoiceSelector.tsx +3 -2
  11. package/src/client/components/Select/index.tsx +53 -21
  12. package/src/client/components/containers/Popover/index.tsx +4 -1
  13. package/src/client/components/data/Time.tsx +16 -12
  14. package/src/client/components/inputv3/base.tsx +1 -0
  15. package/src/client/components/inputv3/index.tsx +3 -1
  16. package/src/client/services/router/components/router.tsx +10 -8
  17. package/src/client/services/router/index.tsx +0 -0
  18. package/src/client/services/router/request/api.ts +9 -3
  19. package/src/common/data/dates.ts +20 -4
  20. package/src/common/data/objets.ts +17 -0
  21. package/src/common/router/index.ts +1 -1
  22. package/src/common/validation/schema.ts +85 -91
  23. package/src/common/validation/validator.ts +4 -6
  24. package/src/common/validation/validators.ts +74 -72
  25. package/src/server/{services → app/container}/console/index.ts +52 -16
  26. package/src/server/app/container/index.ts +54 -0
  27. package/src/server/app/index.ts +22 -22
  28. package/src/server/app/service/index.ts +36 -11
  29. package/src/server/app.tsconfig.json +1 -1
  30. package/src/server/context.ts +1 -1
  31. package/src/server/index.ts +2 -10
  32. package/src/server/services/{users → auth}/index.ts +1 -1
  33. package/src/server/services/database/connection.ts +3 -1
  34. package/src/server/services/database/index.ts +34 -24
  35. package/src/server/services/database/model.ts +28 -0
  36. package/src/server/services/fetch/index.ts +4 -3
  37. package/src/server/services/router/index.ts +4 -1
  38. package/src/server/services/schema/request.ts +4 -11
  39. package/src/server/services/socket/index.ts +1 -1
  40. package/src/server/services/console/service.json +0 -6
  41. /package/src/server/{services → app/container}/console/html.ts +0 -0
  42. /package/src/server/services/{users → auth}/old.ts +0 -0
  43. /package/src/server/services/{users → auth}/router/index.ts +0 -0
  44. /package/src/server/services/{users → auth}/router/request.ts +0 -0
  45. /package/src/server/services/{users → auth}/router/service.json +0 -0
  46. /package/src/server/services/{users → auth}/service.json +0 -0
@@ -8,9 +8,11 @@ import './patch';
8
8
  import path from 'path';
9
9
 
10
10
  // Core
11
+ import type Application from '..';
11
12
  import type { StartedServicesIndex } from '../service';
12
13
  import Services, { ServicesContainer } from '../service/container';
13
14
  import ConfigParser, { TEnvConfig } from './config';
15
+ import Console from './console';
14
16
 
15
17
  /*----------------------------------
16
18
  - CLASS
@@ -26,6 +28,9 @@ export class ApplicationContainer<
26
28
  public Services = Services as ServicesContainer<TServicesIndex>;
27
29
  public Environment: TEnvConfig;
28
30
  public Identity: Config.Identity;
31
+ public Console: Console;
32
+
33
+ public application?: Application;
29
34
 
30
35
  public constructor() {
31
36
 
@@ -33,6 +38,16 @@ export class ApplicationContainer<
33
38
  const configParser = new ConfigParser( this.path.root );
34
39
  this.Environment = configParser.env();
35
40
  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
+ });
36
51
  }
37
52
 
38
53
  // Context
@@ -51,6 +66,45 @@ export class ApplicationContainer<
51
66
  },
52
67
  }
53
68
 
69
+ public start( ApplicationClass: typeof Application ) {
70
+
71
+ // Instanciate Application
72
+ try {
73
+ this.application = new ApplicationClass;
74
+ } catch (error) {
75
+ this.handleBug(error, "Failed to instanciate the Application Class");
76
+ process.exit(1);
77
+ }
78
+
79
+ // Start application
80
+ try {
81
+ this.application.start();
82
+ } catch (error) {
83
+ this.handleBug(error, "Failed to start the Application");
84
+ process.exit(1);
85
+ }
86
+ }
87
+
88
+ public async handleBug( rejection: Error, message: string ) {
89
+ if (this.Console) {
90
+ try {
91
+
92
+ this.Console.createBugReport(rejection);
93
+
94
+ } catch (consoleError) {
95
+ console.error(
96
+ message, rejection,
97
+ "Failed to transmiss the previous error to console:", consoleError
98
+ );
99
+ process.exit(1);
100
+ }
101
+ } else {
102
+ console.error(message, rejection);
103
+ process.exit(1);
104
+ }
105
+ }
106
+
107
+
54
108
  /*----------------------------------
55
109
  - HMR
56
110
  - TODO: move in dev server
@@ -11,7 +11,7 @@ import ServicesContainer, {
11
11
  TRegisteredService,
12
12
  TServiceMetas
13
13
  } from './service/container';
14
- import type { ServerBug } from '../services/console';
14
+ import type { ServerBug } from './container/console';
15
15
 
16
16
  // Built-in
17
17
  import type { default as Router, Request as ServerRequest } from '@server/services/router';
@@ -82,9 +82,6 @@ export class Application<
82
82
  public debug: boolean = false;
83
83
  public launched: boolean = false;
84
84
 
85
- // Mandatory services
86
- public Console = this.use('Core/Console');
87
-
88
85
  /*----------------------------------
89
86
  - INIT
90
87
  ----------------------------------*/
@@ -96,16 +93,20 @@ export class Application<
96
93
  // Application itself doesnt have configuration
97
94
  // Configuration must be handled by application services
98
95
  super(self, {}, {}, self);
99
-
100
- // We can't pass this in super so we assign here
101
- this.parent = this;
102
- this.app = this;
103
-
104
- // Gestion crash
96
+
97
+ // Handle unhandled crash
98
+ this.on('error', e => this.container.handleBug(e, "An error occured in the application"));
99
+
105
100
  process.on('unhandledRejection', (error: any, promise: any) => {
101
+ console.log("unhandledRejection");
106
102
  // We don't log the error here because it's the role of the app to decidehiw to log errors
107
103
  this.runHook('error', error);
108
104
  });
105
+
106
+ // We can't pass this in super so we assign here
107
+ this.parent = this;
108
+ this.app = this;
109
+
109
110
  }
110
111
 
111
112
  /*----------------------------------
@@ -122,16 +123,12 @@ export class Application<
122
123
  - LAUNCH
123
124
  ----------------------------------*/
124
125
 
125
- protected async start() {
126
+ public async start() {
126
127
 
127
128
  console.log("Build date", BUILD_DATE);
128
129
  console.log("Core version", CORE_VERSION);
129
- console.log(`5HTP Core`, process.env.npm_package_version);
130
130
  const startTime = Date.now();
131
131
 
132
- // Handle errors & crashs
133
- this.on('error', e => this.Console.createBugReport(e))
134
-
135
132
  console.info(`[boot] Start services`);
136
133
  await this.startServices();
137
134
  this.debug && console.info(`[boot] Services are ready`);
@@ -147,13 +144,6 @@ export class Application<
147
144
  public async ready() {
148
145
 
149
146
 
150
- }
151
-
152
- // Default error handler
153
- public async reportBug( bug: ServerBug ) {
154
-
155
- console.error( bug.error );
156
-
157
147
  }
158
148
 
159
149
  public async shutdown() {
@@ -208,6 +198,16 @@ export class Application<
208
198
  unused.join(', '));
209
199
  }
210
200
 
201
+ /*----------------------------------
202
+ - ERROR HANDLING
203
+ ----------------------------------*/
204
+ // Default error handler
205
+ public async reportBug( bug: ServerBug ) {
206
+
207
+ console.error( bug.error );
208
+
209
+ }
210
+
211
211
  }
212
212
 
213
213
  export default Application
@@ -58,7 +58,7 @@ export default abstract class Service<
58
58
  TConfig extends TServiceConfig,
59
59
  THooks extends THooksList,
60
60
  TApplication extends Application,
61
- TServicesIndex extends StartedServicesIndex
61
+ TServicesIndex extends StartedServicesIndex = {}
62
62
  > {
63
63
 
64
64
  public started?: Promise<void>;
@@ -144,6 +144,7 @@ export default abstract class Service<
144
144
  // this.use immediatly instanciate the subservice for few reasons:
145
145
  // - The subservice instance can be accesses from another service in the constructor, no matter the order of loading of the services
146
146
  // - Consistency: the subserviuce proprties shouldn't be assogned to two different values according depending on the app lifecycle
147
+ // Don't throw errors here since process.on('unhandledRejection') has not been configurated at this step (constructor ran after properties initialization)
147
148
  public use<
148
149
  TInstalledServices extends this["app"]["servicesContainer"]["allServices"],
149
150
  TServiceId extends keyof TInstalledServices,
@@ -172,8 +173,9 @@ export default abstract class Service<
172
173
  if (registered === undefined) {
173
174
  if (serviceUseOptions.optional)
174
175
  return undefined;
175
- else
176
+ else {
176
177
  throw new Error(`Unable to use service "${serviceId}": This one hasn't been setup.`);
178
+ }
177
179
  }
178
180
 
179
181
  // Bind subservices
@@ -193,20 +195,43 @@ export default abstract class Service<
193
195
 
194
196
  // Instanciate
195
197
  console.log(`[app] Load service`, registered.metas.id);
196
- const ServiceClass = registered.metas.class().default;
198
+ let ServiceClass;
199
+ try {
200
+ ServiceClass = registered.metas.class().default;
201
+ } catch (error) {
202
+ console.error("Failed to load the class of the", registered.metas.id, "service:", error);
203
+ throw error;
204
+ }
197
205
 
198
206
  // Create class instance
199
- const service = new ServiceClass(
200
- this,
201
- registered.config,
202
- registered.subServices,
203
- this.app || this
204
- )
205
- const serviceInstance = service.getServiceInstance();
207
+ this.config.debug && console.log(`[app] Instanciate service`, registered.metas.id);
208
+ let service;
209
+ try {
210
+ service = new ServiceClass(
211
+ this,
212
+ registered.config,
213
+ registered.subServices,
214
+ this.app || this
215
+ )
216
+ } catch (error) {
217
+ console.error("Failed to instanciate class of the", registered.metas.id, "service");
218
+ throw error;
219
+ }
220
+
221
+ // Hande custom instance getter (ex: SQL callable class)
222
+ this.config.debug && console.log(`[app] Get service instance for`, registered.metas.id);
223
+ let serviceInstance;
224
+ try {
225
+ serviceInstance = service.getServiceInstance();
226
+ } catch (error) {
227
+ console.error("Failed to get service instance for the ", registered.metas.id, "service");
228
+ throw error;
229
+ }
206
230
 
207
231
  // Bind his own metas
208
232
  service.metas = registered.metas;
209
233
  ServicesContainer.allServices[ registered.metas.id ] = serviceInstance;
234
+ this.config.debug && console.log(`[app] Service`, registered.metas.id, 'Loaded');
210
235
 
211
236
  return serviceInstance;
212
237
  }
@@ -250,7 +275,7 @@ export default abstract class Service<
250
275
  await service.started.catch(e => {
251
276
  console.error("Catched error while starting service " + serviceScope, e);
252
277
  if (this.app.env.profile === 'prod')
253
- process.exit();
278
+ process.exit(1);
254
279
  else
255
280
  throw e;
256
281
  })
@@ -5,7 +5,7 @@
5
5
  "baseUrl": "..",
6
6
  "paths": {
7
7
 
8
- "@/server/models": ["./server/.generated/models.d.ts"],
8
+ "@/server/models": ["./server/.generated/models.ts"],
9
9
 
10
10
  "@client/*": ["../node_modules/5htp-core/src/client/*"],
11
11
  "@common/*": ["../node_modules/5htp-core/src/common/*"],
@@ -1,4 +1,4 @@
1
1
  import { AsyncLocalStorage } from 'async_hooks';
2
- import type { ChannelInfos } from '@server/services/console';
2
+ import type { ChannelInfos } from '@server/app/container/console';
3
3
 
4
4
  export default new AsyncLocalStorage<ChannelInfos>();
@@ -1,12 +1,4 @@
1
- // Load Application container
2
- import './app/container';
3
-
4
- // Load services setup
1
+ import AppContainer from './app/container';
5
2
  import '@/server/config/*.ts';
6
-
7
- // Load Application
8
3
  import Application from '@/server';
9
- const application = new Application;
10
-
11
- // Start application
12
- application.start();
4
+ AppContainer.start( Application );
@@ -65,7 +65,7 @@ export type TServices = {
65
65
  /*----------------------------------
66
66
  - SERVICE
67
67
  ----------------------------------*/
68
- export default abstract class UsersManagementService<
68
+ export default abstract class AuthService<
69
69
  TUser extends {},
70
70
  TApplication extends Application,
71
71
  TJwtSession extends {} = {},
@@ -188,7 +188,9 @@ export default class DatabaseManager {
188
188
 
189
189
  const table = db[field.table];
190
190
  if (table === undefined) {
191
- console.error("Field infos:", field);
191
+ // We don't throw error, sinc eit can be a virtual table
192
+ //console.error("Field infos:", field);
193
+ return next();
192
194
  throw new Error(`Table metadatas for ${field.db}.${field.table} were not loaded.`);
193
195
  }
194
196
 
@@ -233,9 +233,19 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
233
233
  // SQL query
234
234
  } else if (typeof value === 'function' && value.string !== undefined)
235
235
  value = ' ' + value.string;
236
- else
237
- value = ' ' + mysql.escape(value);
236
+ // Escape value
237
+ else {
238
238
 
239
+ const lastKeyword = stringBefore.trim().split(' ').pop();
240
+
241
+ // Escape table name
242
+ if (lastKeyword === 'FROM')
243
+ value = '`' + value + '`';
244
+ else
245
+ value = mysql.escape(value);
246
+
247
+ value = ' ' + value;
248
+ }
239
249
  stringBefore += value;
240
250
 
241
251
  }
@@ -375,7 +385,7 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
375
385
 
376
386
  // Build query
377
387
  return this.database.query(`
378
- UPDATE ${tableName} SET ${egalitesData} WHERE ${egalitesWhere};
388
+ UPDATE \`${tableName}\` SET ${egalitesData} WHERE ${egalitesWhere};
379
389
  `, opts);
380
390
 
381
391
  }
@@ -396,23 +406,37 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
396
406
  - OPERATIONS: INSERT
397
407
  ----------------------------------*/
398
408
 
399
- public tryInsert = (table: string, data: TObjetDonnees) =>
409
+ public tryInsert = <TData extends TObjetDonnees>(table: string, data: TData | TData[]) =>
400
410
  this.insert(table, data, { try: true });
401
411
 
412
+ public async insert<TData extends TObjetDonnees>(
413
+ path: string,
414
+ data: TData,
415
+ opts?: TInsertQueryOptions<TData>
416
+ ): Promise<TData>;
417
+
418
+ public async insert<TData extends TObjetDonnees>(
419
+ path: string,
420
+ data: TData[],
421
+ opts?: TInsertQueryOptions<TData[]>
422
+ ): Promise<TData>;
423
+
402
424
  public async insert<TData extends TObjetDonnees>(
403
425
  path: string,
404
426
  data: TData | TData[],
405
427
  opts: TInsertQueryOptions<TData> = {}
406
- ): Promise<OkPacket> {
428
+ ): Promise<TData | TData[]> {
407
429
 
408
430
  const table = this.database.getTable(path);
409
431
 
410
432
  // Normalize data
411
- if (!Array.isArray(data))
433
+ let returnSingleResult = false;
434
+ if (!Array.isArray(data)) {
412
435
  data = [data];
413
- else if (data.length === 0) {
436
+ returnSingleResult = true;
437
+ } else if (data.length === 0) {
414
438
  console.warn(LogPrefix, `Insert nothing in ${path}. Cancelled.`);
415
- return emptyOkPacket;
439
+ return [];
416
440
  }
417
441
 
418
442
  // Upsert
@@ -439,28 +463,14 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
439
463
  okPacket.warningCount += queryResult.warningCount;
440
464
  }
441
465
 
442
- return okPacket;
443
-
444
466
  } else {
445
467
  const query = this.buildInsertStatement(table, data, opts) + upsertStatement;
446
468
 
447
469
  const queryResult = await this.database.query<mysql.OkPacket>(query + ';', opts);
448
470
 
449
- return {
450
- constructor: {
451
- name: 'OkPacket'
452
- },
453
- fieldCount: queryResult.fieldCount,
454
- affectedRows: queryResult.affectedRows,
455
- changedRows: queryResult.changedRows,
456
- insertId: queryResult.insertId,
457
- serverStatus: queryResult.serverStatus,
458
- warningCount: queryResult.warningCount,
459
- message: queryResult.message,
460
- procotol41: queryResult.procotol41,
461
- };
462
471
  }
463
- // OLD: return [data, queryResult?.insertId, queryResult];
472
+
473
+ return returnSingleResult ? data[0] : data;
464
474
  }
465
475
 
466
476
  private buildInsertStatement<TData extends TObjetDonnees>(
@@ -0,0 +1,28 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ import type Application from "@server/app";
6
+
7
+ import type SQL from "@server/services/database";
8
+
9
+ /*----------------------------------
10
+ - TYPES
11
+ ----------------------------------*/
12
+
13
+ /*----------------------------------
14
+ - CLASS
15
+ ----------------------------------*/
16
+ export default abstract class Model<TData extends {}> {
17
+
18
+ public abstract tableName: string;
19
+
20
+ public constructor( app: Application & { SQL: SQL }, private SQL = app.SQL) {
21
+
22
+ }
23
+
24
+ public async create( data: TData | TData[] ): Promise< TData | TData[] > {
25
+ return await this.SQL.insert( this.tableName, data );
26
+ }
27
+
28
+ }
@@ -83,7 +83,6 @@ export default class FetchService extends Service<Config, Hooks, Application, Se
83
83
  if (this.services.disks)
84
84
  this.disk = this.services.disks.get( this.config.disk );
85
85
 
86
-
87
86
  }
88
87
 
89
88
  public async ready() {
@@ -116,8 +115,10 @@ export default class FetchService extends Service<Config, Hooks, Application, Se
116
115
  ) {
117
116
 
118
117
  // Parse url if router service is provided
119
- if (this.services.router !== undefined)
120
- url = this.services.router.url(url);
118
+ if (this.services.router === undefined)
119
+ throw new Error(`Please bind the Router service to the Fetch service in order to contact APIs.`);
120
+
121
+ url = this.services.router.url(url);
121
122
 
122
123
  // Send request
123
124
  const res = await got(url, {
@@ -598,7 +598,10 @@ declare type Routes = {
598
598
  console.warn(e);
599
599
 
600
600
  if (request.accepts("html"))
601
- await response.runController(route, { message: e.message });
601
+ await response.runController(route, {
602
+ message: e.message,
603
+ type: e.constructor.name
604
+ });
602
605
  else if (request.accepts("json"))
603
606
  await response.json(e.message);
604
607
  else
@@ -47,17 +47,10 @@ export default class RequestValidator extends ServerSchemaValidator implements R
47
47
  const schema = fields instanceof Schema ? fields : new Schema(fields);
48
48
 
49
49
  // Les InputError seront propagées vers le middleware dédié à la gestion des erreurs
50
- const { values } = schema.validate(
51
- this.request.data,
52
- this.request.data,
53
- {},
54
- {
55
- debug: this.config.debug,
56
- throwError: true,
57
- validateDeps: false
58
- },
59
- []
60
- );
50
+ const values = schema.validate( this.request.data, {
51
+ debug: this.config.debug,
52
+ validateDeps: false
53
+ }, []);
61
54
 
62
55
  return values;
63
56
  }
@@ -14,7 +14,7 @@ import Service, { AnyService, TRegisteredService } from '@server/app/service';
14
14
  import SocketScope, { WebSocket } from './scope';
15
15
  import type Router from '@server/services/router';
16
16
  export type { WebSocket, default as SocketScope } from './scope';
17
- import type UsersManagementService from '../users';
17
+ import type UsersManagementService from '../auth';
18
18
 
19
19
  /*----------------------------------
20
20
  - TYPES
@@ -1,6 +0,0 @@
1
- {
2
- "id": "Core/Console",
3
- "name": "Console",
4
- "parent": "app",
5
- "dependences": []
6
- }
File without changes