5htp-core 0.6.2 → 0.6.3

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.
Files changed (33) hide show
  1. package/client/app/index.ts +2 -1
  2. package/client/assets/css/components/table.less +1 -0
  3. package/client/components/Input.tsx +0 -2
  4. package/client/components/Rte/Editor.tsx +2 -0
  5. package/client/components/Rte/index.tsx +0 -1
  6. package/client/services/router/request/api.ts +0 -9
  7. package/common/router/request/api.ts +0 -8
  8. package/package.json +1 -1
  9. package/server/app/container/console/index.ts +65 -48
  10. package/server/app/index.ts +19 -8
  11. package/server/app/service/index.ts +55 -15
  12. package/server/services/auth/router/index.ts +3 -1
  13. package/server/services/disks/driver.ts +5 -1
  14. package/server/services/disks/drivers/s3/index.ts +2 -2
  15. package/server/services/disks/index.ts +10 -5
  16. package/server/services/email/index.ts +1 -1
  17. package/server/services/prisma/Facet.ts +39 -15
  18. package/server/services/prisma/index.ts +5 -7
  19. package/server/services/router/http/multipart.ts +5 -0
  20. package/server/services/router/index.ts +50 -35
  21. package/server/services/router/request/api.ts +0 -12
  22. package/server/services/router/request/validation/zod.ts +180 -0
  23. package/server/services/router/response/index.ts +14 -9
  24. package/server/services/router/response/page/document.tsx +5 -3
  25. package/server/services/router/service.ts +7 -4
  26. package/server/services/schema/request.ts +21 -34
  27. package/server/services/schema/router/index.ts +3 -3
  28. package/types/icons.d.ts +1 -1
  29. package/common/data/input/validate.ts +0 -54
  30. package/server/services/router/request/validation/index.ts +0 -23
  31. package/server/services/router/request/validation/schema.ts +0 -211
  32. package/server/services/router/request/validation/validator.ts +0 -117
  33. package/server/services/router/request/validation/validators.ts +0 -485
