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.
- package/changelog.md +5 -0
- package/doc/TODO.md +71 -0
- package/package.json +5 -4
- package/src/client/{App.tsx → app/component.tsx} +15 -8
- package/src/client/app/index.ts +128 -0
- package/src/client/app/service.ts +34 -0
- package/src/client/app.tsconfig.json +0 -4
- package/src/client/assets/css/medias.less +14 -0
- package/src/client/components/Card/index.tsx +2 -2
- package/src/client/components/Dialog/Manager.tsx +39 -12
- package/src/client/components/Form/index.tsx +1 -1
- package/src/client/components/button.tsx +2 -2
- package/src/client/components/containers/Popover/index.tsx +1 -1
- package/src/client/components/data/spintext/index.tsx +1 -1
- package/src/client/components/dropdown/index.tsx +1 -1
- package/src/client/components/index.ts +8 -0
- package/src/client/components/input/BaseV2/index.tsx +1 -1
- package/src/client/components/input/UploadImage/index.tsx +1 -1
- package/src/client/hooks/index.ts +5 -0
- package/src/client/hooks/useState/index.tsx +2 -2
- package/src/client/hooks.ts +22 -0
- package/src/client/index.ts +5 -0
- package/src/client/pages/_layout/landing/index.tsx +0 -2
- package/src/client/pages/_messages/400.tsx +2 -2
- package/src/client/pages/_messages/401.tsx +2 -2
- package/src/client/pages/_messages/403.tsx +2 -2
- package/src/client/pages/_messages/404.tsx +2 -2
- package/src/client/pages/_messages/500.tsx +2 -2
- package/src/client/pages/bug.tsx +1 -1
- package/src/client/pages/useHeader.tsx +1 -1
- package/src/client/{context/captcha.ts → services/captcha/index.ts} +0 -0
- package/src/client/services/metrics/index.ts +37 -0
- package/src/client/{router → services/router/components}/Link.tsx +1 -1
- package/src/client/services/router/components/Page.tsx +59 -0
- package/src/client/{router/component.tsx → services/router/components/router.tsx} +43 -74
- package/src/client/services/router/index.tsx +448 -0
- package/src/client/services/router/request/api.ts +229 -0
- package/src/client/{router → services/router}/request/history.ts +0 -0
- package/src/client/services/router/request/index.ts +52 -0
- package/src/client/services/router/response/index.tsx +107 -0
- package/src/client/services/router/response/page.ts +95 -0
- package/src/client/{context/socket.ts → services/socket/index.ts} +2 -2
- package/src/client/utils/dom.ts +1 -1
- package/src/common/app/index.ts +9 -0
- package/src/common/data/chaines/index.ts +9 -6
- package/src/common/data/input/validate.ts +3 -166
- package/src/common/data/objets.ts +25 -0
- package/src/common/data/tableaux.ts +8 -0
- package/src/common/errors/index.ts +3 -1
- package/src/common/router/index.ts +67 -88
- package/src/common/router/layouts.ts +50 -0
- package/src/common/router/register.ts +62 -0
- package/src/common/router/request/api.ts +72 -0
- package/src/common/router/request/index.ts +31 -0
- package/src/common/router/{response.ts → response/index.ts} +9 -13
- package/src/common/router/response/page.ts +40 -56
- package/src/common/validation/index.ts +3 -0
- package/src/common/validation/schema.ts +184 -0
- package/src/common/validation/validator.ts +88 -0
- package/src/common/validation/validators.ts +313 -0
- package/src/server/app/config.ts +9 -27
- package/src/server/app/index.ts +81 -124
- package/src/server/app/service.ts +98 -0
- package/src/server/app.tsconfig.json +0 -8
- package/src/server/error/index.ts +13 -0
- package/src/server/index.ts +5 -0
- package/src/server/patch.ts +0 -6
- package/src/server/{data/Cache.ts → services/cache/index.ts} +79 -47
- package/src/server/services/console/bugReporter.ts +26 -16
- package/src/server/services/console/index.ts +59 -51
- package/src/server/services/cron/index.ts +12 -26
- package/src/server/services/database/bucket.ts +40 -0
- package/src/server/services/database/connection.ts +206 -75
- package/src/server/services/database/datatypes.ts +63 -40
- package/src/server/services/database/index.ts +295 -272
- package/src/server/services/database/metas.ts +246 -135
- package/src/server/services/database/stats.ts +151 -126
- package/src/server/services/email/index.ts +28 -52
- package/src/server/services/{router/request/services → metrics}/detect.ts +8 -10
- package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
- package/src/server/services/{http → router/http}/index.ts +28 -70
- package/src/server/services/{http → router/http}/multipart.ts +0 -0
- package/src/server/services/{http → router/http}/session.ts.old +0 -0
- package/src/server/services/router/index.ts +273 -203
- package/src/server/services/router/request/api.ts +73 -0
- package/src/server/services/router/request/index.ts +16 -97
- package/src/server/services/router/request/service.ts +21 -0
- package/src/server/services/router/response/index.ts +125 -64
- package/src/server/services/router/response/{filter → mask}/Filter.ts +0 -0
- package/src/server/services/router/response/{filter → mask}/index.ts +0 -2
- package/src/server/services/router/response/{filter → mask}/selecteurs.ts +0 -0
- package/src/server/services/router/response/page/document.tsx +194 -0
- package/src/server/services/router/response/page/index.tsx +157 -0
- package/src/server/{libs/pages → services/router/response/page}/schemaGenerator.ts +0 -0
- package/src/server/services/router/service.ts +48 -0
- package/src/server/services/schema/index.ts +47 -0
- package/src/server/services/schema/request.ts +55 -0
- package/src/server/services/schema/router.ts +33 -0
- package/src/server/services/socket/index.ts +38 -43
- package/src/server/services/socket/scope.ts +6 -4
- package/src/server/services/users/index.ts +203 -0
- package/src/server/services/{auth/base.ts → users/old.ts} +28 -112
- package/src/server/services/users/router/index.ts +72 -0
- package/src/server/services/users/router/request.ts +49 -0
- package/src/types/aliases.d.ts +43 -2
- package/templates/composant.tsx +1 -1
- package/templates/modal.tsx +1 -1
- package/templates/page.tsx +1 -1
- package/tsconfig.common.json +0 -4
- package/src/client/context/api.ts +0 -92
- package/src/client/context/index.ts +0 -246
- package/src/client/index.tsx +0 -129
- package/src/client/router/index.ts +0 -286
- package/src/client/router/request/index.ts +0 -106
- package/src/client/router/response/index.ts +0 -38
- package/src/client/router/route.ts +0 -75
- package/src/common/data/input/validators/basic.ts +0 -299
- package/src/common/data/input/validators/build.ts +0 -63
- package/src/common/router/request.ts +0 -83
- package/src/server/data/ApiClient.ts +0 -119
- package/src/server/data/input.ts +0 -41
- package/src/server/libs/pages/document.static.tsx +0 -41
- package/src/server/libs/pages/document.tsx +0 -203
- package/src/server/libs/pages/render.tsx +0 -90
- package/src/server/routes/auth.ts +0 -151
- package/src/server/services/redis/index.ts +0 -71
- 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
|
|
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
|
-
-
|
|
21
|
+
- SERVICE TYPES
|
|
19
22
|
----------------------------------*/
|
|
20
23
|
|
|
21
|
-
export type
|
|
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
|
-
-
|
|
60
|
+
- HELPERS
|
|
41
61
|
----------------------------------*/
|
|
42
62
|
|
|
43
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
-
|
|
69
|
+
- CORE
|
|
71
70
|
----------------------------------*/
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
// TODO: build callable instance sithut instanciating the service
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
if (typeof data === 'object' && data !== null) {
|
|
74
|
+
export default class SQL extends Service<Config, Hooks, Application> {
|
|
77
75
|
|
|
78
|
-
|
|
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
|
-
|
|
85
|
-
|
|
85
|
+
public async register() {
|
|
86
|
+
await this.database.register();
|
|
87
|
+
}
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
public async start() {
|
|
90
|
+
await this.database.start();
|
|
91
|
+
}
|
|
89
92
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
public callableInstance() {
|
|
94
|
+
return callableInstance( this, 'sql' );
|
|
95
|
+
}
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
/*----------------------------------
|
|
98
|
+
- OPERATIONS: PARSING
|
|
99
|
+
----------------------------------*/
|
|
100
|
+
public sql<TRowData extends TObjetDonnees = {}>( strings: TemplateStringsArray, ...data: any[] ) {
|
|
95
101
|
|
|
96
|
-
|
|
102
|
+
const string = this.string(strings, ...data);
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
const query = (opts: TSelectQueryOptions = {}) => this.select<TRowData>(string, opts);
|
|
99
105
|
|
|
100
|
-
|
|
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
|
-
|
|
103
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
113
|
-
|
|
118
|
+
/*query.stats = (periodStr: string, intervalStr: string, columns: string[]) =>
|
|
119
|
+
fetchStats(columns, string, periodStr, intervalStr)*/
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
query.mergeValues = (options: TQueryOptions = {}) =>
|
|
122
|
+
this.database.query(string, options).then( queriesResult => {
|
|
117
123
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
133
|
+
query.string = string;
|
|
128
134
|
|
|
129
|
-
|
|
130
|
-
}
|
|
135
|
+
return query;
|
|
136
|
+
}
|
|
131
137
|
|
|
132
|
-
|
|
138
|
+
public esc(data: any) {
|
|
133
139
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
140
|
+
// JSON object
|
|
141
|
+
// TODO: do it via datatypes.ts
|
|
142
|
+
if (typeof data === 'object' && data !== null) {
|
|
137
143
|
|
|
138
|
-
|
|
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
|
-
|
|
141
|
-
|
|
153
|
+
public string = (strings: TemplateStringsArray, ...data: any[]) => {
|
|
154
|
+
const iMax = data.length - 1;
|
|
142
155
|
|
|
143
|
-
|
|
144
|
-
|
|
156
|
+
if (typeof data === 'function')
|
|
157
|
+
throw new Error(`A function has been passed into the sql string template: ` + data);
|
|
145
158
|
|
|
146
|
-
|
|
159
|
+
return strings.map((stringBefore, i) => {
|
|
147
160
|
|
|
148
|
-
|
|
161
|
+
if (i <= iMax) {
|
|
149
162
|
|
|
150
|
-
|
|
151
|
-
|
|
163
|
+
let value = data[i];
|
|
164
|
+
stringBefore = stringBefore.trim();
|
|
165
|
+
const prefix = stringBefore[stringBefore.length - 1];
|
|
152
166
|
|
|
153
|
-
|
|
167
|
+
if (value === undefined || value === null) {
|
|
154
168
|
|
|
155
|
-
|
|
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
|
-
|
|
163
|
-
d = d.string;
|
|
164
|
-
else
|
|
165
|
-
d = escape(d);
|
|
176
|
+
} else if (prefix === ':' || prefix === '&') {
|
|
166
177
|
|
|
167
|
-
|
|
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
|
-
|
|
184
|
+
const keyword = prefix === '&' ? ' AND ' : ', '
|
|
172
185
|
|
|
173
|
-
|
|
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
|
-
|
|
181
|
-
query: string,
|
|
182
|
-
opts: TQueryOptions = {}
|
|
183
|
-
): Promise<TRowData[] | string | void> => {
|
|
192
|
+
}
|
|
184
193
|
|
|
185
|
-
|
|
186
|
-
|
|
194
|
+
} else if (typeof value === 'function' && value.string !== undefined)
|
|
195
|
+
value = value.string;
|
|
196
|
+
else
|
|
197
|
+
value = mysql.escape(value);
|
|
187
198
|
|
|
188
|
-
|
|
199
|
+
stringBefore += value;
|
|
189
200
|
|
|
190
|
-
|
|
191
|
-
console.log(`[database][query]`, query);
|
|
201
|
+
}
|
|
192
202
|
|
|
193
|
-
|
|
194
|
-
return;
|
|
203
|
+
return stringBefore;
|
|
195
204
|
|
|
196
|
-
|
|
205
|
+
}).join(' ').trim();
|
|
206
|
+
}
|
|
197
207
|
|
|
198
|
-
|
|
208
|
+
/*----------------------------------
|
|
209
|
+
- OPERATIONS: LOW LEVELf
|
|
210
|
+
----------------------------------*/
|
|
199
211
|
|
|
200
|
-
|
|
201
|
-
const
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
233
|
-
|
|
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
|
-
|
|
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
|
-
|
|
247
|
-
sql.query(query, opts).then((resultatRequetes: any) => {
|
|
248
|
-
return resultatRequetes[0] || null;
|
|
249
|
-
});
|
|
239
|
+
return resultatRequetes[0];
|
|
250
240
|
|
|
251
|
-
|
|
252
|
-
sql.query(query, opts).then((resultatRequetes: any) => {
|
|
241
|
+
});
|
|
253
242
|
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
246
|
+
const resultat = resultatRequetes[0];
|
|
258
247
|
|
|
259
|
-
|
|
248
|
+
if (!resultat)
|
|
249
|
+
return null;
|
|
260
250
|
|
|
261
|
-
|
|
262
|
-
sql.query(query, opts).then((resultatRequetes: any) => {
|
|
251
|
+
return Object.values(resultat)[0] as TVal;
|
|
263
252
|
|
|
264
|
-
|
|
253
|
+
});
|
|
265
254
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
);
|
|
265
|
+
/*----------------------------------
|
|
266
|
+
- OPERATIONS: UPDATE
|
|
267
|
+
----------------------------------*/
|
|
277
268
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
285
|
-
----------------------------------*/
|
|
276
|
+
const egalitesData = equalities(data).join(', ')
|
|
277
|
+
const egalitesWhere = equalities(where).join(' AND ')
|
|
286
278
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
-
|
|
295
|
-
const egalitesWhere = equalities(where).join(' AND ')
|
|
283
|
+
}
|
|
296
284
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
337
|
-
|
|
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
|
-
|
|
345
|
-
|
|
359
|
+
private buildInsertStatement<TData extends TObjetDonnees>(
|
|
360
|
+
table: TMetasTable,
|
|
361
|
+
data: TData[],
|
|
362
|
+
opts: TInsertQueryOptions<TData>
|
|
363
|
+
) {
|
|
346
364
|
|
|
347
|
-
|
|
348
|
-
`INTO ` + path + ` (` + colNames.map(col => '`' + col + '`').join(', ') + `) VALUES ` +
|
|
349
|
-
data.map(entry => {
|
|
365
|
+
const colNames = Object.keys(table.colonnes);
|
|
350
366
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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
|
-
|
|
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
|
-
|
|
379
|
+
return '(' + values.join(', ') + ')';
|
|
361
380
|
|
|
362
|
-
|
|
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
|
-
|
|
385
|
+
private buildUpsertStatement<TData extends TObjetDonnees>(
|
|
386
|
+
table: TMetasTable,
|
|
387
|
+
opts: With<TInsertQueryOptions<TData>, 'upsert'>
|
|
388
|
+
): string | null {
|
|
368
389
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
return [data, queryResult.insertId, queryResult];
|
|
372
|
-
}
|
|
390
|
+
let upsert = opts.upsert;
|
|
373
391
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
+
}
|