5htp-core 0.2.4-3 → 0.2.5

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.
@@ -8,6 +8,7 @@ import mysql from 'mysql2/promise';
8
8
  // Core: general
9
9
  import Application from '@server/app';
10
10
  import Service from '@server/app/service';
11
+ import { Anomaly } from '@common/errors';
11
12
 
12
13
  // Core: specific
13
14
  import { SqlError } from './debug';
@@ -26,14 +27,19 @@ const LogPrefix = '[database][connection]';
26
27
  - SERVICE CONFIG
27
28
  ----------------------------------*/
28
29
 
29
- export type DatabaseServiceConfig = {
30
- list: string[],
30
+ type ConnectionConfig = {
31
+ name: string,
32
+ databases: string[],
31
33
  host: string,
32
34
  port: number,
33
35
  login: string,
34
36
  password: string,
35
37
  }
36
38
 
39
+ export type DatabaseServiceConfig = {
40
+ connections: ConnectionConfig[]
41
+ }
42
+
37
43
  export type THooks = {
38
44
 
39
45
  }
@@ -63,10 +69,11 @@ type TSelectQueryResult = any;
63
69
  /*----------------------------------
64
70
  - SERVICES
65
71
  ----------------------------------*/
66
- export default class DatabaseConnection extends Service<DatabaseServiceConfig, THooks, Application> {
72
+ export default class DatabaseManager extends Service<DatabaseServiceConfig, THooks, Application> {
67
73
 
68
74
  private initialized = false;
69
75
  public connection!: mysql.Pool;
76
+ public connectionConfig?: ConnectionConfig;
70
77
 
71
78
  public tables: TDatabasesList = {};
72
79
  public metas = new MetadataParser(this);
@@ -84,35 +91,49 @@ export default class DatabaseConnection extends Service<DatabaseServiceConfig, T
84
91
 
85
92
  this.initialized = false;
86
93
 
87
- this.app.on('cleanup', () => this.cleanup());
94
+ // Try to connect to one of the databases
95
+ const connectionErrors: string[] = []
96
+ for (const connectionConfig of this.config.connections){
97
+ try {
98
+ this.connection = await this.connect(connectionConfig);
99
+ this.connectionConfig = connectionConfig;
100
+ } catch (error) {
101
+ console.warn(LogPrefix, `Failed to connect to ${connectionConfig.name}: ` + error);
102
+ connectionErrors.push(connectionConfig.name + ': ' + error);
103
+ }
104
+ }
105
+
106
+ // Coudnt connect to any database
107
+ if (this.connectionConfig === undefined)
108
+ throw new Anomaly(`Couldnt connect to any database.`, { connectionErrors });
88
109
 
89
- this.connection = await this.connect();
110
+ // Disconnect from the database when the app is terminated
111
+ this.app.on('cleanup', () => this.disconnect());
90
112
 
91
- this.tables = await this.metas.load( this.config.list );
113
+ // Load tables metas
114
+ this.tables = await this.metas.load( this.connectionConfig.databases );
92
115
 
116
+ // Ready to make queries
93
117
  this.initialized = true;
94
-
95
118
  }
96
119
 
97
- public async cleanup() {
120
+ public async disconnect() {
98
121
  return this.connection.end();
99
122
  }
100
123
 
101
124
  /*----------------------------------
102
125
  - INIT
103
126
  ----------------------------------*/
104
- public async connect() {
105
-
106
- console.info(LogPrefix, `Connecting to databases ...`);
107
-
127
+ public async connect({ name, databases, host, login, password, port }: ConnectionConfig) {
128
+ console.info(LogPrefix, `Trying to connect to ${name} ...`);
108
129
  return await mysql.createPool({
109
130
 
110
131
  // Identification
111
- host: this.config.host,
112
- port: this.config.port,
113
- user: this.config.login,
114
- password: this.config.password,
115
- database: this.config.list[0],
132
+ host: host,
133
+ port: port,
134
+ user: login,
135
+ password: password,
136
+ database: databases[0],
116
137
 
117
138
  // Pool
118
139
  waitForConnections: true,
@@ -137,7 +158,7 @@ export default class DatabaseConnection extends Service<DatabaseServiceConfig, T
137
158
  //console.info(LogPrefix, 'queryFormat', query);
138
159
  return query;
139
160
  }
140
- });
161
+ })
141
162
  }
142
163
 
143
164
  private typeCast( field: mysql.Field, next: Function ) {
@@ -216,13 +237,16 @@ export default class DatabaseConnection extends Service<DatabaseServiceConfig, T
216
237
 
217
238
  public getTable( path: string ): TMetasTable {
218
239
 
240
+ if (this.connectionConfig === undefined)
241
+ throw new Error(`No connection has been initialised.`);
242
+
219
243
  // Parse path
220
244
  let db: string, table: string;
221
245
  if (path.includes('.'))
222
246
  ([db, table] = path.split('.'));
223
247
  else {
224
248
  // Only the table = use the main database (first of the list in the config)
225
- db = this.config.list[0];
249
+ db = this.connectionConfig.databases[0];
226
250
  table = path;
227
251
  }
228
252
 
@@ -27,7 +27,7 @@ import BaseRouter, {
27
27
  TRouteOptions, defaultOptions
28
28
  } from '@common/router';
29
29
  import { buildRegex, getRegisterPageArgs } from '@common/router/register';
30
- import { layoutsList } from '@common/router/layouts';
30
+ import { layoutsList, getLayout } from '@common/router/layouts';
31
31
  import { TFetcherList, TFetcher } from '@common/router/request/api';
32
32
  import type { TFrontRenderer } from '@common/router/response/page';
33
33
  import type { TSsrUnresolvedRoute, TRegisterPageArgs } from '@client/services/router';
@@ -147,7 +147,6 @@ export default class ServerRouter<
147
147
  // Since route registering requires all services to be ready,
148
148
  // We load routes only when all services are ready
149
149
  this.app.on('ready', async () => {
150
-
151
150
  // Use require to avoid circular references
152
151
  this.registerRoutes([
153
152
  ...require("metas:@/server/routes/**/*.ts"),
@@ -228,9 +227,13 @@ export default class ServerRouter<
228
227
  options: TRoute["options"],
229
228
  renderer: TFrontRenderer<{}, { message: string }>
230
229
  ) {
230
+
231
+ // Automatic layout form the nearest _layout folder
232
+ const layout = getLayout('Error ' + code, options);
233
+
231
234
  this.errors[code] = {
232
235
  code,
233
- controller: (context: TRouterContext<this>) => new Page(null, renderer, context),
236
+ controller: (context: TRouterContext<this>) => new Page(null, renderer, context, layout),
234
237
  options
235
238
  };
236
239
  }
@@ -503,7 +506,7 @@ declare type Routes = {
503
506
  return response;
504
507
  }
505
508
 
506
- throw new NotFound(`The requested endpoint was not found.`);
509
+ throw new NotFound();
507
510
  }
508
511
 
509
512
  private async resolveApiBatch( fetchers: TFetcherList, request: ServerRequest<this> ) {
@@ -542,9 +545,14 @@ declare type Routes = {
542
545
  // Rapport / debug
543
546
  if (code === 500) {
544
547
 
548
+ // Report error
545
549
  await this.app.runHook('error', e, request);
546
550
 
547
- // Pour déboguer les erreurs HTTP
551
+ // Don't exose technical errors to users
552
+ if (this.app.env.profile === 'prod')
553
+ e.message = "We encountered an internal error, and our team has just been notified. Sorry for the inconvenience.";
554
+
555
+ // Pour déboguer les erreurs HTTP
548
556
  } else if (this.app.env.profile === "dev")
549
557
  console.warn(e);
550
558
 
@@ -1,13 +0,0 @@
1
- #page {
2
- transition: opacity 5s cubic-bezier(0,.3,0,.99);
3
- animation: showMain 1s ease-out;
4
- @keyframes showMain {
5
- 0% { opacity: 0.3; }
6
- 100% { opacity: 1; }
7
- }
8
-
9
- body.loading & {
10
- cursor: wait;
11
- opacity: 0.1;
12
- }
13
- }
@@ -1,5 +0,0 @@
1
- body {
2
-
3
-
4
-
5
- }
@@ -1,55 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
-
5
- // Npm
6
- import React from 'react';
7
-
8
- // Core
9
-
10
- // Core components
11
- import Button, { Props as ButtonProps } from '@client/components/button';
12
- import useHeader from '@client/pages/useHeader';
13
-
14
- /*----------------------------------
15
- - CONTROLEUR
16
- ----------------------------------*/
17
- import './index.less';
18
- export default ({ headline, subtitle, cta, preview }: {
19
-
20
- headline: string,
21
- subtitle: string,
22
- cta: ButtonProps,
23
- preview: [string, string]
24
-
25
- }) => {
26
-
27
- useHeader({
28
- title: headline,
29
- subtitle: subtitle,
30
- });
31
-
32
- /*----------------------------------
33
- - RENDER
34
- ----------------------------------*/
35
- return (
36
- <div id="landing">
37
-
38
- <header>
39
- <div class="text card">
40
- <h1>{headline}</h1>
41
- <p>
42
- {subtitle}
43
- </p>
44
- <Button type="guide" {...cta} />
45
- </div>
46
-
47
- <figure>
48
- <img class="image" src={preview[0]} />
49
- <legend>{preview[1]}</legend>
50
- </figure>
51
- </header>
52
-
53
- </div>
54
- )
55
- }