5htp-core 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/changelog.md +5 -0
  2. package/doc/TODO.md +71 -0
  3. package/package.json +5 -4
  4. package/src/client/{App.tsx → app/component.tsx} +15 -8
  5. package/src/client/app/index.ts +128 -0
  6. package/src/client/app/service.ts +34 -0
  7. package/src/client/app.tsconfig.json +0 -4
  8. package/src/client/assets/css/medias.less +14 -0
  9. package/src/client/components/Card/index.tsx +2 -2
  10. package/src/client/components/Dialog/Manager.tsx +39 -12
  11. package/src/client/components/Form/index.tsx +1 -1
  12. package/src/client/components/button.tsx +2 -2
  13. package/src/client/components/containers/Popover/index.tsx +1 -1
  14. package/src/client/components/data/spintext/index.tsx +1 -1
  15. package/src/client/components/dropdown/index.tsx +1 -1
  16. package/src/client/components/index.ts +8 -0
  17. package/src/client/components/input/BaseV2/index.tsx +1 -1
  18. package/src/client/components/input/UploadImage/index.tsx +1 -1
  19. package/src/client/hooks/index.ts +5 -0
  20. package/src/client/hooks/useState/index.tsx +2 -2
  21. package/src/client/hooks.ts +22 -0
  22. package/src/client/index.ts +5 -0
  23. package/src/client/pages/_layout/landing/index.tsx +0 -2
  24. package/src/client/pages/_messages/400.tsx +2 -2
  25. package/src/client/pages/_messages/401.tsx +2 -2
  26. package/src/client/pages/_messages/403.tsx +2 -2
  27. package/src/client/pages/_messages/404.tsx +2 -2
  28. package/src/client/pages/_messages/500.tsx +2 -2
  29. package/src/client/pages/bug.tsx +1 -1
  30. package/src/client/pages/useHeader.tsx +1 -1
  31. package/src/client/{context/captcha.ts → services/captcha/index.ts} +0 -0
  32. package/src/client/services/metrics/index.ts +37 -0
  33. package/src/client/{router → services/router/components}/Link.tsx +1 -1
  34. package/src/client/services/router/components/Page.tsx +59 -0
  35. package/src/client/{router/component.tsx → services/router/components/router.tsx} +43 -74
  36. package/src/client/services/router/index.tsx +448 -0
  37. package/src/client/services/router/request/api.ts +229 -0
  38. package/src/client/{router → services/router}/request/history.ts +0 -0
  39. package/src/client/services/router/request/index.ts +52 -0
  40. package/src/client/services/router/response/index.tsx +107 -0
  41. package/src/client/services/router/response/page.ts +95 -0
  42. package/src/client/{context/socket.ts → services/socket/index.ts} +2 -2
  43. package/src/client/utils/dom.ts +1 -1
  44. package/src/common/app/index.ts +9 -0
  45. package/src/common/data/chaines/index.ts +9 -6
  46. package/src/common/data/input/validate.ts +3 -166
  47. package/src/common/data/objets.ts +25 -0
  48. package/src/common/data/tableaux.ts +8 -0
  49. package/src/common/errors/index.ts +3 -1
  50. package/src/common/router/index.ts +67 -88
  51. package/src/common/router/layouts.ts +50 -0
  52. package/src/common/router/register.ts +62 -0
  53. package/src/common/router/request/api.ts +72 -0
  54. package/src/common/router/request/index.ts +31 -0
  55. package/src/common/router/{response.ts → response/index.ts} +9 -13
  56. package/src/common/router/response/page.ts +40 -56
  57. package/src/common/validation/index.ts +3 -0
  58. package/src/common/validation/schema.ts +184 -0
  59. package/src/common/validation/validator.ts +88 -0
  60. package/src/common/validation/validators.ts +313 -0
  61. package/src/server/app/config.ts +9 -27
  62. package/src/server/app/index.ts +81 -124
  63. package/src/server/app/service.ts +98 -0
  64. package/src/server/app.tsconfig.json +0 -8
  65. package/src/server/error/index.ts +13 -0
  66. package/src/server/index.ts +5 -0
  67. package/src/server/patch.ts +0 -6
  68. package/src/server/{data/Cache.ts → services/cache/index.ts} +79 -47
  69. package/src/server/services/console/bugReporter.ts +26 -16
  70. package/src/server/services/console/index.ts +59 -51
  71. package/src/server/services/cron/index.ts +12 -26
  72. package/src/server/services/database/bucket.ts +40 -0
  73. package/src/server/services/database/connection.ts +206 -75
  74. package/src/server/services/database/datatypes.ts +63 -40
  75. package/src/server/services/database/index.ts +295 -272
  76. package/src/server/services/database/metas.ts +246 -135
  77. package/src/server/services/database/stats.ts +151 -126
  78. package/src/server/services/email/index.ts +28 -52
  79. package/src/server/services/{router/request/services → metrics}/detect.ts +8 -10
  80. package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
  81. package/src/server/services/{http → router/http}/index.ts +28 -70
  82. package/src/server/services/{http → router/http}/multipart.ts +0 -0
  83. package/src/server/services/{http → router/http}/session.ts.old +0 -0
  84. package/src/server/services/router/index.ts +273 -203
  85. package/src/server/services/router/request/api.ts +73 -0
  86. package/src/server/services/router/request/index.ts +16 -97
  87. package/src/server/services/router/request/service.ts +21 -0
  88. package/src/server/services/router/response/index.ts +125 -64
  89. package/src/server/services/router/response/{filter → mask}/Filter.ts +0 -0
  90. package/src/server/services/router/response/{filter → mask}/index.ts +0 -2
  91. package/src/server/services/router/response/{filter → mask}/selecteurs.ts +0 -0
  92. package/src/server/services/router/response/page/document.tsx +194 -0
  93. package/src/server/services/router/response/page/index.tsx +157 -0
  94. package/src/server/{libs/pages → services/router/response/page}/schemaGenerator.ts +0 -0
  95. package/src/server/services/router/service.ts +48 -0
  96. package/src/server/services/schema/index.ts +47 -0
  97. package/src/server/services/schema/request.ts +55 -0
  98. package/src/server/services/schema/router.ts +33 -0
  99. package/src/server/services/socket/index.ts +38 -43
  100. package/src/server/services/socket/scope.ts +6 -4
  101. package/src/server/services/users/index.ts +203 -0
  102. package/src/server/services/{auth/base.ts → users/old.ts} +28 -112
  103. package/src/server/services/users/router/index.ts +72 -0
  104. package/src/server/services/users/router/request.ts +49 -0
  105. package/src/types/aliases.d.ts +43 -2
  106. package/templates/composant.tsx +1 -1
  107. package/templates/modal.tsx +1 -1
  108. package/templates/page.tsx +1 -1
  109. package/tsconfig.common.json +0 -4
  110. package/src/client/context/api.ts +0 -92
  111. package/src/client/context/index.ts +0 -246
  112. package/src/client/index.tsx +0 -129
  113. package/src/client/router/index.ts +0 -286
  114. package/src/client/router/request/index.ts +0 -106
  115. package/src/client/router/response/index.ts +0 -38
  116. package/src/client/router/route.ts +0 -75
  117. package/src/common/data/input/validators/basic.ts +0 -299
  118. package/src/common/data/input/validators/build.ts +0 -63
  119. package/src/common/router/request.ts +0 -83
  120. package/src/server/data/ApiClient.ts +0 -119
  121. package/src/server/data/input.ts +0 -41
  122. package/src/server/libs/pages/document.static.tsx +0 -41
  123. package/src/server/libs/pages/document.tsx +0 -203
  124. package/src/server/libs/pages/render.tsx +0 -90
  125. package/src/server/routes/auth.ts +0 -151
  126. package/src/server/services/redis/index.ts +0 -71
  127. package/src/server/services/router/request/services/auth.ts +0 -177
