5htp-core 0.1.2 → 0.2.0
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/changelog.md +5 -0
- package/doc/TODO.md +71 -0
- package/package.json +5 -4
- package/src/client/{App.tsx → app/component.tsx} +15 -8
- package/src/client/app/index.ts +128 -0
- package/src/client/app/service.ts +34 -0
- package/src/client/app.tsconfig.json +0 -4
- package/src/client/assets/css/medias.less +14 -0
- package/src/client/components/Card/index.tsx +2 -2
- package/src/client/components/Dialog/Manager.tsx +39 -12
- package/src/client/components/Form/index.tsx +1 -1
- package/src/client/components/button.tsx +2 -2
- package/src/client/components/containers/Popover/index.tsx +1 -1
- package/src/client/components/data/spintext/index.tsx +1 -1
- package/src/client/components/dropdown/index.tsx +1 -1
- package/src/client/components/index.ts +8 -0
- package/src/client/components/input/BaseV2/index.tsx +1 -1
- package/src/client/components/input/UploadImage/index.tsx +1 -1
- package/src/client/hooks/index.ts +5 -0
- package/src/client/hooks/useState/index.tsx +2 -2
- package/src/client/hooks.ts +22 -0
- package/src/client/index.ts +5 -0
- package/src/client/pages/_layout/landing/index.tsx +0 -2
- package/src/client/pages/_messages/400.tsx +2 -2
- package/src/client/pages/_messages/401.tsx +2 -2
- package/src/client/pages/_messages/403.tsx +2 -2
- package/src/client/pages/_messages/404.tsx +2 -2
- package/src/client/pages/_messages/500.tsx +2 -2
- package/src/client/pages/bug.tsx +1 -1
- package/src/client/pages/useHeader.tsx +1 -1
- package/src/client/{context/captcha.ts → services/captcha/index.ts} +0 -0
- package/src/client/services/metrics/index.ts +37 -0
- package/src/client/{router → services/router/components}/Link.tsx +1 -1
- package/src/client/services/router/components/Page.tsx +59 -0
- package/src/client/{router/component.tsx → services/router/components/router.tsx} +43 -74
- package/src/client/services/router/index.tsx +448 -0
- package/src/client/services/router/request/api.ts +229 -0
- package/src/client/{router → services/router}/request/history.ts +0 -0
- package/src/client/services/router/request/index.ts +52 -0
- package/src/client/services/router/response/index.tsx +107 -0
- package/src/client/services/router/response/page.ts +95 -0
- package/src/client/{context/socket.ts → services/socket/index.ts} +2 -2
- package/src/client/utils/dom.ts +1 -1
- package/src/common/app/index.ts +9 -0
- package/src/common/data/chaines/index.ts +9 -6
- package/src/common/data/input/validate.ts +3 -166
- package/src/common/data/objets.ts +25 -0
- package/src/common/data/tableaux.ts +8 -0
- package/src/common/errors/index.ts +3 -1
- package/src/common/router/index.ts +67 -88
- package/src/common/router/layouts.ts +50 -0
- package/src/common/router/register.ts +62 -0
- package/src/common/router/request/api.ts +72 -0
- package/src/common/router/request/index.ts +31 -0
- package/src/common/router/{response.ts → response/index.ts} +9 -13
- package/src/common/router/response/page.ts +40 -56
- package/src/common/validation/index.ts +3 -0
- package/src/common/validation/schema.ts +184 -0
- package/src/common/validation/validator.ts +88 -0
- package/src/common/validation/validators.ts +313 -0
- package/src/server/app/config.ts +9 -27
- package/src/server/app/index.ts +81 -124
- package/src/server/app/service.ts +98 -0
- package/src/server/app.tsconfig.json +0 -8
- package/src/server/error/index.ts +13 -0
- package/src/server/index.ts +5 -0
- package/src/server/patch.ts +0 -6
- package/src/server/{data/Cache.ts → services/cache/index.ts} +79 -47
- package/src/server/services/console/bugReporter.ts +26 -16
- package/src/server/services/console/index.ts +59 -51
- package/src/server/services/cron/index.ts +12 -26
- package/src/server/services/database/bucket.ts +40 -0
- package/src/server/services/database/connection.ts +206 -75
- package/src/server/services/database/datatypes.ts +63 -40
- package/src/server/services/database/index.ts +295 -272
- package/src/server/services/database/metas.ts +246 -135
- package/src/server/services/database/stats.ts +151 -126
- package/src/server/services/email/index.ts +28 -52
- package/src/server/services/{router/request/services → metrics}/detect.ts +8 -10
- package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
- package/src/server/services/{http → router/http}/index.ts +28 -70
- package/src/server/services/{http → router/http}/multipart.ts +0 -0
- package/src/server/services/{http → router/http}/session.ts.old +0 -0
- package/src/server/services/router/index.ts +273 -203
- package/src/server/services/router/request/api.ts +73 -0
- package/src/server/services/router/request/index.ts +16 -97
- package/src/server/services/router/request/service.ts +21 -0
- package/src/server/services/router/response/index.ts +125 -64
- package/src/server/services/router/response/{filter → mask}/Filter.ts +0 -0
- package/src/server/services/router/response/{filter → mask}/index.ts +0 -2
- package/src/server/services/router/response/{filter → mask}/selecteurs.ts +0 -0
- package/src/server/services/router/response/page/document.tsx +194 -0
- package/src/server/services/router/response/page/index.tsx +157 -0
- package/src/server/{libs/pages → services/router/response/page}/schemaGenerator.ts +0 -0
- package/src/server/services/router/service.ts +48 -0
- package/src/server/services/schema/index.ts +47 -0
- package/src/server/services/schema/request.ts +55 -0
- package/src/server/services/schema/router.ts +33 -0
- package/src/server/services/socket/index.ts +38 -43
- package/src/server/services/socket/scope.ts +6 -4
- package/src/server/services/users/index.ts +203 -0
- package/src/server/services/{auth/base.ts → users/old.ts} +28 -112
- package/src/server/services/users/router/index.ts +72 -0
- package/src/server/services/users/router/request.ts +49 -0
- package/src/types/aliases.d.ts +43 -2
- package/templates/composant.tsx +1 -1
- package/templates/modal.tsx +1 -1
- package/templates/page.tsx +1 -1
- package/tsconfig.common.json +0 -4
- package/src/client/context/api.ts +0 -92
- package/src/client/context/index.ts +0 -246
- package/src/client/index.tsx +0 -129
- package/src/client/router/index.ts +0 -286
- package/src/client/router/request/index.ts +0 -106
- package/src/client/router/response/index.ts +0 -38
- package/src/client/router/route.ts +0 -75
- package/src/common/data/input/validators/basic.ts +0 -299
- package/src/common/data/input/validators/build.ts +0 -63
- package/src/common/router/request.ts +0 -83
- package/src/server/data/ApiClient.ts +0 -119
- package/src/server/data/input.ts +0 -41
- package/src/server/libs/pages/document.static.tsx +0 -41
- package/src/server/libs/pages/document.tsx +0 -203
- package/src/server/libs/pages/render.tsx +0 -90
- package/src/server/routes/auth.ts +0 -151
- package/src/server/services/redis/index.ts +0 -71
- package/src/server/services/router/request/services/auth.ts +0 -177
package/src/server/app/index.ts
CHANGED
|
@@ -9,48 +9,53 @@ import fs from 'fs-extra';
|
|
|
9
9
|
|
|
10
10
|
// Core
|
|
11
11
|
import ConfigParser, { TEnvConfig } from './config';
|
|
12
|
+
import { default as Service, AnyService } from './service';
|
|
13
|
+
import type { default as Router, Request as ServerRequest } from '@server/services/router';
|
|
12
14
|
|
|
13
15
|
/*----------------------------------
|
|
14
16
|
- TYPES
|
|
15
17
|
----------------------------------*/
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
type THook = () => Promise<void>;
|
|
19
|
+
export { default as Service, TPriority } from './service';
|
|
19
20
|
|
|
20
|
-
type
|
|
21
|
-
instanciate: boolean
|
|
22
|
-
}
|
|
21
|
+
type Config = {
|
|
23
22
|
|
|
24
|
-
abstract class AsyncService {
|
|
25
|
-
public abstract loading: Promise<void> | undefined;
|
|
26
|
-
public abstract load: () => Promise<void>;
|
|
27
|
-
}
|
|
28
|
-
abstract class Service {
|
|
29
|
-
public abstract load: () => void;
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
type Hooks = {
|
|
26
|
+
ready: {
|
|
27
|
+
args: [],
|
|
28
|
+
},
|
|
29
|
+
cleanup: {
|
|
30
|
+
args: [],
|
|
31
|
+
},
|
|
32
|
+
error: {
|
|
33
|
+
args: [error: Error, request?: ServerRequest<Router>],
|
|
34
|
+
}
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
declare global {
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
|
|
39
|
+
//interface Services { }
|
|
40
|
+
|
|
41
|
+
interface AppHooks {
|
|
42
|
+
|
|
39
43
|
}
|
|
44
|
+
|
|
40
45
|
interface User { }
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
const servicesObj = {}
|
|
44
|
-
|
|
45
48
|
/*----------------------------------
|
|
46
49
|
- FUNCTIONS
|
|
47
50
|
----------------------------------*/
|
|
48
|
-
export class
|
|
51
|
+
export default abstract class Application extends Service<Config, Hooks, /* TODO: this ? */Application> {
|
|
49
52
|
|
|
50
53
|
/*----------------------------------
|
|
51
54
|
- PROPERTIES
|
|
52
55
|
----------------------------------*/
|
|
53
56
|
|
|
57
|
+
public side = 'server' as 'server';
|
|
58
|
+
|
|
54
59
|
// Context
|
|
55
60
|
public hmr: __WebpackModuleApi.Hot | undefined = module.hot;
|
|
56
61
|
|
|
@@ -65,143 +70,101 @@ export class App {
|
|
|
65
70
|
|
|
66
71
|
// Status
|
|
67
72
|
public launched: boolean = false;
|
|
68
|
-
public loading: Promise<void>[] = [];
|
|
69
73
|
public status = {
|
|
70
74
|
services: false
|
|
71
75
|
}
|
|
72
76
|
|
|
73
|
-
|
|
74
|
-
get: (container, serviceId, receiver) => {
|
|
75
|
-
|
|
76
|
-
if (!( serviceId in container ) && typeof serviceId === 'string')
|
|
77
|
-
throw new Error(`Service not loaded: ${serviceId}`);
|
|
78
|
-
|
|
79
|
-
return container[serviceId];
|
|
80
|
-
}
|
|
81
|
-
}) as Core.Services;
|
|
82
|
-
|
|
83
|
-
public hooks: {[name in THookName]: THook[]} = {
|
|
84
|
-
ready: [],
|
|
85
|
-
cleanup: [],
|
|
86
|
-
error: []
|
|
87
|
-
}
|
|
77
|
+
private servicesList: AnyService[] = []
|
|
88
78
|
|
|
89
79
|
/*----------------------------------
|
|
90
80
|
- INIT
|
|
91
81
|
----------------------------------*/
|
|
92
82
|
|
|
83
|
+
public env: TEnvConfig;
|
|
84
|
+
public abstract identity: Config.Identity;
|
|
85
|
+
|
|
93
86
|
public constructor() {
|
|
94
87
|
|
|
88
|
+
// @ts-ignore: can't pass this to super
|
|
89
|
+
super();
|
|
90
|
+
|
|
95
91
|
// Gestion crash
|
|
96
92
|
process.on('unhandledRejection', (error: any, promise: any) => {
|
|
97
93
|
|
|
98
94
|
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.`);
|
|
95
|
+
this.runHook('error', error);
|
|
105
96
|
|
|
106
97
|
});
|
|
107
98
|
|
|
108
99
|
// Load config files
|
|
109
100
|
const configParser = new ConfigParser( this.path.root );
|
|
110
101
|
this.env = configParser.env();
|
|
111
|
-
this.identity = configParser.identity();
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Configs
|
|
115
|
-
public config!: Core.Config.Services;
|
|
116
|
-
public identity: Core.Config.Identity;
|
|
117
|
-
public env: TEnvConfig;
|
|
118
|
-
public configure( config: Core.Config.Services) {
|
|
119
|
-
this.config = config;
|
|
120
|
-
console.log("Configure services with", this.config);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Register a service
|
|
124
|
-
public register<TServiceName extends keyof Core.Services>(
|
|
125
|
-
id: TServiceName,
|
|
126
|
-
Service: TServiceClass,
|
|
127
|
-
options: Partial<TServiceOptions> = {}
|
|
128
|
-
) {
|
|
129
|
-
|
|
130
|
-
// Pas d'export default new Service pour chaque fichier de service,
|
|
131
|
-
// dissuaded'importer ms service sn'importe où, ce qui créé des références circulaires
|
|
132
|
-
console.log(`[services] Registering service ${id} ...`);
|
|
133
|
-
const service = options.instanciate !== false ? new Service() : 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
102
|
}
|
|
160
103
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public on( name: THookName, callback: THook ) {
|
|
167
|
-
this.hooks[ name ].push( callback );
|
|
168
|
-
return this;
|
|
169
|
-
}
|
|
104
|
+
/*----------------------------------
|
|
105
|
+
- REGISTER
|
|
106
|
+
----------------------------------*/
|
|
170
107
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
})
|
|
108
|
+
// Require a service at file scope
|
|
109
|
+
// Only use in files where a service is strictly required
|
|
110
|
+
public use<ServiceType extends Service<{}, {}, this>>( serviceName: string ): ServiceType | undefined {
|
|
111
|
+
return this[ serviceName ];
|
|
182
112
|
}
|
|
183
113
|
|
|
184
114
|
/*----------------------------------
|
|
185
115
|
- LAUNCH
|
|
186
116
|
----------------------------------*/
|
|
187
|
-
|
|
117
|
+
|
|
118
|
+
public async register() {
|
|
119
|
+
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
public async start() {
|
|
188
123
|
|
|
189
124
|
console.info(`[boot] Waiting for all services to be ready ...`);
|
|
190
|
-
await
|
|
125
|
+
await this.startServices()
|
|
191
126
|
|
|
192
127
|
console.info(`[boot] Launching application ...`);
|
|
193
128
|
await this.runHook('ready');
|
|
194
129
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
this.activateHMR();*/
|
|
130
|
+
console.info(`[boot] Run application-specific boot instructions ...`);
|
|
131
|
+
await this.boot();
|
|
198
132
|
|
|
199
133
|
console.info(`[boot] Application is ready.`);
|
|
200
|
-
|
|
201
134
|
this.launched = true;
|
|
202
135
|
|
|
203
136
|
}
|
|
204
137
|
|
|
138
|
+
public registerService( service: AnyService ) {
|
|
139
|
+
console.log(`[app] Register service`, service.constructor?.name);
|
|
140
|
+
this.servicesList.push(service);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public async startServices() {
|
|
144
|
+
|
|
145
|
+
console.log(`[app] Sorting ${this.servicesList.length} services by priority`);
|
|
146
|
+
this.servicesList.sort((s1, s2) => s2.priority - s1.priority);
|
|
147
|
+
|
|
148
|
+
console.log(`[app] Starting ${this.servicesList.length} services.`);
|
|
149
|
+
for (const service of this.servicesList) {
|
|
150
|
+
const serviceClassName = service.constructor?.name;
|
|
151
|
+
console.log(`[app] Start service`, serviceClassName);
|
|
152
|
+
|
|
153
|
+
if (service.register)
|
|
154
|
+
service.register();
|
|
155
|
+
|
|
156
|
+
if (service.start) {
|
|
157
|
+
service.started = service.start();
|
|
158
|
+
await service.started;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(`[app] All ${this.servicesList.length} services were started.`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public abstract boot(): Promise<void>;
|
|
166
|
+
|
|
167
|
+
// TODO: make it work
|
|
205
168
|
private activateHMR() {
|
|
206
169
|
|
|
207
170
|
if (!module.hot) return;
|
|
@@ -216,17 +179,16 @@ export class App {
|
|
|
216
179
|
console.info(`Cleaning application ...`);
|
|
217
180
|
|
|
218
181
|
// Services hooks
|
|
219
|
-
for (const id in this.services) {
|
|
182
|
+
/*for (const id in this.services) {
|
|
220
183
|
const service = this.services[id]
|
|
221
184
|
if (service.cleanup) {
|
|
222
185
|
console.info(`Cleaning ${id} service ...`);
|
|
223
186
|
service.cleanup();
|
|
224
187
|
}
|
|
225
|
-
}
|
|
188
|
+
}*/
|
|
226
189
|
|
|
227
190
|
// Application specific hooks
|
|
228
|
-
|
|
229
|
-
callback();
|
|
191
|
+
this.runHook('cleanup');
|
|
230
192
|
|
|
231
193
|
/*
|
|
232
194
|
console.log("[nettoyage] Arrêt serveur socket ...");
|
|
@@ -239,9 +201,4 @@ export class App {
|
|
|
239
201
|
});
|
|
240
202
|
}
|
|
241
203
|
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const app = new App;
|
|
245
|
-
export const services = app.services;
|
|
246
|
-
export const $ = app.services;
|
|
247
|
-
export default app;
|
|
204
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
import Application from ".";
|
|
6
|
+
|
|
7
|
+
/*----------------------------------
|
|
8
|
+
- TYPES: OPTIONS
|
|
9
|
+
----------------------------------*/
|
|
10
|
+
|
|
11
|
+
export type AnyService = Service<{}, {}, Application>
|
|
12
|
+
|
|
13
|
+
type THookCallback<THookArgs extends THookOptions> = (...args: THookArgs["args"]) => Promise<void>;
|
|
14
|
+
|
|
15
|
+
type THooksList = {
|
|
16
|
+
[hookName: string]: THookOptions
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type THookOptions = {
|
|
20
|
+
args: any[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type TPriority = -2 | -1 | 0 | 1 | 2
|
|
24
|
+
|
|
25
|
+
/*----------------------------------
|
|
26
|
+
- CONFIG
|
|
27
|
+
----------------------------------*/
|
|
28
|
+
|
|
29
|
+
const LogPrefix = '[service]';
|
|
30
|
+
|
|
31
|
+
/*----------------------------------
|
|
32
|
+
- CLASS
|
|
33
|
+
----------------------------------*/
|
|
34
|
+
export default abstract class Service<
|
|
35
|
+
TConfig extends {},
|
|
36
|
+
THooks extends THooksList,
|
|
37
|
+
TApplication extends Application
|
|
38
|
+
> {
|
|
39
|
+
|
|
40
|
+
public priority: TPriority = 0;
|
|
41
|
+
public started?: Promise<void>;
|
|
42
|
+
|
|
43
|
+
public constructor(
|
|
44
|
+
public app: TApplication,
|
|
45
|
+
public config: TConfig,
|
|
46
|
+
) {
|
|
47
|
+
|
|
48
|
+
if (!( this instanceof Application ))
|
|
49
|
+
// Make the app aware of his services
|
|
50
|
+
app.registerService(this);
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public abstract register?(): Promise<void>;
|
|
55
|
+
|
|
56
|
+
public abstract start?(): Promise<void>;
|
|
57
|
+
|
|
58
|
+
/*----------------------------------
|
|
59
|
+
- HOOKS
|
|
60
|
+
----------------------------------*/
|
|
61
|
+
public hooks: {[name in keyof THooks]?: THookCallback< THooks[name] >[]} = {}
|
|
62
|
+
|
|
63
|
+
public on<THookName extends keyof THooksList>(
|
|
64
|
+
name: THookName,
|
|
65
|
+
callback: THookCallback<THooksList[THookName]>
|
|
66
|
+
) {
|
|
67
|
+
|
|
68
|
+
const callbacks = this.hooks[ name ];
|
|
69
|
+
if (callbacks)
|
|
70
|
+
callbacks.push( callback );
|
|
71
|
+
else
|
|
72
|
+
this.hooks[ name ] = [callback]
|
|
73
|
+
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public runHook<THookName extends keyof THooksList>(
|
|
78
|
+
name: THookName,
|
|
79
|
+
...args: THooksList[THookName]["args"]
|
|
80
|
+
) {
|
|
81
|
+
|
|
82
|
+
const callbacks = this.hooks[name];
|
|
83
|
+
if (!callbacks)
|
|
84
|
+
return console.info(LogPrefix, `No ${name} hook defined in the current service instance.`);
|
|
85
|
+
|
|
86
|
+
console.info(`[hook] Run all ${name} hook (${callbacks.length}).`);
|
|
87
|
+
return Promise.all(
|
|
88
|
+
callbacks.map(
|
|
89
|
+
cb => cb(...args).catch(e => {
|
|
90
|
+
console.error(`[hook] Error while executing hook ${name}:`, e);
|
|
91
|
+
})
|
|
92
|
+
)
|
|
93
|
+
).then(() => {
|
|
94
|
+
console.info(`[hook] Hooks ${name} executed with success.`);
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
}
|
|
@@ -5,18 +5,10 @@
|
|
|
5
5
|
"baseUrl": "..",
|
|
6
6
|
"paths": {
|
|
7
7
|
|
|
8
|
-
// RAPPEL: Mettre à jour webpack externals
|
|
9
|
-
|
|
10
8
|
"@client/*": ["../node_modules/5htp-core/src/client/*"],
|
|
11
9
|
"@common/*": ["../node_modules/5htp-core/src/common/*"],
|
|
12
10
|
"@server/*": ["../node_modules/5htp-core/src/server/*"],
|
|
13
11
|
|
|
14
|
-
"@validator": ["../node_modules/5htp-core/src/server/data/input"],
|
|
15
|
-
"@router": ["../node_modules/5htp-core/src/server/services/router"],
|
|
16
|
-
"@errors": ["../node_modules/5htp-core/src/common/errors"],
|
|
17
|
-
|
|
18
|
-
"@models": ["../server/models"],
|
|
19
|
-
|
|
20
12
|
"@/*": ["./*"],
|
|
21
13
|
|
|
22
14
|
// ATTENTION: Les références à preact doivent toujours pointer vers la même instance
|
package/src/server/patch.ts
CHANGED
|
@@ -10,7 +10,7 @@ import hInterval from 'human-interval';
|
|
|
10
10
|
import fs from 'fs-extra';
|
|
11
11
|
|
|
12
12
|
// Core
|
|
13
|
-
import
|
|
13
|
+
import Application, { Service } from '@server/app';
|
|
14
14
|
|
|
15
15
|
// Libs
|
|
16
16
|
|
|
@@ -18,15 +18,17 @@ import app from '@server/app';
|
|
|
18
18
|
- CONFIG
|
|
19
19
|
----------------------------------*/
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const LogPrefix = '[cache]';
|
|
22
22
|
|
|
23
23
|
/*----------------------------------
|
|
24
24
|
- TYPES
|
|
25
25
|
----------------------------------*/
|
|
26
26
|
|
|
27
|
-
type TPrimitiveValue = string | boolean | number | undefined |
|
|
27
|
+
type TPrimitiveValue = string | boolean | number | undefined | TPrimitiveValue[] | {
|
|
28
|
+
[key: string]: TPrimitiveValue
|
|
29
|
+
}
|
|
28
30
|
|
|
29
|
-
type TExpirationDelay = string | number | Date
|
|
31
|
+
type TExpirationDelay = 'never' | string | number | Date;
|
|
30
32
|
|
|
31
33
|
type CacheEntry = {
|
|
32
34
|
// Value
|
|
@@ -35,47 +37,70 @@ type CacheEntry = {
|
|
|
35
37
|
expiration?: number
|
|
36
38
|
};
|
|
37
39
|
|
|
40
|
+
/*----------------------------------
|
|
41
|
+
- TYPES
|
|
42
|
+
----------------------------------*/
|
|
43
|
+
|
|
44
|
+
export type Config = {
|
|
45
|
+
debug: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type Hooks = {
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
/*----------------------------------
|
|
39
53
|
- SERVICE
|
|
40
54
|
----------------------------------*/
|
|
41
|
-
class Cache {
|
|
55
|
+
export default class Cache extends Service<Config, Hooks, Application> {
|
|
42
56
|
|
|
43
|
-
private cacheFile = path.join(app.path.data, 'cache/mem.json');
|
|
57
|
+
private cacheFile = path.join(this.app.path.data, 'cache/mem.json');
|
|
44
58
|
|
|
45
59
|
private data: {[key: string]: CacheEntry | undefined} = {};
|
|
46
60
|
|
|
47
61
|
private changes: number = 0;
|
|
48
62
|
|
|
49
|
-
public
|
|
63
|
+
public async register() {
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public async start() {
|
|
50
69
|
|
|
51
70
|
setInterval(() => this.cleanMem(), 10000);
|
|
52
71
|
|
|
72
|
+
// Restore persisted data
|
|
53
73
|
if (fs.existsSync(this.cacheFile))
|
|
54
74
|
this.data = fs.readJSONSync(this.cacheFile)
|
|
55
75
|
}
|
|
56
76
|
|
|
57
77
|
private cleanMem() {
|
|
58
78
|
|
|
59
|
-
|
|
60
|
-
|
|
79
|
+
// Remove expired data
|
|
61
80
|
const now = Date.now();
|
|
62
81
|
for (const key in this.data) {
|
|
63
82
|
const entry = this.data[ key ];
|
|
64
|
-
if (entry?.expiration && entry.expiration < now)
|
|
83
|
+
if (entry?.expiration && entry.expiration < now) {
|
|
84
|
+
this.config.debug && console.log(LogPrefix, `Delete expired data: ${key}`);
|
|
65
85
|
this.del(key);
|
|
86
|
+
}
|
|
66
87
|
}
|
|
67
88
|
|
|
68
|
-
|
|
89
|
+
// Write changes
|
|
90
|
+
if (this.changes > 0) {
|
|
69
91
|
fs.outputJSONSync(this.cacheFile, this.data);
|
|
92
|
+
this.config.debug && console.log(LogPrefix, `Flush ${this.changes} changes`);
|
|
93
|
+
this.changes = 0;
|
|
94
|
+
}
|
|
70
95
|
}
|
|
71
96
|
|
|
72
97
|
// Expiration = Durée de vie en secondes ou date max
|
|
73
98
|
// Retourne null quand pas de valeur
|
|
74
99
|
public get<TValeur extends TPrimitiveValue>(
|
|
75
100
|
cle: string,
|
|
76
|
-
func
|
|
77
|
-
expiration
|
|
78
|
-
avecDetails
|
|
101
|
+
func: (() => Promise<TValeur>),
|
|
102
|
+
expiration: TExpirationDelay,
|
|
103
|
+
avecDetails: true
|
|
79
104
|
): Promise<CacheEntry>;
|
|
80
105
|
|
|
81
106
|
public get<TValeur extends TPrimitiveValue>(
|
|
@@ -83,47 +108,44 @@ class Cache {
|
|
|
83
108
|
func: (() => Promise<TValeur>),
|
|
84
109
|
expiration?: TExpirationDelay,
|
|
85
110
|
avecDetails?: false
|
|
86
|
-
): Promise<
|
|
111
|
+
): Promise<TValeur>;
|
|
87
112
|
|
|
88
113
|
public async get<TValeur extends TPrimitiveValue>(
|
|
89
114
|
cle: string,
|
|
90
|
-
func
|
|
91
|
-
expiration
|
|
115
|
+
func: (() => Promise<TValeur>),
|
|
116
|
+
expiration: TExpirationDelay = 'never',
|
|
92
117
|
avecDetails?: boolean
|
|
93
|
-
): Promise<
|
|
118
|
+
): Promise<TValeur | CacheEntry> {
|
|
94
119
|
|
|
95
|
-
let
|
|
96
|
-
|
|
97
|
-
debug && console.log(`[cache] Get "${cle}".`);
|
|
120
|
+
let entry: CacheEntry | undefined = this.data[cle];
|
|
98
121
|
|
|
99
122
|
// Expired
|
|
100
|
-
if (
|
|
101
|
-
debug && console.log(`
|
|
102
|
-
|
|
123
|
+
if (entry?.expiration && entry.expiration < Date.now()){
|
|
124
|
+
this.config.debug && console.log(LogPrefix, `Key ${cle} expired.`);
|
|
125
|
+
entry = undefined;
|
|
103
126
|
}
|
|
104
127
|
|
|
105
128
|
// Donnée inexistante
|
|
106
|
-
if (
|
|
129
|
+
if (entry === undefined) {
|
|
130
|
+
|
|
131
|
+
this.config.debug && console.log(LogPrefix, `Get "${cle}": refresh value`);
|
|
107
132
|
|
|
108
133
|
// Rechargement
|
|
109
|
-
|
|
134
|
+
entry = {
|
|
110
135
|
value: await func(),
|
|
111
|
-
expiration: expiration
|
|
112
|
-
? this.delayToTimestamp(expiration)
|
|
113
|
-
: undefined
|
|
136
|
+
expiration: this.delayToTimestamp(expiration)
|
|
114
137
|
}
|
|
115
138
|
|
|
116
139
|
// undefined retourné = pas d'enregistrement
|
|
117
|
-
if (
|
|
118
|
-
await this.set(cle,
|
|
119
|
-
}
|
|
140
|
+
//if (entry.value !== undefined)
|
|
141
|
+
await this.set(cle, entry.value, expiration);
|
|
120
142
|
|
|
121
|
-
|
|
122
|
-
|
|
143
|
+
} else
|
|
144
|
+
this.config.debug && console.log(LogPrefix, `Get "${cle}": restored via cache`);
|
|
123
145
|
|
|
124
146
|
return avecDetails
|
|
125
|
-
?
|
|
126
|
-
:
|
|
147
|
+
? entry
|
|
148
|
+
: entry.value as TValeur;
|
|
127
149
|
};
|
|
128
150
|
|
|
129
151
|
/**
|
|
@@ -137,9 +159,9 @@ class Cache {
|
|
|
137
159
|
* - null: no expiration (default)
|
|
138
160
|
* @returns A void promise
|
|
139
161
|
*/
|
|
140
|
-
public set( cle: string, val: TPrimitiveValue, expiration: TExpirationDelay =
|
|
162
|
+
public set( cle: string, val: TPrimitiveValue, expiration: TExpirationDelay = 'never' ): void {
|
|
141
163
|
|
|
142
|
-
debug && console.log("
|
|
164
|
+
this.config.debug && console.log(LogPrefix, "Updating cache " + cle);
|
|
143
165
|
this.data[ cle ] = {
|
|
144
166
|
value: val,
|
|
145
167
|
expiration: this.delayToTimestamp(expiration)
|
|
@@ -157,24 +179,34 @@ class Cache {
|
|
|
157
179
|
/*----------------------------------
|
|
158
180
|
- UTILS
|
|
159
181
|
----------------------------------*/
|
|
160
|
-
|
|
182
|
+
/**
|
|
183
|
+
*
|
|
184
|
+
* @param delay
|
|
185
|
+
* @returns number (timestamp when the data expired) or undefined (never expires)
|
|
186
|
+
*/
|
|
187
|
+
private delayToTimestamp( delay: TExpirationDelay ): number | undefined {
|
|
188
|
+
|
|
189
|
+
if (delay === 'now') {
|
|
190
|
+
|
|
191
|
+
return Date.now();
|
|
192
|
+
|
|
193
|
+
} else if (delay === 'never') {
|
|
194
|
+
|
|
195
|
+
return undefined;
|
|
161
196
|
|
|
162
197
|
// H expression
|
|
163
|
-
if (typeof delay === 'string') {
|
|
198
|
+
} else if (typeof delay === 'string') {
|
|
164
199
|
|
|
165
200
|
const ms = hInterval(delay);
|
|
166
201
|
if (ms === undefined) throw new Error(`Invalid period string: ` + delay);
|
|
167
202
|
return Date.now() + ms;
|
|
168
203
|
|
|
169
|
-
//
|
|
204
|
+
// Lifetime in seconds
|
|
170
205
|
} else if (typeof delay === 'number')
|
|
171
206
|
return Date.now() + delay;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return delay.getTime();
|
|
207
|
+
|
|
208
|
+
// Date limit
|
|
175
209
|
else
|
|
176
|
-
return
|
|
210
|
+
return delay.getTime();
|
|
177
211
|
}
|
|
178
212
|
}
|
|
179
|
-
|
|
180
|
-
export default new Cache;
|