@@ -1,485 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
-
5
- // Npm
6
- import trim from 'validator/lib/trim';
7
- import isISO8601 from 'validator/lib/isISO8601';
8
- import toDate from 'validator/lib/toDate';
9
- import isEmail from 'validator/lib/isEmail';
10
- import isURL from 'validator/lib/isURL';
11
-
12
- import normalizeUrl, { Options as NormalizeUrlOptions } from 'normalize-url';
13
-
14
- // Core
15
- import { InputError } from '@common/errors';
16
-
17
- // Speciific
18
- import Schema, { TSchemaFields } from './schema'
19
- import Validator, { TValidatorOptions, EXCLUDE_VALUE, TValidatorDefinition } from './validator'
20
-
21
- /*----------------------------------
22
- - TYPES
23
- ----------------------------------*/
24
-
25
- export type TFileValidator = TValidatorOptions<File> & {
26
- type?: string[], // Raccourci, ou liste de mimetype
27
- taille?: number,
28
- disk?: string, // Disk to upload files to
29
- }
30
-
31
- type TSchemaSubtype = Schema<{}> | TSchemaFields | TValidatorDefinition;
32
-
33
- type TSubtype = TSchemaSubtype | Validator<any> | TValidatorDefinition;
34
-
35
- /*----------------------------------
36
- - CONST
37
- ----------------------------------*/
38
-
39
- export type TRichTextValidatorOptions = {
40
- attachements?: TFileValidator
41
- }
42
-
43
- export const getFieldValidator = (field: TValidatorDefinition) => {
44
-
45
- if (Array.isArray(field)) {
46
-
47
- const [validatorName, validatorArgs] = field;
48
- const getValidator = validators[validatorName];
49
- if (getValidator === undefined)
50
- throw new Error('Unknown validator: ' + validatorName);
51
-
52
- return getValidator(...validatorArgs);
53
-
54
- // TSchemaFields
55
- } else if (field.constructor === Object) {
56
-
57
- return new Schema(field as TSchemaFields);
58
-
59
- // Schema
60
- } else
61
- return field as Validator<any>
62
- }
63
-
64
- // Recursive function to validate each node
65
- function validateLexicalNode(node: any, opts: TRichTextValidatorOptions ) {
66
-
67
- // Each node should be an object with a `type` property
68
- if (typeof node !== 'object' || !node.type || typeof node.type !== 'string')
69
- throw new InputError("Invalid rich text value (3).");
70
-
71
- // Validate text nodes
72
- if (node.type === 'text') {
73
-
74
- if (typeof node.text !== 'string')
75
- throw new InputError("Invalid rich text value (4).");
76
-
77
- // Validate paragraph, heading, or other structural nodes that may contain children
78
- } else if (['paragraph', 'heading', 'list', 'listitem'].includes(node.type)) {
79
-
80
- if (!Array.isArray(node.children) || !node.children.every(children => validateLexicalNode(children, opts))) {
81
- throw new InputError("Invalid rich text value (5).");
82
- }
83
-
84
- // Files upload
85
- } else if (node.type === 'image') {
86
-
87
- // Check if allowed
88
- /*if (opts.attachements === undefined)
89
- throw new InputError("Image attachments not allowed in this rich text field.");*/
90
-
91
- // TODO: check mime
92
-
93
-
94
- // Upload file
95
-
96
-
97
- }
98
-
99
- return true;
100
- }
101
-
102
- /*----------------------------------
103
- - CLASS
104
- ----------------------------------*/
105
- export class SchemaValidators {
106
-
107
- /*----------------------------------
108
- - UTILITIES
109
- ----------------------------------*/
110
- // Make every field optional
111
- public partial = <TFields extends TSchemaFields>(schema: TFields, fieldsList?: (keyof TFields)[] ) => {
112
-
113
- if (fieldsList === undefined)
114
- fieldsList = Object.keys(schema) as (keyof TFields)[];
115
-
116
- const partialSchema: Partial<TFields> = {};
117
- for (const key of fieldsList) {
118
-
119
- if (!(key in schema))
120
- throw new Error("The field " + key + " is not in the schema.");
121
-
122
- // Only if validator
123
- if (schema[key] instanceof Validator)
124
- partialSchema[key] = new Validator(schema[key].type, schema[key].validateType, {
125
- ...schema[key].options,
126
- opt: true
127
- });
128
- else
129
- partialSchema[key] = schema[key];
130
- }
131
-
132
- return partialSchema as TFields;
133
- }
134
-
135
- /*----------------------------------
136
- - CONTENEURS
137
- ----------------------------------*/
138
- public object = ( subtype?: TSchemaSubtype, { ...opts }: TValidatorOptions<object> & {
139
-
140
- } = {}) =>
141
- new Validator<object>('object', (val, options, path) => {
142
-
143
- // The value should be an object
144
- if (typeof val !== 'object' || val.constructor !== Object)
145
- throw new InputError("This value must be an object.");
146
-
147
- // If no subtype, return the object as is
148
- if (subtype === undefined)
149
- return val;
150
-
151
- // If subtype is a schema
152
- const schema = getFieldValidator(subtype) as Schema<{}>;
153
-
154
- // Validate schema
155
- const value = schema.validate(val, options, path);
156
-
157
- return value;
158
- }, opts)
159
-
160
- public array = ( subtype: TSubtype, { choice, min, max, ...opts }: TValidatorOptions<any[]> & {
161
- choice?: any[],
162
- min?: number,
163
- max?: number
164
- } = {}) => new Validator<any[]>('array', (items, options, path) => {
165
-
166
- // Type
167
- if (!Array.isArray(items))
168
- throw new InputError("This value must be a list.");
169
-
170
- // Items number
171
- if ((min !== undefined && items.length < min))
172
- throw new InputError(`Please select at least ${min} items.`);
173
- if ((max !== undefined && items.length > max))
174
- throw new InputError(`Please select maximum ${max} items.`);
175
-
176
- // Verif each item
177
- if (subtype === undefined)
178
- return items;
179
-
180
- const validator = getFieldValidator(subtype);
181
-
182
- items = items.map( item =>
183
- validator.validate( item, options, path )
184
- )
185
-
186
- return items;
187
- }, {
188
- ...opts,
189
- //multiple: true, // Sélection multiple
190
- })
191
-
192
- public choice = (choices?: any[], { multiple, ...opts }: TValidatorOptions<any> & {
193
- multiple?: boolean
194
- } = {}) => new Validator<any>('choice', (val, options, path) => {
195
-
196
- // Empty array = undefined if not required
197
- if (val.length === 0 && opts.opt)
198
- return undefined;
199
-
200
- // Normalize for verifications
201
- const choicesValues = choices?.map(v => typeof v === 'object' ? v.value : v)
202
-
203
- const checkChoice = ( choice: any ) => {
204
-
205
- // Choice object = extract value
206
- // We check for choice objec via the label prop, as the value can be undefined (and so, not transmitted)
207
- if (typeof choice === 'object' && ('label' in choice) && typeof choice.label === 'string' && typeof choice.value !== 'object')
208
- choice = choice.value;
209
-
210
- // If choices list rpovided, check if the choice is in the choices list
211
- if (choicesValues !== undefined && !choicesValues.includes(choice))
212
- throw new InputError("Invalid value: " + choice + ". Must be: " + choicesValues.join(', '));
213
-
214
- return choice;
215
-
216
- }
217
-
218
- // Check every choice
219
- if (Array.isArray( val ))
220
- val = val.map(checkChoice)
221
- else
222
- val = checkChoice( val );
223
-
224
- return val;
225
-
226
- }, opts, { choices, multiple })
227
-
228
- /*----------------------------------
229
- - CHAINES
230
- ----------------------------------*/
231
- public string = ({ min, max, in: choices, ...opts }: TValidatorOptions<string> & {
232
- min?: number,
233
- max?: number,
234
- in?: string[]
235
- } = {}) => new Validator<string>('string', (val, options, path) => {
236
-
237
- // Check type
238
- if (val === '')
239
- return undefined;
240
- else if (typeof val === 'number')
241
- return val.toString();
242
- else if (typeof val !== 'string')
243
- throw new InputError("This value must be a string.");
244
-
245
- // Whitespace
246
- val = trim(val);
247
-
248
- // In
249
- if (choices !== undefined && !choices.includes(val))
250
- throw new InputError(`Invalid value: ${val}. Must be one of: ${choices.join(', ')}`);
251
-
252
- // Min size
253
- if (min !== undefined && val.length < min)
254
- throw new InputError(`Must be at least ` + min + ' characters');
255
-
256
- // Max size
257
- if (max !== undefined && val.length > max)
258
- if (options?.autoCorrect)
259
- val = val.substring(0, max);
260
- else
261
- throw new InputError(`Must be up to ` + max + ' characters');
262
-
263
- return val;
264
-
265
- }, opts)
266
-
267
- public url = (opts: TValidatorOptions<string> & {
268
- normalize?: NormalizeUrlOptions
269
- } = {}) =>
270
- new Validator<string>('url', (inputVal, options, path) => {
271
-
272
- let val = this.string(opts).validate(inputVal, options, path);
273
-
274
- // Check if URL
275
- if (!isURL(val, {
276
- // https://www.npmjs.com/package/validator
277
- }))
278
- throw new InputError(`Please provide a valid URL.`);
279
-
280
- // Normalize
281
- if (opts.normalize !== undefined)
282
- val = normalizeUrl(val, opts.normalize);
283
-
284
- return val;
285
- }, opts)
286
-
287
- public email = (opts: TValidatorOptions<string> & {} = {}) =>
288
- new Validator<string>('email', (inputVal, options, path) => {
289
-
290
- let val = this.string(opts).validate(inputVal, options, path);
291
-
292
- if (!isEmail(val))
293
- throw new InputError("Please enter a valid email address.");
294
-
295
- // Disable normalzation !!! We should keep the email as it was entered by the user
296
- /*const normalizedEmail = normalizeEmail(val, {
297
- all_lowercase: true,
298
- gmail_lowercase: true,
299
- gmail_remove_dots: false,
300
- gmail_remove_subaddress: true,
301
- gmail_convert_googlemaildotcom: true,
302
-
303
- outlookdotcom_lowercase: true,
304
- outlookdotcom_remove_subaddress: true,
305
-
306
- yahoo_lowercase: true,
307
- yahoo_remove_subaddress: true,
308
-
309
- yandex_lowercase: true,
310
-
311
- icloud_lowercase: true,
312
- icloud_remove_subaddress: true,
313
- });*/
314
-
315
- const normalizedEmail = val.toLowerCase();
316
- console.log("validate email, inou", val, normalizedEmail);
317
-
318
- return normalizedEmail;
319
- }, opts)
320
-
321
- /*----------------------------------
322
- - NOMBRES
323
- ----------------------------------*/
324
- // On ne spread pas min et max afin quils soient passés dans les props du composant
325
- public number = (withDecimals: boolean) => ({ ...opts }: TValidatorOptions<number> & {
326
- min?: number,
327
- max?: number,
328
- step?: number,
329
- } = {}) => new Validator<number>('number', (val, options, path) => {
330
-
331
- // Tente conversion chaine en nombre
332
- if (typeof val === 'string')
333
- val = withDecimals ? parseFloat(val) : parseInt(val);
334
-
335
- if (opts.min === undefined)
336
- opts.min = 0;
337
-
338
- // Type de donnée
339
- if (Number.isNaN(val) || typeof val !== 'number') {
340
- if (options?.autoCorrect)
341
- val = opts.min;
342
- else
343
- throw new InputError("This value must be a number.");
344
- }
345
-
346
- // Minimum
347
- if (val < opts.min)
348
- if (options?.autoCorrect)
349
- val = opts.min;
350
- else
351
- throw new InputError(`Must be at least ` + opts.min);
352
-
353
- // Maximum
354
- if (opts.max !== undefined && val > opts.max)
355
- if (options?.autoCorrect)
356
- val = opts.max;
357
- else
358
- throw new InputError(`Must be up to ` + opts.max);
359
-
360
- return val;
361
- }, {
362
- // Force une valeur par défaut si requis
363
- defaut: opts.opt ? undefined : (opts.min || 0),
364
- ...opts,
365
- })
366
-
367
- public int = this.number(false)
368
-
369
- public float = this.number(true)
370
-
371
- public bool = (opts: TValidatorOptions<boolean> & {} = {}) =>
372
- new Validator<boolean>('bool', (val, options, path) => {
373
-
374
- if (typeof val !== 'boolean' && !['true', 'false'].includes(val))
375
- throw new InputError("This value must be a boolean.");
376
-
377
- val = !!val;
378
-
379
- return val;
380
- }, {
381
- defaut: false,
382
- ...opts
383
- })
384
-
385
- /*----------------------------------
386
- - AUTRES
387
- ----------------------------------*/
388
- public date = (opts: TValidatorOptions<Date> & {
389
-
390
- } = {}) => new Validator<Date>('date', (val, options, path) => {
391
-
392
- const chaine = typeof val == 'string';
393
-
394
- // Chaine = format iso
395
- if (chaine) {
396
-
397
- if (!isISO8601(val))
398
- throw new InputError("This value must be a date.");
399
-
400
- val = toDate(val);
401
-
402
- } else if (!(val instanceof Date))
403
- throw new InputError("This value must be a date.");
404
-
405
- return val;
406
-
407
- }, {
408
- //defaut: new Date,
409
- ...opts,
410
- })
411
-
412
- public richText(opts: TValidatorOptions<string> & TRichTextValidatorOptions = {}) {
413
- return new Validator<string>('richText', (val, options, path) => {
414
-
415
- // We get a stringified json as input since the editor workds with JSON string
416
- try {
417
- val = JSON.parse(val);
418
- } catch (error) {
419
- console.error("Failed to parse rich text json:", error, val);
420
- throw new InputError("Invalid rich text format.");
421
- }
422
-
423
- // Check that the root exists and has a valid type
424
- if (!val || typeof val !== 'object' || typeof val.root !== 'object' || val.root.type !== 'root')
425
- throw new InputError("Invalid rich text value (1).");
426
-
427
- // Check if root has children array
428
- if (!Array.isArray(val.root.children))
429
- throw new InputError("Invalid rich text value (2).");
430
-
431
- // Validate each child node in root
432
- for (const child of val.root.children) {
433
- validateLexicalNode(child, opts);
434
- }
435
-
436
- return val;
437
-
438
- }, {
439
- //defaut: new Date,
440
- ...opts,
441
- })
442
- }
443
-
444
- /*----------------------------------
445
- - FICHIER
446
- ----------------------------------*/
447
- public file = ({ type, taille, ...opts }: TFileValidator & {
448
-
449
- } = {}) => new Validator<File>('file', (val, options, path) => {
450
-
451
- // Chaine = url ancien fichier = exclusion de la valeur pour conserver l'ancien fichier
452
- // NOTE: Si la valeur est présente mais undefined, alors on supprimera le fichier
453
- if (typeof val === 'string')
454
- return EXCLUDE_VALUE;
455
-
456
- if (!(val instanceof File))
457
- throw new InputError(`Must be a File (${typeof val} received)`);
458
-
459
- // MIME
460
- if (type !== undefined) {
461
-
462
- const mimeMatch = type.some( t => t === val.type || val.type.startsWith(t + '/') );
463
- if (!mimeMatch)
464
- throw new InputError('Only the following formats are allowed: ' + type.join(', ') + '. The file you gave is ' + val.type + '.');
465
-
466
- }
467
-
468
- // Taille
469
- if (taille) {
470
- const tailleFichier = val.size / 1024 / 1024; // Mo
471
- if (tailleFichier > taille)
472
- throw new InputError(`Le fichier ne doit pas faire plus de ${taille} Mo (taille reçue: ${tailleFichier} Mo)`);
473
- }
474
-
475
- return val;
476
-
477
- }, {
478
- //defaut: new Date,
479
- ...opts,
480
- })
481
- }
482
-
483
- const validators = new SchemaValidators();
484
-
485
- export default validators;