5htp-core 0.2.2 → 0.2.4-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.
- package/package.json +1 -1
- package/src/client/app/component.tsx +1 -2
- package/src/client/app/index.ts +2 -6
- package/src/client/assets/css/components/button.less +4 -3
- package/src/client/assets/css/components/card.less +26 -11
- package/src/client/assets/css/components/lists.less +1 -1
- package/src/client/assets/css/components/other.less +0 -11
- package/src/client/assets/css/components.less +17 -3
- package/src/client/assets/css/core.less +55 -0
- package/src/client/assets/css/theme.less +0 -4
- package/src/client/assets/css/utils/layouts.less +1 -1
- package/src/client/assets/css/utils/medias.less +0 -47
- package/src/client/components/Dialog/Manager.tsx +1 -4
- package/src/client/components/Dialog/card.tsx +1 -1
- package/src/client/components/Dialog/index.less +2 -2
- package/src/client/components/Form.ts +154 -0
- package/src/client/components/{Form → Form_old}/index.tsx +0 -0
- package/src/client/components/{Form → Form_old}/index.tsx.old +0 -0
- package/src/client/components/Select/index.tsx +159 -24
- package/src/client/components/containers/Popover/index.tsx +38 -120
- package/src/client/components/data/progressbar/circular/index.tsx +1 -3
- package/src/client/components/dropdown/index.tsx +8 -13
- package/src/client/components/inputv3/base.less +0 -1
- package/src/client/components/inputv3/base.tsx +17 -6
- package/src/client/components/inputv3/string/index.tsx +23 -10
- package/src/client/services/router/components/Page.tsx +16 -18
- package/src/client/services/router/components/router.tsx +3 -3
- package/src/client/services/router/index.tsx +17 -14
- package/src/client/services/router/request/api.ts +6 -3
- package/src/client/services/router/response/index.tsx +4 -0
- package/src/client/services/router/response/page.ts +2 -1
- package/src/common/router/index.ts +1 -1
- package/src/common/router/layouts.ts +38 -6
- package/src/common/router/register.ts +3 -9
- package/src/common/router/request/api.ts +3 -1
- package/src/common/validation/index.ts +1 -0
- package/src/common/validation/schema.ts +8 -3
- package/src/common/validation/validators.ts +21 -10
- package/src/server/app/index.ts +4 -1
- package/src/server/services/console/bugReporter.ts +2 -19
- package/src/server/services/database/connection.ts +8 -18
- package/src/server/services/database/index.ts +27 -19
- package/src/server/services/router/index.ts +3 -3
- package/src/server/services/router/request/api.ts +3 -0
- package/src/server/services/router/response/index.ts +7 -4
- package/src/server/services/router/response/mask/Filter.ts +16 -72
- package/src/server/services/router/response/mask/index.ts +4 -7
- package/src/server/services/router/response/page/document.tsx +2 -2
- package/src/server/services/router/response/page/index.tsx +2 -2
- package/src/server/services/schema/request.ts +4 -2
|
@@ -34,26 +34,24 @@ export default ({ page, isCurrent }: { page: Page, isCurrent?: boolean }) => {
|
|
|
34
34
|
|
|
35
35
|
}, [page]);
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
/*
|
|
38
38
|
<div
|
|
39
39
|
class={"page" + (isCurrent ? ' current' : '')}
|
|
40
40
|
id={page.chunkId === undefined ? undefined : 'page_' + page.chunkId}
|
|
41
41
|
>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
</div>
|
|
58
|
-
)
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
// Make request parameters and api data accessible from the page component
|
|
45
|
+
return page.renderer ? (
|
|
46
|
+
|
|
47
|
+
<page.renderer
|
|
48
|
+
// Services
|
|
49
|
+
{...context}
|
|
50
|
+
// URL params
|
|
51
|
+
{...context.request.data}
|
|
52
|
+
// API data
|
|
53
|
+
{...apiData}
|
|
54
|
+
/>
|
|
55
|
+
|
|
56
|
+
) : null
|
|
59
57
|
}
|
|
@@ -77,8 +77,8 @@ export default ({ service: router }: { service: Router }) => {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// If if the layout changed
|
|
80
|
-
const curLayout =
|
|
81
|
-
const newLayout = newpage?.
|
|
80
|
+
const curLayout = pages.current?.layout;
|
|
81
|
+
const newLayout = newpage?.layout;
|
|
82
82
|
if (newLayout && curLayout && newLayout.path !== curLayout.path) {
|
|
83
83
|
|
|
84
84
|
// TEMPORARY FIX: reload everything when we change layout
|
|
@@ -126,7 +126,7 @@ export default ({ service: router }: { service: Router }) => {
|
|
|
126
126
|
return history?.listen(async (locationUpdate) => {
|
|
127
127
|
|
|
128
128
|
// Load the concerned route
|
|
129
|
-
const request = new ClientRequest(locationUpdate.location, context);
|
|
129
|
+
const request = new ClientRequest(locationUpdate.location, context.router);
|
|
130
130
|
await resolvePage(request);
|
|
131
131
|
|
|
132
132
|
// Scroll to the selected content via url hash
|
|
@@ -116,21 +116,24 @@ export type THookCallback<TRouter extends ClientRouter> = (request: ClientReques
|
|
|
116
116
|
|
|
117
117
|
type THookName = 'location.change' | 'page.changed'
|
|
118
118
|
|
|
119
|
-
type Config = {
|
|
119
|
+
type Config<TAdditionnalContext extends {} = {}> = {
|
|
120
120
|
preload: string[], // List of globs
|
|
121
|
+
context: () => TAdditionnalContext
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
/*----------------------------------
|
|
124
125
|
- ROUTER
|
|
125
126
|
----------------------------------*/
|
|
126
127
|
export default class ClientRouter<
|
|
127
|
-
TApplication extends ClientApplication = ClientApplication
|
|
128
|
-
|
|
128
|
+
TApplication extends ClientApplication = ClientApplication,
|
|
129
|
+
TAdditionnalContext extends {} = {}
|
|
130
|
+
> extends Service<Config<TAdditionnalContext>, ClientApplication> implements BaseRouter {
|
|
129
131
|
|
|
130
|
-
|
|
132
|
+
// Context data
|
|
133
|
+
public context = window["ssr"] as (TBasicSSrData | undefined);
|
|
131
134
|
public ssrRoutes = window["routes"] as TSsrUnresolvedRoute[];
|
|
132
135
|
|
|
133
|
-
public constructor(app: TApplication, config: Config) {
|
|
136
|
+
public constructor(app: TApplication, config: Config<TAdditionnalContext>) {
|
|
134
137
|
|
|
135
138
|
super(app, config);
|
|
136
139
|
}
|
|
@@ -157,7 +160,7 @@ export default class ClientRouter<
|
|
|
157
160
|
|
|
158
161
|
const loaders: TRoutesLoaders = { ...coreRoutes, ...appRoutes }
|
|
159
162
|
let currentRoute: TUnresolvedRoute | undefined;
|
|
160
|
-
debug && console.log(LogPrefix, `Indexing routes and finding the current route from ssr data:`, this.
|
|
163
|
+
debug && console.log(LogPrefix, `Indexing routes and finding the current route from ssr data:`, this.context);
|
|
161
164
|
|
|
162
165
|
// Associe la liste des routes (obtenue via ssr) à leur loader
|
|
163
166
|
for (let routeIndex = 0; routeIndex < this.ssrRoutes.length; routeIndex++) {
|
|
@@ -195,9 +198,9 @@ export default class ClientRouter<
|
|
|
195
198
|
if (currentRoute === undefined) {
|
|
196
199
|
|
|
197
200
|
const isCurrentRoute = (
|
|
198
|
-
this.
|
|
201
|
+
this.context !== undefined
|
|
199
202
|
&&
|
|
200
|
-
route.chunk === this.
|
|
203
|
+
route.chunk === this.context.page.chunkId
|
|
201
204
|
);
|
|
202
205
|
|
|
203
206
|
if (isCurrentRoute) {
|
|
@@ -212,7 +215,7 @@ export default class ClientRouter<
|
|
|
212
215
|
|
|
213
216
|
public page(...args: TRegisterPageArgs): TRoute {
|
|
214
217
|
|
|
215
|
-
const { path, options, controller, renderer } = getRegisterPageArgs(...args);
|
|
218
|
+
const { path, options, controller, renderer, layout } = getRegisterPageArgs(...args);
|
|
216
219
|
|
|
217
220
|
// S'il s'agit d'une page, son id doit avoir été injecté via le plugin babel
|
|
218
221
|
const id = options["id"];
|
|
@@ -230,7 +233,7 @@ export default class ClientRouter<
|
|
|
230
233
|
...defaultOptions,
|
|
231
234
|
...options
|
|
232
235
|
},
|
|
233
|
-
controller: (context: TClientOrServerContext) => new ClientPage(controller, renderer, context)
|
|
236
|
+
controller: (context: TClientOrServerContext) => new ClientPage(controller, renderer, context, layout)
|
|
234
237
|
};
|
|
235
238
|
|
|
236
239
|
this.routes.push(route);
|
|
@@ -343,15 +346,15 @@ export default class ClientRouter<
|
|
|
343
346
|
|
|
344
347
|
// Restituate SSR response
|
|
345
348
|
let apiData: {} = {}
|
|
346
|
-
if (this.
|
|
349
|
+
if (this.context) {
|
|
347
350
|
|
|
348
351
|
console.log("SSR Response restitution ...");
|
|
349
352
|
|
|
350
|
-
request.user = this.
|
|
353
|
+
request.user = this.context.user || null;
|
|
351
354
|
|
|
352
|
-
request.data = this.
|
|
355
|
+
request.data = this.context.request.data;
|
|
353
356
|
|
|
354
|
-
apiData = this.
|
|
357
|
+
apiData = this.context.page.data || {};
|
|
355
358
|
}
|
|
356
359
|
|
|
357
360
|
// Replacer api data par ssr data
|
|
@@ -127,7 +127,7 @@ export default class ApiClient implements ApiClientService {
|
|
|
127
127
|
|
|
128
128
|
return await this.fetch<TData>(method, path, data, options).catch((e) => {
|
|
129
129
|
this.app.handleError(e);
|
|
130
|
-
throw e;
|
|
130
|
+
throw e; // Throw to break the loop
|
|
131
131
|
})
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -136,8 +136,9 @@ export default class ApiClient implements ApiClientService {
|
|
|
136
136
|
// Pick the fetchers where the data is needed
|
|
137
137
|
const fetchersToRun: TFetcherList = {};
|
|
138
138
|
let fetchersCount: number = 0;
|
|
139
|
-
for (const fetcherId in fetchers)
|
|
140
|
-
|
|
139
|
+
for (const fetcherId in fetchers)
|
|
140
|
+
// The fetcher can be undefined
|
|
141
|
+
if (!( fetcherId in alreadyLoadedData ) && fetchers[ fetcherId ]) {
|
|
141
142
|
fetchersToRun[ fetcherId ] = fetchers[ fetcherId ]
|
|
142
143
|
fetchersCount++;
|
|
143
144
|
}
|
|
@@ -157,6 +158,8 @@ export default class ApiClient implements ApiClientService {
|
|
|
157
158
|
|
|
158
159
|
});
|
|
159
160
|
|
|
161
|
+
// Errors will be catched in the caller
|
|
162
|
+
|
|
160
163
|
return { ...alreadyLoadedData, ...fetchedData }
|
|
161
164
|
}
|
|
162
165
|
|
|
@@ -42,6 +42,8 @@ export type TRouterContext<TRouter extends ClientRouter = ClientRouter, TApplica
|
|
|
42
42
|
// Expose client application services (api, socket, ...)
|
|
43
43
|
//TRouter["app"]
|
|
44
44
|
TApplication
|
|
45
|
+
&
|
|
46
|
+
ReturnType<TRouter["config"]["context"]>
|
|
45
47
|
)
|
|
46
48
|
|
|
47
49
|
/*----------------------------------
|
|
@@ -80,6 +82,8 @@ export default class ClientPageResponse<
|
|
|
80
82
|
request: this.request,
|
|
81
83
|
route: this.route,
|
|
82
84
|
api: this.request.api,
|
|
85
|
+
|
|
86
|
+
...this.request.router.config.context()
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
context.context = context;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type { ComponentChild } from 'preact';
|
|
7
7
|
|
|
8
8
|
// Core
|
|
9
|
-
import type { TClientOrServerContext } from '@common/router';
|
|
9
|
+
import type { TClientOrServerContext, Layout } from '@common/router';
|
|
10
10
|
import PageResponse, { TDataProvider, TFrontRenderer } from "@common/router/response/page";
|
|
11
11
|
|
|
12
12
|
// Specific
|
|
@@ -32,6 +32,7 @@ export default class ClientPage<TRouter = ClientRouter> extends PageResponse<TRo
|
|
|
32
32
|
public dataProvider: TDataProvider | null,
|
|
33
33
|
public component: TFrontRenderer,
|
|
34
34
|
public context: TClientOrServerContext,
|
|
35
|
+
public layout?: Layout,
|
|
35
36
|
|
|
36
37
|
public route = context.route
|
|
37
38
|
) {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { ComponentChild } from 'preact';
|
|
7
7
|
// Core
|
|
8
8
|
import type { ClientContext } from '@/client/context';
|
|
9
|
+
import type { TRouteOptions } from '.';
|
|
9
10
|
// App
|
|
10
11
|
import layouts from '@/client/pages/**/_layout/index.tsx';
|
|
11
12
|
|
|
@@ -30,20 +31,51 @@ export type ImportedLayouts = {
|
|
|
30
31
|
- UTILS
|
|
31
32
|
----------------------------------*/
|
|
32
33
|
// TODO: getLayot only on server side, and pass the layout chunk id
|
|
33
|
-
export const getLayout = (routePath: string
|
|
34
|
+
export const getLayout = (routePath: string, routeOptions?: TRouteOptions): Layout | undefined => {
|
|
35
|
+
|
|
36
|
+
if (routeOptions === undefined)
|
|
37
|
+
return undefined;
|
|
38
|
+
// W don't want a layout on this page
|
|
39
|
+
if (routeOptions.layout === false)
|
|
40
|
+
return undefined;
|
|
41
|
+
|
|
42
|
+
// options.id has been injected via the babel plugon
|
|
43
|
+
const chunkId = routeOptions["id"];
|
|
44
|
+
if (chunkId === undefined)
|
|
45
|
+
throw new Error(`ID has not injected for the following page route: ${routePath}`);
|
|
34
46
|
|
|
35
|
-
//console.log(`[router][layouts] Get layout for "${routePath}".`);
|
|
36
|
-
|
|
37
47
|
let layout: Layout | undefined;
|
|
38
|
-
|
|
48
|
+
|
|
49
|
+
// Layout via name
|
|
50
|
+
if (routeOptions.layout !== undefined) {
|
|
51
|
+
|
|
52
|
+
const LayoutComponent = layouts[routeOptions.layout];
|
|
53
|
+
if (LayoutComponent === undefined)
|
|
54
|
+
throw new Error(`No layout found with ID: ${routeOptions.layout}. registered layouts: ${Object.keys(layouts)}`);
|
|
55
|
+
|
|
56
|
+
layout = {
|
|
57
|
+
path: routeOptions.layout,
|
|
58
|
+
Component: layouts[routeOptions.layout]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
} else {
|
|
62
|
+
|
|
63
|
+
// Automatic layout via the nearest _layout folder
|
|
39
64
|
for (const layoutPath in layouts)
|
|
40
|
-
if (
|
|
65
|
+
if (
|
|
66
|
+
// The layout is nammed index when it's at the root (@/client/pages/_layout)
|
|
67
|
+
layoutPath === 'index'
|
|
68
|
+
// Exact match
|
|
69
|
+
|| chunkId === layoutPath
|
|
70
|
+
// Parent
|
|
71
|
+
|| chunkId.startsWith( layoutPath + '_' )
|
|
72
|
+
)
|
|
41
73
|
layout = {
|
|
42
74
|
path: layoutPath,
|
|
43
75
|
Component: layouts[layoutPath]
|
|
44
76
|
};
|
|
45
77
|
}
|
|
46
|
-
|
|
78
|
+
|
|
47
79
|
//console.log(`[router][layouts] Get layout for "${routePath}". Found:`, layout);
|
|
48
80
|
|
|
49
81
|
return layout;
|
|
@@ -29,16 +29,10 @@ export const getRegisterPageArgs = (...args: TRegisterPageArgs) => {
|
|
|
29
29
|
else
|
|
30
30
|
([path, options, controller, renderer] = args)
|
|
31
31
|
|
|
32
|
-
//
|
|
33
|
-
const
|
|
34
|
-
if (chunkId === undefined)
|
|
35
|
-
throw new Error(`ID has not injected for the following page route: ${path}`);
|
|
32
|
+
// Automatic layout form the nearest _layout folder
|
|
33
|
+
const layout = getLayout(path, options);
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
if (options.layout !== false)
|
|
39
|
-
options.layout = getLayout(chunkId);
|
|
40
|
-
|
|
41
|
-
return { path, options, controller, renderer }
|
|
35
|
+
return { path, options, controller, renderer, layout }
|
|
42
36
|
|
|
43
37
|
}
|
|
44
38
|
|
|
@@ -10,7 +10,9 @@ import type { FileToUpload } from '@client/components/inputv3/file';
|
|
|
10
10
|
- TYPES
|
|
11
11
|
----------------------------------*/
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// The fetcher can be undefined if we put a condition on it
|
|
14
|
+
// By example if we want to fetch an api endpoint only if the url contains a certain url parameter
|
|
15
|
+
export type TFetcherList = { [id: string]: TFetcher | undefined }
|
|
14
16
|
|
|
15
17
|
export type TPostData = {[key: string]: PrimitiveValue}
|
|
16
18
|
|
|
@@ -14,6 +14,10 @@ import { default as Validator, EXCLUDE_VALUE } from './validator';
|
|
|
14
14
|
|
|
15
15
|
export type TSchemaFields = { [fieldName: string]: Schema<{}> | Validator<any> }
|
|
16
16
|
|
|
17
|
+
type TSchemaOptions = {
|
|
18
|
+
opt?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
type TOptsValider = {
|
|
18
22
|
debug?: boolean,
|
|
19
23
|
throwError?: boolean,
|
|
@@ -46,7 +50,8 @@ const LogPrefix = '[schema][validator]';
|
|
|
46
50
|
export default class Schema<TFields extends TSchemaFields> {
|
|
47
51
|
|
|
48
52
|
public constructor(
|
|
49
|
-
public fields: TFields
|
|
53
|
+
public fields: TFields,
|
|
54
|
+
public options: TSchemaOptions = {}
|
|
50
55
|
) {
|
|
51
56
|
|
|
52
57
|
}
|
|
@@ -88,7 +93,7 @@ export default class Schema<TFields extends TSchemaFields> {
|
|
|
88
93
|
opts.debug && console.warn(LogPrefix, '[' + champ + ']', 'Exclusion (pas présent dans le schéma)');
|
|
89
94
|
continue;
|
|
90
95
|
}
|
|
91
|
-
|
|
96
|
+
|
|
92
97
|
const cheminA = [...chemin, champ]
|
|
93
98
|
const cheminAstr = cheminA.join('.')
|
|
94
99
|
|
|
@@ -173,7 +178,7 @@ export default class Schema<TFields extends TSchemaFields> {
|
|
|
173
178
|
if (nbErreurs !== 0 && opts.throwError === true) {
|
|
174
179
|
throw new InputErrorSchema(erreurs);
|
|
175
180
|
}
|
|
176
|
-
|
|
181
|
+
|
|
177
182
|
opts.debug && console.log(LogPrefix, '', dataToValidate, '=>', output);
|
|
178
183
|
|
|
179
184
|
return {
|
|
@@ -63,8 +63,10 @@ export default class SchemaValidator {
|
|
|
63
63
|
return val;
|
|
64
64
|
}, opts)
|
|
65
65
|
|
|
66
|
-
public array = (subtype?: Validator<any
|
|
67
|
-
choice?: any[]
|
|
66
|
+
public array = (subtype?: Validator<any>, { choice, min, max, ...opts }: TValidator<any[]> & {
|
|
67
|
+
choice?: any[],
|
|
68
|
+
min?: number,
|
|
69
|
+
max?: number
|
|
68
70
|
} = {}) => {
|
|
69
71
|
|
|
70
72
|
if (subtype !== undefined)
|
|
@@ -72,12 +74,17 @@ export default class SchemaValidator {
|
|
|
72
74
|
|
|
73
75
|
return new Validator<any[]>('array', (items, input, output, corriger) => {
|
|
74
76
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
+
// Type
|
|
77
78
|
if (!Array.isArray(items))
|
|
78
|
-
throw new InputError("This value must be
|
|
79
|
+
throw new InputError("This value must be a list.");
|
|
80
|
+
|
|
81
|
+
// Items number
|
|
82
|
+
if ((min !== undefined && items.length < min))
|
|
83
|
+
throw new InputError(`Please select at least ${min} items.`);
|
|
84
|
+
if ((max !== undefined && items.length > max))
|
|
85
|
+
throw new InputError(`Please select maximum ${max} items.`);
|
|
79
86
|
|
|
80
|
-
// Verif
|
|
87
|
+
// Verif each item
|
|
81
88
|
if (subtype !== undefined) {
|
|
82
89
|
if (false/*subtype instanceof Schema*/) {
|
|
83
90
|
|
|
@@ -85,9 +92,9 @@ export default class SchemaValidator {
|
|
|
85
92
|
|
|
86
93
|
} else {
|
|
87
94
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
95
|
+
items = items.map( item =>
|
|
96
|
+
subtype.validate( item, items, items, corriger )
|
|
97
|
+
)
|
|
91
98
|
}
|
|
92
99
|
}
|
|
93
100
|
|
|
@@ -101,7 +108,7 @@ export default class SchemaValidator {
|
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
public choice = (values: any[], opts: TValidator<any> & {} = {}) =>
|
|
104
|
-
new Validator<any>('
|
|
111
|
+
new Validator<any>('choice', (val, input, output) => {
|
|
105
112
|
|
|
106
113
|
if (!values.includes(val))
|
|
107
114
|
throw new InputError("Invalid value. Must be: " + values.join(', '));
|
|
@@ -116,6 +123,10 @@ export default class SchemaValidator {
|
|
|
116
123
|
public string = ({ min, max, ...opts }: TValidator<string> & { min?: number, max?: number } = {}) =>
|
|
117
124
|
new Validator<string>('string', (val, input, output, corriger?: boolean) => {
|
|
118
125
|
|
|
126
|
+
// Choice value from Select component
|
|
127
|
+
if (typeof val === 'object' && val.value)
|
|
128
|
+
val = val.value;
|
|
129
|
+
|
|
119
130
|
if (val === '')
|
|
120
131
|
return undefined;
|
|
121
132
|
else if (typeof val === 'number')
|
package/src/server/app/index.ts
CHANGED
|
@@ -87,7 +87,7 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
|
|
|
87
87
|
----------------------------------*/
|
|
88
88
|
|
|
89
89
|
public env: TEnvConfig;
|
|
90
|
-
public
|
|
90
|
+
public identity: Config.Identity;
|
|
91
91
|
|
|
92
92
|
public constructor() {
|
|
93
93
|
|
|
@@ -103,6 +103,9 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
|
|
|
103
103
|
// Load config files
|
|
104
104
|
const configParser = new ConfigParser( this.path.root );
|
|
105
105
|
this.env = configParser.env();
|
|
106
|
+
this.identity = configParser.identity();
|
|
107
|
+
|
|
108
|
+
console.log(`[boot] Environment:`, this.env);
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
/*----------------------------------
|
|
@@ -170,7 +170,7 @@ export default class BugReporter {
|
|
|
170
170
|
channelType,
|
|
171
171
|
channelId,
|
|
172
172
|
// User
|
|
173
|
-
user: request?.user?.
|
|
173
|
+
user: request?.user?.email,
|
|
174
174
|
ip: request?.ip,
|
|
175
175
|
// Error
|
|
176
176
|
error,
|
|
@@ -180,25 +180,8 @@ export default class BugReporter {
|
|
|
180
180
|
|
|
181
181
|
await this.sendToTransporters(bugReport);
|
|
182
182
|
|
|
183
|
-
// TODO: Move on App side
|
|
184
|
-
/*if (app.isLoaded('sql'))
|
|
185
|
-
// Memorize
|
|
186
|
-
$.sql.insert('BugServer', {
|
|
187
|
-
// Context
|
|
188
|
-
hash: hash,
|
|
189
|
-
date: now,
|
|
190
|
-
channelType,
|
|
191
|
-
channelId,
|
|
192
|
-
// User
|
|
193
|
-
user: request?.user?.name,
|
|
194
|
-
ip: request?.ip,
|
|
195
|
-
// Error
|
|
196
|
-
stacktrace: error.stack || error.message,
|
|
197
|
-
logs: logsHtml
|
|
198
|
-
});*/
|
|
199
|
-
|
|
200
183
|
// Update error message
|
|
201
|
-
error.message = "
|
|
184
|
+
error.message = "We encountered an internal error, and our team has just been notified. Sorry for the inconvenience.";
|
|
202
185
|
}
|
|
203
186
|
|
|
204
187
|
public async application( report: AppBugInfos ) {
|
|
@@ -28,18 +28,10 @@ const LogPrefix = '[database][connection]';
|
|
|
28
28
|
|
|
29
29
|
export type DatabaseServiceConfig = {
|
|
30
30
|
list: string[],
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
password: string,
|
|
36
|
-
},
|
|
37
|
-
prod: {
|
|
38
|
-
host: string,
|
|
39
|
-
port: number,
|
|
40
|
-
login: string,
|
|
41
|
-
password: string,
|
|
42
|
-
}
|
|
31
|
+
host: string,
|
|
32
|
+
port: number,
|
|
33
|
+
login: string,
|
|
34
|
+
password: string,
|
|
43
35
|
}
|
|
44
36
|
|
|
45
37
|
export type THooks = {
|
|
@@ -113,15 +105,13 @@ export default class DatabaseConnection extends Service<DatabaseServiceConfig, T
|
|
|
113
105
|
|
|
114
106
|
console.info(LogPrefix, `Connecting to databases ...`);
|
|
115
107
|
|
|
116
|
-
const creds = this.config[ this.app.env.profile ];
|
|
117
|
-
|
|
118
108
|
return await mysql.createPool({
|
|
119
109
|
|
|
120
110
|
// Identification
|
|
121
|
-
host:
|
|
122
|
-
port:
|
|
123
|
-
user:
|
|
124
|
-
password:
|
|
111
|
+
host: this.config.host,
|
|
112
|
+
port: this.config.port,
|
|
113
|
+
user: this.config.login,
|
|
114
|
+
password: this.config.password,
|
|
125
115
|
database: this.config.list[0],
|
|
126
116
|
|
|
127
117
|
// Pool
|
|
@@ -279,7 +279,7 @@ export default class SQL extends Service<Config, Hooks, Application> {
|
|
|
279
279
|
public update<TData extends TObjetDonnees>(
|
|
280
280
|
tableName: string,
|
|
281
281
|
data: TData[],
|
|
282
|
-
where
|
|
282
|
+
where?: (keyof TData)[],
|
|
283
283
|
opts?: TUpdateQueryOptions<TData>
|
|
284
284
|
);
|
|
285
285
|
|
|
@@ -287,30 +287,42 @@ export default class SQL extends Service<Config, Hooks, Application> {
|
|
|
287
287
|
public update<TData extends TObjetDonnees>(
|
|
288
288
|
tableName: string,
|
|
289
289
|
data: TData,
|
|
290
|
-
where
|
|
290
|
+
where?: (keyof TData)[] | TObjetDonnees,
|
|
291
291
|
opts?: TUpdateQueryOptions<TData>
|
|
292
292
|
);
|
|
293
293
|
|
|
294
294
|
public update<TData extends TObjetDonnees>(...args: [
|
|
295
295
|
tableName: string,
|
|
296
296
|
data: TData[],
|
|
297
|
-
where
|
|
297
|
+
where?: (keyof TData)[],
|
|
298
298
|
opts?: TUpdateQueryOptions<TData>
|
|
299
299
|
] | [
|
|
300
300
|
tableName: string,
|
|
301
301
|
data: TData,
|
|
302
|
-
where
|
|
302
|
+
where?: (keyof TData)[] | TObjetDonnees,
|
|
303
303
|
opts?: TUpdateQueryOptions<TData>
|
|
304
|
-
]) {
|
|
304
|
+
]): Promise<ResultSetHeader> {
|
|
305
305
|
|
|
306
306
|
let [tableName, data, where, opts] = args;
|
|
307
307
|
|
|
308
308
|
// Multiple updates in one
|
|
309
309
|
if (Array.isArray( data ))
|
|
310
310
|
return this.database.query(
|
|
311
|
-
data.map(record =>
|
|
311
|
+
data.map( record =>
|
|
312
|
+
this.update(tableName, record, where, { ...opts, returnQuery: true })
|
|
313
|
+
).join(';\n')
|
|
312
314
|
)
|
|
313
315
|
|
|
316
|
+
// Automatic where based on pks
|
|
317
|
+
if (where === undefined) {
|
|
318
|
+
|
|
319
|
+
const tableMetas = this.database.getTable(tableName);
|
|
320
|
+
if (tableMetas.pk.length === 0)
|
|
321
|
+
throw new Error(`Tried to build the where condition based on the pks list, but no pk is attached to this table ${tableMetas.chemin}`);
|
|
322
|
+
|
|
323
|
+
where = tableMetas.pk;
|
|
324
|
+
}
|
|
325
|
+
|
|
314
326
|
// No condition specified = use the pks
|
|
315
327
|
if (Array.isArray(where)) {
|
|
316
328
|
const whereColNames = where;
|
|
@@ -380,14 +392,9 @@ export default class SQL extends Service<Config, Hooks, Application> {
|
|
|
380
392
|
let querySuffix: string = '';
|
|
381
393
|
|
|
382
394
|
// Upsert
|
|
383
|
-
if (opts.upsert !== undefined)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
opts.try = true;
|
|
387
|
-
else
|
|
388
|
-
querySuffix += ' ' + upsertStatement;
|
|
389
|
-
}
|
|
390
|
-
|
|
395
|
+
if (opts.upsert !== undefined)
|
|
396
|
+
querySuffix += ' ' + this.buildUpsertStatement<TData>(table, opts as With<TInsertQueryOptions<TData>, 'upsert'>);
|
|
397
|
+
|
|
391
398
|
// Create basic insert query
|
|
392
399
|
const query = this.buildInsertStatement(table, data, opts) + querySuffix;
|
|
393
400
|
|
|
@@ -436,15 +443,14 @@ export default class SQL extends Service<Config, Hooks, Application> {
|
|
|
436
443
|
private buildUpsertStatement<TData extends TObjetDonnees>(
|
|
437
444
|
table: TMetasTable,
|
|
438
445
|
opts: With<TInsertQueryOptions<TData>, 'upsert'>
|
|
439
|
-
): string
|
|
446
|
+
): string {
|
|
440
447
|
|
|
441
448
|
const valuesToUpdate = this.getValuesToUpdate(table, opts.upsert);
|
|
442
449
|
|
|
443
450
|
// All columns are ps
|
|
444
451
|
const valuesToUpdatesEntries = Object.entries(valuesToUpdate);
|
|
445
452
|
if (valuesToUpdatesEntries.length === 0)
|
|
446
|
-
|
|
447
|
-
return null;
|
|
453
|
+
throw new Error(`You should provide at least one column to update in case of the record already exists.`);
|
|
448
454
|
|
|
449
455
|
return 'ON DUPLICATE KEY UPDATE ' + valuesToUpdatesEntries.map(([ colName, value ]) =>
|
|
450
456
|
'`' + colName + '` = ' + value
|
|
@@ -465,7 +471,9 @@ export default class SQL extends Service<Config, Hooks, Application> {
|
|
|
465
471
|
if (colsToUpdate === '*') {
|
|
466
472
|
|
|
467
473
|
console.log(LogPrefix, `Automatic upsert into ${table.chemin} using ${table.pk.join(', ')} as pk`);
|
|
468
|
-
valuesNamesToUpdate = table.columnNamesButPk;
|
|
474
|
+
valuesNamesToUpdate = Object.keys(table.colonnes);// table.columnNamesButPk;
|
|
475
|
+
// We don't take columnNamesButPk, because if all the columns are pks, we don't have yny value for the ON DUPLICATE KEY
|
|
476
|
+
// Meaning
|
|
469
477
|
|
|
470
478
|
} else if (Array.isArray( colsToUpdate )) {
|
|
471
479
|
|
|
@@ -479,7 +487,7 @@ export default class SQL extends Service<Config, Hooks, Application> {
|
|
|
479
487
|
valuesToUpdate[ colKey ] = this.esc(customValuesToUpdate[ colKey ]);
|
|
480
488
|
|
|
481
489
|
if (updateAll)
|
|
482
|
-
valuesNamesToUpdate = table.columnNamesButPk;
|
|
490
|
+
valuesNamesToUpdate = Object.keys(table.colonnes);//table.columnNamesButPk;
|
|
483
491
|
|
|
484
492
|
}
|
|
485
493
|
|