5htp-core 0.2.1 → 0.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.
Files changed (68) hide show
  1. package/package.json +10 -3
  2. package/src/client/app/index.ts +2 -2
  3. package/src/client/assets/css/components/card.less +0 -3
  4. package/src/client/assets/css/components/other.less +2 -4
  5. package/src/client/assets/css/components/table.less +1 -2
  6. package/src/client/assets/css/components.less +4 -0
  7. package/src/client/assets/css/core.less +0 -1
  8. package/src/client/assets/css/theme.less +2 -2
  9. package/src/client/assets/css/utils/medias.less +21 -0
  10. package/src/client/components/Card/index.tsx +8 -5
  11. package/src/client/components/Dialog/index.less +3 -3
  12. package/src/client/components/Row/index.less +2 -0
  13. package/src/client/components/Row/index.tsx +44 -10
  14. package/src/client/components/Video/index.less +39 -0
  15. package/src/client/components/Video/index.tsx +69 -0
  16. package/src/client/components/containers/Popover/index.tsx +2 -2
  17. package/src/client/components/data/Time.tsx +1 -1
  18. package/src/client/components/data/progressbar/circular/index.tsx +1 -1
  19. package/src/client/components/index.ts +24 -8
  20. package/src/client/components/input/BaseV2/index.tsx +0 -1
  21. package/src/client/components/{input/BaseV2/index.less → inputv3/base.less} +1 -1
  22. package/src/client/components/inputv3/base.tsx +73 -0
  23. package/src/client/components/{input/UploadImage → inputv3/file}/Bouton.tsx +0 -0
  24. package/src/client/components/inputv3/file/FileToUpload.ts +34 -0
  25. package/src/client/components/inputv3/file/index.less +59 -0
  26. package/src/client/components/inputv3/file/index.tsx +157 -0
  27. package/src/client/components/{input → inputv3/string}/index.tsx +41 -27
  28. package/src/client/pages/bug.tsx +3 -4
  29. package/src/client/services/router/index.tsx +0 -1
  30. package/src/client/services/router/request/api.ts +20 -12
  31. package/src/client/services/router/request/multipart.ts +27 -0
  32. package/src/common/data/chaines/greetings.ts +1 -1
  33. package/src/common/data/dates.ts +1 -1
  34. package/src/common/data/input/validate.ts +0 -9
  35. package/src/common/data/markdown.ts +1 -1
  36. package/src/common/errors/index.ts +16 -12
  37. package/src/common/router/request/api.ts +11 -3
  38. package/src/common/validation/schema.ts +21 -20
  39. package/src/common/validation/validators.ts +3 -6
  40. package/src/server/app/commands.ts +149 -0
  41. package/src/server/app/index.ts +23 -4
  42. package/src/server/app/service.ts +4 -0
  43. package/src/server/services/cache/commands.ts +41 -0
  44. package/src/server/services/cache/index.ts +102 -34
  45. package/src/server/services/console/index.ts +1 -1
  46. package/src/server/services/database/connection.ts +38 -22
  47. package/src/server/services/database/datatypes.ts +51 -12
  48. package/src/server/services/database/index.ts +133 -40
  49. package/src/server/services/database/metas.ts +63 -37
  50. package/src/server/services/database/repository.ts +26 -0
  51. package/src/server/services/email/index.ts +102 -42
  52. package/src/server/services/fetch/index.ts +110 -0
  53. package/src/server/services/router/http/multipart.ts +70 -41
  54. package/src/server/services/router/index.ts +35 -4
  55. package/src/server/services/router/request/index.ts +8 -6
  56. package/src/server/services/schema/index.ts +4 -11
  57. package/src/server/services/schema/request.ts +16 -7
  58. package/src/server/services/schema/router.ts +6 -2
  59. package/src/server/{services_old → services/security/encrypt}/aes.ts +33 -14
  60. package/src/server/services/users/index.ts +3 -3
  61. package/src/server/services/users/router/index.ts +0 -2
  62. package/src/types/global/utils.d.ts +11 -1
  63. package/tsconfig.common.json +3 -0
  64. package/src/client/components/input/Textarea.tsx +0 -57
  65. package/src/client/components/input/Upload.tsx +0 -5
  66. package/src/client/components/input/UploadImage/index.less +0 -93
  67. package/src/client/components/input/UploadImage/index.tsx +0 -220
  68. package/src/common/data/file.ts +0 -25
