odac 1.0.0 → 1.1.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/.github/workflows/auto-pr-description.yml +3 -1
- package/CHANGELOG.md +127 -0
- package/README.md +39 -36
- package/bin/odac.js +1 -31
- package/client/odac.js +871 -994
- package/docs/backend/01-overview/03-development-server.md +7 -7
- package/docs/backend/02-structure/01-typical-project-layout.md +1 -0
- package/docs/backend/03-config/00-configuration-overview.md +9 -0
- package/docs/backend/03-config/01-database-connection.md +1 -1
- package/docs/backend/04-routing/02-controller-less-view-routes.md +9 -3
- package/docs/backend/04-routing/09-websocket.md +29 -0
- package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +2 -0
- package/docs/backend/05-controllers/03-controller-classes.md +27 -41
- package/docs/backend/05-forms/01-custom-forms.md +103 -95
- package/docs/backend/05-forms/02-automatic-database-insert.md +21 -21
- package/docs/backend/07-views/02-rendering-a-view.md +1 -1
- package/docs/backend/07-views/03-variables.md +5 -5
- package/docs/backend/07-views/04-request-data.md +1 -1
- package/docs/backend/07-views/08-backend-javascript.md +1 -1
- package/docs/backend/08-database/01-getting-started.md +100 -0
- package/docs/backend/08-database/02-basics.md +136 -0
- package/docs/backend/08-database/03-advanced.md +84 -0
- package/docs/backend/08-database/04-migrations.md +48 -0
- package/docs/backend/09-validation/01-the-validator-service.md +1 -0
- package/docs/backend/10-authentication/03-register.md +8 -1
- package/docs/backend/10-authentication/04-odac-register-forms.md +46 -46
- package/docs/backend/10-authentication/05-session-management.md +1 -1
- package/docs/backend/10-authentication/06-odac-login-forms.md +48 -48
- package/docs/backend/10-authentication/07-magic-links.md +134 -0
- package/docs/backend/11-mail/01-the-mail-service.md +118 -28
- package/docs/backend/12-streaming/01-streaming-overview.md +2 -2
- package/docs/backend/13-utilities/01-odac-var.md +7 -7
- package/docs/backend/13-utilities/02-ipc.md +73 -0
- package/docs/frontend/01-overview/01-introduction.md +5 -1
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +1 -1
- package/docs/index.json +16 -124
- package/eslint.config.mjs +5 -47
- package/package.json +9 -4
- package/src/Auth.js +362 -104
- package/src/Config.js +7 -2
- package/src/Database.js +188 -0
- package/src/Ipc.js +330 -0
- package/src/Mail.js +408 -37
- package/src/Odac.js +65 -9
- package/src/Request.js +70 -48
- package/src/Route/Cron.js +4 -1
- package/src/Route/Internal.js +214 -11
- package/src/Route/Middleware.js +7 -2
- package/src/Route.js +106 -26
- package/src/Server.js +80 -11
- package/src/Storage.js +165 -0
- package/src/Validator.js +94 -2
- package/src/View/Form.js +193 -17
- package/src/View.js +46 -1
- package/src/WebSocket.js +18 -3
- package/template/config.json +1 -1
- package/template/route/www.js +12 -10
- package/test/core/{Candy.test.js → Odac.test.js} +2 -2
- package/docs/backend/08-database/01-database-connection.md +0 -99
- package/docs/backend/08-database/02-using-mysql.md +0 -322
- package/src/Mysql.js +0 -575
package/src/Mysql.js
DELETED
|
@@ -1,575 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
const mysql = require('mysql2')
|
|
3
|
-
|
|
4
|
-
class Raw {
|
|
5
|
-
constructor(value) {
|
|
6
|
-
this.value = value
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
class Mysql {
|
|
11
|
-
#conn
|
|
12
|
-
#database
|
|
13
|
-
#defining = false
|
|
14
|
-
#table = []
|
|
15
|
-
#arr = {}
|
|
16
|
-
#stack = []
|
|
17
|
-
#statements = ['=', '>', '>=', '<', '<=', '!=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', 'IS', 'IS NOT']
|
|
18
|
-
|
|
19
|
-
constructor(table, conn) {
|
|
20
|
-
this.#conn = conn
|
|
21
|
-
this.#stack = new Error().stack.split('\n').splice(3)
|
|
22
|
-
if (table) {
|
|
23
|
-
this.#arr.table = table
|
|
24
|
-
this.#define(table)
|
|
25
|
-
this.#defining = 1
|
|
26
|
-
while (this.#defining && this.#defining < 255) {
|
|
27
|
-
this.#defining++
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// function having(){
|
|
33
|
-
// if(count(func_get_args()) == 1 && !is_array(func_get_args()[0])){
|
|
34
|
-
// $this->arr['having'] = is_numeric(func_get_args()[0]) ? "id='".func_get_args()[0]."'" : "";
|
|
35
|
-
// }elseif(count(func_get_args()) > 0){
|
|
36
|
-
// $this->arr['having'] = isset($this->arr['having']) && trim($this->arr['having'])!='' ? $this->arr['having'].' AND '.$this->whereExtract(func_get_args()) : $this->whereExtract(func_get_args());
|
|
37
|
-
// }
|
|
38
|
-
// return new static($this->table,$this->arr);
|
|
39
|
-
// }
|
|
40
|
-
|
|
41
|
-
// function whereJson($col,$val){
|
|
42
|
-
// //return 'JSON_SEARCH('.$col.', "one", "'.$val.'") IS NOT NULL';
|
|
43
|
-
// return new static($this->table,$this->arr);
|
|
44
|
-
// }
|
|
45
|
-
// function cache($t=3600){
|
|
46
|
-
// if(!is_numeric($t)){
|
|
47
|
-
// $exp = explode(' ',str_replace(' ',' ',$t));
|
|
48
|
-
// if($exp[1] == 'second') $t = intval(trim($exp[0]));
|
|
49
|
-
// if($exp[1] == 'minute') $t = intval(trim($exp[0])) * 60;
|
|
50
|
-
// if($exp[1] == 'hour') $t = intval(trim($exp[0])) * 60 * 60;
|
|
51
|
-
// if($exp[1] == 'day') $t = intval(trim($exp[0])) * 60 * 60 * 24;
|
|
52
|
-
// if($exp[1] == 'week') $t = intval(trim($exp[0])) * 60 * 60 * 24 * 7;
|
|
53
|
-
// if($exp[1] == 'month') $t = intval(trim($exp[0])) * 60 * 60 * 24 * 30;
|
|
54
|
-
// if($exp[1] == 'year') $t = intval(trim($exp[0])) * 60 * 60 * 24 * 365;
|
|
55
|
-
// }
|
|
56
|
-
// $this->arr['cache'] = $t;
|
|
57
|
-
// return new static($this->table,$this->arr);
|
|
58
|
-
// }
|
|
59
|
-
|
|
60
|
-
async #define(table) {
|
|
61
|
-
return new Promise(resolve => {
|
|
62
|
-
if (!Odac.Mysql.db[this.#database]) Odac.Mysql.db[this.#database] = {}
|
|
63
|
-
this.#table[table] = Odac.Mysql.db[this.#database][table]
|
|
64
|
-
if (this.#table[table]) {
|
|
65
|
-
this.#defining = false
|
|
66
|
-
return resolve(true)
|
|
67
|
-
}
|
|
68
|
-
let columns = []
|
|
69
|
-
this.#conn.query(`SHOW COLUMNS FROM ${this.escape(table, 'table')}`, (err, result) => {
|
|
70
|
-
if (err) {
|
|
71
|
-
this.#error(err)
|
|
72
|
-
this.#defining = false
|
|
73
|
-
return resolve(false)
|
|
74
|
-
}
|
|
75
|
-
for (let get of result) {
|
|
76
|
-
columns[get.Field] = get
|
|
77
|
-
if (get.Key == 'PRI') {
|
|
78
|
-
if (!this.#table[table]) this.#table[table] = {}
|
|
79
|
-
this.#table[table].primary = get.Field
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
if (!this.#table[table]) this.#table[table] = {}
|
|
83
|
-
this.#table[table].columns = columns
|
|
84
|
-
Odac.Mysql.db[this.#database][table] = this.#table[table]
|
|
85
|
-
this.#defining = false
|
|
86
|
-
return resolve(true)
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async delete() {
|
|
92
|
-
let query = this.query('delete')
|
|
93
|
-
let run = await this.run(query)
|
|
94
|
-
if (run === false) return false
|
|
95
|
-
this.affected = run.affectedRows
|
|
96
|
-
// if($this->affected > 0) self::clearcache();
|
|
97
|
-
return this
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
#error(err, query) {
|
|
101
|
-
err = 'Odac Mysql Error: ' + (err?.message ?? 'Unknown error').trim() + '\n'
|
|
102
|
-
if (query) err += 'Query: ' + query + '\n'
|
|
103
|
-
while (this.#stack.length > 0) {
|
|
104
|
-
let line = this.#stack.shift().replace('at', '')
|
|
105
|
-
if (line.includes('/node_modules/odac/framework/src/')) break
|
|
106
|
-
else if (!line.includes('(node:')) err += line + '\n'
|
|
107
|
-
}
|
|
108
|
-
console.error(err)
|
|
109
|
-
return false
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
escape(v, type) {
|
|
113
|
-
if (!type) type = 'value'
|
|
114
|
-
if (v && v instanceof Raw) return ' ' + v.value + ' '
|
|
115
|
-
if (type == 'value') {
|
|
116
|
-
if (v === null) return 'NULL'
|
|
117
|
-
if (typeof v === 'object')
|
|
118
|
-
return (
|
|
119
|
-
' (' +
|
|
120
|
-
Object.values(v)
|
|
121
|
-
.map(val => mysql.escape(val))
|
|
122
|
-
.join(',') +
|
|
123
|
-
') '
|
|
124
|
-
)
|
|
125
|
-
return `${mysql.escape(v)}`
|
|
126
|
-
} else if (type == 'table' || type == 'col') {
|
|
127
|
-
let as = ''
|
|
128
|
-
if (typeof v === 'object') {
|
|
129
|
-
as = Object.values(v)[0]
|
|
130
|
-
v = Object.keys(v)[0]
|
|
131
|
-
as = type == 'col' ? ` AS ${mysql.escapeId(as)} ` : ` ${mysql.escapeId(as)} `
|
|
132
|
-
}
|
|
133
|
-
if (v.includes('.')) {
|
|
134
|
-
return (
|
|
135
|
-
v
|
|
136
|
-
.split('.')
|
|
137
|
-
.map(val => mysql.escapeId(val))
|
|
138
|
-
.join('.') + as
|
|
139
|
-
)
|
|
140
|
-
}
|
|
141
|
-
return mysql.escapeId(v) + as
|
|
142
|
-
} else if (type == 'statement' || type == 'st') {
|
|
143
|
-
return this.#statements.includes(v.toUpperCase()) ? v.toUpperCase() : '='
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
first(b = false) {
|
|
148
|
-
return new Promise((resolve, reject) => {
|
|
149
|
-
this.#arr.limit = 1
|
|
150
|
-
this.get(b)
|
|
151
|
-
.then(sql => {
|
|
152
|
-
if (sql === false || !sql[0]) return resolve(false)
|
|
153
|
-
return resolve(sql[0])
|
|
154
|
-
})
|
|
155
|
-
.catch(reject)
|
|
156
|
-
})
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async get(b) {
|
|
160
|
-
if (!b) b = false
|
|
161
|
-
let data = []
|
|
162
|
-
// if(isset($this->arr['cache'])){
|
|
163
|
-
// $md5_query = md5($query);
|
|
164
|
-
// $md5_table = md5($this->arr['table']);
|
|
165
|
-
// $file = "cache/mysql/".md5(Mysql::$name)."/$md5_table"."_$md5_query";
|
|
166
|
-
// $cache = Odac::storage($file)->get('cache');
|
|
167
|
-
// if(isset($cache->date) && ($cache->date >= (time() - $this->arr['cache']))) return $cache->data;
|
|
168
|
-
// }
|
|
169
|
-
let query = this.query('get')
|
|
170
|
-
let sql = await this.run(query)
|
|
171
|
-
// console.log(sql);
|
|
172
|
-
if (sql === false) return this.#error()
|
|
173
|
-
for (let row of sql) {
|
|
174
|
-
for (let [key, value] of Object.entries(row)) row[key] = await this.type(key, value)
|
|
175
|
-
data.push(row)
|
|
176
|
-
}
|
|
177
|
-
// while($row = mysqli_fetch_assoc($sql)){
|
|
178
|
-
// foreach($row as $key => $value) $row[$key] = $this->type($key, $value);
|
|
179
|
-
// $data[] = $b ? $row : (object)$row;
|
|
180
|
-
// }
|
|
181
|
-
// mysqli_free_result($sql);
|
|
182
|
-
// if(isset($cache)){
|
|
183
|
-
// $cache->data = $data;
|
|
184
|
-
// $cache->date = time();
|
|
185
|
-
// Odac::storage($file)->set('cache', $cache);
|
|
186
|
-
// }
|
|
187
|
-
return data
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async insert(arr) {
|
|
191
|
-
this.id = 1
|
|
192
|
-
let ext = await this.#valuesExtract(arr)
|
|
193
|
-
this.#arr['into'] = ext['into']
|
|
194
|
-
this.#arr['values'] = ext['values']
|
|
195
|
-
let query = this.query('insert')
|
|
196
|
-
let run = await this.run(query)
|
|
197
|
-
if (run === false) return false
|
|
198
|
-
this.id = run.insertId
|
|
199
|
-
this.affected = run.affectedRows
|
|
200
|
-
// if(this.affected > 0) this.clearcache();
|
|
201
|
-
return this
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
insertIgnore(arr) {
|
|
205
|
-
this.#arr.ignore = true
|
|
206
|
-
return this.insert(arr)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
order(v1, v2 = 'asc') {
|
|
210
|
-
// if(is_array($v1) && (!isset($v1['ct']) || $v1['ct'] != $GLOBALS['candy_token_mysql'])){
|
|
211
|
-
// $order = [];
|
|
212
|
-
// foreach($v1 as $key => $val)
|
|
213
|
-
// if(!is_int($key)) $order[] = $this->escape($key,'col').(strtolower($val) == 'desc' ? ' DESC' : ' ASC');
|
|
214
|
-
// else $order[] = $this->escape($val,'col').' ASC';
|
|
215
|
-
// $this->arr['order by'] = implode(',',$order);
|
|
216
|
-
/* }else */ this.#arr['order by'] = this.escape(v1, 'col') + (v2.toLowerCase() == 'desc' ? ' DESC' : ' ASC')
|
|
217
|
-
return this
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
orWhere(...args) {
|
|
221
|
-
this.#arr.where =
|
|
222
|
-
this.#arr.where && this.#arr.where.trim() != '' ? `${this.#arr.where} OR ${this.#whereExtract(args)}` : this.#whereExtract(args)
|
|
223
|
-
return this
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async replace(arr) {
|
|
227
|
-
// $this->id = 1;
|
|
228
|
-
let ext = await this.#valuesExtract(arr)
|
|
229
|
-
this.#arr['into'] = ext['into']
|
|
230
|
-
this.#arr['values'] = ext['values']
|
|
231
|
-
let query = this.query('replace')
|
|
232
|
-
let run = await this.run(query)
|
|
233
|
-
if (run === false) return false
|
|
234
|
-
this.id = run.insertId
|
|
235
|
-
this.affected = run.affectedRows
|
|
236
|
-
// if($sql === false) return $this->error();
|
|
237
|
-
// $this->success = $sql;
|
|
238
|
-
// self::clearcache();
|
|
239
|
-
return this
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
async rows() {
|
|
243
|
-
// if(isset($this->arr['cache'])){
|
|
244
|
-
// $md5_query = md5($query);
|
|
245
|
-
// $md5_table = md5($this->arr['table']);
|
|
246
|
-
// $file = "cache/mysql/".md5(Mysql::$name)."/$md5_table"."_$md5_query"."_r";
|
|
247
|
-
// $cache = Odac::storage($file)->get('cache');
|
|
248
|
-
// if(isset($cache->date) && ($cache->date >= (time() - $this->arr['cache']))) return $cache->data;
|
|
249
|
-
// }
|
|
250
|
-
let query = this.query('get')
|
|
251
|
-
let sql = await this.run(query)
|
|
252
|
-
if (sql === false) return this.#error()
|
|
253
|
-
let rows = sql.length
|
|
254
|
-
// if(isset($cache)){
|
|
255
|
-
// $cache->data = $rows;
|
|
256
|
-
// $cache->date = time();
|
|
257
|
-
// Odac::storage($file)->set('cache', $cache);
|
|
258
|
-
// }
|
|
259
|
-
return rows
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
run(query, params) {
|
|
263
|
-
return new Promise(resolve => {
|
|
264
|
-
if (!query) return resolve(false)
|
|
265
|
-
if (!this.#conn) return resolve(false)
|
|
266
|
-
if (this.#conn.state == 'disconnected') Odac.Mysql.init()
|
|
267
|
-
const args = params ? [query, params] : [query]
|
|
268
|
-
args.push((err, result) => {
|
|
269
|
-
if (err) return resolve(this.#error(err, query))
|
|
270
|
-
return resolve(result)
|
|
271
|
-
})
|
|
272
|
-
this.#conn.query(...args)
|
|
273
|
-
})
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
select(...args) {
|
|
277
|
-
this.#arr['select'] = this.#arr['select'] ?? []
|
|
278
|
-
if (args.length == 1 && (typeof args[0] === 'object' || args[0] instanceof Raw)) {
|
|
279
|
-
if (args[0] instanceof Raw) {
|
|
280
|
-
this.#arr['select'].push(args[0].value)
|
|
281
|
-
} else {
|
|
282
|
-
for (let key of Object.keys(args[0])) {
|
|
283
|
-
let value = args[0][key]
|
|
284
|
-
if (isNaN(key)) this.#arr['select'].push(this.escape(key, 'col') + ' AS ' + this.escape(value, 'col'))
|
|
285
|
-
else this.#arr['select'].push(this.escape(value, 'col'))
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
} else {
|
|
289
|
-
for (let key of args) this.#arr['select'].push(this.escape(key, 'col'))
|
|
290
|
-
}
|
|
291
|
-
return this
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
async set(arr, val) {
|
|
295
|
-
let vars = ''
|
|
296
|
-
if (!['array', 'object'].includes(typeof arr) && val !== undefined)
|
|
297
|
-
vars += this.escape(arr, 'col') + ' = ' + this.escape(await this.type(arr, val, 'encode')) + ','
|
|
298
|
-
else
|
|
299
|
-
for (let [key, value] of Object.entries(arr))
|
|
300
|
-
vars += this.escape(key, 'col') + ' = ' + this.escape(await this.type(key, value, 'encode')) + ','
|
|
301
|
-
this.#arr.set = vars.substring(0, vars.length - 1)
|
|
302
|
-
let query = this.query('set')
|
|
303
|
-
let run = await this.run(query)
|
|
304
|
-
if (run === false) return this.#error()
|
|
305
|
-
this.affected = run.affectedRows
|
|
306
|
-
if (this.affected > 0) this.#clearcache()
|
|
307
|
-
return this
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
groupBy(...args) {
|
|
311
|
-
this.#arr['group by'] = this.#arr['group by'] ?? ''
|
|
312
|
-
let select = this.#arr['group by'] ? this.#arr['group by'].split(',') : []
|
|
313
|
-
// if(count(func_get_args())==1 && is_array(func_get_args()[0])){
|
|
314
|
-
// if(isset(func_get_args()[0]['ct']) && isset(func_get_args()[0]['v']) && func_get_args()[0]['ct'] == $GLOBALS['candy_token_mysql']){
|
|
315
|
-
// $select[] = func_get_args()[0]['v'];
|
|
316
|
-
// }else{
|
|
317
|
-
// foreach(func_get_args()[0] as $key => $value){
|
|
318
|
-
// $select[] = $this->escape($value,'col');
|
|
319
|
-
// }
|
|
320
|
-
// }
|
|
321
|
-
/* } else */ for (let key of args) select.push(this.escape(key, 'col'))
|
|
322
|
-
this.#arr['group by'] = select.join(', ')
|
|
323
|
-
return this
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
limit(v1, v2 = null) {
|
|
327
|
-
this.#arr['limit'] = v2 === null ? v1 : `${v1}, ${v2}`
|
|
328
|
-
return this
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
leftJoin(tb, col1, st = null, col2 = null) {
|
|
332
|
-
return this.join(tb, col1, st, col2, 'left join')
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
rightJoin(tb, col1, st = null, col2 = null) {
|
|
336
|
-
return this.join(tb, col1, st, col2, 'right join')
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
join(tb, col1, st = null, col2 = null, type = 'inner join') {
|
|
340
|
-
this.#arr[type] = this.#arr[type] ?? []
|
|
341
|
-
this.#define(Array.isArray(tb) ? Object.keys(tb)[0] : tb)
|
|
342
|
-
tb = this.escape(tb, 'table')
|
|
343
|
-
let state
|
|
344
|
-
if (st === null && col2 === null) {
|
|
345
|
-
col1 = this.#whereExtract(col1)
|
|
346
|
-
col2 = ''
|
|
347
|
-
state = ''
|
|
348
|
-
} else {
|
|
349
|
-
col1 = this.escape(col1, 'col')
|
|
350
|
-
col2 = this.escape(col2 !== null ? col2 : st, 'col')
|
|
351
|
-
state = this.escape(col2 !== null ? st : '=', 'st')
|
|
352
|
-
}
|
|
353
|
-
this.#arr[type].push(`${tb} ON ${col1} ${state} ${col2}`)
|
|
354
|
-
return this
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
#clearcache() {
|
|
358
|
-
// if(!isset($this->arr['table'])) return false;
|
|
359
|
-
// $md5_table = md5($this->arr['table']);
|
|
360
|
-
// $file = "storage/cache/mysql/".md5(Mysql::$name)."/$md5_table*";
|
|
361
|
-
// foreach(glob($file) as $key) unlink($key);
|
|
362
|
-
return true
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
query(type = 'get') {
|
|
366
|
-
const arr_q = ['inner join', 'right join', 'left join', 'where', 'group by', 'having', 'order by', 'limit']
|
|
367
|
-
let query = ''
|
|
368
|
-
for (let key of arr_q) {
|
|
369
|
-
if (this.#arr[key]) {
|
|
370
|
-
if (Array.isArray(this.#arr[key])) {
|
|
371
|
-
query += ' ' + key.toUpperCase() + ' ' + this.#arr[key].join(' ' + key.toUpperCase() + ' ')
|
|
372
|
-
} else {
|
|
373
|
-
query += ' ' + key.toUpperCase() + ' '
|
|
374
|
-
query += this.#arr[key]
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
switch (type) {
|
|
379
|
-
case 'get':
|
|
380
|
-
query = `SELECT ${this.#arr.select ? this.#arr.select.join(', ') : '*'} FROM ${this.escape(this.#arr.table, 'table')} ${query}`
|
|
381
|
-
break
|
|
382
|
-
case 'set':
|
|
383
|
-
query = `UPDATE ${this.escape(this.#arr['table'], 'table')} SET ${this.#arr['set']} ${query}`
|
|
384
|
-
break
|
|
385
|
-
case 'insert':
|
|
386
|
-
query = `INSERT ${this.#arr.ignore ? 'IGNORE' : ''} INTO ${this.escape(this.#arr.table, 'table')} ${this.#arr.into} VALUES ${this.#arr.values}`
|
|
387
|
-
break
|
|
388
|
-
case 'delete':
|
|
389
|
-
query = `DELETE FROM ${this.escape(this.#arr.table, 'table')} ${query}`
|
|
390
|
-
break
|
|
391
|
-
case 'replace':
|
|
392
|
-
query = `REPLACE INTO ${this.escape(this.#arr.table, 'table')} ${this.#arr.into} VALUES ${this.#arr.values}`
|
|
393
|
-
break
|
|
394
|
-
}
|
|
395
|
-
return query
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
async type(col, value, action = 'decode') {
|
|
399
|
-
if (!this.types) this.types = {}
|
|
400
|
-
if (!this.types[col]) {
|
|
401
|
-
this.types[col] = 'string'
|
|
402
|
-
for (const key of Object.keys(this.#table)) {
|
|
403
|
-
if (!this.#table[key]) await this.#define(key)
|
|
404
|
-
if (!this.#table[key]) throw new Error(`Table ${key} not found`)
|
|
405
|
-
if (!this.#arr.select && this.#table[key].columns[col]?.Type) {
|
|
406
|
-
this.types[col] = this.#table[key].columns[col]?.Type ?? this.types[col]
|
|
407
|
-
break
|
|
408
|
-
} else if (!this.#arr.select) {
|
|
409
|
-
continue
|
|
410
|
-
} else if (Odac.Var(this.#arr.select).contains(' AS "' + col + '"')) {
|
|
411
|
-
// $exp = explode(' ,',explode(" AS \"$col\"",$this->arr['select'])[0]);
|
|
412
|
-
// $real_col = explode('.',Odac::var(trim(end($exp)))->clear('`'));
|
|
413
|
-
// $real_table = trim($real_col[0]);
|
|
414
|
-
// $real_col = trim($real_col[1]);
|
|
415
|
-
// $this->types[$col] = $this->types[$col] = $this->table[$real_table]['columns'][$real_col]['Type'] ?? $this->types[$col];
|
|
416
|
-
break
|
|
417
|
-
} else if (Odac.Var(this.#arr.select).containsAny(' `' + col + '`', ' `' + key + '`.`' + col + '`')) {
|
|
418
|
-
this.types[col] = this.#table[key].columns[col].Type ?? this.types[col]
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
if (action == 'decode') {
|
|
423
|
-
if (Odac.Var(this.types[col]).isBegin('tinyint(1)')) value = value ? true : false
|
|
424
|
-
else if (Odac.Var(this.types[col]).isBegin('int')) value = parseInt(value)
|
|
425
|
-
else if (Odac.Var(this.types[col]).isBegin('double')) value = parseFloat(value)
|
|
426
|
-
else if (Odac.Var(this.types[col]).isBegin('float')) value = parseFloat(value)
|
|
427
|
-
else if (Odac.Var(this.types[col]).isBegin('boolean')) value = parseInt(value)
|
|
428
|
-
else if (Odac.Var(this.types[col]).isBegin('json')) value = JSON.parse(value)
|
|
429
|
-
} else if (!(value instanceof Raw)) {
|
|
430
|
-
if (Odac.Var(this.types[col]).isBegin('tinyint(1)')) value = parseInt(value)
|
|
431
|
-
else if (Odac.Var(this.types[col]).isBegin('int')) value = parseInt(value)
|
|
432
|
-
else if (Odac.Var(this.types[col]).isBegin('double')) value = parseFloat(value)
|
|
433
|
-
else if (Odac.Var(this.types[col]).isBegin('float')) value = parseFloat(value)
|
|
434
|
-
else if (Odac.Var(this.types[col]).isBegin('boolean')) value = parseInt(value)
|
|
435
|
-
else if (Odac.Var(this.types[col]).isBegin('json')) value = JSON.stringify(value)
|
|
436
|
-
else if (Odac.Var(this.types[col]).isBegin('date', 'datetime', 'timestamp')) value = Odac.Var(value).date('Y-m-d H:i:s')
|
|
437
|
-
}
|
|
438
|
-
return value
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
async #valuesExtract(arr) {
|
|
442
|
-
let query_key = []
|
|
443
|
-
let query_val = []
|
|
444
|
-
let multiple = false
|
|
445
|
-
let keys = Object.keys(arr)
|
|
446
|
-
for (let i = 0; i < keys.length; i++) {
|
|
447
|
-
let key = keys[i]
|
|
448
|
-
let val = arr[key]
|
|
449
|
-
// if(is_object($val)) $val = (array)$val;
|
|
450
|
-
// if(is_array($val) && !isset($this->table[$this->arr['table']]['columns'][$key]) && (!isset($val['ct']) || $val['ct']!=$GLOBALS['candy_token_mysql'])){
|
|
451
|
-
// $multiple = true;
|
|
452
|
-
// $ex = $this->valuesExtract($val);
|
|
453
|
-
// $query_key = $ex['into'];
|
|
454
|
-
// $query_val[] = $ex['values'];
|
|
455
|
-
/* }else */ if (val === null) {
|
|
456
|
-
query_key.push(this.escape(key, 'col'))
|
|
457
|
-
query_val.push('NULL')
|
|
458
|
-
} else {
|
|
459
|
-
query_key.push(this.escape(key, 'col'))
|
|
460
|
-
query_val.push(this.escape(await this.type(key, val, 'encode')))
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
return {
|
|
464
|
-
into: !multiple ? `(${query_key.join(',')})` : query_key,
|
|
465
|
-
values: !multiple ? `(${query_val.join(',')})` : query_val.join(',')
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
where(...args) {
|
|
470
|
-
if (args.length == 1 && typeof args[0] !== 'object' && !(args[0] instanceof Raw)) {
|
|
471
|
-
this.#arr.where = this.#whereExtract([this.#table[this.#arr.table].primary, args[0]])
|
|
472
|
-
} else if (args.length > 0) {
|
|
473
|
-
this.#arr.where =
|
|
474
|
-
this.#arr.where && this.#arr.where.trim() != '' ? `${this.#arr.where} AND ${this.#whereExtract(args)}` : this.#whereExtract(args)
|
|
475
|
-
}
|
|
476
|
-
return this
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
#whereExtract(arr) {
|
|
480
|
-
let q = ''
|
|
481
|
-
let loop = 1
|
|
482
|
-
let in_arr = false
|
|
483
|
-
let state = '='
|
|
484
|
-
let last = 0
|
|
485
|
-
for (const key of arr) {
|
|
486
|
-
if (key && Array.isArray(key) && state != 'IN' && state != 'NOT IN' && !(key instanceof Raw)) {
|
|
487
|
-
q += last == 1 ? ' AND ' + this.#whereExtract(key) : this.#whereExtract(key)
|
|
488
|
-
in_arr = true
|
|
489
|
-
last = 1
|
|
490
|
-
} else if (arr.length == 2 && loop == 2) {
|
|
491
|
-
q += ' = ' + this.escape(key)
|
|
492
|
-
} else if (in_arr) {
|
|
493
|
-
q += key.toUpperCase() == 'OR' ? ' OR ' : ' AND '
|
|
494
|
-
last = 2
|
|
495
|
-
} else if (arr.length == 3 && loop == 2) {
|
|
496
|
-
state = this.#statements.includes(key.toUpperCase()) ? key.toUpperCase() : '='
|
|
497
|
-
q += ' ' + state
|
|
498
|
-
last = 1
|
|
499
|
-
} else if (key === null) {
|
|
500
|
-
q += ' NULL '
|
|
501
|
-
} else {
|
|
502
|
-
q += this.escape(key, loop == 1 ? 'table' : 'value')
|
|
503
|
-
last = 1
|
|
504
|
-
}
|
|
505
|
-
loop++
|
|
506
|
-
}
|
|
507
|
-
return `(${q})`
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
module.exports = {
|
|
512
|
-
conn: {},
|
|
513
|
-
db: {},
|
|
514
|
-
init: function () {
|
|
515
|
-
return new Promise(resolve => {
|
|
516
|
-
if (!Odac.Config.database) return resolve(false)
|
|
517
|
-
let multiple = typeof Odac.Config.database[Object.keys(Odac.Config.database)[0]] === 'object'
|
|
518
|
-
let dbs = multiple ? Odac.Config.database : {default: Odac.Config.database}
|
|
519
|
-
for (let key of Object.keys(dbs)) {
|
|
520
|
-
let db = dbs[key]
|
|
521
|
-
if (db.type && db.type != 'mysql') continue
|
|
522
|
-
Odac.Mysql.conn[key] = mysql.createConnection({
|
|
523
|
-
host: db.host ?? '127.0.0.1',
|
|
524
|
-
user: db.user,
|
|
525
|
-
password: db.password,
|
|
526
|
-
database: db.database,
|
|
527
|
-
stringifyObjects: true
|
|
528
|
-
})
|
|
529
|
-
Odac.Mysql.conn[key].connect(err => {
|
|
530
|
-
if (err) {
|
|
531
|
-
console.error(`Odac Mysql Error: Failed to connect to database '${key}'`)
|
|
532
|
-
console.error(`Host: ${db.host ?? '127.0.0.1'}`)
|
|
533
|
-
console.error(`User: ${db.user}`)
|
|
534
|
-
console.error(`Database: ${db.database}`)
|
|
535
|
-
console.error(`Error: ${err.message}`)
|
|
536
|
-
return resolve(false)
|
|
537
|
-
}
|
|
538
|
-
})
|
|
539
|
-
Odac.Mysql.conn[key].query('SHOW TABLES', (err, result) => {
|
|
540
|
-
if (err) {
|
|
541
|
-
console.error(`Odac Mysql Error: Failed to query tables from database '${key}'`)
|
|
542
|
-
console.error(`Error: ${err.message}`)
|
|
543
|
-
return resolve(false)
|
|
544
|
-
}
|
|
545
|
-
for (let table of result)
|
|
546
|
-
for (let key of Object.keys(table)) {
|
|
547
|
-
let t = () => {
|
|
548
|
-
new Mysql(table[key], Odac.Mysql.conn['default'])
|
|
549
|
-
}
|
|
550
|
-
t()
|
|
551
|
-
}
|
|
552
|
-
})
|
|
553
|
-
}
|
|
554
|
-
return resolve(true)
|
|
555
|
-
})
|
|
556
|
-
},
|
|
557
|
-
database: function (name) {
|
|
558
|
-
if (!Odac.Mysql.conn[name]) return null
|
|
559
|
-
return new Mysql(name, Odac.Mysql.conn[name])
|
|
560
|
-
},
|
|
561
|
-
run: function (query, params) {
|
|
562
|
-
if (!Odac.Mysql.conn['default']) return Promise.resolve(false)
|
|
563
|
-
return new Mysql(null, Odac.Mysql.conn['default']).run(query, params)
|
|
564
|
-
},
|
|
565
|
-
table: function (name) {
|
|
566
|
-
if (!Odac.Mysql.conn['default']) return null
|
|
567
|
-
return new Mysql(name, Odac.Mysql.conn['default'])
|
|
568
|
-
},
|
|
569
|
-
raw: function (query) {
|
|
570
|
-
if (typeof query !== 'string') {
|
|
571
|
-
throw new Error('Mysql.raw() requires a string parameter')
|
|
572
|
-
}
|
|
573
|
-
return new Raw(query)
|
|
574
|
-
}
|
|
575
|
-
}
|