@@ -8,385 +8,408 @@ import dottie from 'dottie';
8
8
  const safeStringify = require('fast-safe-stringify'); // remplace les références circulairs par un [Circular]
9
9
 
10
10
  // Core: general
11
- import app, { $ } from '@server/app';
11
+ import type Application from '@server/app';
12
+ import Service from '@server/app/service';
13
+ import { callableInstance } from '@common/data/objets';
12
14
  import { NotFound } from '@common/errors';
13
15
 
14
16
  // Services
15
- import './connection';
17
+ import Database, { DatabaseServiceConfig, TQueryOptions } from './connection';
18
+ import type { TMetasTable } from './metas';
16
19
 
17
20
  /*----------------------------------
18
- - DEFINITIONS TYPES
21
+ - SERVICE TYPES
19
22
  ----------------------------------*/
20
23
 
21
- export type SqlQuery = ReturnType<typeof sql>;
24
+ export type Config = {
25
+
26
+ }
27
+
28
+ export type Hooks = {
22
29
 
23
- export type TQueryOptions = {
24
- simulate?: boolean,
25
- log?: boolean,
26
- returnQuery?: boolean
27
30
  }
28
31
 
32
+ /*----------------------------------
33
+ - DEFINITIONS TYPES
34
+ ----------------------------------*/
35
+
36
+ export type SqlQuery = SQL
37
+
29
38
  export type TSelectQueryOptions = TQueryOptions & {
30
39
 
31
40
  }
32
41
 
33
42
  export type TInsertQueryOptions<TData extends TObjetDonnees = TObjetDonnees> = TQueryOptions & {
34
- upsert?: (keyof TData)[],
43
+ upsert?: (keyof TData)[] | true, // When true, we use table.upsertableColumns
35
44
  upsertMode?: 'increment'
36
45
  try?: boolean
37
46
  }
38
47
 
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
+ }
58
+
39
59
  /*----------------------------------
40
- - SERVICE CONFIG
60
+ - HELPERS
41
61
  ----------------------------------*/