@@ -187,7 +187,8 @@ export default class DatabaseConnection extends Service<DatabaseServiceConfig, T
187
187
 
188
188
  const mysqlType = {
189
189
  name: field.type as TMySQLTypeName,
190
- params: []
190
+ params: [],
191
+ raw: 'undefined', // Not needed here
191
192
  }
192
193
 
193
194
  let jsTypeName = mysqlToJs[ mysqlType.name ];
@@ -200,7 +201,8 @@ export default class DatabaseConnection extends Service<DatabaseServiceConfig, T
200
201
  sql: mysqlType,
201
202
  js: {
202
203
  name: jsTypeName,
203
- params: []
204
+ params: [],
205
+ raw: 'undefined', // Not needed here
204
206
  }
205
207
  }
206
208
  }
@@ -244,6 +246,23 @@ export default class DatabaseConnection extends Service<DatabaseServiceConfig, T
244
246
 
245
247
  }
246
248
 
249
+ public checkValue( value: unknown, column: TMetasColonne ): string | false {
250
+
251
+ const jsType = jsTypes[ column.type.js.name ];
252
+
253
+ const isValid = (value === undefined || value === null)
254
+ ? column.optional
255
+ : jsType.check( value, column );
256
+
257
+ if (isValid)
258
+ return false;
259
+
260
+ console.log(column.type.sql.params)
261
+
262
+ return `Data expected to match: ${column.type.js.raw} (MySQL: ${column.type.sql.raw}) ||. Got: ` + JSON.stringify(value);;
263
+
264
+ }
265
+
247
266
  /*----------------------------------
248
267
  - QUERY
249
268
  ----------------------------------*/
@@ -254,30 +273,30 @@ export default class DatabaseConnection extends Service<DatabaseServiceConfig, T
254
273
  return new Bucket(queryOptions, queriesList);
255
274
  }
256
275
 
257
- public async query<TResult extends TQueryResult>(
276
+ public query<TResult extends TQueryResult>(
258
277
  query: string,
259
278
  opts: TQueryOptions<'bucket', 'bucket'>
260
- ): Promise<Bucket>;
279
+ ): Bucket;
261
280
 
262
- public async query<TResult extends TQueryResult>(
281
+ public query<TResult extends TQueryResult>(
263
282
  query: string,
264
283
  opts: TQueryOptions<'returnQuery', 'returnQuery'>
265
- ): Promise<string>;
284
+ ): string;
266
285
 
267
- public async query<TResult extends TQueryResult>(
286
+ public query<TResult extends TQueryResult>(
268
287
  query: string,
269
288
  opts: TQueryOptions<'simulate', 'simulate'>
270
- ): Promise<void>;
289
+ ): void;
271
290
 
