5htp-core 0.0.8 → 0.0.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.
- package/package.json +1 -1
- package/src/client/assets/css/components/card.less +0 -7
- package/src/client/assets/css/layouts.less +11 -10
- package/src/client/assets/css/text/icons.less +0 -0
- package/src/client/assets/css/text/text.less +11 -6
- package/src/client/components/Select/index.tsx +58 -0
- package/src/client/components/data/progressbar/circular/index.tsx +1 -1
- package/src/client/components/data/progressbar/index.less +4 -2
- package/src/client/components/dropdown/index.tsx +12 -21
- package/src/client/components/input/BaseV2/index.tsx +4 -2
- package/src/client/context/index.ts +45 -40
- package/src/client/router/component.tsx +1 -0
- package/src/client/router/request/index.ts +0 -0
- package/src/common/data/number/percentage.ts +4 -2
- package/src/common/router/index.ts +1 -1
- package/src/server/app/index.ts +70 -17
- package/src/server/data/Cache.ts +10 -4
- package/src/server/patch.ts +0 -11
- package/src/server/services/console/bugReporter.ts +31 -27
- package/src/server/services/console/html.ts +0 -0
- package/src/server/services/console/index.ts +1 -1
- package/src/server/services/socket/index.ts +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "5htp-core",
|
|
3
3
|
"description": "5-HTP, scientifically called 5-Hydroxytryptophan, is the precursor of happiness neurotransmitter.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.9",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/5htp-core.git",
|
|
7
7
|
"license": "MIT",
|
|
@@ -217,6 +217,7 @@
|
|
|
217
217
|
Déclarer une grid: grid x-3
|
|
218
218
|
Placer un element: x1-3
|
|
219
219
|
*/
|
|
220
|
+
@gridUnit: 6.66rem;
|
|
220
221
|
.zones(@nb, @i: 1) when (@i <= @nb) {
|
|
221
222
|
/*----------------------------------
|
|
222
223
|
- ZONES DE LA GRILLE
|
|
@@ -287,23 +288,23 @@
|
|
|
287
288
|
// Automatique
|
|
288
289
|
.taille-auto(@min, @max) when (@min <= @max) {
|
|
289
290
|
&.xa@{min}-@{max} {
|
|
290
|
-
grid-template-columns: repeat(auto-fit, minmax(@min *
|
|
291
|
+
grid-template-columns: repeat(auto-fit, minmax(@min * @gridUnit, @max * @gridUnit))
|
|
291
292
|
}
|
|
292
293
|
&.ya@{min}-@{max} {
|
|
293
|
-
grid-template-rows: repeat(auto-fit, minmax(@min *
|
|
294
|
+
grid-template-rows: repeat(auto-fit, minmax(@min * @gridUnit, @max * @gridUnit))
|
|
294
295
|
}
|
|
295
296
|
.taille-auto(@min + 1, @max);
|
|
296
297
|
}
|
|
297
298
|
&.xa@{i} {
|
|
298
|
-
grid-template-columns: repeat(auto-fit, minmax(@i *
|
|
299
|
+
grid-template-columns: repeat(auto-fit, minmax(@i * @gridUnit, 1fr));
|
|
299
300
|
&5 {
|
|
300
|
-
grid-template-columns: repeat(auto-fit, minmax(@i *
|
|
301
|
+
grid-template-columns: repeat(auto-fit, minmax(@i * @gridUnit + 4.15rem, 1fr));
|
|
301
302
|
}
|
|
302
303
|
}
|
|
303
304
|
&.ya@{i} {
|
|
304
|
-
grid-template-rows: repeat(auto-fit, minmax(@i *
|
|
305
|
+
grid-template-rows: repeat(auto-fit, minmax(@i * @gridUnit, 1fr));
|
|
305
306
|
&5 {
|
|
306
|
-
grid-template-rows: repeat(auto-fit, minmax(@i *
|
|
307
|
+
grid-template-rows: repeat(auto-fit, minmax(@i * @gridUnit + 4.15rem, 1fr));
|
|
307
308
|
}
|
|
308
309
|
}
|
|
309
310
|
.taille-auto(1, @i);
|
|
@@ -347,10 +348,10 @@
|
|
|
347
348
|
break-inside: avoid;
|
|
348
349
|
}
|
|
349
350
|
|
|
350
|
-
&.xa1 { column-width:
|
|
351
|
-
&.xa2 { column-width:
|
|
351
|
+
&.xa1 { column-width: @gridUnit; }
|
|
352
|
+
&.xa2 { column-width: @gridUnit * 2; }
|
|
352
353
|
&,
|
|
353
|
-
&.xa3 { column-width:
|
|
354
|
-
&.xa4 { column-width:
|
|
354
|
+
&.xa3 { column-width: @gridUnit * 3; }
|
|
355
|
+
&.xa4 { column-width: @gridUnit * 4; }
|
|
355
356
|
|
|
356
357
|
}
|
|
File without changes
|
|
@@ -5,6 +5,7 @@ a {
|
|
|
5
5
|
color: inherit;
|
|
6
6
|
cursor: pointer;
|
|
7
7
|
text-decoration: underline;
|
|
8
|
+
text-decoration: none;
|
|
8
9
|
|
|
9
10
|
&:not([disabled]):hover,
|
|
10
11
|
&.active {
|
|
@@ -12,12 +13,16 @@ a {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
&.super {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
z-index:
|
|
16
|
+
text-decoration: none;
|
|
17
|
+
&::after {
|
|
18
|
+
content: ' ';
|
|
19
|
+
position: absolute;
|
|
20
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
21
|
+
z-index: 5;
|
|
22
|
+
|
|
23
|
+
~ a, ~ * a, ~ button, ~ * button {
|
|
24
|
+
z-index: 6;
|
|
25
|
+
}
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
// Core
|
|
9
|
+
import Button from '@client/components/button';
|
|
10
|
+
import Dropdown, { TDialogControls, Props as DropdownProps } from '@client/components/dropdown';
|
|
11
|
+
|
|
12
|
+
/*----------------------------------
|
|
13
|
+
- CONST
|
|
14
|
+
----------------------------------*/
|
|
15
|
+
|
|
16
|
+
/*----------------------------------
|
|
17
|
+
- TYPES
|
|
18
|
+
----------------------------------*/
|
|
19
|
+
|
|
20
|
+
type Choices = string[]
|
|
21
|
+
|
|
22
|
+
type SearchResultsFunction = (keywords: string) => Choices
|
|
23
|
+
|
|
24
|
+
export type Props = DropdownProps & {
|
|
25
|
+
title: string,
|
|
26
|
+
choices: Choices,
|
|
27
|
+
value?: string,
|
|
28
|
+
onChange: (value: string) => void,
|
|
29
|
+
search?: true | SearchResultsFunction
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/*----------------------------------
|
|
33
|
+
- COMONENT
|
|
34
|
+
----------------------------------*/
|
|
35
|
+
|
|
36
|
+
export default ({ title, choices, value, onChange, ...dropDownProps }: Props) => {
|
|
37
|
+
const refModal = React.useRef<TDialogControls>(null);
|
|
38
|
+
return (
|
|
39
|
+
<Dropdown {...dropDownProps} content={(
|
|
40
|
+
<ul class="card col menu">
|
|
41
|
+
{choices.map( jt => (
|
|
42
|
+
<li>
|
|
43
|
+
<Button active={jt === value} onClick={() => {
|
|
44
|
+
onChange(jt);
|
|
45
|
+
refModal.current?.close();
|
|
46
|
+
}}>
|
|
47
|
+
{jt}
|
|
48
|
+
</Button>
|
|
49
|
+
</li>
|
|
50
|
+
))}
|
|
51
|
+
</ul>
|
|
52
|
+
)} iconR="chevron-down" refModal={refModal}>
|
|
53
|
+
|
|
54
|
+
{value || title}
|
|
55
|
+
|
|
56
|
+
</Dropdown>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -99,8 +99,8 @@ div.progressbar_old {
|
|
|
99
99
|
.progressbar-svg {
|
|
100
100
|
|
|
101
101
|
position: relative;
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
width: @sizeComponent;
|
|
103
|
+
height: @sizeComponent;
|
|
104
104
|
|
|
105
105
|
> svg {
|
|
106
106
|
|
|
@@ -142,6 +142,8 @@ div.progressbar_old {
|
|
|
142
142
|
left: 0;
|
|
143
143
|
right: 0;
|
|
144
144
|
bottom: 0;
|
|
145
|
+
// Text should fit inside the progressbar
|
|
146
|
+
font-size: 0.8em;
|
|
145
147
|
|
|
146
148
|
> i,
|
|
147
149
|
> img {
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
// Npm
|
|
6
6
|
import React from 'react';
|
|
7
|
-
import { ComponentChild } from 'preact';
|
|
7
|
+
import { ComponentChild, RefObject } from 'preact';
|
|
8
8
|
|
|
9
9
|
// Core
|
|
10
10
|
import Button, { Props as ButtonProps } from '../button';
|
|
11
|
+
import { TDialogControls } from '../Dialog/Manager';
|
|
12
|
+
export type { TDialogControls } from '../Dialog/Manager';
|
|
11
13
|
|
|
12
14
|
// Libs
|
|
13
15
|
import useContexte from '@client/context';
|
|
@@ -17,23 +19,8 @@ import useContexte from '@client/context';
|
|
|
17
19
|
----------------------------------*/
|
|
18
20
|
|
|
19
21
|
export type Props = ButtonProps & {
|
|
20
|
-
content: ComponentChild
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export type TAction<TDonnee> = {
|
|
24
|
-
icone?: TIcons,
|
|
25
|
-
label: ComponentChild,
|
|
26
|
-
multi?: boolean,
|
|
27
|
-
|
|
28
|
-
onClick?: (donnees: TDonnee, index: number) => void,
|
|
29
|
-
lien?: (donnees: TDonnee, index: number) => string,
|
|
30
|
-
bouton?: (donnees: TDonnee, index: number) => ButtonProps
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export type TActionsPopover = {
|
|
34
|
-
show: () => void,
|
|
35
|
-
hide: () => void,
|
|
36
|
-
toggle: () => void
|
|
22
|
+
content: ComponentChild,
|
|
23
|
+
refModal?: RefObject<TDialogControls>
|
|
37
24
|
}
|
|
38
25
|
|
|
39
26
|
/*----------------------------------
|
|
@@ -45,15 +32,19 @@ export default (props: Props) => {
|
|
|
45
32
|
|
|
46
33
|
let {
|
|
47
34
|
content,
|
|
48
|
-
|
|
35
|
+
refModal,
|
|
49
36
|
...buttonProps
|
|
50
37
|
} = props;
|
|
51
38
|
|
|
52
39
|
const refButton = React.useRef<HTMLElement>(null);
|
|
53
40
|
|
|
54
|
-
const open = () =>
|
|
41
|
+
const open = () => {
|
|
42
|
+
const modalInstance = modal.show(() => content);
|
|
43
|
+
if (refModal)
|
|
44
|
+
refModal.current = modalInstance;
|
|
45
|
+
}
|
|
55
46
|
|
|
56
47
|
return (
|
|
57
|
-
<Button {...buttonProps} onClick={open} refElem={refButton} />
|
|
48
|
+
<Button {...buttonProps} onClick={(open)} refElem={refButton} />
|
|
58
49
|
)
|
|
59
50
|
}
|
|
@@ -57,8 +57,10 @@ export function useInput<TValue>(
|
|
|
57
57
|
// External value change
|
|
58
58
|
React.useEffect(() => {
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
if (externalValue !== undefined && externalValue !== state.value) {
|
|
61
|
+
console.log("External value change", externalValue);
|
|
62
|
+
setState({ value: externalValue, valueSource: 'external' })
|
|
63
|
+
}
|
|
62
64
|
|
|
63
65
|
}, [externalValue]);
|
|
64
66
|
|
|
@@ -112,42 +112,6 @@ export class ClientContext {
|
|
|
112
112
|
this.request = request;
|
|
113
113
|
this.user = this.request.user || { ...GuestUser };
|
|
114
114
|
|
|
115
|
-
this.api.reload = (ids?: string | string[], params?: TObjetDonnees) => {
|
|
116
|
-
|
|
117
|
-
if (this.page === undefined)
|
|
118
|
-
throw new Error("context.page is missing");
|
|
119
|
-
|
|
120
|
-
if (ids === undefined)
|
|
121
|
-
ids = Object.keys(this.page.fetchers);
|
|
122
|
-
else if (typeof ids === 'string')
|
|
123
|
-
ids = [ids];
|
|
124
|
-
|
|
125
|
-
console.log("[api] Reload data", ids, params, this.page.fetchers);
|
|
126
|
-
|
|
127
|
-
for (const id of ids) {
|
|
128
|
-
|
|
129
|
-
const fetcher = this.page.fetchers[id];
|
|
130
|
-
if (fetcher === undefined)
|
|
131
|
-
return console.error(`Unable to reload ${id}: Request not found in fetchers list.`);
|
|
132
|
-
|
|
133
|
-
if (params !== undefined)
|
|
134
|
-
fetcher.data = { ...(fetcher.data || {}), ...params };
|
|
135
|
-
|
|
136
|
-
console.log("[api][reload]", id, fetcher.method, fetcher.path, fetcher.data);
|
|
137
|
-
const indicator = this.toast.loading("Loading ...");
|
|
138
|
-
|
|
139
|
-
this.request.fetchAsync(fetcher.method, fetcher.path, fetcher.data).then((data) => {
|
|
140
|
-
|
|
141
|
-
this.api.set({ [id]: data });
|
|
142
|
-
|
|
143
|
-
}).finally(() => {
|
|
144
|
-
|
|
145
|
-
indicator.close(true);
|
|
146
|
-
|
|
147
|
-
})
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
115
|
}
|
|
152
116
|
|
|
153
117
|
// Is overwrote by the native app
|
|
@@ -194,10 +158,51 @@ export class ClientContext {
|
|
|
194
158
|
|
|
195
159
|
// fetch doit appartenir à response, et non clientcontxt
|
|
196
160
|
// car la méthode varie selon client (http) ou serveur (router.resolve)
|
|
197
|
-
public api =
|
|
198
|
-
|
|
199
|
-
set: (
|
|
200
|
-
|
|
161
|
+
public api = {
|
|
162
|
+
...this.request.api,
|
|
163
|
+
set: (newData: TObjetDonnees) => {
|
|
164
|
+
|
|
165
|
+
console.log("[api] Update page data", newData);
|
|
166
|
+
if (this.page)
|
|
167
|
+
this.page.setAllData(curData => ({ ...curData, ...newData }));
|
|
168
|
+
|
|
169
|
+
},
|
|
170
|
+
reload: (ids?: string | string[], params?: TObjetDonnees) => {
|
|
171
|
+
|
|
172
|
+
if (this.page === undefined)
|
|
173
|
+
throw new Error("context.page is missing");
|
|
174
|
+
|
|
175
|
+
if (ids === undefined)
|
|
176
|
+
ids = Object.keys(this.page.fetchers);
|
|
177
|
+
else if (typeof ids === 'string')
|
|
178
|
+
ids = [ids];
|
|
179
|
+
|
|
180
|
+
console.log("[api] Reload data", ids, params, this.page.fetchers);
|
|
181
|
+
|
|
182
|
+
for (const id of ids) {
|
|
183
|
+
|
|
184
|
+
const fetcher = this.page.fetchers[id];
|
|
185
|
+
if (fetcher === undefined)
|
|
186
|
+
return console.error(`Unable to reload ${id}: Request not found in fetchers list.`);
|
|
187
|
+
|
|
188
|
+
if (params !== undefined)
|
|
189
|
+
fetcher.data = { ...(fetcher.data || {}), ...params };
|
|
190
|
+
|
|
191
|
+
console.log("[api][reload]", id, fetcher.method, fetcher.path, fetcher.data);
|
|
192
|
+
const indicator = this.toast.loading("Loading ...");
|
|
193
|
+
|
|
194
|
+
this.request.fetchAsync(fetcher.method, fetcher.path, fetcher.data).then((data) => {
|
|
195
|
+
|
|
196
|
+
this.api.set({ [id]: data });
|
|
197
|
+
|
|
198
|
+
}).finally(() => {
|
|
199
|
+
|
|
200
|
+
indicator.close(true);
|
|
201
|
+
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
201
206
|
|
|
202
207
|
public handleError(e: Error) {
|
|
203
208
|
switch (e.http) {
|
|
@@ -50,6 +50,7 @@ const Page = ({ page, isCurrent }: { page: PageResponse, isCurrent?: boolean })
|
|
|
50
50
|
id={page.id === undefined ? undefined : 'page_' + page.id}
|
|
51
51
|
>
|
|
52
52
|
|
|
53
|
+
{/* Make request parameters and api data accessible from the page component */}
|
|
53
54
|
{page.component ? (
|
|
54
55
|
|
|
55
56
|
<page.component {...gui.request.data} {...data} />
|
|
File without changes
|
|
@@ -11,11 +11,13 @@ export type TVariation = {
|
|
|
11
11
|
color: string,
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export const variationStr = (value: number, reference: number,
|
|
14
|
+
export const variationStr = (value: number, reference: number, options: {
|
|
15
|
+
lowerIsBetter?: boolean
|
|
16
|
+
} = {}): TVariation => {
|
|
15
17
|
const pc = variation(value, reference);
|
|
16
18
|
const pcStr = pc.toFixed(2);
|
|
17
19
|
return {
|
|
18
20
|
txt: ((pc > 0) ? '+' + pcStr : pcStr) + '%',
|
|
19
|
-
color: (lowerIsBetter ? pc > 0 : pc < 0) ? 'ea3943' : '16c784'
|
|
21
|
+
color: (options.lowerIsBetter ? pc > 0 : pc < 0) ? 'ea3943' : '16c784'
|
|
20
22
|
}
|
|
21
23
|
}
|
|
@@ -35,7 +35,7 @@ const getLayout = (routePath: string | undefined): Layout | undefined => {
|
|
|
35
35
|
if (routePath === layoutPath || routePath.startsWith( layoutPath + '/' ))
|
|
36
36
|
layout = { path: layoutPath, Component: layouts[layoutPath] };
|
|
37
37
|
}
|
|
38
|
-
layout && console.log(
|
|
38
|
+
//layout && console.log(`${routePath}: Using Layout: ${layout.path}`);
|
|
39
39
|
return layout;
|
|
40
40
|
}
|
|
41
41
|
|
package/src/server/app/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ import ConfigParser, { TEnvConfig } from './config';
|
|
|
15
15
|
----------------------------------*/
|
|
16
16
|
|
|
17
17
|
type THookName = 'ready' | 'cleanup' | 'error'
|
|
18
|
-
type THook = () => void
|
|
18
|
+
type THook = () => Promise<void>;
|
|
19
19
|
|
|
20
20
|
type TServiceOptions = {
|
|
21
21
|
instanciate: boolean
|
|
@@ -91,6 +91,20 @@ export class App {
|
|
|
91
91
|
----------------------------------*/
|
|
92
92
|
|
|
93
93
|
public constructor() {
|
|
94
|
+
|
|
95
|
+
// Gestion crash
|
|
96
|
+
process.on('unhandledRejection', (error: any, promise: any) => {
|
|
97
|
+
|
|
98
|
+
console.error("Unhandled promise rejection:", error);
|
|
99
|
+
|
|
100
|
+
// Send email report
|
|
101
|
+
if (this.isLoaded('console'))
|
|
102
|
+
$.console.bugReport.server(error);
|
|
103
|
+
else
|
|
104
|
+
console.error(`Unable to send bug report: console service not loaded.`);
|
|
105
|
+
|
|
106
|
+
});
|
|
107
|
+
|
|
94
108
|
// Load config files
|
|
95
109
|
const configParser = new ConfigParser( this.path.root );
|
|
96
110
|
this.env = configParser.env();
|
|
@@ -106,21 +120,47 @@ export class App {
|
|
|
106
120
|
console.log("Configure services with", this.config);
|
|
107
121
|
}
|
|
108
122
|
|
|
109
|
-
|
|
123
|
+
// Register a service
|
|
124
|
+
public register<TServiceName extends keyof Core.Services>(
|
|
125
|
+
id: TServiceName,
|
|
126
|
+
Service: TServiceClass,
|
|
127
|
+
options: Partial<TServiceOptions> = {}
|
|
128
|
+
) {
|
|
110
129
|
|
|
111
130
|
// Pas d'export default new Service pour chaque fichier de service,
|
|
112
131
|
// dissuaded'importer ms service sn'importe où, ce qui créé des références circulaires
|
|
113
|
-
console.log(`
|
|
132
|
+
console.log(`[services] Registering service ${id} ...`);
|
|
114
133
|
const service = options.instanciate !== false ? new Service() : Service;
|
|
115
|
-
this.services[id] = service;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
service
|
|
134
|
+
this.services[id as string] = service;
|
|
135
|
+
|
|
136
|
+
if ('load' in service) {
|
|
137
|
+
|
|
138
|
+
console.log(`[services] Starting service ${id} ...`);
|
|
139
|
+
|
|
140
|
+
// Lorsque service.load est async, une propriété loading doit etre présente
|
|
141
|
+
// De façon à ce que les autres services puissent savoir quand ce service est prêt
|
|
142
|
+
if ('loading' in service) {
|
|
143
|
+
|
|
144
|
+
console.log(`[services] Waiting service ${id} to be fully loaded ...`);
|
|
145
|
+
service.loading = service.load().then(() => {
|
|
146
|
+
console.info(`[service] Service ${id} successfully started.`);
|
|
147
|
+
}).catch(e => {
|
|
148
|
+
// Bug report via email
|
|
149
|
+
console.error(`[service] Error while starting the ${id} service:`, e);
|
|
150
|
+
e.message = `Start ${id} service: ` + e.message;
|
|
151
|
+
$.console.bugReport.server(e);
|
|
152
|
+
});;
|
|
153
|
+
|
|
154
|
+
this.loading.push(service.loading);
|
|
155
|
+
|
|
156
|
+
} else
|
|
157
|
+
service.load();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Test if a service was registered
|
|
162
|
+
public isLoaded( id: keyof Core.Services ) {
|
|
163
|
+
return id in this.services;
|
|
124
164
|
}
|
|
125
165
|
|
|
126
166
|
public on( name: THookName, callback: THook ) {
|
|
@@ -128,23 +168,36 @@ export class App {
|
|
|
128
168
|
return this;
|
|
129
169
|
}
|
|
130
170
|
|
|
171
|
+
public runHook( hookName: THookName ) {
|
|
172
|
+
console.info(`[hook] Run all ${hookName} hook (${this.hooks.ready.length}).`);
|
|
173
|
+
return Promise.all(
|
|
174
|
+
this.hooks.ready.map(
|
|
175
|
+
cb => cb().catch(e => {
|
|
176
|
+
console.error(`[hook] Error while executing hook ${hookName}:`, e);
|
|
177
|
+
})
|
|
178
|
+
)
|
|
179
|
+
).then(() => {
|
|
180
|
+
console.info(`[hook] Hooks ${hookName} executed with success.`);
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
131
184
|
/*----------------------------------
|
|
132
185
|
- LAUNCH
|
|
133
186
|
----------------------------------*/
|
|
134
187
|
public async launch() {
|
|
135
188
|
|
|
136
|
-
console.info(`Waiting for services to be ready ...`);
|
|
189
|
+
console.info(`[boot] Waiting for all services to be ready ...`);
|
|
137
190
|
await Promise.all( this.loading );
|
|
138
191
|
|
|
139
|
-
console.info(`Launching application ...`);
|
|
140
|
-
await
|
|
192
|
+
console.info(`[boot] Launching application ...`);
|
|
193
|
+
await this.runHook('ready');
|
|
141
194
|
|
|
142
195
|
// NOTE: Useless ?
|
|
143
196
|
/*if (this.hmr)
|
|
144
197
|
this.activateHMR();*/
|
|
145
198
|
|
|
146
|
-
console.info(`Application is ready.`);
|
|
147
|
-
|
|
199
|
+
console.info(`[boot] Application is ready.`);
|
|
200
|
+
|
|
148
201
|
this.launched = true;
|
|
149
202
|
|
|
150
203
|
}
|
package/src/server/data/Cache.ts
CHANGED
|
@@ -14,6 +14,12 @@ import app from '@server/app';
|
|
|
14
14
|
|
|
15
15
|
// Libs
|
|
16
16
|
|
|
17
|
+
/*----------------------------------
|
|
18
|
+
- CONFIG
|
|
19
|
+
----------------------------------*/
|
|
20
|
+
|
|
21
|
+
const debug = false;
|
|
22
|
+
|
|
17
23
|
/*----------------------------------
|
|
18
24
|
- TYPES
|
|
19
25
|
----------------------------------*/
|
|
@@ -50,7 +56,7 @@ class Cache {
|
|
|
50
56
|
|
|
51
57
|
private cleanMem() {
|
|
52
58
|
|
|
53
|
-
console.log("[cache] Clean memory");
|
|
59
|
+
debug && console.log("[cache] Clean memory");
|
|
54
60
|
|
|
55
61
|
const now = Date.now();
|
|
56
62
|
for (const key in this.data) {
|
|
@@ -88,11 +94,11 @@ class Cache {
|
|
|
88
94
|
|
|
89
95
|
let retour: CacheEntry | undefined = this.data[cle];
|
|
90
96
|
|
|
91
|
-
console.log(`[cache] Get "${cle}".`);
|
|
97
|
+
debug && console.log(`[cache] Get "${cle}".`);
|
|
92
98
|
|
|
93
99
|
// Expired
|
|
94
100
|
if (retour?.expiration && retour.expiration < Date.now()){
|
|
95
|
-
console.log(`[cache] Key ${cle} expired.`);
|
|
101
|
+
debug && console.log(`[cache] Key ${cle} expired.`);
|
|
96
102
|
retour = undefined;
|
|
97
103
|
}
|
|
98
104
|
|
|
@@ -133,7 +139,7 @@ class Cache {
|
|
|
133
139
|
*/
|
|
134
140
|
public set( cle: string, val: TPrimitiveValue, expiration: TExpirationDelay = null ): void {
|
|
135
141
|
|
|
136
|
-
console.log("[cache] Updating cache " + cle);
|
|
142
|
+
debug && console.log("[cache] Updating cache " + cle);
|
|
137
143
|
this.data[ cle ] = {
|
|
138
144
|
value: val,
|
|
139
145
|
expiration: this.delayToTimestamp(expiration)
|
package/src/server/patch.ts
CHANGED
|
@@ -4,17 +4,6 @@ moduleAlias.addAliases({
|
|
|
4
4
|
'react-dom': "preact/compat",
|
|
5
5
|
})
|
|
6
6
|
|
|
7
|
-
/*----------------------------------
|
|
8
|
-
- DEBUG
|
|
9
|
-
----------------------------------*/
|
|
10
|
-
|
|
11
|
-
// Gestion crash
|
|
12
|
-
process.on('unhandledRejection', (error: any, promise: any) => {
|
|
13
|
-
|
|
14
|
-
console.error("Unhandled promise rejection:", error);
|
|
15
|
-
|
|
16
|
-
});
|
|
17
|
-
|
|
18
7
|
/*----------------------------------
|
|
19
8
|
- DATES & TIMZEONE
|
|
20
9
|
----------------------------------*/
|
|
@@ -70,7 +70,7 @@ export default class BugReporter {
|
|
|
70
70
|
public async server( error: Error, request?: ServerRequest ) {
|
|
71
71
|
|
|
72
72
|
// error should be printed in the console, so they're acccessible from logs
|
|
73
|
-
console.error(error);
|
|
73
|
+
console.error(`Sending bug report for the following error:`, error);
|
|
74
74
|
|
|
75
75
|
// Prevent duplicates
|
|
76
76
|
if (!this.shouldSendReport('server', request?.user?.name, undefined, error.message))
|
|
@@ -79,7 +79,6 @@ export default class BugReporter {
|
|
|
79
79
|
// Get context
|
|
80
80
|
const now = new Date();
|
|
81
81
|
const hash = uuid();
|
|
82
|
-
const erroTitle = "Server Bug: " + error.message;
|
|
83
82
|
const { channelType, channelId } = this.console.getChannel();
|
|
84
83
|
|
|
85
84
|
// On envoi l'email avant l'insertion dans bla bdd
|
|
@@ -90,31 +89,36 @@ export default class BugReporter {
|
|
|
90
89
|
);
|
|
91
90
|
|
|
92
91
|
// Send notification
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
92
|
+
if (app.isLoaded('email'))
|
|
93
|
+
$.email.send({
|
|
94
|
+
to: app.identity.author.email,
|
|
95
|
+
subject: "Server bug: " + error.message,
|
|
96
|
+
html: `
|
|
97
|
+
<a href="${app.env.url}/admin/activity/requests/${channelId}">
|
|
98
|
+
View Request details & console
|
|
99
|
+
</a>
|
|
100
|
+
<br/>
|
|
101
|
+
${logsHtml}
|
|
102
|
+
`
|
|
103
|
+
});
|
|
104
|
+
else
|
|
105
|
+
console.error("Unable to send bug report: email service not loaded.");
|
|
106
|
+
|
|
107
|
+
/*if (app.isLoaded('sql'))
|
|
108
|
+
// Memorize
|
|
109
|
+
$.sql.insert('BugServer', {
|
|
110
|
+
// Context
|
|
111
|
+
hash: hash,
|
|
112
|
+
date: now,
|
|
113
|
+
channelType,
|
|
114
|
+
channelId,
|
|
115
|
+
// User
|
|
116
|
+
user: request?.user?.name,
|
|
117
|
+
ip: request?.ip,
|
|
118
|
+
// Error
|
|
119
|
+
stacktrace: error.stack || error.message,
|
|
120
|
+
logs: logsHtml
|
|
121
|
+
});*/
|
|
118
122
|
|
|
119
123
|
// Update error message
|
|
120
124
|
error.message = "A bug report has been sent to my personal mailbox. Sorry for the inconvenience.";
|
|
File without changes
|
|
@@ -256,7 +256,7 @@ export class Console {
|
|
|
256
256
|
let html = logs.map( log => logToHTML( log, this )).join('\n');
|
|
257
257
|
|
|
258
258
|
if (full) {
|
|
259
|
-
const consoleCss = `background: #000; padding: 20px; font-family: 'monospace'; font-size: 12px; line-height: 20px;`
|
|
259
|
+
const consoleCss = `background: #000; padding: 20px; font-family: 'Fira Mono', 'monospace', 'Monaco'; font-size: 12px; line-height: 20px;`
|
|
260
260
|
html = '<div style="' + consoleCss + '">' + html + '</div>';
|
|
261
261
|
}
|
|
262
262
|
|