42
62
 
43
- export type DatabaseServiceConfig = {
44
- list: string[],
45
- dev: {
46
- host: string,
47
- port: number,
48
- login: string,
49
- password: string,
50
- },
51
- prod: {
52
- host: string,
53
- port: number,
54
- login: string,
55
- password: string,
56
- }
57
- }
63
+ const LogPrefix = '[database]'
58
64
 
59
- declare global {
60
- namespace Core {
61
- namespace Config {
62
- interface Services {
63
- database: DatabaseServiceConfig
64
- }
65
- }
66
- }
67
- }
65
+ const equalities = (data: TObjetDonnees, keys = Object.keys(data)) =>
66
+ keys.map(k => '`' + k + '` = ' + mysql.escape( data[k] ))
68
67
 
69
68
  /*----------------------------------
70
- - HELPERS
69
+ - CORE
71
70
  ----------------------------------*/
72
71
 
73
- const escape = (data: any) => {
72
+ // TODO: build callable instance sithut instanciating the service
74
73
 
75
- // JSON object
76
- if (typeof data === 'object' && data !== null) {
74
+ export default class SQL extends Service<Config, Hooks, Application> {
77
75
 
78
- if (data.constructor.name === "Object" || Array.isArray(data))
79
- data = safeStringify(data);
76
+ public database: Database;
80
77
 
78
+ public constructor( app: Application, config: DatabaseServiceConfig ) {
79
+
80
+ super(app, {});
81
81
 
82
+ this.database = new Database(app, config);
82
83
  }
83
84
 
84
- return mysql.escape(data);
85
- }
85
+ public async register() {
86
+ await this.database.register();
87
+ }
86
88
 
87
- const equalities = (data: TObjetDonnees, keys = Object.keys(data)) =>
88
- keys.map(k => '`' + k + '` = ' + escape( data[k] ))
89
+ public async start() {
90
+ await this.database.start();
91
+ }
89
92
 
90
- /*----------------------------------
91
- - CORE
92
- ----------------------------------*/
93
+ public callableInstance() {
94
+ return callableInstance( this, 'sql' );
95
+ }
93
96
 
94
- export type SQL = typeof sql;
97
+ /*----------------------------------
98
+ - OPERATIONS: PARSING
99
+ ----------------------------------*/
100
+ public sql<TRowData extends TObjetDonnees = {}>( strings: TemplateStringsArray, ...data: any[] ) {
95
101
 
96
- export function sql<TRowData extends TObjetDonnees = {}>(strings: TemplateStringsArray, ...data: any[]) {
102
+ const string = this.string(strings, ...data);
97
103
 
98
- const string = sql.string(strings, ...data);
104
+ const query = (opts: TSelectQueryOptions = {}) => this.select<TRowData>(string, opts);
99
105
 
100
- const query = (options: TSelectQueryOptions = {}) => sql.select<TRowData>(string, options);
106
+ query.all = query;
107
+ query.run = (opts: TQueryOptions = {}) => this.database.query<TRowData[]>(string, opts);
108
+ query.then = (cb: (data: any) => void) => query().then(cb);
101
109
 
102
- query.all = query;
103
- query.run = (opts: TQueryOptions = {}) => sql.query<TRowData[]>(string, opts);
104
- query.then = (cb: (data: any) => void) => query().then(cb);
110
+ query.first = <TRowData extends TObjetDonnees = {}>(opts: TSelectQueryOptions = {}) => this.first<TRowData>(string, opts);
111
+ query.firstOrFail = (message?: string, opts: TQueryOptions = {}) => this.firstOrFail<TRowData>(string, message, opts);
105
112
 
106
- query.first = <TRowData extends TObjetDonnees = {}>() => sql.first<TRowData>(string);
107
- query.value = <TValue extends any = number>() => sql.selectVal<TValue>(string);
108
- query.count = () => sql.count(string);
109
- query.firstOrFail = (message?: string) => sql.firstOrFail<TRowData>(string, message);
110
- query.exists = () => sql.exists(string);
113
+ query.value = <TValue extends any = number>(opts: TQueryOptions = {}) => this.selectVal<TValue>(string, opts);
114
+
115
+ query.count = (opts: TQueryOptions = {}) => this.count(string, opts);
116
+ query.exists = (opts: TQueryOptions = {}) => this.exists(string, opts);
111
117
 
112
- /*query.stats = (periodStr: string, intervalStr: string, columns: string[]) =>
113
- fetchStats(columns, string, periodStr, intervalStr)*/
118
+ /*query.stats = (periodStr: string, intervalStr: string, columns: string[]) =>
119
+ fetchStats(columns, string, periodStr, intervalStr)*/
114
120
 
115
- query.mergeValues = (options: TQueryOptions = {}) =>
116
- sql.query(string, options).then( queriesResult => {
121
+ query.mergeValues = (options: TQueryOptions = {}) =>
122
+ this.database.query(string, options).then( queriesResult => {
117
123
 
118
- const data: TObjetDonnees = {};
119
- for (const queryResult of queriesResult)
120
- if (queryResult.length !== 0)
121
- for (const k in queryResult[0])
122
- data[k] = queryResult[0][k];
123
- return data;
124
+ const data: TObjetDonnees = {};
125
+ for (const queryResult of queriesResult)
126
+ if (queryResult.length !== 0)
127
+ for (const k in queryResult[0])
128
+ data[k] = queryResult[0][k];
129
+ return data;
124
130
 
125
- })
131
+ })
126
132
 
127
- query.string = string;
133
+ query.string = string;
128
134
 
129
- return query;
130
- }
135
+ return query;
136
+ }
131
137
 
132
- sql.esc = escape;
138
+ public esc(data: any) {
133
139
 
134
- sql.string = (strings: TemplateStringsArray, ...data: any[]) => {
135
- const iMax = data.length - 1;
136
- return strings.map((s, i) => {
140
+ // JSON object
141
+ // TODO: do it via datatypes.ts
142
+ if (typeof data === 'object' && data !== null) {
137
143
 
138
- if (i <= iMax) {
144
+ if (Array.isArray(data))
145
+ data = data.join(',')
146
+ else if (data.constructor.name === "Object")
147
+ data = safeStringify(data);
148
+ }
149
+
150
+ return mysql.escape(data);
151
+ }
139
152
 
140
- let d = data[i];
141
- const prefix = s[s.length - 1];
153
+ public string = (strings: TemplateStringsArray, ...data: any[]) => {
154
+ const iMax = data.length - 1;
142
155
 
143
- if (typeof data === 'function')
144
- throw new Error(`A function has been passed into the sql string template: ` + data);
156
+ if (typeof data === 'function')
157
+ throw new Error(`A function has been passed into the sql string template: ` + data);
145
158
 
146
- if (prefix === ':' || prefix === '&') {
159
+ return strings.map((stringBefore, i) => {
147
160
 
148
- s = s.substring(0, s.length - 1);
161
+ if (i <= iMax) {
149
162
 
150
- // Object: `WHERE :${filters}` => `SET requestId = "" AND col = 3`
151
- if (typeof d === 'object') {
163
+ let value = data[i];
164
+ stringBefore = stringBefore.trim();
165
+ const prefix = stringBefore[stringBefore.length - 1];
152
166
 
153
- const keyword = prefix === '&' ? ' AND ' : ', '
167
+ if (value === undefined || value === null) {
154
168
 
155
- d = Object.keys(d).length === 0 ? '1' : equalities(d).join( keyword );
156
-
157
- // String: `SET :${column} = ${data}` => `SET balance = 10`
158
- } else {
169
+ value = 'NULL';
159
170
 
160
- }
171
+ // Replace ""= NULL" by "IS NULL"
172
+ console.log("signBeforesignBefore", prefix, stringBefore);
173
+ if (prefix === '=')
174
+ stringBefore = stringBefore.substring(0, stringBefore.length - 1) + 'IS ';
161
175
 
162
- } else if (typeof d === 'function' && d.string !== undefined)
163
- d = d.string;
164
- else
165
- d = escape(d);
176
+ } else if (prefix === ':' || prefix === '&') {
166
177
 
167
- s += d;
178
+ // Remove the prefix
179
+ stringBefore = stringBefore.substring(0, stringBefore.length - 1);
168
180
 
169
- }
181
+ // Object: `WHERE :${filters}` => `SET requestId = "" AND col = 3`
182
+ if (typeof value === 'object') {
170
183
 
171
- return s;
184
+ const keyword = prefix === '&' ? ' AND ' : ', '
172
185
 
173
- }).join(' ').trim();
174
- }
186
+ value = Object.keys(value).length === 0 ? '1' : equalities(value).join( keyword );
187
+
188
+ // String: `SET :${column} = ${data}` => `SET balance = 10`
189
+ } else {
175
190
 
176
- /*----------------------------------
177
- - OPERATIONS: LOW LEVELf
178
- ----------------------------------*/
179
191
 
180
- sql.query = async <TRowData extends TObjetDonnees = {}>(
181
- query: string,
182
- opts: TQueryOptions = {}
183
- ): Promise<TRowData[] | string | void> => {
192
+ }
184
193
 
185
- if (opts.returnQuery === true)
186
- return query;
194
+ } else if (typeof value === 'function' && value.string !== undefined)
195
+ value = value.string;
196
+ else
197
+ value = mysql.escape(value);
187
198
 
188
- await $.database.loading;
199
+ stringBefore += value;
189
200
 
190
- if (opts.log === true)
191
- console.log(`[database][query]`, query);
201
+ }
192
202
 
193
- if (opts.simulate === true)
194
- return;
203
+ return stringBefore;
195
204
 
196
- try {
205
+ }).join(' ').trim();
206
+ }
197
207
 
198
- const startTime = Date.now();
208
+ /*----------------------------------
209
+ - OPERATIONS: LOW LEVELf
210
+ ----------------------------------*/
199
211
 
200
- // Lancement de la requête
201
- const [rows, fields] = await $.database.connection.query(query);
212
+ public bulk = (queries: (SqlQuery | string)[], opts?: TQueryOptions) => {
213
+ const allqueries = queries.map(q => typeof q === "string" ? q : q.string).join(';\n');
214
+ //console.log("Bulk query", allqueries);
215
+ return this.database.query(allqueries, opts);
216
+ }
202
217
 
203
- if (opts.log !== false) {
204
- const { channelType, channelId } = $.console.getChannel();
205
- if (channelId !== 'admin')
206
- $.console.sqlQueries.push({
207
- channelType,
208
- channelId,
209
- date: new Date(),
210
- query: query.trim(),
211
- time: Date.now() - startTime,
212
- });
213
- }
218
+ /*----------------------------------
219
+ - OPERATIONS: READ
220
+ ----------------------------------*/
214
221
 
215
- return rows as unknown as TRowData[];
216
-
217
- } catch (error) {
218
-
219
- // NOTE: Le formatteur sql peut planter quand la requete est trop complexe (ex: json)
220
- try {
221
- console.error("Query that caused error:\n", $.console.printSql(query));
222
- } catch (error) {
223
- console.error("Query that caused error:\n", query);
224
- }
225
-
226
- throw error;
227
-
222
+ public select = async <TRowData extends any>(query: string, opts: TSelectQueryOptions = {}): Promise<TRowData[]> => {
223
+ const rows = await this.database.query(query, opts);
224
+ return dottie.transform(rows);
228
225
  }
226
+ public query = this.select;
229
227
 
230
- }
228
+ public first = <TRowData extends TObjetDonnees = {}>(query: string, opts: TSelectQueryOptions = {}): Promise<TRowData> =>
229
+ this.database.query(query, opts).then((resultatRequetes: any) => {
230
+ return resultatRequetes[0] || null;
231
+ });
231
232
 
232
- sql.bulk = (queries: (SqlQuery | string)[]) => {
233
- const allqueries = queries.map(q => typeof q === "string" ? q : q.string).join(';\n');
234
- console.log("Bulk query", allqueries);
235
- return sql.query(allqueries);
236
- }
233
+ public firstOrFail = <TRowData extends TObjetDonnees = {}>(query: string, message?: string, opts: TSelectQueryOptions = {}): Promise<TRowData> =>
234
+ this.database.query(query, opts).then((resultatRequetes: any) => {
237
235
 
238
- /*----------------------------------
239
- - OPERATIONS: READ
240
- ----------------------------------*/
241
- sql.select = async <TRowData extends any>(query: string, opts: TSelectQueryOptions): Promise<TRowData[]> => {
242
- const rows = await sql.query(query, opts);
243
- return dottie.transform(rows);
244
- }
236
+ if (resultatRequetes.length === 0)
237
+ throw new NotFound(message);
245
238
 
246
- sql.first = <TRowData extends TObjetDonnees = {}>(query: string, opts: TSelectQueryOptions = {}): Promise<TRowData> =>
247
- sql.query(query, opts).then((resultatRequetes: any) => {
248
- return resultatRequetes[0] || null;
249
- });
239
+ return resultatRequetes[0];
250
240
 
251
- sql.firstOrFail = <TRowData extends TObjetDonnees = {}>(query: string, message?: string, opts: TSelectQueryOptions = {}): Promise<TRowData> =>
252
- sql.query(query, opts).then((resultatRequetes: any) => {
241
+ });
253
242
 
254
- if (resultatRequetes.length === 0)
255
- throw new NotFound(message);
243
+ public selectVal = <TVal extends any>(query: string, opts?: TQueryOptions): Promise<TVal | null> =>
244
+ this.database.query(query, opts).then((resultatRequetes: any) => {
256
245
 
257
- return resultatRequetes[0];
246
+ const resultat = resultatRequetes[0];
258
247
 
259
- });
248
+ if (!resultat)
249
+ return null;
260
250
 
261
- sql.selectVal = <TVal extends any>(query: string, opts?: TQueryOptions): Promise<TVal | null> =>
262
- sql.query(query, opts).then((resultatRequetes: any) => {
251
+ return Object.values(resultat)[0] as TVal;
263
252
 
264
- const resultat = resultatRequetes[0];
253
+ });
265
254
 
266
- if (!resultat)
267
- return null;
268
-
269
- return Object.values(resultat)[0] as TVal;
255
+ public count = (query: string, opts?: TQueryOptions): Promise<number> =>
256
+ this.selectVal<number>('SELECT COUNT(*) ' + query).then(val =>
257
+ val === null ? 0 : val
258
+ );
270
259
 
271
- });
260
+ public exists = async (query: string, opts?: TQueryOptions): Promise<boolean> => {
261
+ const count = await this.count(query, opts);
262
+ return count !== 0;
263
+ }
272
264
 
273
- sql.count = (query: string, opts?: TQueryOptions): Promise<number> =>
274
- sql.selectVal<number>('SELECT COUNT(*) ' + query).then(val =>
275
- val === null ? 0 : val
276
- );
265
+ /*----------------------------------
266
+ - OPERATIONS: UPDATE
267
+ ----------------------------------*/
277
268
 
278
- sql.exists = async (query: string, opts?: TQueryOptions): Promise<boolean> => {
279
- const count = await sql.count(query);
280
- return count !== 0;
281
- }
269
+ public update = <TData extends TObjetDonnees>(
270
+ table: string,
271
+ data: TData,
272
+ where: TObjetDonnees,
273
+ opts?: TInsertQueryOptions<TData>
274
+ ) => {
282
275
 
283
- /*----------------------------------
284
- - OPERATIONS: UPDATE
285
- ----------------------------------*/
276
+ const egalitesData = equalities(data).join(', ')
277
+ const egalitesWhere = equalities(where).join(' AND ')
286
278
 
287
- sql.update = <TData extends TObjetDonnees>(
288
- table: string,
289
- data: TData,
290
- where: TObjetDonnees,
291
- opts?: TInsertQueryOptions<TData>
292
- ) => {
279
+ return this.database.query(`
280
+ UPDATE ${table} SET ${egalitesData} WHERE ${egalitesWhere};
281
+ `, opts);
293
282
 
294
- const egalitesData = equalities(data).join(', ')
295
- const egalitesWhere = equalities(where).join(' AND ')
283
+ }
296
284
 
297
- return sql.query(`
298
- UPDATE ${table} SET ${egalitesData} WHERE ${egalitesWhere};
299
- `, opts);
285
+ public upsert<TData extends TObjetDonnees>(
286
+ path: string,
287
+ data: TData[] | TData,
288
+ colsToUpdate?: (keyof TData)[],
289
+ opts: TInsertQueryOptions<TData> = {}
290
+ ) {
291
+ return this.insert(path, data, {
292
+ ...opts,
293
+ upsert: colsToUpdate || true
294
+ });
295
+ }
300
296
 
301
- }
297
+ /*----------------------------------
298
+ - OPERATIONS: INSERT
299
+ ----------------------------------*/
300
+
301
+ public tryInsert = (table: string, data: TObjetDonnees) =>
302
+ this.insert(table, data, { try: true });
303
+
304
+ public async insert<TData extends TObjetDonnees>(
305
+ path: string,
306
+ data: TData | TData[],
307
+ opts: TInsertQueryOptions<TData> = {}
308
+ ): Promise<TInsertResult> {
309
+
310
+ const table = this.database.getTable(path);
311
+
312
+ // Normalize data
313
+ if (!Array.isArray(data))
314
+ data = [data];
315
+ else if (data.length === 0) {
316
+ console.warn(LogPrefix, `Insert nothing in ${path}. Cancelled.`);
317
+ return {
318
+ fieldCount: 0,
319
+ affectedRows: 0,
320
+ changedRows: 0,
321
+ insertId: 0,
322
+ serverStatus: 0,
323
+ warningCount: 0,
324
+ message: '',
325
+ procotol41: false,
326
+ };
327
+ }
302
328
 
303
- sql.upsert = <TData extends TObjetDonnees>(
304
- path: string,
305
- data: TData | TData[],
306
- colsToUpdate: (keyof TData)[],
307
- opts: TInsertQueryOptions<TData> = {}
308
- ) =>
309
- sql.insert(path, data, {
310
- ...opts,
311
- upsert: colsToUpdate
312
- });
329
+ let querySuffix: string = '';
313
330
 
314
- /*----------------------------------
315
- - OPERATIONS: INSERT
316
- ----------------------------------*/
331
+ // Upsert
332
+ if (opts.upsert !== undefined) {
333
+ const upsertStatement = this.buildUpsertStatement<TData>(table, opts as With<TInsertQueryOptions<TData>, 'upsert'>);
334
+ if (upsertStatement === null)
335
+ opts.try = true;
336
+ else
337
+ querySuffix += ' ' + upsertStatement;
338
+ }
317
339
 
318
- sql.tryInsert = (table: string, data: TObjetDonnees) =>
319
- sql.insert(table, data, { try: true });
320
-
321
- sql.insert = async <TData extends TObjetDonnees>(
322
- path: string,
323
- data: TData | TData[],
324
- opts: TInsertQueryOptions<TData> = {}
325
- ) => {
326
-
327
- let db: string, table: string;
328
- if (path.includes('.'))
329
- ([db, table] = path.split('.'));
330
- else {
331
- // Only the table = use the main database (first of the list in the config)
332
- db = $.database.config.list[0];
333
- table = path;
334
- }
340
+ // Create basic insert query
341
+ const query = this.buildInsertStatement(table, data, opts) + querySuffix;
342
+
343
+ const queryResult = await this.database.query<mysql.OkPacket>(query + ';', opts);
344
+
345
+ return {
346
+ fieldCount: queryResult.fieldCount,
347
+ affectedRows: queryResult.affectedRows,
348
+ changedRows: queryResult.changedRows,
349
+ insertId: queryResult.insertId,
350
+ serverStatus: queryResult.serverStatus,
351
+ warningCount: queryResult.warningCount,
352
+ message: queryResult.message,
353
+ procotol41: queryResult.procotol41,
354
+ };
335
355
 
336
- if ($.database.tables[db] === undefined)
337
- throw new Error(`Database "${db}" not loaded. Did you add it in the database config ?`);
338
- if ($.database.tables[db][table] === undefined)
339
- throw new Error(`Table "${db}.${table}" not loaded.`);
340
-
341
- const columns = $.database.tables[db][table].colonnes;
342
- const colNames = Object.keys(columns);
356
+ // OLD: return [data, queryResult?.insertId, queryResult];
357
+ }
343
358
 
344
- if (!Array.isArray(data))
345
- data = [data];
359
+ private buildInsertStatement<TData extends TObjetDonnees>(
360
+ table: TMetasTable,
361
+ data: TData[],
362
+ opts: TInsertQueryOptions<TData>
363
+ ) {
346
364
 
347
- let query = `INSERT ` + (opts.try ? 'IGNORE ' : '') +
348
- `INTO ` + path + ` (` + colNames.map(col => '`' + col + '`').join(', ') + `) VALUES ` +
349
- data.map(entry => {
365
+ const colNames = Object.keys(table.colonnes);
350
366
 
351
- const values: string[] = [];
352
- for (const col of colNames)
353
- if (col in entry)
354
- values.push( escape(entry[col]) );
355
- else
356
- values.push("DEFAULT");
367
+ return (
368
+ `INSERT ` + (opts.try ? 'IGNORE ' : '') +
369
+ `INTO ` + table.chemin + ` (` + colNames.map( col => '`' + col + '`').join(', ') + `) VALUES ` +
370
+ data.map(entry => {
357
371
 
358
- return '(' + values.join(', ') + ')';
372
+ const values: string[] = [];
373
+ for (const col of colNames)
374
+ if (col in entry)
375
+ values.push( this.esc( entry[col] ));
376
+ else
377
+ values.push("DEFAULT");
359
378
 
360
- }).join(', ');
379
+ return '(' + values.join(', ') + ')';
361
380
 
362
- if (opts.upsert !== undefined)
363
- query += ' ON DUPLICATE KEY UPDATE ' + opts.upsert.map( col =>
364
- '`' + col + '` = ' + (opts.upsertMode === 'increment' ? '`' + col + '` + ' : '') + 'VALUES(' + col + ')'
381
+ }).join(', ')
365
382
  )
383
+ }
366
384
 
367
- const queryResult = await sql.query(query + ';', opts);
385
+ private buildUpsertStatement<TData extends TObjetDonnees>(
386
+ table: TMetasTable,
387
+ opts: With<TInsertQueryOptions<TData>, 'upsert'>
388
+ ): string | null {
368
389
 
369
- console.log("opts.returnQuery", opts.returnQuery, "queryResult", queryResult);
370
-
371
- return [data, queryResult.insertId, queryResult];
372
- }
390
+ let upsert = opts.upsert;
373
391
 
374
- /*----------------------------------
375
- - OTHER
376
- ----------------------------------*/
377
- sql.delete = (table: string, where: TObjetDonnees = {}, opts?: TInsertQueryOptions) =>
378
- sql.query(`
379
- DELETE FROM ${table} WHERE ${equalities(where).join(' AND ')};
380
- `, opts);
381
-
382
- /*----------------------------------
383
- - REGISTER SERVICE
384
- ----------------------------------*/
385
- app.register('sql', sql, { instanciate: false });
386
- declare global {
387
- namespace Core {
388
- interface Services {
389
- sql: SQL;
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;
390
396
  }
397
+
398
+ // All columns are ps
399
+ if (upsert.length === 0)
400
+ // Replace by insert ignore
401
+ return null;
402
+
403
+ return 'ON DUPLICATE KEY UPDATE ' + upsert.map((col: string) =>
404
+ '`' + col + '` = ' + (opts.upsertMode === 'increment' ? '`' + col + '` + ' : '') + 'VALUES(' + col + ')'
405
+ )
391
406
  }
392
- }
407
+
408
+ /*----------------------------------
409
+ - OTHER
410
+ ----------------------------------*/
411
+ public delete = (table: string, where: TObjetDonnees = {}, opts?: TQueryOptions) =>
412
+ this.database.query(`
413
+ DELETE FROM ${table} WHERE ${equalities(where).join(' AND ')};
414
+ `, opts);
415
+ }