272
- public async query<TResult extends TQueryResult>(
291
+ public query<TResult extends TQueryResult>(
273
292
  query: string,
274
293
  opts?: TQueryOptions
275
294
  ): Promise<TResult>;
276
295
 
277
- public async query<TResult extends TQueryResult>(
296
+ public query<TResult extends TQueryResult>(
278
297
  query: string,
279
298
  opts: TQueryOptions = {}
280
- ): Promise<TResult | Bucket | string | void> {
299
+ ): Promise<TResult> | Bucket | string | void {
281
300
 
282
301
  if (opts.bucket)
283
302
  return opts.bucket.add(query);
@@ -285,28 +304,25 @@ export default class DatabaseConnection extends Service<DatabaseServiceConfig, T
285
304
  if (opts.returnQuery === true)
286
305
  return query;
287
306
 
288
- if (opts.log === true)
307
+ if (opts.log === true || opts.simulate === true)
289
308
  console.log(`[database][query]`, query);
290
309
 
291
310
  if (opts.simulate === true)
292
311
  return;
293
-
294
- try {
295
-
296
- const startTime = Date.now();
297
-
298
- // Lancement de la requête
299
- const [rows, fields] = await this.connection.query(query);
312
+
313
+ const startTime = Date.now();
314
+ return this.connection.query(query).then(([rows, fields]) => {
300
315
 
301
316
  if (opts.log !== false)
302
317
  this.log(query, startTime);
303
318
 
304
319
  return rows as unknown as TResult;
305
-
306
- } catch (error) {
320
+
321
+ }).catch((error) => {
307
322
 
308
323
  throw new SqlError(error, query);
309
- }
324
+
325
+ })
310
326
  }
311
327
 
312
328
  private log( query: string, startTime: number ) {
@@ -1,7 +1,7 @@
1
1
  /*----------------------------------
2
2
  - DEPENDANCES
3
3
  ----------------------------------*/
4
- import type { TMetasColonne } from './metas';
4
+ import type { TMetasColonne, TMySQLType } from './metas';
5
5
 
6
6
  /*----------------------------------
7
7
  - TYPES
@@ -9,7 +9,8 @@ import type { TMetasColonne } from './metas';
9
9
 
10
10
  type JsType = {
11
11
  parse: (value: string) => any,
12
- print: (col: TMetasColonne) => string
12
+ check: (value: unknown, col: TMetasColonne) => boolean,
13
+ print: (mysqlTypeParams: TMySQLType["params"]) => string,
13
14
  }
14
15
 
15
16
  export type TJsTypeName = keyof typeof js;
@@ -18,43 +19,81 @@ export type TMySQLTypeName = keyof typeof mysqlToJs;
18
19
  /*----------------------------------
19
20
  - LISTS
20
21
  ----------------------------------*/
22
+ /*
23
+ TODO: to merge avec the schema library
24
+ */
21
25
  export const js = {
22
26
  array: {
23
27
  parse: (val: string) => val.split(','),
24
- print: (col) => (col.type.sql.params.length
25
- ? '(' + col.type.sql.params.map( param => "'" + param + "'").join(' | ') + ')'
28
+ print: (mysqlTypeParams: TMySQLType["params"]) => (mysqlTypeParams !== undefined
29
+ ? '(' + mysqlTypeParams.map( param => "'" + param + "'").join(' | ') + ')'
26
30
  : 'string'
27
- ) + '[]'
31
+ ) + '[]',
32
+ // Check before using into a mysql query
33
+ check: (val: unknown, col: TMetasColonne) => Array.isArray(val) && (
34
+ col.type.sql.params === undefined
35
+ ||
36
+ val.every( item => col.type.sql.params?.includes( item ))
37
+ )
28
38
  },
29
39
  enum: {
30
40
  parse: (val: string) => val,
31
- print: (col) => col.type.sql.params.map( param => "'" + param + "'").join(' | ')
41
+ print: (mysqlTypeParams: TMySQLType["params"]) => mysqlTypeParams !== undefined
42
+ ? mysqlTypeParams.map( param => "'" + param + "'").join(' | ')
43
+ : 'string',
44
+ // Check before using into a mysql query
45
+ check: (val: unknown, col: TMetasColonne) => typeof val === 'string' && (
46
+ col.type.sql.params === undefined
47
+ ||
48
+ col.type.sql.params.includes( val )
49
+ )
32
50
  },
33
51
  float: {
34
52
  parse: (val: string) => parseFloat(val),
35
- print: (col) => 'number'
53
+ print: () => 'number',
54
+ // Check before using into a mysql query
55
+ check: (val: unknown) => typeof val === 'number'
36
56
  },
37
57
  int: {
38
58
  parse: (val: string) => parseFloat(val),
39
- print: (col) => 'number'
59
+ print: () => 'number',
60
+ // Check before using into a mysql query
61
+ check: (val: unknown, col: TMetasColonne) => (
62
+ typeof val === 'number'
63
+ ||
64
+ // We assume that a int(1) is possibly a boolean
65
+ (
66
+ typeof val === 'boolean'
67
+ /*&&
68
+ col.type.sql.params[]*/
69
+ )
70
+ )
40
71
  },
41
72
  date: {
42
73
  parse: (val: string) => new Date(val),
43
- print: (col) => 'Date'
74
+ print: () => 'Date',
75
+ // Check before using into a mysql query
76
+ check: (val: unknown) => typeof val === 'number' || typeof val === 'string' || (typeof val === 'object' && val instanceof Date)
44
77
  },
45
78
  string: {
46
79
  parse: (val: string) => val,
47
- print: (col) => 'string'
80
+ print: () => 'string',
81
+ // Check before using into a mysql query
82
+ check: (val: unknown) => typeof val === 'string'
48
83
  },
49
84
  object: {
50
85
  parse: (val: string) => JSON.parse(val),
51
- print: (col) => 'object'
86
+ print: () => 'object',
87
+ // Check before using into a mysql query
88
+ check: (val: unknown) => typeof val === 'object'
52
89
  },
53
90
 
54
91
  // When we were not able to find an equivalent
55
92
  unknown: {
56
93
  parse: (val: any) => val,
57
- print: (col) => 'any'
94
+ print: (mysqlTypeParams: TMySQLType["params"]) => 'any',
95
+ // Check before using into a mysql query
96
+ check: (val: unknown) => true
58
97
  },
59
98
  } as const
60
99
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  // Npm
6
6
  import mysql from 'mysql2/promise';
7
+ import type { ResultSetHeader } from 'mysql2';
7
8
  import dottie from 'dottie';
8
9
  const safeStringify = require('fast-safe-stringify'); // remplace les références circulairs par un [Circular]
9
10
 
@@ -21,6 +22,8 @@ import type { TMetasTable } from './metas';
21
22
  - SERVICE TYPES
22
23
  ----------------------------------*/
23
24
 
25
+ export { default as Repository } from './repository';
26
+
24
27
  export type Config = {
25
28
 
26
29
  }
@@ -33,28 +36,32 @@ export type Hooks = {
33
36
  - DEFINITIONS TYPES
34
37
  ----------------------------------*/
35
38
 
36
- export type SqlQuery = SQL
39
+ export type SqlQuery = ReturnType<SQL["sql"]>
37
40
 
38
41
  export type TSelectQueryOptions = TQueryOptions & {
39
42
 
40
43
  }
41
44
 
45
+ export type TUpdateQueryOptions<TData extends TObjetDonnees = TObjetDonnees> = TQueryOptions;
46
+
42
47
  export type TInsertQueryOptions<TData extends TObjetDonnees = TObjetDonnees> = TQueryOptions & {
43
- upsert?: (keyof TData)[] | true, // When true, we use table.upsertableColumns
48
+ upsert?: TColsToUpsert<TData>, // When "*", we use table.upsertableColumns
44
49
  upsertMode?: 'increment'
45
50
  try?: boolean
46
51
  }
47
52
 
48
- type TInsertResult = {
49
- fieldCount: number;
50
- affectedRows: number;
51
- changedRows: number;
52
- insertId: number;
53
- serverStatus: number;
54
- warningCount: number;
55
- message: string;
56
- procotol41: boolean;
57
- }
53
+ type TColsToUpsert<TData extends TObjetDonnees> = (
54
+ // Update all updatable columns (default)
55
+ '*'
56
+ |
57
+ // Specify which values exactly we have to update
58
+ // If '*', provided, we update all the updatable values and override with specific values
59
+ // { '*': true } is equivalent to '*'
60
+ ({ '*'?: true } & Partial<TData>)
61
+ |
62
+ // A list of columns to update
63
+ (keyof TData)[]
64
+ )
58
65
 
59
66
  /*----------------------------------
60
67
  - HELPERS
@@ -63,7 +70,7 @@ type TInsertResult = {
63
70
  const LogPrefix = '[database]'
64
71
 
65
72
  const equalities = (data: TObjetDonnees, keys = Object.keys(data)) =>
66
- keys.map(k => '`' + k + '` = ' + mysql.escape( data[k] ))
73
+ keys.map(k => '' + k + ' = ' + mysql.escape( data[k] ))
67
74
 
68
75
  /*----------------------------------
69
76
  - CORE
@@ -164,15 +171,16 @@ export default class SQL extends Service<Config, Hooks, Application> {
164
171
  stringBefore = stringBefore.trim();
165
172
  const prefix = stringBefore[stringBefore.length - 1];
166
173
 
174
+ // Null
167
175
  if (value === undefined || value === null) {
168
176
 
169
177
  value = 'NULL';
170
178
 
171
179
  // Replace ""= NULL" by "IS NULL"
172
- console.log("signBeforesignBefore", prefix, stringBefore);
173
180
  if (prefix === '=')
174
181
  stringBefore = stringBefore.substring(0, stringBefore.length - 1) + 'IS ';
175
182
 
183
+ // Prefix = special parse
176
184
  } else if (prefix === ':' || prefix === '&') {
177
185
 
178
186
  // Remove the prefix
@@ -191,6 +199,7 @@ export default class SQL extends Service<Config, Hooks, Application> {
191
199
 
192
200
  }
193
201
 
202
+ // SQL query
194
203
  } else if (typeof value === 'function' && value.string !== undefined)
195
204
  value = value.string;
196
205
  else
@@ -266,18 +275,61 @@ export default class SQL extends Service<Config, Hooks, Application> {
266
275
  - OPERATIONS: UPDATE
267
276
  ----------------------------------*/
268
277
 
269
- public update = <TData extends TObjetDonnees>(
270
- table: string,
278
+ // Update multiple records
279
+ public update<TData extends TObjetDonnees>(
280
+ tableName: string,
281
+ data: TData[],
282
+ where: (keyof TData)[],
283
+ opts?: TUpdateQueryOptions<TData>
284
+ );
285
+
286
+ // Update one record
287
+ public update<TData extends TObjetDonnees>(
288
+ tableName: string,
271
289
  data: TData,
272
- where: TObjetDonnees,
273
- opts?: TInsertQueryOptions<TData>
274
- ) => {
290
+ where: (keyof TData)[] | TObjetDonnees,
291
+ opts?: TUpdateQueryOptions<TData>
292
+ );
293
+
294
+ public update<TData extends TObjetDonnees>(...args: [
295
+ tableName: string,
296
+ data: TData[],
297
+ where: (keyof TData)[],
298
+ opts?: TUpdateQueryOptions<TData>
299
+ ] | [
300
+ tableName: string,
301
+ data: TData,
302
+ where: (keyof TData)[] | TObjetDonnees,
303
+ opts?: TUpdateQueryOptions<TData>
304
+ ]) {
305
+
306
+ let [tableName, data, where, opts] = args;
307
+
308
+ // Multiple updates in one
309
+ if (Array.isArray( data ))
310
+ return this.database.query(
311
+ data.map(record => this.update(tableName, record, where, opts)).join(';\n')
312
+ )
313
+
314
+ // No condition specified = use the pks
315
+ if (Array.isArray(where)) {
316
+ const whereColNames = where;
317
+ where = {}
318
+ for (const whereCol of whereColNames) {
319
+ const whereValue = data[whereCol];
320
+ if (whereValue === undefined)
321
+ throw new Error(`The column "${whereCol}" is used as a where value, but no value has been provided in the data to update.`);
322
+ where[ whereCol ] = whereValue;
323
+ }
324
+ }
275
325
 
326
+ // Create equalities
276
327
  const egalitesData = equalities(data).join(', ')
277
328
  const egalitesWhere = equalities(where).join(' AND ')
278
329
 
330
+ // Build query
279
331
  return this.database.query(`
280
- UPDATE ${table} SET ${egalitesData} WHERE ${egalitesWhere};
332
+ UPDATE ${tableName} SET ${egalitesData} WHERE ${egalitesWhere};
281
333
  `, opts);
282
334
 
283
335
  }
@@ -285,12 +337,12 @@ export default class SQL extends Service<Config, Hooks, Application> {
285
337
  public upsert<TData extends TObjetDonnees>(
286
338
  path: string,
287
339
  data: TData[] | TData,
288
- colsToUpdate?: (keyof TData)[],
340
+ colsToUpdate: TColsToUpsert<TData> = '*',
289
341
  opts: TInsertQueryOptions<TData> = {}
290
342
  ) {
291
343
  return this.insert(path, data, {
292
344
  ...opts,
293
- upsert: colsToUpdate || true
345
+ upsert: colsToUpdate
294
346
  });
295
347
  }
296
348
 
@@ -305,7 +357,7 @@ export default class SQL extends Service<Config, Hooks, Application> {
305
357
  path: string,
306
358
  data: TData | TData[],
307
359
  opts: TInsertQueryOptions<TData> = {}
308
- ): Promise<TInsertResult> {
360
+ ): Promise<ResultSetHeader> {
309
361
 
310
362
  const table = this.database.getTable(path);
311
363
 
@@ -320,9 +372,8 @@ export default class SQL extends Service<Config, Hooks, Application> {
320
372
  changedRows: 0,
321
373
  insertId: 0,
322
374
  serverStatus: 0,
323
- warningCount: 0,
324
- message: '',
325
- procotol41: false,
375
+ warningStatus: 0,
376
+ info: undefined
326
377
  };
327
378
  }
328
379
 
@@ -387,29 +438,71 @@ export default class SQL extends Service<Config, Hooks, Application> {
387
438
  opts: With<TInsertQueryOptions<TData>, 'upsert'>
388
439
  ): string | null {
389
440
 
390
- let upsert = opts.upsert;
391
-
392
- // Auto
393
- if (upsert === true) {
394
- console.log(LogPrefix, `Automatic upsert into ${table.chemin} using ${table.pk.join(', ')} as pk`);
395
- upsert = table.columnNamesButPk;
396
- }
441
+ const valuesToUpdate = this.getValuesToUpdate(table, opts.upsert);
397
442
 
398
443
  // All columns are ps
399
- if (upsert.length === 0)
444
+ const valuesToUpdatesEntries = Object.entries(valuesToUpdate);
445
+ if (valuesToUpdatesEntries.length === 0)
400
446
  // Replace by insert ignore
401
447
  return null;
402
448
 
403
- return 'ON DUPLICATE KEY UPDATE ' + upsert.map((col: string) =>
404
- '`' + col + '` = ' + (opts.upsertMode === 'increment' ? '`' + col + '` + ' : '') + 'VALUES(' + col + ')'
449
+ return 'ON DUPLICATE KEY UPDATE ' + valuesToUpdatesEntries.map(([ colName, value ]) =>
450
+ '`' + colName + '` = ' + value
405
451
  )
406
452
  }
407
453
 
454
+ // TODO: Fix typings
455
+ private getValuesToUpdate<TData extends TObjetDonnees>(
456
+ table: TMetasTable,
457
+ colsToUpdate: TColsToUpsert<TData>
458
+ ) {
459
+
460
+ // Column name => SQL
461
+ let valuesToUpdate: Partial<TData> = {};
462
+
463
+ // Define which columns to update when the record already exists
464
+ let valuesNamesToUpdate: (keyof TData)[] = [];
465
+ if (colsToUpdate === '*') {
466
+
467
+ console.log(LogPrefix, `Automatic upsert into ${table.chemin} using ${table.pk.join(', ')} as pk`);
468
+ valuesNamesToUpdate = table.columnNamesButPk;
469
+
470
+ } else if (Array.isArray( colsToUpdate )) {
471
+
472
+ valuesNamesToUpdate = colsToUpdate;
473
+
474
+ } else {
475
+
476
+ const { '*': updateAll, ...customValuesToUpdate } = colsToUpdate;
477
+
478
+ for (const colKey in customValuesToUpdate)
479
+ valuesToUpdate[ colKey ] = this.esc(customValuesToUpdate[ colKey ]);
480
+
481
+ if (updateAll)
482
+ valuesNamesToUpdate = table.columnNamesButPk;
483
+
484
+ }
485
+
486
+ for (const colToUpdate of valuesNamesToUpdate)
487
+ if (!( colToUpdate in valuesToUpdate ))
488
+ valuesToUpdate[ colToUpdate ] = "VALUES(`" + colToUpdate + "`)";
489
+
490
+ return valuesToUpdate;
491
+ }
492
+
408
493
  /*----------------------------------
409
494
  - OTHER
410
495
  ----------------------------------*/
411
- public delete = (table: string, where: TObjetDonnees = {}, opts?: TQueryOptions) =>
412
- this.database.query(`
413
- DELETE FROM ${table} WHERE ${equalities(where).join(' AND ')};
414
- `, opts);
496
+ public delete(
497
+ table: string,
498
+ where: TObjetDonnees | SQL = {},
499
+ opts?: TQueryOptions
500
+ ): Promise<ResultSetHeader> {
501
+
502
+ const whereSql = typeof where === 'function' && where['string'] !== undefined
503
+ ? where['string']
504
+ : equalities(where).join(' AND ');
505
+
506
+ return this.database.query(`DELETE FROM ${table} WHERE ${whereSql};`, opts);
507
+ }
415
508
  }