5htp-core 0.2.1 → 0.2.2-2
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 +10 -3
- package/src/client/app/index.ts +2 -2
- package/src/client/assets/css/components/card.less +0 -3
- package/src/client/assets/css/components/lists.less +1 -1
- package/src/client/assets/css/components/other.less +2 -4
- package/src/client/assets/css/components/table.less +1 -2
- package/src/client/assets/css/components.less +4 -0
- package/src/client/assets/css/core.less +0 -1
- package/src/client/assets/css/theme.less +2 -2
- package/src/client/assets/css/utils/medias.less +21 -0
- package/src/client/components/Card/index.tsx +8 -5
- package/src/client/components/Dialog/index.less +3 -3
- package/src/client/components/Row/index.less +2 -0
- package/src/client/components/Row/index.tsx +44 -10
- package/src/client/components/Video/index.less +39 -0
- package/src/client/components/Video/index.tsx +69 -0
- package/src/client/components/containers/Popover/index.tsx +2 -2
- package/src/client/components/data/Time.tsx +1 -1
- package/src/client/components/data/progressbar/circular/index.tsx +1 -1
- package/src/client/components/index.ts +24 -8
- package/src/client/components/input/BaseV2/index.tsx +0 -1
- package/src/client/components/{input/BaseV2/index.less → inputv3/base.less} +1 -1
- package/src/client/components/inputv3/base.tsx +73 -0
- package/src/client/components/{input/UploadImage → inputv3/file}/Bouton.tsx +0 -0
- package/src/client/components/inputv3/file/FileToUpload.ts +34 -0
- package/src/client/components/inputv3/file/index.less +59 -0
- package/src/client/components/inputv3/file/index.tsx +157 -0
- package/src/client/components/{input → inputv3/string}/index.tsx +41 -27
- package/src/client/pages/bug.tsx +3 -4
- package/src/client/services/router/index.tsx +0 -1
- package/src/client/services/router/request/api.ts +20 -12
- package/src/client/services/router/request/multipart.ts +27 -0
- package/src/common/data/chaines/greetings.ts +1 -1
- package/src/common/data/dates.ts +1 -1
- package/src/common/data/input/validate.ts +0 -9
- package/src/common/data/markdown.ts +1 -1
- package/src/common/errors/index.ts +16 -12
- package/src/common/router/request/api.ts +11 -3
- package/src/common/validation/schema.ts +21 -20
- package/src/common/validation/validators.ts +3 -6
- package/src/server/app/commands.ts +149 -0
- package/src/server/app/index.ts +25 -5
- package/src/server/app/service.ts +4 -0
- package/src/server/services/cache/commands.ts +41 -0
- package/src/server/services/cache/index.ts +102 -34
- package/src/server/services/console/index.ts +1 -1
- package/src/server/services/database/connection.ts +38 -22
- package/src/server/services/database/datatypes.ts +51 -12
- package/src/server/services/database/index.ts +133 -40
- package/src/server/services/database/metas.ts +63 -37
- package/src/server/services/database/repository.ts +26 -0
- package/src/server/services/email/index.ts +102 -42
- package/src/server/services/fetch/index.ts +110 -0
- package/src/server/services/router/http/multipart.ts +70 -41
- package/src/server/services/router/index.ts +35 -4
- package/src/server/services/router/request/index.ts +8 -6
- package/src/server/services/schema/index.ts +4 -11
- package/src/server/services/schema/request.ts +16 -7
- package/src/server/services/schema/router.ts +6 -2
- package/src/server/{services_old → services/security/encrypt}/aes.ts +33 -14
- package/src/server/services/users/index.ts +3 -3
- package/src/server/services/users/router/index.ts +0 -2
- package/src/types/global/utils.d.ts +11 -1
- package/tsconfig.common.json +3 -0
- package/src/client/components/input/Textarea.tsx +0 -57
- package/src/client/components/input/Upload.tsx +0 -5
- package/src/client/components/input/UploadImage/index.less +0 -93
- package/src/client/components/input/UploadImage/index.tsx +0 -220
- package/src/common/data/file.ts +0 -25
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
5
|
// Core
|
|
6
|
-
import {
|
|
6
|
+
import { CoreError, TListeErreursSaisie, InputErrorSchema } from '@common/errors';
|
|
7
7
|
|
|
8
8
|
// Specific
|
|
9
9
|
import { default as Validator, EXCLUDE_VALUE } from './validator';
|
|
@@ -15,10 +15,12 @@ import { default as Validator, EXCLUDE_VALUE } from './validator';
|
|
|
15
15
|
export type TSchemaFields = { [fieldName: string]: Schema<{}> | Validator<any> }
|
|
16
16
|
|
|
17
17
|
type TOptsValider = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
debug?: boolean,
|
|
19
|
+
throwError?: boolean,
|
|
20
|
+
|
|
21
|
+
validateAll?: boolean,
|
|
22
|
+
validateDeps?: boolean,
|
|
23
|
+
autoCorrect?: boolean,
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export type TValidationResult<TFields extends TSchemaFields> = {
|
|
@@ -36,8 +38,6 @@ export type TValidatedData<TFields extends TSchemaFields> = {
|
|
|
36
38
|
- CONST
|
|
37
39
|
----------------------------------*/
|
|
38
40
|
|
|
39
|
-
const debug = true;
|
|
40
|
-
|
|
41
41
|
const LogPrefix = '[schema][validator]';
|
|
42
42
|
|
|
43
43
|
/*----------------------------------
|
|
@@ -63,14 +63,15 @@ export default class Schema<TFields extends TSchemaFields> {
|
|
|
63
63
|
): TValidationResult<TFields> {
|
|
64
64
|
|
|
65
65
|
opts = {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
debug: false,
|
|
67
|
+
throwError: false,
|
|
68
|
+
validateAll: false,
|
|
69
|
+
validateDeps: true,
|
|
70
|
+
autoCorrect: false,
|
|
70
71
|
...opts,
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
const clesAvalider = Object.keys(opts.
|
|
74
|
+
const clesAvalider = Object.keys(opts.validateAll === true ? this.fields : dataToValidate);
|
|
74
75
|
|
|
75
76
|
let outputSchema = output;
|
|
76
77
|
for (const branche of chemin)
|
|
@@ -84,7 +85,7 @@ export default class Schema<TFields extends TSchemaFields> {
|
|
|
84
85
|
// La donnée est répertoriée dans le schema
|
|
85
86
|
const field = this.fields[champ];
|
|
86
87
|
if (field === undefined) {
|
|
87
|
-
debug && console.warn(LogPrefix, '[' + champ + ']', 'Exclusion (pas présent dans le schéma)');
|
|
88
|
+
opts.debug && console.warn(LogPrefix, '[' + champ + ']', 'Exclusion (pas présent dans le schéma)');
|
|
88
89
|
continue;
|
|
89
90
|
}
|
|
90
91
|
|
|
@@ -140,24 +141,24 @@ export default class Schema<TFields extends TSchemaFields> {
|
|
|
140
141
|
// Validation
|
|
141
142
|
try {
|
|
142
143
|
|
|
143
|
-
const val = field.validate(valOrigine, allData, output, opts.
|
|
144
|
+
const val = field.validate(valOrigine, allData, output, opts.autoCorrect);
|
|
144
145
|
|
|
145
146
|
// Exclusion seulement si explicitement demandé
|
|
146
147
|
// IMPORTANT: Conserver les values undefined
|
|
147
148
|
// La présence d'un valeur undefined peut être utile, par exemple, pour indiquer qu'on souhaite supprimer une donnée
|
|
148
149
|
// Exemple: undefinec = suppression fichier | Absende donnée = conservation fihcier actuel
|
|
149
150
|
if (val === EXCLUDE_VALUE)
|
|
150
|
-
debug && console.log(LogPrefix, '[' + cheminA + '] Exclusion demandée');
|
|
151
|
+
opts.debug && console.log(LogPrefix, '[' + cheminA + '] Exclusion demandée');
|
|
151
152
|
else
|
|
152
153
|
outputSchema[champ] = val;
|
|
153
154
|
|
|
154
|
-
debug && console.log(LogPrefix, '[' + cheminA + ']', valOrigine, '=>', val);
|
|
155
|
+
opts.debug && console.log(LogPrefix, '[' + cheminA + ']', valOrigine, '=>', val);
|
|
155
156
|
|
|
156
157
|
} catch (error) {
|
|
157
158
|
|
|
158
|
-
debug && console.warn(LogPrefix, '[' + cheminA + ']', valOrigine, '||
|
|
159
|
+
opts.debug && console.warn(LogPrefix, '[' + cheminA + ']', valOrigine, '|| CoreError:', error);
|
|
159
160
|
|
|
160
|
-
if (error instanceof
|
|
161
|
+
if (error instanceof CoreError) {
|
|
161
162
|
|
|
162
163
|
// Référencement erreur
|
|
163
164
|
erreurs[cheminAstr] = [error.message]
|
|
@@ -169,11 +170,11 @@ export default class Schema<TFields extends TSchemaFields> {
|
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
|
|
172
|
-
if (nbErreurs !== 0 && opts.
|
|
173
|
+
if (nbErreurs !== 0 && opts.throwError === true) {
|
|
173
174
|
throw new InputErrorSchema(erreurs);
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
debug && console.log(LogPrefix, '', dataToValidate, '=>', output);
|
|
177
|
+
opts.debug && console.log(LogPrefix, '', dataToValidate, '=>', output);
|
|
177
178
|
|
|
178
179
|
return {
|
|
179
180
|
values: output as TValidatedData<TFields>,
|
|
@@ -12,8 +12,7 @@ import {
|
|
|
12
12
|
|
|
13
13
|
// Core
|
|
14
14
|
import { InputError } from '@common/errors';
|
|
15
|
-
import
|
|
16
|
-
import NormalizedFile from '@common/data/file';
|
|
15
|
+
import FileToUpload from '@client/components/inputv3/file/FileToUpload';
|
|
17
16
|
|
|
18
17
|
// Speciific
|
|
19
18
|
import Validator, { TValidator } from './validator'
|
|
@@ -26,7 +25,7 @@ import Dropdown from '@client/components/dropdown.old';
|
|
|
26
25
|
- TYPES
|
|
27
26
|
----------------------------------*/
|
|
28
27
|
|
|
29
|
-
export type TFileValidator = TValidator<
|
|
28
|
+
export type TFileValidator = TValidator<FileToUpload> & {
|
|
30
29
|
type?: (keyof typeof raccourcisMime) | string[], // Raccourci, ou liste de mimetype
|
|
31
30
|
taille?: number
|
|
32
31
|
}
|
|
@@ -274,9 +273,7 @@ export default class SchemaValidator {
|
|
|
274
273
|
output: TObjetDonnees
|
|
275
274
|
): File | undefined => {
|
|
276
275
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (!(val instanceof NormalizedFile))
|
|
276
|
+
if (!(val instanceof FileToUpload))
|
|
280
277
|
throw new InputError(`Must be a File (${typeof val} received)`);
|
|
281
278
|
|
|
282
279
|
// MIME
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import yargsParser from 'yargs-parser';
|
|
7
|
+
|
|
8
|
+
// Core
|
|
9
|
+
import Application, { Service } from '@server/app';
|
|
10
|
+
import { NotFound } from '@common/errors';
|
|
11
|
+
|
|
12
|
+
/*----------------------------------
|
|
13
|
+
- TYPES
|
|
14
|
+
----------------------------------*/
|
|
15
|
+
|
|
16
|
+
type CommandCallback<TArgs extends any[]> = (...args: TArgs) => Promise<any>
|
|
17
|
+
|
|
18
|
+
export type CommandsList = {
|
|
19
|
+
[commandName: string]: Command
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type Command<TArgs extends any[] = any[]> = {
|
|
23
|
+
name: string,
|
|
24
|
+
description: string,
|
|
25
|
+
run?: CommandCallback<TArgs>
|
|
26
|
+
childrens: CommandsList
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/*----------------------------------
|
|
30
|
+
- SERVICE TYPES
|
|
31
|
+
----------------------------------*/
|
|
32
|
+
|
|
33
|
+
const LogPrefix = `[commands]`;
|
|
34
|
+
|
|
35
|
+
export type Config = {
|
|
36
|
+
debug: boolean
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type Hooks = {
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/*----------------------------------
|
|
44
|
+
- SERVICE
|
|
45
|
+
----------------------------------*/
|
|
46
|
+
export default class CommandsManager extends Service<Config, Hooks, Application> {
|
|
47
|
+
|
|
48
|
+
public priority = 2 as 2;
|
|
49
|
+
|
|
50
|
+
public commandsIndex: CommandsList = {}
|
|
51
|
+
|
|
52
|
+
public async register() {
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public async start() {
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/*----------------------------------
|
|
63
|
+
- DEFINITIONS
|
|
64
|
+
----------------------------------*/
|
|
65
|
+
public command<TArgs extends any[]>(
|
|
66
|
+
...args: (
|
|
67
|
+
[name: string, description: string, childrens: Command[]]
|
|
68
|
+
|
|
|
69
|
+
[name: string, description: string, run: CommandCallback<TArgs>, childrens?: Command[]]
|
|
70
|
+
)
|
|
71
|
+
): Command {
|
|
72
|
+
|
|
73
|
+
let name: string, description: string;
|
|
74
|
+
let childrens: Command[] | undefined;
|
|
75
|
+
let run: CommandCallback<TArgs> | undefined;
|
|
76
|
+
|
|
77
|
+
if (typeof args[2] === 'object')
|
|
78
|
+
([name, description, childrens] = args)
|
|
79
|
+
else
|
|
80
|
+
([name, description, run, childrens] = args)
|
|
81
|
+
|
|
82
|
+
const command: Command = {
|
|
83
|
+
name,
|
|
84
|
+
description,
|
|
85
|
+
run,
|
|
86
|
+
childrens: childrens ? this.indexFromList(childrens) : {}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return command;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private indexFromList( list: Command[] ): CommandsList {
|
|
93
|
+
|
|
94
|
+
const index: CommandsList = {}
|
|
95
|
+
for (const command of list)
|
|
96
|
+
index[ command.name ] = command;
|
|
97
|
+
|
|
98
|
+
return index;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/*----------------------------------
|
|
102
|
+
- REGISTER
|
|
103
|
+
----------------------------------*/
|
|
104
|
+
public fromList( list: Command[] ) {
|
|
105
|
+
for (const command of list) {
|
|
106
|
+
|
|
107
|
+
if (this.commandsIndex[ command.name ] !== undefined)
|
|
108
|
+
throw new Error(`Tried to register command "${command.name}", but it already has been defined.`);
|
|
109
|
+
|
|
110
|
+
this.commandsIndex[ command.name ] = command;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/*----------------------------------
|
|
115
|
+
- RUN
|
|
116
|
+
----------------------------------*/
|
|
117
|
+
public async run( commandString: string ) {
|
|
118
|
+
|
|
119
|
+
const { _, ...args } = yargsParser(commandString);
|
|
120
|
+
|
|
121
|
+
this.config.debug && console.log(LogPrefix, `Run command: ${commandString} | Parsed:`, { _, ...args });
|
|
122
|
+
|
|
123
|
+
let command: Command | undefined;
|
|
124
|
+
for (const commandName of _) {
|
|
125
|
+
|
|
126
|
+
const commandsList: CommandsList = command === undefined
|
|
127
|
+
? this.commandsIndex
|
|
128
|
+
: command.childrens;
|
|
129
|
+
|
|
130
|
+
command = commandsList[commandName];
|
|
131
|
+
|
|
132
|
+
if (command === undefined)
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (command === undefined)
|
|
137
|
+
throw new NotFound(`Command not found.`);
|
|
138
|
+
|
|
139
|
+
if (command.run === undefined)
|
|
140
|
+
throw new NotFound(`This command isn't runnable.`);
|
|
141
|
+
|
|
142
|
+
// TODO: order correctly & validate type according to injected typescript typedefs (command.run.params)
|
|
143
|
+
const argsList = Object.values(args);
|
|
144
|
+
|
|
145
|
+
const result = await command.run(argsList);
|
|
146
|
+
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
}
|
package/src/server/app/index.ts
CHANGED
|
@@ -10,13 +10,17 @@ import fs from 'fs-extra';
|
|
|
10
10
|
// Core
|
|
11
11
|
import ConfigParser, { TEnvConfig } from './config';
|
|
12
12
|
import { default as Service, AnyService } from './service';
|
|
13
|
+
import CommandsManager from './commands';
|
|
14
|
+
|
|
15
|
+
// Built-in
|
|
13
16
|
import type { default as Router, Request as ServerRequest } from '@server/services/router';
|
|
14
17
|
|
|
15
18
|
/*----------------------------------
|
|
16
19
|
- TYPES
|
|
17
20
|
----------------------------------*/
|
|
18
21
|
|
|
19
|
-
export { default as Service
|
|
22
|
+
export { default as Service } from './service';
|
|
23
|
+
export type { TPriority } from './service';
|
|
20
24
|
|
|
21
25
|
type Config = {
|
|
22
26
|
|
|
@@ -61,6 +65,8 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
|
|
|
61
65
|
|
|
62
66
|
public path = {
|
|
63
67
|
root: process.cwd(),
|
|
68
|
+
typings: process.cwd() + '/var/typings',
|
|
69
|
+
cache: process.cwd() + '/var/cache',
|
|
64
70
|
data: process.cwd() + '/var/data',
|
|
65
71
|
log: process.cwd() + '/var/log',
|
|
66
72
|
public: process.cwd() + '/public',
|
|
@@ -81,7 +87,7 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
|
|
|
81
87
|
----------------------------------*/
|
|
82
88
|
|
|
83
89
|
public env: TEnvConfig;
|
|
84
|
-
public
|
|
90
|
+
public identity: Config.Identity;
|
|
85
91
|
|
|
86
92
|
public constructor() {
|
|
87
93
|
|
|
@@ -90,15 +96,14 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
|
|
|
90
96
|
|
|
91
97
|
// Gestion crash
|
|
92
98
|
process.on('unhandledRejection', (error: any, promise: any) => {
|
|
93
|
-
|
|
94
|
-
console.error("Unhandled promise rejection:", error);
|
|
99
|
+
// We don't log the error here because it's the role of the app to decidehiw to log errors
|
|
95
100
|
this.runHook('error', error);
|
|
96
|
-
|
|
97
101
|
});
|
|
98
102
|
|
|
99
103
|
// Load config files
|
|
100
104
|
const configParser = new ConfigParser( this.path.root );
|
|
101
105
|
this.env = configParser.env();
|
|
106
|
+
this.identity = configParser.identity();
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
/*----------------------------------
|
|
@@ -111,6 +116,16 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
|
|
|
111
116
|
return this[ serviceName ];
|
|
112
117
|
}
|
|
113
118
|
|
|
119
|
+
/*----------------------------------
|
|
120
|
+
- COMMANDS
|
|
121
|
+
----------------------------------*/
|
|
122
|
+
|
|
123
|
+
private commandsManager = new CommandsManager(this, { debug: true });
|
|
124
|
+
|
|
125
|
+
public command( ...args: Parameters<CommandsManager["command"]> ) {
|
|
126
|
+
return this.commandsManager.command(...args);
|
|
127
|
+
}
|
|
128
|
+
|
|
114
129
|
/*----------------------------------
|
|
115
130
|
- LAUNCH
|
|
116
131
|
----------------------------------*/
|
|
@@ -153,6 +168,11 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
|
|
|
153
168
|
if (service.register)
|
|
154
169
|
service.register();
|
|
155
170
|
|
|
171
|
+
// Register commands
|
|
172
|
+
if (service.commands)
|
|
173
|
+
this.commandsManager.fromList(service.commands);
|
|
174
|
+
|
|
175
|
+
// Start service
|
|
156
176
|
if (service.start) {
|
|
157
177
|
service.started = service.start();
|
|
158
178
|
await service.started;
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
- DEPENDANCES
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
|
+
// Specific
|
|
5
6
|
import Application from ".";
|
|
7
|
+
import type { Command } from "./commands";
|
|
6
8
|
|
|
7
9
|
/*----------------------------------
|
|
8
10
|
- TYPES: OPTIONS
|
|
@@ -40,6 +42,8 @@ export default abstract class Service<
|
|
|
40
42
|
public priority: TPriority = 0;
|
|
41
43
|
public started?: Promise<void>;
|
|
42
44
|
|
|
45
|
+
public commands?: Command[];
|
|
46
|
+
|
|
43
47
|
public constructor(
|
|
44
48
|
public app: TApplication,
|
|
45
49
|
public config: TConfig,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import sizeOf from 'object-sizeof';
|
|
7
|
+
|
|
8
|
+
// Core
|
|
9
|
+
|
|
10
|
+
// Specific
|
|
11
|
+
import type CacheManager from '.';
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- TYPES
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
|
|
17
|
+
/*----------------------------------
|
|
18
|
+
- SERVICE
|
|
19
|
+
----------------------------------*/
|
|
20
|
+
export default (cache: CacheManager, app = cache.app) => [
|
|
21
|
+
|
|
22
|
+
app.command('cache', 'Manage the cache service', [
|
|
23
|
+
|
|
24
|
+
app.command('list', 'List cache entries', async () => {
|
|
25
|
+
|
|
26
|
+
return Object.entries(cache.data).map(([ key, entry ]) => ({
|
|
27
|
+
key,
|
|
28
|
+
type: typeof entry?.value,
|
|
29
|
+
size: sizeOf(entry?.value),
|
|
30
|
+
expires: entry?.expiration || 'No expiration'
|
|
31
|
+
}))
|
|
32
|
+
}),
|
|
33
|
+
|
|
34
|
+
app.command('delete', 'List cache entries', async (key?: string) => {
|
|
35
|
+
|
|
36
|
+
await cache.del(key);
|
|
37
|
+
|
|
38
|
+
return true;
|
|
39
|
+
})
|
|
40
|
+
])
|
|
41
|
+
]
|
|
@@ -12,7 +12,8 @@ import fs from 'fs-extra';
|
|
|
12
12
|
// Core
|
|
13
13
|
import Application, { Service } from '@server/app';
|
|
14
14
|
|
|
15
|
-
//
|
|
15
|
+
// Specific
|
|
16
|
+
import registerCommands from './commands';
|
|
16
17
|
|
|
17
18
|
/*----------------------------------
|
|
18
19
|
- CONFIG
|
|
@@ -30,13 +31,26 @@ type TPrimitiveValue = string | boolean | number | undefined | TPrimitiveValue[]
|
|
|
30
31
|
|
|
31
32
|
type TExpirationDelay = 'never' | string | number | Date;
|
|
32
33
|
|
|
33
|
-
type CacheEntry = {
|
|
34
|
+
type CacheEntry<TValue extends TPrimitiveValue =TPrimitiveValue > = {
|
|
34
35
|
// Value
|
|
35
|
-
value:
|
|
36
|
+
value: TValue,
|
|
36
37
|
// Expiration Timestamp
|
|
37
|
-
expiration?: number
|
|
38
|
+
expiration?: number,
|
|
39
|
+
changes: number
|
|
38
40
|
};
|
|
39
41
|
|
|
42
|
+
type TCacheGetOrUpdateArgs<TValeur extends TPrimitiveValue> = [
|
|
43
|
+
cle: string,
|
|
44
|
+
func: (() => Promise<TValeur>),
|
|
45
|
+
expiration?: TExpirationDelay,
|
|
46
|
+
avecDetails?: boolean
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
type TCacheGetOnlyArgs = [
|
|
50
|
+
cle: string,
|
|
51
|
+
avecDetails: true
|
|
52
|
+
]
|
|
53
|
+
|
|
40
54
|
/*----------------------------------
|
|
41
55
|
- TYPES
|
|
42
56
|
----------------------------------*/
|
|
@@ -53,15 +67,16 @@ export type Hooks = {
|
|
|
53
67
|
- SERVICE
|
|
54
68
|
----------------------------------*/
|
|
55
69
|
export default class Cache extends Service<Config, Hooks, Application> {
|
|
56
|
-
|
|
57
|
-
private cacheFile = path.join(this.app.path.data, 'cache/mem.json');
|
|
58
70
|
|
|
59
|
-
|
|
71
|
+
public commands = registerCommands(this);
|
|
72
|
+
|
|
73
|
+
private cacheDir = this.app.path.cache;
|
|
60
74
|
|
|
61
|
-
|
|
75
|
+
public data: {[key: string]: CacheEntry | undefined} = {};
|
|
62
76
|
|
|
63
77
|
public async register() {
|
|
64
78
|
|
|
79
|
+
|
|
65
80
|
|
|
66
81
|
}
|
|
67
82
|
|
|
@@ -70,8 +85,20 @@ export default class Cache extends Service<Config, Hooks, Application> {
|
|
|
70
85
|
setInterval(() => this.cleanMem(), 10000);
|
|
71
86
|
|
|
72
87
|
// Restore persisted data
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
await this.restore();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private restore() {
|
|
92
|
+
const files = fs.readdirSync( this.cacheDir );
|
|
93
|
+
for (const file of files) {
|
|
94
|
+
|
|
95
|
+
if (!file.endsWith('.json'))
|
|
96
|
+
continue;
|
|
97
|
+
|
|
98
|
+
const entryKey = file.substring(0, file.length - 5);
|
|
99
|
+
this.data[ entryKey ] = fs.readJSONSync( path.join(this.cacheDir, file) );
|
|
100
|
+
console.log(LogPrefix, `Restored cache entry ${entryKey}`);
|
|
101
|
+
}
|
|
75
102
|
}
|
|
76
103
|
|
|
77
104
|
private cleanMem() {
|
|
@@ -87,13 +114,29 @@ export default class Cache extends Service<Config, Hooks, Application> {
|
|
|
87
114
|
}
|
|
88
115
|
|
|
89
116
|
// Write changes
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
117
|
+
for (const entryKey in this.data) {
|
|
118
|
+
|
|
119
|
+
const entry = this.data[entryKey];
|
|
120
|
+
if (!entry?.changes)
|
|
121
|
+
continue;
|
|
122
|
+
|
|
123
|
+
this.config.debug && console.log(LogPrefix, `Flush ${entry.changes} changes for ${entryKey}`);
|
|
124
|
+
|
|
125
|
+
entry.changes = 0;
|
|
126
|
+
const entryFile = this.getEntryFile(entryKey);
|
|
127
|
+
fs.outputJSONSync(entryFile , this.data[entryKey]);
|
|
94
128
|
}
|
|
95
129
|
}
|
|
96
130
|
|
|
131
|
+
private getEntryFile( entryKey: string ) {
|
|
132
|
+
return path.join(this.cacheDir, entryKey + '.json');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public get<TValeur extends TPrimitiveValue>(
|
|
136
|
+
cle: string,
|
|
137
|
+
avecDetails?: true
|
|
138
|
+
): Promise<CacheEntry<TValeur> | TValeur | undefined>;
|
|
139
|
+
|
|
97
140
|
// Expiration = Durée de vie en secondes ou date max
|
|
98
141
|
// Retourne null quand pas de valeur
|
|
99
142
|
public get<TValeur extends TPrimitiveValue>(
|
|
@@ -101,7 +144,7 @@ export default class Cache extends Service<Config, Hooks, Application> {
|
|
|
101
144
|
func: (() => Promise<TValeur>),
|
|
102
145
|
expiration: TExpirationDelay,
|
|
103
146
|
avecDetails: true
|
|
104
|
-
): Promise<CacheEntry
|
|
147
|
+
): Promise<CacheEntry<TValeur>>;
|
|
105
148
|
|
|
106
149
|
public get<TValeur extends TPrimitiveValue>(
|
|
107
150
|
cle: string,
|
|
@@ -110,14 +153,25 @@ export default class Cache extends Service<Config, Hooks, Application> {
|
|
|
110
153
|
avecDetails?: false
|
|
111
154
|
): Promise<TValeur>;
|
|
112
155
|
|
|
113
|
-
public async get<TValeur extends TPrimitiveValue>(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
156
|
+
public async get<TValeur extends TPrimitiveValue, TArgs extends TCacheGetOnlyArgs | TCacheGetOrUpdateArgs<TValeur> = TCacheGetOnlyArgs | TCacheGetOrUpdateArgs<TValeur>>(
|
|
157
|
+
...args: TArgs
|
|
158
|
+
): Promise< TValeur | CacheEntry<TValeur> | (TArgs extends TCacheGetOnlyArgs ? undefined : TValeur)> {
|
|
159
|
+
|
|
160
|
+
let cle: string;
|
|
161
|
+
let func: (() => Promise<TValeur>) | undefined;
|
|
162
|
+
let expiration: TExpirationDelay | undefined;
|
|
163
|
+
let avecDetails: boolean | undefined = true;
|
|
119
164
|
|
|
120
|
-
|
|
165
|
+
if (typeof args[1] === 'function') {
|
|
166
|
+
([ cle, func, expiration, avecDetails ] = args);
|
|
167
|
+
} else {
|
|
168
|
+
([ cle, avecDetails ] = args);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (expiration === undefined)
|
|
172
|
+
expiration = 'never';
|
|
173
|
+
|
|
174
|
+
let entry: CacheEntry<TValeur> | undefined = this.data[cle];
|
|
121
175
|
|
|
122
176
|
// Expired
|
|
123
177
|
if (entry?.expiration && entry.expiration < Date.now()){
|
|
@@ -126,22 +180,26 @@ export default class Cache extends Service<Config, Hooks, Application> {
|
|
|
126
180
|
}
|
|
127
181
|
|
|
128
182
|
// Donnée inexistante
|
|
129
|
-
if (entry
|
|
183
|
+
if (entry !== undefined) {
|
|
184
|
+
|
|
185
|
+
this.config.debug && console.log(LogPrefix, `Get "${cle}": restored via cache`);
|
|
186
|
+
|
|
187
|
+
} else if (func !== undefined) {
|
|
130
188
|
|
|
131
189
|
this.config.debug && console.log(LogPrefix, `Get "${cle}": refresh value`);
|
|
132
190
|
|
|
133
191
|
// Rechargement
|
|
134
192
|
entry = {
|
|
135
193
|
value: await func(),
|
|
136
|
-
expiration: this.delayToTimestamp(expiration)
|
|
194
|
+
expiration: this.delayToTimestamp(expiration),
|
|
195
|
+
changes: 0
|
|
137
196
|
}
|
|
138
197
|
|
|
139
|
-
|
|
140
|
-
//if (entry.value !== undefined)
|
|
198
|
+
if (expiration !== 'now')
|
|
141
199
|
await this.set(cle, entry.value, expiration);
|
|
142
200
|
|
|
143
201
|
} else
|
|
144
|
-
|
|
202
|
+
return undefined;
|
|
145
203
|
|
|
146
204
|
return avecDetails
|
|
147
205
|
? entry
|
|
@@ -160,19 +218,29 @@ export default class Cache extends Service<Config, Hooks, Application> {
|
|
|
160
218
|
* @returns A void promise
|
|
161
219
|
*/
|
|
162
220
|
public set( cle: string, val: TPrimitiveValue, expiration: TExpirationDelay = 'never' ): void {
|
|
163
|
-
|
|
221
|
+
|
|
222
|
+
// TODO: check is key contains illegal characters
|
|
223
|
+
|
|
164
224
|
this.config.debug && console.log(LogPrefix, "Updating cache " + cle);
|
|
165
225
|
this.data[ cle ] = {
|
|
166
226
|
value: val,
|
|
167
|
-
expiration: this.delayToTimestamp(expiration)
|
|
227
|
+
expiration: this.delayToTimestamp(expiration),
|
|
228
|
+
changes: 1
|
|
168
229
|
}
|
|
169
|
-
|
|
170
|
-
this.changes++;
|
|
171
230
|
};
|
|
172
231
|
|
|
173
|
-
public del(
|
|
174
|
-
|
|
175
|
-
|
|
232
|
+
public del( key?: string ): void {
|
|
233
|
+
|
|
234
|
+
if (key === undefined) {
|
|
235
|
+
this.data = {};
|
|
236
|
+
console.log(LogPrefix, "Deleting all keys from cache");
|
|
237
|
+
fs.removeSync( this.cacheDir );
|
|
238
|
+
} else {
|
|
239
|
+
this.data[ key ] = undefined;
|
|
240
|
+
console.log(LogPrefix, `Deleting key "${key}" from cache`);
|
|
241
|
+
const entryFile = this.getEntryFile(key);
|
|
242
|
+
fs.removeSync( entryFile );
|
|
243
|
+
}
|
|
176
244
|
}
|
|
177
245
|
|
|
178
246
|
|
|
@@ -305,7 +305,7 @@ export default class Console extends Service<Config, Hooks, Application> {
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
public printSql = (requete: string) => highlight(
|
|
308
|
-
formatSql(requete, { indent: ' '.repeat(4) }),
|
|
308
|
+
requete,//formatSql(requete, { indent: ' '.repeat(4) }),
|
|
309
309
|
{ language: 'sql', ignoreIllegals: true }
|
|
310
310
|
)
|
|
311
311
|
|