5htp-core 0.2.7 → 0.2.8
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/components/Dialog/Manager.tsx +28 -11
- package/src/client/components/Dialog/card.tsx +0 -2
- package/src/client/components/Dialog/index.less +44 -35
- package/src/client/components/Form.ts +5 -2
- package/src/client/components/Select/ChoiceSelector.tsx +29 -29
- package/src/client/components/Select/index.tsx +5 -5
- package/src/client/components/containers/Popover/getPosition.ts +2 -2
- package/src/client/components/containers/Popover/index.tsx +11 -7
- package/src/client/components/dropdown/index.tsx +11 -5
- package/src/client/components/dropdown.old/index.tsx +2 -2
- package/src/client/components/inputv3/base.less +1 -0
- package/src/client/components/inputv3/index.tsx +32 -10
- package/src/client/utils/dom.ts +1 -3
- package/src/common/validation/validators.ts +2 -1
- package/src/server/app/index.ts +1 -0
- package/src/server/services/console/index.ts +6 -4
- package/src/server/services/disks/driver.ts +86 -0
- package/src/server/services/disks/index.ts +63 -0
- package/src/server/services/fetch/index.ts +1 -1
- package/src/server/services/router/index.ts +7 -3
- package/src/server/services/router/response/index.ts +10 -10
- package/src/server/services/security/encrypt/aes.ts +22 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "5htp-core",
|
|
3
3
|
"description": "Convenient TypeScript framework designed for Performance and Productivity.",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.8",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/5htp-core.git",
|
|
7
7
|
"license": "MIT",
|
|
@@ -46,6 +46,7 @@ export type TDialogControls = {
|
|
|
46
46
|
type DialogActions = {
|
|
47
47
|
|
|
48
48
|
setToasts: ( setter: (old: ComponentChild[]) => ComponentChild[]) => void,
|
|
49
|
+
setModals: ( setter: (old: ComponentChild[]) => ComponentChild[]) => void,
|
|
49
50
|
|
|
50
51
|
show: (
|
|
51
52
|
// On utilise une fonction pour pouvoir accéder aux fonctions (close, ...) lors de la déclaration des infos de la toast
|
|
@@ -81,9 +82,13 @@ export const createDialog = (app: Application, isToast: boolean): DialogActions
|
|
|
81
82
|
let onClose: TOnCloseCallback<TReturnType>;
|
|
82
83
|
const id = idA++;
|
|
83
84
|
|
|
85
|
+
const setDialog = isToast
|
|
86
|
+
? instance.setToasts
|
|
87
|
+
: instance.setModals;
|
|
88
|
+
|
|
84
89
|
const close = (retour: TReturnType) => {
|
|
85
90
|
|
|
86
|
-
|
|
91
|
+
setDialog(q => q.filter(m => m.id !== id))
|
|
87
92
|
|
|
88
93
|
if (onClose !== undefined)
|
|
89
94
|
onClose(retour);
|
|
@@ -145,7 +150,7 @@ export const createDialog = (app: Application, isToast: boolean): DialogActions
|
|
|
145
150
|
|
|
146
151
|
render["id"] = id;
|
|
147
152
|
|
|
148
|
-
|
|
153
|
+
setDialog(q => [...q, render]);
|
|
149
154
|
});
|
|
150
155
|
|
|
151
156
|
return {
|
|
@@ -159,6 +164,7 @@ export const createDialog = (app: Application, isToast: boolean): DialogActions
|
|
|
159
164
|
show: show,
|
|
160
165
|
|
|
161
166
|
setToasts: undefined as unknown as DialogActions["setToasts"],
|
|
167
|
+
setModals: undefined as unknown as DialogActions["setModals"],
|
|
162
168
|
|
|
163
169
|
confirm: (title: string, content: string | ComponentChild, defaultBtn: 'Yes'|'No' = 'No') => show<boolean>(({ close }) => (
|
|
164
170
|
<div class="card col">
|
|
@@ -228,16 +234,19 @@ export default () => {
|
|
|
228
234
|
|
|
229
235
|
const app = useContext();
|
|
230
236
|
|
|
231
|
-
const [
|
|
237
|
+
const [modals, setModals] = React.useState<ComponentChild[]>([]);
|
|
238
|
+
const [toasts, setToasts] = React.useState<ComponentChild[]>([]);
|
|
232
239
|
|
|
233
|
-
if (app.side === 'client')
|
|
234
|
-
app.modal.
|
|
240
|
+
if (app.side === 'client') {
|
|
241
|
+
app.modal.setModals = setModals;
|
|
242
|
+
app.toast.setToasts = setToasts;
|
|
243
|
+
}
|
|
235
244
|
|
|
236
245
|
React.useEffect(() => {
|
|
237
246
|
|
|
238
247
|
console.log('Updated toast list');
|
|
239
248
|
|
|
240
|
-
const modals = document.querySelectorAll("#
|
|
249
|
+
const modals = document.querySelectorAll("#modals > .modal");
|
|
241
250
|
if (modals.length === 0)
|
|
242
251
|
return;
|
|
243
252
|
|
|
@@ -259,10 +268,18 @@ export default () => {
|
|
|
259
268
|
|
|
260
269
|
});
|
|
261
270
|
|
|
262
|
-
return
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
271
|
+
return <>
|
|
272
|
+
{modals.length !== 0 ? (
|
|
273
|
+
<div id="modals">
|
|
274
|
+
{modals}
|
|
275
|
+
</div>
|
|
276
|
+
) : null}
|
|
277
|
+
|
|
278
|
+
{toasts.length !== 0 ? (
|
|
279
|
+
<div id="toasts">
|
|
280
|
+
{toasts}
|
|
281
|
+
</div>
|
|
282
|
+
) : null}
|
|
283
|
+
</>
|
|
267
284
|
|
|
268
285
|
}
|
|
@@ -169,7 +169,6 @@ export default ({
|
|
|
169
169
|
) : title}
|
|
170
170
|
|
|
171
171
|
{(!prison && close) && (
|
|
172
|
-
|
|
173
172
|
<Button class="close" icon="solid/times" size="s" shape="pill" onClick={async () => {
|
|
174
173
|
if (typeof close === "function") {
|
|
175
174
|
|
|
@@ -179,7 +178,6 @@ export default ({
|
|
|
179
178
|
close(false);
|
|
180
179
|
}
|
|
181
180
|
}} />
|
|
182
|
-
|
|
183
181
|
)}
|
|
184
182
|
|
|
185
183
|
</header>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@toast-zindex: 999;
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
#modals, #toasts {
|
|
4
4
|
|
|
5
5
|
z-index: @toast-zindex;
|
|
6
6
|
|
|
@@ -10,11 +10,10 @@
|
|
|
10
10
|
justify-content: flex-end;
|
|
11
11
|
gap: @spacing;
|
|
12
12
|
padding: @spacing;
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
&,
|
|
15
15
|
> .modal {
|
|
16
16
|
position: fixed;
|
|
17
|
-
top: 0px;
|
|
18
17
|
left: 0px;
|
|
19
18
|
right: 0px;
|
|
20
19
|
bottom: 0px;
|
|
@@ -35,40 +34,13 @@
|
|
|
35
34
|
margin-top: 0;
|
|
36
35
|
}
|
|
37
36
|
}
|
|
37
|
+
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
> .card {
|
|
41
|
-
|
|
42
|
-
text-align: left;
|
|
43
|
-
max-width: 450px;
|
|
44
|
-
z-index: 999;
|
|
45
|
-
cursor: pointer;
|
|
46
|
-
padding-right: @spacing * 1.5;
|
|
47
|
-
|
|
48
|
-
animation: aff-toast 0.1s ease;
|
|
49
|
-
@keyframes aff-toast {
|
|
50
|
-
0% {
|
|
51
|
-
opacity: 0.5;
|
|
52
|
-
transform: scale(0.5);
|
|
53
|
-
}
|
|
54
|
-
100% {
|
|
55
|
-
opacity: 1;
|
|
56
|
-
transform: scale(1);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
> i {
|
|
61
|
-
color: @c1;
|
|
62
|
-
flex: 0 0 1em;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
h2 {
|
|
66
|
-
font-size: 1em;
|
|
67
|
-
}
|
|
39
|
+
#modals {
|
|
68
40
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
41
|
+
&,
|
|
42
|
+
> .modal {
|
|
43
|
+
top: 0px;
|
|
72
44
|
}
|
|
73
45
|
|
|
74
46
|
> .modal {
|
|
@@ -136,6 +108,43 @@
|
|
|
136
108
|
}
|
|
137
109
|
}
|
|
138
110
|
|
|
111
|
+
#toasts {
|
|
112
|
+
// Toast
|
|
113
|
+
> .card {
|
|
114
|
+
|
|
115
|
+
text-align: left;
|
|
116
|
+
max-width: 450px;
|
|
117
|
+
z-index: 999;
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
padding-right: @spacing * 1.5;
|
|
120
|
+
|
|
121
|
+
animation: aff-toast 0.1s ease;
|
|
122
|
+
@keyframes aff-toast {
|
|
123
|
+
0% {
|
|
124
|
+
opacity: 0.5;
|
|
125
|
+
transform: scale(0.5);
|
|
126
|
+
}
|
|
127
|
+
100% {
|
|
128
|
+
opacity: 1;
|
|
129
|
+
transform: scale(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
> i {
|
|
134
|
+
color: @c1;
|
|
135
|
+
flex: 0 0 1em;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
h2 {
|
|
139
|
+
font-size: 1em;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
p {
|
|
143
|
+
text-align: left;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
139
148
|
// Selecteur moins profond pour que les clases utilitaires (w-a-x) soient prioritaires
|
|
140
149
|
.modal > .card {
|
|
141
150
|
max-width: 500px;
|
|
@@ -158,6 +158,9 @@ export default function useForm<TFormData extends {}>(
|
|
|
158
158
|
if (fields.current === null || Object.keys(schema).join(',') !== Object.keys(fields.current).join(',')) {
|
|
159
159
|
fields.current = {}
|
|
160
160
|
for (const fieldName in schema.fields) {
|
|
161
|
+
|
|
162
|
+
const validator = schema.fields[fieldName];
|
|
163
|
+
|
|
161
164
|
fields.current[fieldName] = {
|
|
162
165
|
|
|
163
166
|
// Value control
|
|
@@ -182,8 +185,8 @@ export default function useForm<TFormData extends {}>(
|
|
|
182
185
|
|
|
183
186
|
// Error
|
|
184
187
|
errors: state.errors[fieldName],
|
|
185
|
-
required:
|
|
186
|
-
validator:
|
|
188
|
+
required: validator.options?.opt !== true,
|
|
189
|
+
validator: validator,
|
|
187
190
|
}
|
|
188
191
|
}
|
|
189
192
|
}
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
// Npm
|
|
6
6
|
import React from 'react';
|
|
7
|
-
import type { ComponentChild, RefObject } from 'preact';
|
|
7
|
+
import type { JSX, ComponentChild, RefObject } from 'preact';
|
|
8
8
|
import type { StateUpdater } from 'preact/hooks';
|
|
9
9
|
|
|
10
10
|
// Core
|
|
11
11
|
import Button from '@client/components/button';
|
|
12
12
|
import Input from '@client/components/inputv3';
|
|
13
|
+
import type { TDialogControls } from '@client/components/dropdown';
|
|
13
14
|
|
|
14
15
|
/*----------------------------------
|
|
15
16
|
- TYPES
|
|
@@ -42,7 +43,7 @@ export type Props = (
|
|
|
42
43
|
required?: boolean,
|
|
43
44
|
noneSelection?: false | string,
|
|
44
45
|
currentList: Choice[],
|
|
45
|
-
|
|
46
|
+
refDropdown?: RefObject<TDialogControls>
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
/*----------------------------------
|
|
@@ -53,7 +54,7 @@ export type Props = (
|
|
|
53
54
|
- we don't want the selector to be rendered before the dropdown content is dhown
|
|
54
55
|
- this component is called multiple time
|
|
55
56
|
*/
|
|
56
|
-
export default ({
|
|
57
|
+
export default React.forwardRef<HTMLDivElement, Props>(({
|
|
57
58
|
choices: initChoices,
|
|
58
59
|
validator,
|
|
59
60
|
required,
|
|
@@ -64,8 +65,9 @@ export default ({
|
|
|
64
65
|
inline,
|
|
65
66
|
multiple,
|
|
66
67
|
currentList,
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
refDropdown,
|
|
69
|
+
...otherProps
|
|
70
|
+
}: Props, ref) => {
|
|
69
71
|
|
|
70
72
|
/*----------------------------------
|
|
71
73
|
- INIT
|
|
@@ -102,7 +104,7 @@ export default ({
|
|
|
102
104
|
- RENDER
|
|
103
105
|
----------------------------------*/
|
|
104
106
|
return (
|
|
105
|
-
<div
|
|
107
|
+
<div {...otherProps} className={(inline ? '' : 'card ') + "col al-top " + (otherProps.className || '')} ref={ref}>
|
|
106
108
|
|
|
107
109
|
{enableSearch && (
|
|
108
110
|
<Input icon="search"
|
|
@@ -113,21 +115,6 @@ export default ({
|
|
|
113
115
|
/>
|
|
114
116
|
)}
|
|
115
117
|
|
|
116
|
-
{currentList.length !== 0 && (
|
|
117
|
-
<ul class="col menu">
|
|
118
|
-
{currentList.map(choice => (
|
|
119
|
-
<Button size="s" onClick={() => {
|
|
120
|
-
onChange( current => multiple
|
|
121
|
-
? current.filter(c => c.value !== choice.value)
|
|
122
|
-
: undefined
|
|
123
|
-
);
|
|
124
|
-
}} suffix={<i src="check" class="fg primary" />}>
|
|
125
|
-
{choice.label}
|
|
126
|
-
</Button>
|
|
127
|
-
))}
|
|
128
|
-
</ul>
|
|
129
|
-
)}
|
|
130
|
-
|
|
131
118
|
{choices === null ? (
|
|
132
119
|
<div class="row h-3 al-center">
|
|
133
120
|
<i src="spin" />
|
|
@@ -136,15 +123,28 @@ export default ({
|
|
|
136
123
|
<ul class="col menu">
|
|
137
124
|
{choices.map( choice => {
|
|
138
125
|
const isCurrent = currentList.some(c => c.value === choice.value);
|
|
139
|
-
return
|
|
126
|
+
return (
|
|
140
127
|
<li>
|
|
141
128
|
<Button size="s" onClick={() => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
129
|
+
onChange( current => {
|
|
130
|
+
|
|
131
|
+
console.log("click select", current, multiple, choice);
|
|
132
|
+
|
|
133
|
+
return multiple
|
|
134
|
+
? (isCurrent
|
|
135
|
+
? current.filter(c => c.value !== choice.value)
|
|
136
|
+
: [...(current || []), choice]
|
|
137
|
+
)
|
|
138
|
+
: (isCurrent
|
|
139
|
+
? undefined
|
|
140
|
+
: choice
|
|
141
|
+
)
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!multiple)
|
|
145
|
+
refDropdown?.current?.close(true);
|
|
146
|
+
|
|
147
|
+
}} suffix={ isCurrent && <i src="check" class="fg primary" /> }>
|
|
148
148
|
{/*search.keywords ? (
|
|
149
149
|
<span>
|
|
150
150
|
|
|
@@ -169,4 +169,4 @@ export default ({
|
|
|
169
169
|
)}
|
|
170
170
|
</div>
|
|
171
171
|
)
|
|
172
|
-
}
|
|
172
|
+
})
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
8
|
// Core
|
|
9
|
-
import Dropdown, {
|
|
9
|
+
import Dropdown, { TDropdownControl, Props as DropdownProps } from '@client/components/dropdown';
|
|
10
10
|
|
|
11
11
|
// Specific
|
|
12
12
|
import ChoiceSelector, {
|
|
@@ -36,7 +36,7 @@ export default ({
|
|
|
36
36
|
- INIT
|
|
37
37
|
----------------------------------*/
|
|
38
38
|
|
|
39
|
-
const
|
|
39
|
+
const refDropdown = React.useRef<TDropdownControl>(null);
|
|
40
40
|
|
|
41
41
|
/*----------------------------------
|
|
42
42
|
- ACTIONS
|
|
@@ -56,9 +56,9 @@ export default ({
|
|
|
56
56
|
{props.inline ? (
|
|
57
57
|
<ChoiceSelector {...props} currentList={currentList} />
|
|
58
58
|
) : (
|
|
59
|
-
<Dropdown {...props} content={(
|
|
60
|
-
<ChoiceSelector {...props} currentList={currentList} />
|
|
61
|
-
)} iconR="chevron-down"
|
|
59
|
+
<Dropdown {...props} content={(
|
|
60
|
+
<ChoiceSelector {...props} currentList={currentList} refDropdown={refDropdown} />
|
|
61
|
+
)} iconR="chevron-down" refDropdown={refDropdown}>
|
|
62
62
|
|
|
63
63
|
{currentList.length === 0
|
|
64
64
|
? title
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type TSide = "left"|"top"|"right"|"bottom";
|
|
2
2
|
|
|
3
|
-
const debug =
|
|
3
|
+
const debug = false;
|
|
4
4
|
|
|
5
5
|
export type TPosition = ReturnType<typeof corrigerPosition>
|
|
6
6
|
|
|
@@ -129,7 +129,7 @@ export default function corrigerPosition(
|
|
|
129
129
|
debug && console.log(`[popover] Left: Conservation`, posFinale.left);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
console.log({ posInit, dimsPop, frontieres }, { posFinale });
|
|
132
|
+
debug && console.log({ posInit, dimsPop, frontieres }, { posFinale });
|
|
133
133
|
|
|
134
134
|
return {
|
|
135
135
|
css: posFinale,
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
5
|
// Npm
|
|
6
|
-
import React
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { JSX, ComponentChild } from 'preact';
|
|
7
8
|
import type { StateUpdater } from 'preact/hooks';
|
|
8
9
|
|
|
9
10
|
// Libs
|
|
@@ -15,11 +16,11 @@ import useContexte from '@/client/context';
|
|
|
15
16
|
- TYPES
|
|
16
17
|
----------------------------------*/
|
|
17
18
|
|
|
18
|
-
export type Props = {
|
|
19
|
+
export type Props = JSX.HTMLAttributes<HTMLDivElement> & {
|
|
19
20
|
id?: string,
|
|
20
21
|
|
|
21
22
|
// Display
|
|
22
|
-
content?: JSX.Element
|
|
23
|
+
content?: JSX.Element,
|
|
23
24
|
state: [boolean, StateUpdater<boolean>],
|
|
24
25
|
width?: number | string,
|
|
25
26
|
disable?: boolean
|
|
@@ -53,7 +54,7 @@ export default (props: Props) => {
|
|
|
53
54
|
...autresProps
|
|
54
55
|
} = props;
|
|
55
56
|
|
|
56
|
-
const [position, setPosition] = React.useState<TPosition>(undefined);
|
|
57
|
+
const [position, setPosition] = React.useState<TPosition | undefined>(undefined);
|
|
57
58
|
const refCont = React.useRef<HTMLElement>(null);
|
|
58
59
|
const refContent = React.useRef<HTMLElement>(null);
|
|
59
60
|
|
|
@@ -61,7 +62,7 @@ export default (props: Props) => {
|
|
|
61
62
|
|
|
62
63
|
// Màj visibilite
|
|
63
64
|
React.useEffect(() => {
|
|
64
|
-
if (shown === true) {
|
|
65
|
+
if (shown === true && refContent.current !== null && refCont.current !== null) {
|
|
65
66
|
|
|
66
67
|
// Positionnement si affichage
|
|
67
68
|
setPosition(
|
|
@@ -70,7 +71,7 @@ export default (props: Props) => {
|
|
|
70
71
|
refContent.current,
|
|
71
72
|
false,
|
|
72
73
|
side,
|
|
73
|
-
frame || document.getElementById('page')
|
|
74
|
+
frame || document.getElementById('page') || undefined
|
|
74
75
|
)
|
|
75
76
|
);
|
|
76
77
|
|
|
@@ -100,17 +101,20 @@ export default (props: Props) => {
|
|
|
100
101
|
|
|
101
102
|
let renderedContent: ComponentChild;
|
|
102
103
|
if (active) {
|
|
103
|
-
content = typeof content === 'function' ? React.createElement(content) : content;
|
|
104
|
+
//content = typeof content === 'function' ? React.createElement(content) : content;
|
|
105
|
+
console.log("render content", content);
|
|
104
106
|
renderedContent = React.cloneElement(
|
|
105
107
|
content,
|
|
106
108
|
{
|
|
107
109
|
className: (content.props.className || '')
|
|
108
110
|
+ ' card white popover pd-1'
|
|
109
111
|
+ (position ? ' pos_' + position.cote : ''),
|
|
112
|
+
|
|
110
113
|
ref: (ref: any) => {
|
|
111
114
|
if (ref !== null)
|
|
112
115
|
refContent.current = ref
|
|
113
116
|
},
|
|
117
|
+
|
|
114
118
|
style: {
|
|
115
119
|
...(content.props.style || {}),
|
|
116
120
|
...(position ? {
|
|
@@ -8,18 +8,19 @@ 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
11
|
import Popover from '../containers/Popover';
|
|
13
12
|
|
|
14
13
|
/*----------------------------------
|
|
15
14
|
- TYPES
|
|
16
15
|
----------------------------------*/
|
|
17
16
|
|
|
18
|
-
export type { TDialogControls } from '../Dialog/Manager';
|
|
19
|
-
|
|
20
17
|
export type Props = ButtonProps & {
|
|
21
18
|
content: ComponentChild | (() => ComponentChild),
|
|
22
|
-
|
|
19
|
+
refDropdown?: RefObject<TDropdownControl>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type TDropdownControl = {
|
|
23
|
+
close: () => void
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/*----------------------------------
|
|
@@ -29,7 +30,7 @@ export default (props: Props) => {
|
|
|
29
30
|
|
|
30
31
|
let {
|
|
31
32
|
content,
|
|
32
|
-
|
|
33
|
+
refDropdown,
|
|
33
34
|
...buttonProps
|
|
34
35
|
} = props;
|
|
35
36
|
|
|
@@ -37,6 +38,11 @@ export default (props: Props) => {
|
|
|
37
38
|
|
|
38
39
|
const refButton = React.useRef<HTMLElement>(null);
|
|
39
40
|
|
|
41
|
+
if (refDropdown)
|
|
42
|
+
refDropdown.current = {
|
|
43
|
+
close: () => popoverState[1]( false )
|
|
44
|
+
}
|
|
45
|
+
|
|
40
46
|
return (
|
|
41
47
|
<Popover content={content} state={popoverState}>
|
|
42
48
|
<Button {...buttonProps} refElem={refButton} />
|
|
@@ -10,7 +10,7 @@ import { ComponentChild } from 'preact';
|
|
|
10
10
|
import Button, { Props as ButtonProps } from '../button';
|
|
11
11
|
|
|
12
12
|
// Libs
|
|
13
|
-
import {
|
|
13
|
+
import { TPopover, PopoverProps } from './Manager';
|
|
14
14
|
import useContexte from '@client/context';
|
|
15
15
|
|
|
16
16
|
/*----------------------------------
|
|
@@ -77,7 +77,7 @@ export default (props: Props) => {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const refButton = React.useRef<HTMLElement>(null);
|
|
80
|
-
const refPopover = React.useRef<
|
|
80
|
+
const refPopover = React.useRef<TPopover>(null);
|
|
81
81
|
|
|
82
82
|
let classe = buttonProps.class === undefined
|
|
83
83
|
? "dropdown"
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
// Npm
|
|
6
6
|
import React from 'react';
|
|
7
|
-
import {
|
|
7
|
+
import { VNode, JSX } from 'preact';
|
|
8
8
|
import TextareaAutosize from 'react-textarea-autosize';
|
|
9
9
|
|
|
10
10
|
// Core libs
|
|
11
11
|
import { useInput, InputBaseProps } from './base';
|
|
12
|
+
import { default as Validator } from '../../../common/validation/validator';
|
|
13
|
+
import type SchemaValidators from '@common/validation/validators';
|
|
12
14
|
|
|
13
15
|
/*----------------------------------
|
|
14
16
|
- TYPES
|
|
@@ -17,20 +19,25 @@ export type Props = {
|
|
|
17
19
|
|
|
18
20
|
// Decoration
|
|
19
21
|
icon?: string,
|
|
20
|
-
prefix?:
|
|
21
|
-
suffix?:
|
|
22
|
+
prefix?: VNode,
|
|
23
|
+
suffix?: VNode,
|
|
22
24
|
iconR?: string,
|
|
23
25
|
|
|
24
26
|
// State
|
|
25
27
|
inputRef?: React.Ref<HTMLInputElement>
|
|
26
28
|
|
|
27
|
-
// Behavior
|
|
28
|
-
type?: 'email' | 'password' | 'longtext' | 'number',
|
|
29
|
-
choice?: string[] | ((input: string) => Promise<string[]>),
|
|
30
|
-
|
|
31
29
|
// Actions
|
|
32
30
|
onPressEnter?: (value: string) => void,
|
|
33
|
-
}
|
|
31
|
+
} & ({
|
|
32
|
+
type?: 'email' | 'password' | 'longtext'
|
|
33
|
+
validator?: Validator<string>,
|
|
34
|
+
|
|
35
|
+
// Behavior
|
|
36
|
+
choice?: string[] | ((input: string) => Promise<string[]>),
|
|
37
|
+
} | {
|
|
38
|
+
type: 'number',
|
|
39
|
+
validator?: ReturnType< SchemaValidators["number"] >,
|
|
40
|
+
})
|
|
34
41
|
|
|
35
42
|
/*----------------------------------
|
|
36
43
|
- COMPOSANT
|
|
@@ -41,7 +48,7 @@ export default ({
|
|
|
41
48
|
// State
|
|
42
49
|
inputRef, errors,
|
|
43
50
|
// Behavior
|
|
44
|
-
type, choice,
|
|
51
|
+
type, choice, validator,
|
|
45
52
|
// Actions
|
|
46
53
|
onPressEnter,
|
|
47
54
|
...props
|
|
@@ -104,7 +111,7 @@ export default ({
|
|
|
104
111
|
suffix = <i src={iconR} />
|
|
105
112
|
|
|
106
113
|
// When no value, show the lable as a placeholder
|
|
107
|
-
if (value === '')
|
|
114
|
+
if (value === '' || value === undefined)
|
|
108
115
|
className += ' empty';
|
|
109
116
|
if (focus)
|
|
110
117
|
className += ' focus';
|
|
@@ -114,6 +121,21 @@ export default ({
|
|
|
114
121
|
if (props.className !== undefined)
|
|
115
122
|
className += ' ' + props.className;
|
|
116
123
|
|
|
124
|
+
/*----------------------------------
|
|
125
|
+
- VALIDATION
|
|
126
|
+
----------------------------------*/
|
|
127
|
+
|
|
128
|
+
// Map vaidation options to input props
|
|
129
|
+
if (validator?.options) {
|
|
130
|
+
if (type === 'number') {
|
|
131
|
+
({
|
|
132
|
+
min: fieldProps.min,
|
|
133
|
+
max: fieldProps.max,
|
|
134
|
+
steps: fieldProps.steps,
|
|
135
|
+
} = validator.options)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
117
139
|
/*----------------------------------
|
|
118
140
|
- RENDER
|
|
119
141
|
----------------------------------*/
|
package/src/client/utils/dom.ts
CHANGED
|
@@ -69,11 +69,9 @@ export const blurable = (...args: [HTMLElement, Function][]) => {
|
|
|
69
69
|
|
|
70
70
|
export const focusContent = ( container: HTMLElement ) => {
|
|
71
71
|
|
|
72
|
-
const toFocus = container.querySelector(
|
|
72
|
+
const toFocus = container.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLButtonElement>(
|
|
73
73
|
'input, textarea, button.btn.primary, footer > button.btn'
|
|
74
74
|
) || container;
|
|
75
75
|
|
|
76
|
-
console.log('Element to focus', toFocus);
|
|
77
|
-
// TODO: Type only docusable elemnts
|
|
78
76
|
toFocus?.focus();
|
|
79
77
|
}
|
|
@@ -41,7 +41,7 @@ const raccourcisMime = {
|
|
|
41
41
|
/*----------------------------------
|
|
42
42
|
- CLASS
|
|
43
43
|
----------------------------------*/
|
|
44
|
-
export default class
|
|
44
|
+
export default class SchemaValidators {
|
|
45
45
|
|
|
46
46
|
/*----------------------------------
|
|
47
47
|
- CONTENEURS
|
|
@@ -185,6 +185,7 @@ export default class SchemaValidator {
|
|
|
185
185
|
public number = (withDecimals: boolean) => ({ ...opts }: TValidator<number> & {
|
|
186
186
|
min?: number,
|
|
187
187
|
max?: number,
|
|
188
|
+
step?: number,
|
|
188
189
|
} = {}) => new Validator<number>('number', (val, input, output, corriger?: boolean) => {
|
|
189
190
|
|
|
190
191
|
// Vérifications suivantes inutiles si des values spécifiques ont été fournies
|
package/src/server/app/index.ts
CHANGED
|
@@ -68,6 +68,7 @@ export default abstract class Application extends Service<Config, Hooks, /* TODO
|
|
|
68
68
|
public: process.cwd() + '/public',
|
|
69
69
|
|
|
70
70
|
// TODO: move to disk
|
|
71
|
+
var: process.cwd() + '/var',
|
|
71
72
|
typings: process.cwd() + '/var/typings',
|
|
72
73
|
cache: process.cwd() + '/var/cache',
|
|
73
74
|
data: process.cwd() + '/var/data',
|
|
@@ -190,10 +190,10 @@ export default class Console extends Service<Config, Hooks, Application> {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
private clean() {
|
|
193
|
-
this.config.debug && console.log(LogPrefix, `Clean logs buffer. Current size:`, this.logs.length, '/', this.config.bufferLimit);
|
|
193
|
+
/*this.config.debug && console.log(LogPrefix, `Clean logs buffer. Current size:`, this.logs.length, '/', this.config.bufferLimit);
|
|
194
194
|
const bufferOverflow = this.logs.length - this.config.bufferLimit;
|
|
195
195
|
if (bufferOverflow > 0)
|
|
196
|
-
this.logs = this.logs.slice(bufferOverflow)
|
|
196
|
+
this.logs = this.logs.slice(bufferOverflow);*/
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
/*----------------------------------
|
|
@@ -213,6 +213,8 @@ export default class Console extends Service<Config, Hooks, Application> {
|
|
|
213
213
|
console.error(`Error caused by this query:`, printedQuery);
|
|
214
214
|
}
|
|
215
215
|
console.error(LogPrefix, `Sending bug report for the following error:`, error);
|
|
216
|
+
if (error.dataForDebugging !== undefined)
|
|
217
|
+
console.error(LogPrefix, `More data about the error:`, error.dataForDebugging);
|
|
216
218
|
|
|
217
219
|
// Prevent spamming the mailbox if infinite loop
|
|
218
220
|
const bugId = ['server', request?.user?.name, undefined, error.message].filter(e => !!e).join('::');
|
|
@@ -230,7 +232,7 @@ export default class Console extends Service<Config, Hooks, Application> {
|
|
|
230
232
|
// On envoi l'email avant l'insertion dans bla bdd
|
|
231
233
|
// Car cette denrière a plus de chances de provoquer une erreur
|
|
232
234
|
const logsHtml = this.printHtml(
|
|
233
|
-
this.logs
|
|
235
|
+
this.logs/*.filter(e => e.channelId === channelId)*/.slice(-100),
|
|
234
236
|
true
|
|
235
237
|
);
|
|
236
238
|
|
|
@@ -372,7 +374,7 @@ export default class Console extends Service<Config, Hooks, Application> {
|
|
|
372
374
|
}
|
|
373
375
|
}
|
|
374
376
|
|
|
375
|
-
return this.printHtml( entries
|
|
377
|
+
return this.printHtml( entries );
|
|
376
378
|
}
|
|
377
379
|
|
|
378
380
|
public printHtml(logs: TLog[], full: boolean = false): string {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPS
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
import Application, { Service } from '@server/app';
|
|
7
|
+
|
|
8
|
+
/*----------------------------------
|
|
9
|
+
- CONFIG
|
|
10
|
+
----------------------------------*/
|
|
11
|
+
|
|
12
|
+
export type THooks = {
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/*----------------------------------
|
|
17
|
+
- TYPE
|
|
18
|
+
----------------------------------*/
|
|
19
|
+
|
|
20
|
+
export type TDrivercnfig = {
|
|
21
|
+
|
|
22
|
+
debug: boolean,
|
|
23
|
+
|
|
24
|
+
rootDir: string,
|
|
25
|
+
buckets: {
|
|
26
|
+
[id: string]: string
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type SourceFile = {
|
|
31
|
+
name: string,
|
|
32
|
+
path: string,
|
|
33
|
+
modified: number,
|
|
34
|
+
parentFolder: string,
|
|
35
|
+
source: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type TOutputFileOptions = {
|
|
39
|
+
encoding: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type TReadFileOptions = {
|
|
43
|
+
encoding?: 'string'|'buffer',
|
|
44
|
+
withMetas?: boolean
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/*----------------------------------
|
|
48
|
+
- CLASS
|
|
49
|
+
----------------------------------*/
|
|
50
|
+
|
|
51
|
+
export default abstract class FsDriver<
|
|
52
|
+
Config extends TDrivercnfig = TDrivercnfig,
|
|
53
|
+
TBucketName = keyof Config["buckets"]
|
|
54
|
+
> {
|
|
55
|
+
|
|
56
|
+
public constructor( public app: Application, public config: Config ) {
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public abstract mount(): Promise<void>;
|
|
61
|
+
|
|
62
|
+
public abstract readDir( bucketName: TBucketName, dirname?: string ): Promise<SourceFile[]>;
|
|
63
|
+
|
|
64
|
+
public abstract readFile(
|
|
65
|
+
bucketName: TBucketName,
|
|
66
|
+
filename: string,
|
|
67
|
+
options: TReadFileOptions
|
|
68
|
+
): Promise<string>;
|
|
69
|
+
|
|
70
|
+
public abstract createReadStream( bucketName: TBucketName, filename: string );
|
|
71
|
+
|
|
72
|
+
public abstract exists( bucketName: TBucketName, filename: string ): Promise<boolean>;
|
|
73
|
+
|
|
74
|
+
public abstract move( bucketName: TBucketName, source: string, destination: string, options: { overwrite?: boolean }): Promise<void>;
|
|
75
|
+
|
|
76
|
+
public abstract outputFile( bucketName: TBucketName, filename: string, content: string, encoding: TOutputFileOptions ): Promise<{
|
|
77
|
+
path: string
|
|
78
|
+
}>;
|
|
79
|
+
|
|
80
|
+
public abstract readJSON( bucketName: TBucketName, filename: string ): Promise<any>;
|
|
81
|
+
|
|
82
|
+
public abstract delete( bucketName: TBucketName, filename: string ): Promise<boolean>;
|
|
83
|
+
|
|
84
|
+
public abstract unmount(): Promise<void>;
|
|
85
|
+
|
|
86
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPS
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Core
|
|
6
|
+
import Application, { Service } from '@server/app';
|
|
7
|
+
|
|
8
|
+
// Specific
|
|
9
|
+
import type Driver from './driver';
|
|
10
|
+
|
|
11
|
+
/*----------------------------------
|
|
12
|
+
- TYPES
|
|
13
|
+
----------------------------------*/
|
|
14
|
+
|
|
15
|
+
type TMountpointList = { [name: string]: Driver }
|
|
16
|
+
|
|
17
|
+
type Config<MountpointList extends TMountpointList> = {
|
|
18
|
+
default: keyof MountpointList,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type Hooks = {
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/*----------------------------------
|
|
26
|
+
- SERVICE
|
|
27
|
+
----------------------------------*/
|
|
28
|
+
export default class DisksManager<
|
|
29
|
+
MountpointList extends TMountpointList = {},
|
|
30
|
+
TConfig extends Config<MountpointList> = Config<MountpointList>,
|
|
31
|
+
TApplication extends Application = Application
|
|
32
|
+
> extends Service<TConfig, Hooks, TApplication> {
|
|
33
|
+
|
|
34
|
+
public default: Driver;
|
|
35
|
+
|
|
36
|
+
public constructor(
|
|
37
|
+
public app: TApplication,
|
|
38
|
+
public config: TConfig,
|
|
39
|
+
public mounted: MountpointList
|
|
40
|
+
) {
|
|
41
|
+
|
|
42
|
+
super(app, config);
|
|
43
|
+
|
|
44
|
+
if (Object.keys( mounted ).length === 0)
|
|
45
|
+
throw new Error("At least one disk should be mounted.");
|
|
46
|
+
|
|
47
|
+
const defaultDisk = mounted[ config.default ];
|
|
48
|
+
if (defaultDisk === undefined)
|
|
49
|
+
console.log(`Default disk "${config.default as string}" not mounted.`);
|
|
50
|
+
|
|
51
|
+
this.default = defaultDisk;
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async register() {
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public async start() {
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
@@ -93,7 +93,7 @@ export default class FetchService extends Service<Config, Hooks, Application> {
|
|
|
93
93
|
|
|
94
94
|
// Convert to webp and finalize
|
|
95
95
|
const processedBuffer = await processing.webp({ quality }).toBuffer().catch(e => {
|
|
96
|
-
console.error(LogPrefix, `Error while processing image at ${
|
|
96
|
+
console.error(LogPrefix, `Error while processing image at ${imageFileUrl}:`, e);
|
|
97
97
|
return null;
|
|
98
98
|
})
|
|
99
99
|
|
|
@@ -21,6 +21,7 @@ import type { GlobImportedWithMetas } from 'babel-plugin-glob-import';
|
|
|
21
21
|
// Core
|
|
22
22
|
import Application, { Service } from '@server/app';
|
|
23
23
|
import context from '@server/context';
|
|
24
|
+
import type DiskDriver from '@server/services/disks/driver';
|
|
24
25
|
import { CoreError, NotFound } from '@common/errors';
|
|
25
26
|
import BaseRouter, {
|
|
26
27
|
TRoute, TErrorRoute, TRouteModule,
|
|
@@ -88,6 +89,8 @@ export type Config<
|
|
|
88
89
|
|
|
89
90
|
debug: boolean,
|
|
90
91
|
|
|
92
|
+
disk: DiskDriver,
|
|
93
|
+
|
|
91
94
|
http: HttpServiceConfig
|
|
92
95
|
|
|
93
96
|
// Set it as a function, so when we instanciate the services, we can callthis.router to pass the router instance in roiuter services
|
|
@@ -282,7 +285,7 @@ export default class ServerRouter<
|
|
|
282
285
|
//await TrackingService.LoadCache();
|
|
283
286
|
|
|
284
287
|
// Generate typescript typings
|
|
285
|
-
if (this.app.env.profile
|
|
288
|
+
if (this.app.env.profile === 'dev')
|
|
286
289
|
this.genTypings();
|
|
287
290
|
|
|
288
291
|
// Ordonne par ordre de priorité
|
|
@@ -550,11 +553,12 @@ declare type Routes = {
|
|
|
550
553
|
// Rapport / debug
|
|
551
554
|
if (code === 500) {
|
|
552
555
|
|
|
556
|
+
// Print the error here so the stacktrace appears in the bug report logs
|
|
557
|
+
console.log(LogPrefix, "Error catched from the router:", e);
|
|
558
|
+
|
|
553
559
|
// Report error
|
|
554
560
|
await this.app.runHook('error', e, request);
|
|
555
561
|
|
|
556
|
-
console.log("ERROR 500 VIA ROUTER", e);
|
|
557
|
-
|
|
558
562
|
// Don't exose technical errors to users
|
|
559
563
|
if (this.app.env.profile === 'prod')
|
|
560
564
|
e.message = "We encountered an internal error, and our team has just been notified. Sorry for the inconvenience.";
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
----------------------------------*/
|
|
9
9
|
|
|
10
10
|
// Npm
|
|
11
|
-
import fs from 'fs-extra';
|
|
12
11
|
import express from 'express';
|
|
13
12
|
|
|
14
13
|
// Core
|
|
@@ -241,28 +240,29 @@ export default class ServerResponse<
|
|
|
241
240
|
}
|
|
242
241
|
|
|
243
242
|
// TODO: https://github.com/adonisjs/http-server/blob/develop/src/Response/index.ts#L430
|
|
244
|
-
public file( fichier: string ) {
|
|
243
|
+
public async file( fichier: string ) {
|
|
245
244
|
|
|
246
245
|
// Securité
|
|
247
246
|
if (fichier.includes('..'))
|
|
248
247
|
throw new Forbidden("Disallowed");
|
|
249
248
|
|
|
250
|
-
// Force absolute path
|
|
251
|
-
if (!fichier.startsWith( this.app.path.root ))
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
249
|
+
// // Force absolute path
|
|
250
|
+
// if (!fichier.startsWith( this.app.path.root ))
|
|
251
|
+
// fichier = fichier[0] === '/'
|
|
252
|
+
// ? this.app.path.root + '/bin' + fichier
|
|
253
|
+
// : this.app.path.data + '/' + fichier;
|
|
255
254
|
|
|
256
|
-
|
|
255
|
+
const disk = this.router.config.disk;
|
|
257
256
|
|
|
258
257
|
// Verif existance
|
|
259
|
-
|
|
258
|
+
const fileExists = await disk.exists('data', fichier);
|
|
259
|
+
if (!fileExists) {
|
|
260
260
|
console.log("File " + fichier + " was not found.");
|
|
261
261
|
throw new NotFound();
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
// envoi fichier
|
|
265
|
-
this.data =
|
|
265
|
+
this.data = await disk.readFile('data', fichier);
|
|
266
266
|
return this.end();
|
|
267
267
|
}
|
|
268
268
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
5
|
// Nodejs
|
|
6
|
-
import crypto from 'crypto';
|
|
6
|
+
import crypto, { Encoding } from 'crypto';
|
|
7
7
|
|
|
8
8
|
// Core
|
|
9
9
|
import Application, { Service } from '@server/app';
|
|
@@ -20,7 +20,11 @@ import { Forbidden } from '@common/errors';
|
|
|
20
20
|
|
|
21
21
|
export type Config = {
|
|
22
22
|
debug?: boolean,
|
|
23
|
+
// Initialisation vector
|
|
24
|
+
// Generate one here: https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx
|
|
23
25
|
iv: string,
|
|
26
|
+
// Define usage-specific keys
|
|
27
|
+
// You can also generate one via the link upper
|
|
24
28
|
keys: {[keyName: string]: string}
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -28,6 +32,14 @@ export type Hooks = {
|
|
|
28
32
|
|
|
29
33
|
}
|
|
30
34
|
|
|
35
|
+
type TEncryptOptions = {
|
|
36
|
+
encoding: Encoding
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type TDecryptOptions = {
|
|
40
|
+
encoding: Encoding
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
/*----------------------------------
|
|
32
44
|
- SERVICE
|
|
33
45
|
----------------------------------*/
|
|
@@ -41,27 +53,31 @@ export default class AES<TConfig extends Config = Config> extends Service<TConfi
|
|
|
41
53
|
|
|
42
54
|
}
|
|
43
55
|
|
|
44
|
-
public encrypt( keyName: keyof TConfig["keys"], data: any
|
|
56
|
+
public encrypt( keyName: keyof TConfig["keys"], data: any, options: TEncryptOptions = {
|
|
57
|
+
encoding: 'base64url'
|
|
58
|
+
}) {
|
|
45
59
|
|
|
46
60
|
const encKey = this.config.keys[ keyName as keyof typeof this.config.keys ];
|
|
47
61
|
|
|
48
62
|
data = JSON.stringify(data);
|
|
49
63
|
|
|
50
64
|
let cipher = crypto.createCipheriv('aes-256-cbc', encKey, this.config.iv);
|
|
51
|
-
let encrypted = cipher.update(data, 'utf8',
|
|
52
|
-
encrypted += cipher.final(
|
|
65
|
+
let encrypted = cipher.update(data, 'utf8', options.encoding);
|
|
66
|
+
encrypted += cipher.final(options.encoding);
|
|
53
67
|
return encrypted;
|
|
54
68
|
|
|
55
69
|
}
|
|
56
70
|
|
|
57
|
-
public decrypt( keyName: keyof TConfig["keys"], data: string
|
|
71
|
+
public decrypt( keyName: keyof TConfig["keys"], data: string, options: TDecryptOptions = {
|
|
72
|
+
encoding: 'base64url'
|
|
73
|
+
}) {
|
|
58
74
|
|
|
59
75
|
const encKey = this.config.keys[ keyName as keyof typeof this.config.keys ];
|
|
60
76
|
|
|
61
77
|
try {
|
|
62
78
|
|
|
63
79
|
let decipher = crypto.createDecipheriv('aes-256-cbc', encKey, this.config.iv);
|
|
64
|
-
let decrypted = decipher.update(data,
|
|
80
|
+
let decrypted = decipher.update(data, options.encoding, 'utf8');
|
|
65
81
|
return JSON.parse(decrypted + decipher.final('utf8'));
|
|
66
82
|
|
|
67
83
|
} catch (error) {
|