zet-lib 3.3.0 → 3.3.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.
- package/lib/Form.js +2 -2
- package/lib/connection.js +164 -88
- package/lib/views/zgenerator/routerApp.ejs +359 -359
- package/package.json +1 -1
package/lib/Form.js
CHANGED
|
@@ -512,11 +512,11 @@ Form.field = (obj) => {
|
|
|
512
512
|
});
|
|
513
513
|
|
|
514
514
|
displayForm += `<div id="dragdrop_${obj.id}" class="row contentfields">
|
|
515
|
-
<div class="col-
|
|
515
|
+
<div class="col-6">
|
|
516
516
|
<h5>${obj.attributes.left}</h5>
|
|
517
517
|
<ol class="mydragable${obj.id} divboxlittle" data-type="left" data-name="${obj.name}[]">${leftButtons}</ol>
|
|
518
518
|
</div>
|
|
519
|
-
<div class="col-
|
|
519
|
+
<div class="col-6">
|
|
520
520
|
<h5>${obj.attributes.right}</h5>
|
|
521
521
|
<ol class="mydragable${obj.id} divboxlittle" data-type="right" data-name="trashx">
|
|
522
522
|
${rightButtons}
|
package/lib/connection.js
CHANGED
|
@@ -21,6 +21,77 @@ pool.on('error', (err) => {
|
|
|
21
21
|
console.error('Unexpected error on idle client', err)
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
+
// Validasi identifier (nama tabel) untuk mencegah SQL injection
|
|
25
|
+
const SAFE_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)?$/
|
|
26
|
+
const sanitizeTableName = (table) => {
|
|
27
|
+
if (typeof table !== 'string' || !table.trim()) return ''
|
|
28
|
+
const t = table.trim()
|
|
29
|
+
if (!SAFE_IDENTIFIER.test(t)) {
|
|
30
|
+
throw new Error(`Invalid table name: ${t}`)
|
|
31
|
+
}
|
|
32
|
+
return t
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Validasi limit/offset sebagai integer non-negatif
|
|
36
|
+
const toSafeNonNegativeInt = (val, fallback) => {
|
|
37
|
+
if (val === undefined || val === null) return fallback
|
|
38
|
+
const n = parseInt(val, 10)
|
|
39
|
+
if (!Number.isInteger(n) || n < 0) return fallback
|
|
40
|
+
return n
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Validasi satu segmen ORDER BY (identifier + optional ASC/DESC) untuk mencegah SQL injection
|
|
44
|
+
const SAFE_IDENTIFIER_PART = /^[a-zA-Z_][a-zA-Z0-9_]*$/
|
|
45
|
+
const sanitizeOrderBySegment = (segment) => {
|
|
46
|
+
if (typeof segment !== 'string' || !segment.trim()) return ''
|
|
47
|
+
const parts = segment.trim().split(/\s+/)
|
|
48
|
+
const ident = parts[0]
|
|
49
|
+
const dir = parts[1] ? parts[1].toUpperCase() : ''
|
|
50
|
+
if (dir && dir !== 'ASC' && dir !== 'DESC') return ''
|
|
51
|
+
const identParts = ident.split('.')
|
|
52
|
+
const valid = identParts.length > 0 && identParts.every(p => SAFE_IDENTIFIER_PART.test(p))
|
|
53
|
+
if (!valid) return ''
|
|
54
|
+
const quotedIdent = identParts.map(p => `"${p}"`).join('.')
|
|
55
|
+
return dir ? `${quotedIdent} ${dir}` : quotedIdent
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Validasi nama kolom (identifier tunggal) untuk INSERT/UPDATE
|
|
59
|
+
const sanitizeColumnName = (key) => SAFE_IDENTIFIER_PART.test(String(key).trim()) ? String(key).trim() : null
|
|
60
|
+
|
|
61
|
+
// Validasi klausa RETURNING: hanya RETURNING * atau RETURNING col1, col2, ...
|
|
62
|
+
const SAFE_RETURNING = /^RETURNING \*$/i
|
|
63
|
+
const SAFE_RETURNING_COLS = /^RETURNING ([a-zA-Z_][a-zA-Z0-9_]*)(,\s*[a-zA-Z_][a-zA-Z0-9_]*)*$/i
|
|
64
|
+
const sanitizeReturning = (clause) => {
|
|
65
|
+
if (typeof clause !== 'string' || !clause.trim()) return 'RETURNING *'
|
|
66
|
+
const s = clause.trim()
|
|
67
|
+
if (SAFE_RETURNING.test(s)) return s
|
|
68
|
+
if (SAFE_RETURNING_COLS.test(s)) return s
|
|
69
|
+
return 'RETURNING *'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Validasi field di WHERE (identifier atau schema.table)
|
|
73
|
+
const sanitizeWhereField = (field) => {
|
|
74
|
+
if (field == null || typeof field !== 'string' || !field.trim()) return ''
|
|
75
|
+
const identParts = String(field).trim().split('.')
|
|
76
|
+
const valid = identParts.length > 0 && identParts.every(p => SAFE_IDENTIFIER_PART.test(p))
|
|
77
|
+
if (!valid) return ''
|
|
78
|
+
return identParts.map(p => `"${p}"`).join('.')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Whitelist operator perbandingan untuk WHERE
|
|
82
|
+
const SAFE_WHERE_OPERATORS = new Set(['=', '!=', '<>', '<', '>', '<=', '>=', 'LIKE', 'ILIKE', 'IS NOT', 'IS'])
|
|
83
|
+
const sanitizeWhereOption = (option) => {
|
|
84
|
+
if (option == null || typeof option !== 'string') return ''
|
|
85
|
+
const s = String(option).trim().toUpperCase()
|
|
86
|
+
return SAFE_WHERE_OPERATORS.has(s) ? s : ''
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const sanitizeWhereConnector = (op) => {
|
|
90
|
+
if (op == null || typeof op !== 'string') return ' AND '
|
|
91
|
+
const s = String(op).trim().toUpperCase()
|
|
92
|
+
return (s === 'OR' ? ' OR ' : ' AND ')
|
|
93
|
+
}
|
|
94
|
+
|
|
24
95
|
const connection = {}
|
|
25
96
|
|
|
26
97
|
connection.query = async (string, arr) => {
|
|
@@ -53,9 +124,11 @@ const orderByFn = (obj) => {
|
|
|
53
124
|
}
|
|
54
125
|
}
|
|
55
126
|
}
|
|
56
|
-
if (obj.hasOwnProperty('order_by')) {
|
|
57
|
-
|
|
58
|
-
|
|
127
|
+
if (obj.hasOwnProperty('order_by') && Array.isArray(obj.order_by) && obj.order_by.length) {
|
|
128
|
+
const segments = obj.order_by.map(sanitizeOrderBySegment).filter(Boolean)
|
|
129
|
+
if (segments.length) {
|
|
130
|
+
orderBy = ` ORDER BY ${segments.join(', ')} `
|
|
131
|
+
}
|
|
59
132
|
}
|
|
60
133
|
|
|
61
134
|
return orderBy
|
|
@@ -67,7 +140,7 @@ const whereFn = (obj) => {
|
|
|
67
140
|
const whereArray = obj.whereArray || []
|
|
68
141
|
let increment = 1
|
|
69
142
|
let arr = [],
|
|
70
|
-
|
|
143
|
+
wherequery = []
|
|
71
144
|
for (const key in where) {
|
|
72
145
|
wherequery.push(key.indexOf('.') > -1 ? ` ${key} = $${increment} ` : ` "${key}" = $${increment}`)
|
|
73
146
|
arr.push(where[key])
|
|
@@ -93,14 +166,17 @@ const whereFn = (obj) => {
|
|
|
93
166
|
} else {
|
|
94
167
|
field = item.field.indexOf('.') > -1 ? item.field : item.field ? ` "${item.field}" ` : ''
|
|
95
168
|
}
|
|
96
|
-
//
|
|
97
|
-
//JSON_CONTAINS(color, '"Red"' ,'$')
|
|
169
|
+
// PostgreSQL: jsonb containment (@>) dengan parameter untuk keamanan
|
|
98
170
|
if (item.isJSON) {
|
|
99
|
-
wherequery += andOr + `
|
|
171
|
+
wherequery += andOr + ` (${String(field).trim()})::jsonb @> $${increment}::jsonb ${operator}`
|
|
172
|
+
arr.push(JSON.stringify(item.value))
|
|
173
|
+
increment++
|
|
100
174
|
hasWhere = true
|
|
101
175
|
} else {
|
|
102
176
|
if (type == 'json') {
|
|
103
|
-
wherequery += andOr + `
|
|
177
|
+
wherequery += andOr + ` (${String(field).trim()})::jsonb @> $${increment}::jsonb ${operator}`
|
|
178
|
+
arr.push(JSON.stringify(item.value))
|
|
179
|
+
increment++
|
|
104
180
|
hasWhere = true
|
|
105
181
|
} else if (type == 'inline') {
|
|
106
182
|
//select * from attendance where employee_id = 4803 and date IN ('2023-12-21','2023-12-22')
|
|
@@ -150,11 +226,13 @@ const whereFn = (obj) => {
|
|
|
150
226
|
}
|
|
151
227
|
|
|
152
228
|
connection.results = async (obj) => {
|
|
229
|
+
const table = sanitizeTableName(obj.table || '')
|
|
153
230
|
const select = obj.select || '*'
|
|
154
|
-
const table = obj.table || ''
|
|
155
231
|
const statement = obj.statement || ''
|
|
156
|
-
const
|
|
157
|
-
const
|
|
232
|
+
const limitVal = toSafeNonNegativeInt(obj.limit, null)
|
|
233
|
+
const offsetVal = obj.hasOwnProperty('offset') ? toSafeNonNegativeInt(obj.offset, 0) : (obj.limit ? 0 : null)
|
|
234
|
+
const limit = limitVal !== null ? ` LIMIT ${limitVal} ` : ''
|
|
235
|
+
const offset = offsetVal !== null ? ` OFFSET ${offsetVal} ` : ''
|
|
158
236
|
const orderBy = orderByFn(obj)
|
|
159
237
|
const values = obj.values || []
|
|
160
238
|
const objJoin = obj.joins || []
|
|
@@ -166,35 +244,16 @@ connection.results = async (obj) => {
|
|
|
166
244
|
const wheres = whereObj.where
|
|
167
245
|
const arr = whereObj.arr
|
|
168
246
|
|
|
169
|
-
|
|
170
|
-
if (select.trim().toLowerCase() === 'count(id) as count' && !wheres) {
|
|
171
|
-
// No filter, use fast count from pg_class
|
|
172
|
-
const sql = `SELECT reltuples::bigint AS count FROM pg_class WHERE relname = '${table}'`;
|
|
173
|
-
try {
|
|
174
|
-
const start = Date.now();
|
|
175
|
-
const result = await pool.query(sql);
|
|
176
|
-
const elapsed = Date.now() - start;
|
|
177
|
-
console.log(`[zet-lib] Fast count for ${table}: ${elapsed}ms`);
|
|
178
|
-
return result.rows;
|
|
179
|
-
} catch (e) {
|
|
180
|
-
console.log(sql)
|
|
181
|
-
console.log(e.toString())
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const sql = `SELECT ${select} FROM "${table}" ${join} ${wheres} ${statement} ${orderBy} ${limit} ${offset}`
|
|
247
|
+
const sql = `SELECT ${select} FROM "${table}" ${join} ${wheres} ${statement} ${orderBy} ${limit} ${offset}`.replace(/\s+/g, ' ').trim()
|
|
186
248
|
try {
|
|
187
|
-
const start = Date.now()
|
|
249
|
+
const start = Date.now()
|
|
188
250
|
const result = await pool.query(sql, arr.length ? arr : values.length ? values : null)
|
|
189
|
-
const elapsed = Date.now() - start;
|
|
190
|
-
/*if (select.trim().toLowerCase() === 'count(id) as count') {
|
|
191
|
-
console.log(`[zet-lib] Count query for ${table}: ${elapsed}ms`);
|
|
192
|
-
}*/
|
|
193
251
|
return !result.rows ? [] : result.rows
|
|
194
252
|
} catch (e) {
|
|
195
253
|
console.log(sql)
|
|
196
254
|
console.log(arr)
|
|
197
255
|
console.log(e.toString())
|
|
256
|
+
throw e
|
|
198
257
|
}
|
|
199
258
|
}
|
|
200
259
|
|
|
@@ -229,25 +288,26 @@ connection.result = async (obj) => {
|
|
|
229
288
|
}
|
|
230
289
|
|
|
231
290
|
connection.insert = async (obj) => {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const data = obj.data
|
|
291
|
+
const table = sanitizeTableName(obj.table || '')
|
|
292
|
+
if (!table) throw new Error('Table name is required for insert')
|
|
293
|
+
const data = { ...obj.data }
|
|
294
|
+
const returning = sanitizeReturning(data.returning || 'RETURNING *')
|
|
295
|
+
delete data.returning
|
|
235
296
|
let increment = 1
|
|
236
297
|
const datas = []
|
|
237
298
|
const values = []
|
|
238
299
|
const arr = []
|
|
239
|
-
const returning = data.returning || 'RETURNING *'
|
|
240
|
-
delete data.returning
|
|
241
300
|
for (const key in data) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
301
|
+
const col = sanitizeColumnName(key)
|
|
302
|
+
if (col) {
|
|
303
|
+
datas.push(col)
|
|
304
|
+
values.push(`$${increment}`)
|
|
305
|
+
arr.push(data[key])
|
|
306
|
+
increment++
|
|
307
|
+
}
|
|
246
308
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
console.log(arr)
|
|
250
|
-
*/
|
|
309
|
+
if (datas.length === 0) throw new Error('At least one valid column is required for insert')
|
|
310
|
+
const sql = `INSERT INTO "${table}" ("${datas.join('","')}") VALUES (${values.join(',')}) ${returning}`
|
|
251
311
|
|
|
252
312
|
try {
|
|
253
313
|
const results = await pool.query(sql, arr)
|
|
@@ -261,45 +321,59 @@ connection.insert = async (obj) => {
|
|
|
261
321
|
}
|
|
262
322
|
|
|
263
323
|
connection.update = async (obj) => {
|
|
264
|
-
const table = obj.table
|
|
265
|
-
|
|
266
|
-
const
|
|
324
|
+
const table = sanitizeTableName(obj.table || '')
|
|
325
|
+
if (!table) throw new Error('Table name is required for update')
|
|
326
|
+
const data = { ...obj.data }
|
|
327
|
+
const returning = sanitizeReturning(data.returning || 'RETURNING *')
|
|
267
328
|
delete data.returning
|
|
268
329
|
|
|
269
330
|
const where = obj.where || {}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
dataArr = []
|
|
331
|
+
const whereArray = obj.whereArray || []
|
|
332
|
+
const arr = []
|
|
333
|
+
const dataArr = []
|
|
274
334
|
let wherequery = []
|
|
275
335
|
let increment = 1
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
336
|
+
|
|
337
|
+
for (const key in data) {
|
|
338
|
+
const col = sanitizeColumnName(key)
|
|
339
|
+
if (col) {
|
|
340
|
+
dataArr.push(`"${col}" = $${increment}`)
|
|
341
|
+
arr.push(data[key])
|
|
342
|
+
increment++
|
|
343
|
+
}
|
|
280
344
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
345
|
+
if (dataArr.length === 0) throw new Error('At least one valid column is required for update')
|
|
346
|
+
|
|
347
|
+
for (const key in where) {
|
|
348
|
+
const col = sanitizeColumnName(key)
|
|
349
|
+
if (col) {
|
|
350
|
+
wherequery.push(`"${col}" = $${increment}`)
|
|
351
|
+
arr.push(where[key])
|
|
352
|
+
increment++
|
|
353
|
+
}
|
|
285
354
|
}
|
|
286
|
-
wherequery =
|
|
355
|
+
wherequery = wherequery.length ? wherequery.join(' AND ') : ''
|
|
356
|
+
|
|
287
357
|
if (whereArray.length) {
|
|
288
358
|
let andOr = wherequery ? ' AND ' : ''
|
|
289
359
|
whereArray.forEach((item, index) => {
|
|
290
360
|
if (index > 0) andOr = ''
|
|
291
|
-
const
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
361
|
+
const field = sanitizeWhereField(item.field)
|
|
362
|
+
const option = sanitizeWhereOption(item.option)
|
|
363
|
+
const connector = sanitizeWhereConnector(item.operator)
|
|
364
|
+
if (field && option) {
|
|
365
|
+
wherequery += `${andOr} ${field} ${option} $${increment} ${connector}`
|
|
366
|
+
arr.push(item.value)
|
|
367
|
+
increment++
|
|
368
|
+
}
|
|
296
369
|
})
|
|
297
|
-
wherequery = wherequery.slice(0, -5)
|
|
370
|
+
if (wherequery.endsWith(' AND ')) wherequery = wherequery.slice(0, -5)
|
|
371
|
+
else if (wherequery.endsWith(' OR ')) wherequery = wherequery.slice(0, -4)
|
|
298
372
|
}
|
|
299
|
-
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
373
|
+
|
|
374
|
+
const wheres = wherequery ? ' WHERE ' + wherequery : ''
|
|
375
|
+
const sql = `UPDATE "${table}" SET ${dataArr.join(', ')}${wheres} ${returning}`
|
|
376
|
+
|
|
303
377
|
try {
|
|
304
378
|
const result = await pool.query(sql, arr)
|
|
305
379
|
return result.rows[0]
|
|
@@ -312,21 +386,23 @@ connection.update = async (obj) => {
|
|
|
312
386
|
}
|
|
313
387
|
|
|
314
388
|
connection.delete = async (obj) => {
|
|
315
|
-
const table = obj.table
|
|
389
|
+
const table = sanitizeTableName(obj.table || '')
|
|
390
|
+
if (!table) throw new Error('Table name is required for delete')
|
|
316
391
|
const where = obj.where || {}
|
|
317
|
-
|
|
318
|
-
|
|
392
|
+
const arr = []
|
|
393
|
+
const wherequery = []
|
|
319
394
|
let increment = 1
|
|
320
395
|
for (const key in where) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
396
|
+
const col = sanitizeColumnName(key)
|
|
397
|
+
if (col) {
|
|
398
|
+
wherequery.push(`"${col}" = $${increment}`)
|
|
399
|
+
arr.push(where[key])
|
|
400
|
+
increment++
|
|
401
|
+
}
|
|
324
402
|
}
|
|
325
|
-
|
|
326
|
-
const wheres =
|
|
327
|
-
const sql = `DELETE FROM "${table}"
|
|
328
|
-
/*console.log(sql);
|
|
329
|
-
console.log(arr)*/
|
|
403
|
+
const whereClause = wherequery.length ? wherequery.join(' AND ') : ''
|
|
404
|
+
const wheres = whereClause ? ' WHERE ' + whereClause : ''
|
|
405
|
+
const sql = `DELETE FROM "${table}"${wheres} RETURNING *`
|
|
330
406
|
try {
|
|
331
407
|
return await pool.query(sql, arr)
|
|
332
408
|
} catch (e) {
|
|
@@ -346,7 +422,7 @@ connection.insertData = async(tableName, data) => {
|
|
|
346
422
|
}
|
|
347
423
|
|
|
348
424
|
pgPool = new Pool(configPG);
|
|
349
|
-
|
|
425
|
+
|
|
350
426
|
const columns = Object.keys(data);
|
|
351
427
|
if (columns.length === 0) {
|
|
352
428
|
throw new Error('No columns found in the data object');
|
|
@@ -381,7 +457,7 @@ connection.deleteData = async(tableName, where) => {
|
|
|
381
457
|
}
|
|
382
458
|
|
|
383
459
|
pgPool = new Pool(configPG);
|
|
384
|
-
|
|
460
|
+
|
|
385
461
|
const whereColumns = Object.keys(where);
|
|
386
462
|
if (whereColumns.length === 0) {
|
|
387
463
|
throw new Error('No where conditions provided for delete');
|
|
@@ -417,13 +493,13 @@ connection.insertMultipleRecords = async(tableName,records) => {
|
|
|
417
493
|
}
|
|
418
494
|
|
|
419
495
|
pgPool = new Pool(configPG);
|
|
420
|
-
|
|
496
|
+
|
|
421
497
|
// Validasi bahwa semua records memiliki struktur yang sama
|
|
422
498
|
const firstRecord = records[0];
|
|
423
499
|
if (!firstRecord || typeof firstRecord !== 'object') {
|
|
424
500
|
throw new Error('First record is invalid or empty');
|
|
425
501
|
}
|
|
426
|
-
|
|
502
|
+
|
|
427
503
|
const columns = Object.keys(firstRecord);
|
|
428
504
|
if (columns.length === 0) {
|
|
429
505
|
throw new Error('No columns found in the first record');
|
|
@@ -444,7 +520,7 @@ connection.insertMultipleRecords = async(tableName,records) => {
|
|
|
444
520
|
const placeholders = records.map((_, rowIndex) =>
|
|
445
521
|
`(${columns.map((_, colIndex) => `$${rowIndex * columns.length + colIndex + 1}`).join(',')})`
|
|
446
522
|
).join(',');
|
|
447
|
-
|
|
523
|
+
|
|
448
524
|
// Create the INSERT query
|
|
449
525
|
const insertQuery = `
|
|
450
526
|
INSERT INTO "${tableName}" (${columns.map(col => `"${col}"`).join(',')})
|
|
@@ -1,359 +1,359 @@
|
|
|
1
|
-
const router = require('express').Router();
|
|
2
|
-
const {csrf} = require('zet-lib');
|
|
3
|
-
const csrfProtection = csrf({cookie: true});
|
|
4
|
-
const {Util, access, connection, moduleLib, zDebug, zRoute, zRole, zDataTable, zForm} = require('zet-lib');
|
|
5
|
-
const MYMODEL = require('./../models/[[[TABLE_NAME]]]');
|
|
6
|
-
|
|
7
|
-
router.get('/', async (req, res) => {
|
|
8
|
-
await zRoute.attributeData(res, MYMODEL);
|
|
9
|
-
let dataTable = new zDataTable(res.locals);
|
|
10
|
-
dataTable.filterMODEL = await zRoute.dataTableFilterSync(req,res, MYMODEL, res.locals.gridFilter);
|
|
11
|
-
const levels = zRole.myLevel(req, res, MYMODEL.table);
|
|
12
|
-
dataTable.levels = levels;
|
|
13
|
-
res.render(`layouts/${layout}`, {
|
|
14
|
-
dataTable: dataTable,
|
|
15
|
-
levels : levels,
|
|
16
|
-
titleHeader: `<strong class="text text-info">${MYMODEL.title}</strong>`,
|
|
17
|
-
breadcrumb : zForm.breadcrumbIndex(),
|
|
18
|
-
myModal : zForm.modal({
|
|
19
|
-
attributeData: res.locals.attributeData,
|
|
20
|
-
visibles: res.locals.visibles,
|
|
21
|
-
invisibles: res.locals.invisibles,
|
|
22
|
-
routeName: MYMODEL.table,
|
|
23
|
-
}, LANGUAGE),
|
|
24
|
-
renderHead: `${MYMODEL.routeName}/indexcss.ejs`,
|
|
25
|
-
renderBody: `${MYMODEL.routeName}/index.ejs`,
|
|
26
|
-
renderEnd: `${MYMODEL.routeName}/indexjs.ejs`
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
router.post('/list', async (req, res) => {
|
|
31
|
-
const data = await zRoute.listData(req, res, MYMODEL, zRole);
|
|
32
|
-
res.json(data);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
router.get('/create', csrfProtection, async(req, res) => {
|
|
36
|
-
let data = MYMODEL.datas;
|
|
37
|
-
const zForms = await zRoute.formsFieldSync(req, res, MYMODEL, data);
|
|
38
|
-
res.render(`layouts/${layout}`, {
|
|
39
|
-
data: data,
|
|
40
|
-
zForms: zForms,
|
|
41
|
-
titleHeader: `${LANGUAGE['form_create']} : <strong><a href="/${MYMODEL.routeName}">${MYMODEL.title}</a></strong>`,
|
|
42
|
-
csrfToken: req.csrfToken(),
|
|
43
|
-
breadcrumb : zForm.breadcrumbCreate(MYMODEL.routeName),
|
|
44
|
-
renderBody: `${MYMODEL.routeName}/create.ejs`,
|
|
45
|
-
renderEnd: `${MYMODEL.routeName}/createjs.ejs`
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
router.post('/create', csrfProtection, async (req, res) => {
|
|
50
|
-
let json = Util.jsonSuccess(LANGUAGE['data_saved']);
|
|
51
|
-
try {
|
|
52
|
-
let data = zRoute.post(req,res, MYMODEL)[MYMODEL.routeName];
|
|
53
|
-
const validator = zRoute.validator(data, MYMODEL);
|
|
54
|
-
if(validator.status == 0) return res.json(validator.message);
|
|
55
|
-
const result = await zRoute.insertSQL(req, res, MYMODEL.table, data);
|
|
56
|
-
json.data=result;
|
|
57
|
-
} catch (err) {
|
|
58
|
-
if(Object.hasOwn(err,"sqlMessage")){
|
|
59
|
-
json = Util.flashError(err.sqlMessage);
|
|
60
|
-
} else {
|
|
61
|
-
json = Util.flashError(err.toString());
|
|
62
|
-
}
|
|
63
|
-
zDebug(req, res, err);
|
|
64
|
-
}
|
|
65
|
-
res.json(json);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
router.get('/update/:id', csrfProtection, async (req, res) => {
|
|
69
|
-
const id = parseInt(req.params.id);
|
|
70
|
-
const results = await connection.results({
|
|
71
|
-
select:zRoute.selectData(MYMODEL),
|
|
72
|
-
table: MYMODEL.table,
|
|
73
|
-
where: {
|
|
74
|
-
id: id,
|
|
75
|
-
company_id: res.locals.companyId
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
if (results.length == 0) {
|
|
79
|
-
req.session.sessionFlash = Util.flashError(LANGUAGE['data_not_found']);
|
|
80
|
-
return res.redirect('/' + MYMODEL.routeName);
|
|
81
|
-
}
|
|
82
|
-
let data = results[0];
|
|
83
|
-
const zForms = await zRoute.formsFieldSync(req, res, MYMODEL, data);
|
|
84
|
-
res.render(`layouts/${layout}`, {
|
|
85
|
-
data: data,
|
|
86
|
-
zForms: zForms,
|
|
87
|
-
titleHeader: `${LANGUAGE['form_update']} : <strong><a href="/${MYMODEL.routeName}">${MYMODEL.title}</a></strong>`,
|
|
88
|
-
csrfToken: req.csrfToken(),
|
|
89
|
-
breadcrumb : zForm.breadcrumbUpdate(MYMODEL.routeName, id),
|
|
90
|
-
renderBody: `${MYMODEL.routeName}/update.ejs`,
|
|
91
|
-
renderEnd: `${MYMODEL.routeName}/updatejs.ejs`,
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
router.post('/update/:id', csrfProtection, async (req, res) => {
|
|
96
|
-
let json = Util.jsonSuccess(LANGUAGE['data_saved']);
|
|
97
|
-
try {
|
|
98
|
-
const id = parseInt(req.params.id);
|
|
99
|
-
let data = zRoute.post(req,res, MYMODEL)[MYMODEL.routeName];
|
|
100
|
-
const validator = zRoute.validator(data, MYMODEL);
|
|
101
|
-
if(validator.status == 0) return res.json(validator.message);
|
|
102
|
-
const result = await zRoute.updateSQL(req, res,MYMODEL.table, data, {id:id});
|
|
103
|
-
json.data=result;
|
|
104
|
-
} catch (err) {
|
|
105
|
-
console.log(err)
|
|
106
|
-
if(Object.hasOwn(err,"sqlMessage")){
|
|
107
|
-
json = Util.flashError(err.sqlMessage);
|
|
108
|
-
} else {
|
|
109
|
-
json = Util.flashError(err.toString());
|
|
110
|
-
}
|
|
111
|
-
zDebug(req, res, err)
|
|
112
|
-
}
|
|
113
|
-
res.json(json);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
router.delete('/delete', async (req, res) => {
|
|
117
|
-
const id = req.body.id;
|
|
118
|
-
let json = Util.jsonSuccess(LANGUAGE['data_delete']);
|
|
119
|
-
try {
|
|
120
|
-
await zRoute.deleteSQL(MYMODEL.table, id, res.locals.companyId);
|
|
121
|
-
} catch (err) {
|
|
122
|
-
json = Util.flashError(err.toString());
|
|
123
|
-
zDebug(req, res, err)
|
|
124
|
-
}
|
|
125
|
-
res.json(json);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
router.get('/view/:id', async (req, res) => {
|
|
129
|
-
await zRoute.attributeData(res, MYMODEL);
|
|
130
|
-
const id = req.params.id;
|
|
131
|
-
let data = {};
|
|
132
|
-
let zForms = {};
|
|
133
|
-
try {
|
|
134
|
-
const results = await connection.results({select:zRoute.selectData(MYMODEL),table:MYMODEL.table,where:{id:id}});
|
|
135
|
-
if (results.length == 0) {
|
|
136
|
-
req.session.sessionFlash = Util.flashError(LANGUAGE['data_not_found']);
|
|
137
|
-
return res.redirect('/' + MYMODEL.routeName);
|
|
138
|
-
}
|
|
139
|
-
data = results[0];
|
|
140
|
-
zForms = await zRoute.viewFormsSync(req, res, MYMODEL, data);
|
|
141
|
-
} catch (err){
|
|
142
|
-
zDebug(req, res, err);
|
|
143
|
-
res.send("Err");
|
|
144
|
-
}
|
|
145
|
-
res.render(`layouts/${layout}`, {
|
|
146
|
-
data: data,
|
|
147
|
-
Util:Util,
|
|
148
|
-
zForms:zForms,
|
|
149
|
-
titleHeader: `View : <strong><a href="/${MYMODEL.routeName}">${MYMODEL.title}</a></strong>`,
|
|
150
|
-
breadcrumb : zForm.breadcrumbView(MYMODEL.routeName),
|
|
151
|
-
levels: zRole.myLevel(req, res, MYMODEL.table),
|
|
152
|
-
renderBody: `${MYMODEL.routeName}/view.ejs`
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
router.get('/excel-query', async (req,res) => {
|
|
157
|
-
await zRoute.excelQuery(req, res, MYMODEL);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
router.get('/excel', async (req, res) => {
|
|
161
|
-
const fields = MYMODEL.keysExcel;
|
|
162
|
-
const results = await connection.results({
|
|
163
|
-
table: MYMODEL.table,
|
|
164
|
-
where: {company_id: res.locals.companyId},
|
|
165
|
-
orderBy: ['id', 'DESC']
|
|
166
|
-
});
|
|
167
|
-
await zRoute.excel(req, res, MYMODEL, fields, results);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
router.get('/sample', async (req, res) => {
|
|
171
|
-
let fields = [];
|
|
172
|
-
for (let i = 0; i < MYMODEL.keysExcel.length; i++) {
|
|
173
|
-
if (!Util.in_array(MYMODEL.keysExcel[i], Util.nots)) {
|
|
174
|
-
fields.push(MYMODEL.keysExcel[i]);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
const results = await connection.results({
|
|
178
|
-
table:MYMODEL.table,
|
|
179
|
-
where:{
|
|
180
|
-
company_id:res.locals.companyId
|
|
181
|
-
},
|
|
182
|
-
orderBy:["id","desc"], limit:10
|
|
183
|
-
});
|
|
184
|
-
let callback=(result,field)=> {
|
|
185
|
-
return MYMODEL.widgets[field] && MYMODEL.widgets[field].name == 'datepicker' ? Util.dateSql(result[field]) : result[field];
|
|
186
|
-
};
|
|
187
|
-
await zRoute.excel(req, res, MYMODEL, fields, results, callback);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
router.get('/import',csrfProtection, async (req, res) => {
|
|
191
|
-
const room = res.locals.token;
|
|
192
|
-
res.render(`layouts/${layout}`, {
|
|
193
|
-
room: room,
|
|
194
|
-
titleHeader: `Import / Upload data : <strong><a href="/${MYMODEL.routeName}">${MYMODEL.title}</a></strong>`,
|
|
195
|
-
csrfToken: req.csrfToken(),
|
|
196
|
-
breadcrumb : zForm.breadcrumbImport(MYMODEL.routeName),
|
|
197
|
-
renderBody: `${MYMODEL.routeName}/import.ejs`,
|
|
198
|
-
renderEnd: `${MYMODEL.routeName}/importjs.ejs`
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
router.post('/import', async(req, res) => {
|
|
204
|
-
res.json(await zRoute.import(req,res,MYMODEL));
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
router.get('/approval/:id', async (req, res) => {
|
|
208
|
-
await zRoute.attributeData(res, MYMODEL);
|
|
209
|
-
const id = req.params.id;
|
|
210
|
-
let data = {};
|
|
211
|
-
let data2 = {};
|
|
212
|
-
let zForms = {};
|
|
213
|
-
let zForms2 = {};
|
|
214
|
-
let result = {};
|
|
215
|
-
try {
|
|
216
|
-
result = await connection.result({table: MYMODEL.table, where: {id: id}});
|
|
217
|
-
if (!result.id) {
|
|
218
|
-
req.session.sessionFlash = Util.flashError(LANGUAGE['data_not_found']);
|
|
219
|
-
return res.redirect('/' + MYMODEL.routeName);
|
|
220
|
-
}
|
|
221
|
-
data=result;
|
|
222
|
-
moduleLib.editor(req, res);
|
|
223
|
-
zForms = await zRoute.viewFormsSync(req, res, MYMODEL, result);
|
|
224
|
-
const contentScript = `$("#submit_draft").on("click", function () {$("#status").val(1);ajaxPost("/zzapproval",$("#form-group").serializeArray(), function (data) {
|
|
225
|
-
if (data.status == 0) {
|
|
226
|
-
toastr.error(data.message, data.title);
|
|
227
|
-
} else {
|
|
228
|
-
toastr.success(data.title, data.message);
|
|
229
|
-
}
|
|
230
|
-
});});
|
|
231
|
-
$("#submit").on("click", function () {
|
|
232
|
-
$('[name="zapprovals[status]"]').val(2);
|
|
233
|
-
ajaxPost("/zzapproval",$("#form-group").serializeArray(), function (data) {
|
|
234
|
-
if (data.status == 0) {
|
|
235
|
-
toastr.error(data.message, data.title);
|
|
236
|
-
} else {
|
|
237
|
-
toastr.success(data.title, data.message);
|
|
238
|
-
location.href = "";
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
`;
|
|
243
|
-
await moduleLib.addScript(req, res, contentScript);
|
|
244
|
-
const MODEL_APROVALS = require("./../models/zapprovals");
|
|
245
|
-
const rows = await connection.results({
|
|
246
|
-
table: "zapprovals",
|
|
247
|
-
where: {table: MYMODEL.table, company_id: res.locals.companyId, id_data: id}
|
|
248
|
-
});
|
|
249
|
-
if (rows.length) {
|
|
250
|
-
data2 = rows[0];
|
|
251
|
-
} else {
|
|
252
|
-
let obj = {};
|
|
253
|
-
obj.token = Util.uuid();
|
|
254
|
-
obj.status = 1;
|
|
255
|
-
obj.table = MYMODEL.table;
|
|
256
|
-
obj.company_id = res.locals.companyId;
|
|
257
|
-
obj.id_data = id;
|
|
258
|
-
obj.created_by = res.locals.userId;
|
|
259
|
-
obj.updated_by = res.locals.userId;
|
|
260
|
-
obj.created_at = Util.now();
|
|
261
|
-
let zfields = await connection.result({table: "zfields", where: {table: MYMODEL.table}});
|
|
262
|
-
let jsonApproval = zfields.approval_json || {}
|
|
263
|
-
obj.template = jsonApproval.content || {}
|
|
264
|
-
let title = jsonApproval.title;
|
|
265
|
-
if (title) {
|
|
266
|
-
let titleData = title;
|
|
267
|
-
for (var key in data) {
|
|
268
|
-
titleData = Util.replaceAll(titleData, "{{" + key + "}}", data[key])
|
|
269
|
-
}
|
|
270
|
-
obj.title = titleData
|
|
271
|
-
}
|
|
272
|
-
if (jsonApproval.hasOwnProperty("approvers")) {
|
|
273
|
-
obj.approvers = JSON.stringify(jsonApproval.approvers);
|
|
274
|
-
obj.knowings = JSON.stringify(jsonApproval.knowings);
|
|
275
|
-
obj.type = 1;
|
|
276
|
-
}
|
|
277
|
-
//insert default data
|
|
278
|
-
await connection.insert({table: "zapprovals", data: obj});
|
|
279
|
-
data2 = obj;
|
|
280
|
-
}
|
|
281
|
-
zForms2 = await zRoute.formsFieldSync(req, res, MODEL_APROVALS, data2);
|
|
282
|
-
} catch (err) {
|
|
283
|
-
zDebug(req, res, err);
|
|
284
|
-
res.send("Err");
|
|
285
|
-
}
|
|
286
|
-
res.render(`layouts/${layout}`, {
|
|
287
|
-
zForms: zForms,
|
|
288
|
-
zForms2: zForms2,
|
|
289
|
-
data: result,
|
|
290
|
-
result:result,
|
|
291
|
-
data2: data2,
|
|
292
|
-
Util: Util,
|
|
293
|
-
levels: zRole.myLevel(req, res, MYMODEL.table),
|
|
294
|
-
breadcrumb : zForm.breadcrumbApproval(MYMODEL.routeName, id),
|
|
295
|
-
renderBody: `${MYMODEL.routeName}/approval.ejs`
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
router.get('/preview/:token', async (req, res) => {
|
|
300
|
-
let token = req.params.token;
|
|
301
|
-
let data = {}, data2 = {};
|
|
302
|
-
let template = "";
|
|
303
|
-
let footers = "";
|
|
304
|
-
try {
|
|
305
|
-
let zfields = await connection.result({table: "zfields", where: {table: MYMODEL.table}});
|
|
306
|
-
let jsonApproval = zfields.approval_json || {}
|
|
307
|
-
let mytemplate = jsonApproval.content || {}
|
|
308
|
-
let results = await connection.results({table: "zapprovals", where: {token: token}});
|
|
309
|
-
if (results.length == 0) {
|
|
310
|
-
req.session.sessionFlash = Util.flashError(LANGUAGE['data_not_found']);
|
|
311
|
-
return res.redirect('/' + MYMODEL.routeName);
|
|
312
|
-
}
|
|
313
|
-
if(results[0].status == 1){
|
|
314
|
-
await connection.update({
|
|
315
|
-
table : "zapprovals",
|
|
316
|
-
data: {
|
|
317
|
-
template : mytemplate
|
|
318
|
-
},
|
|
319
|
-
where : {
|
|
320
|
-
token:token
|
|
321
|
-
}
|
|
322
|
-
})
|
|
323
|
-
results = await connection.results({table: "zapprovals", where: {token: token}});
|
|
324
|
-
}
|
|
325
|
-
let MYMODEL2 = require("./../models/zapprovals")
|
|
326
|
-
data = await zRoute.viewTable(req, res, MYMODEL2, results[0], true);
|
|
327
|
-
let row = await connection.result({table: results[0].table, where: {id: results[0].id_data}});
|
|
328
|
-
data2 = await zRoute.viewTable(req, res, MYMODEL, row, true);
|
|
329
|
-
template = data.template;
|
|
330
|
-
let title = data.title || ""
|
|
331
|
-
for (var key in data2) {
|
|
332
|
-
template = Util.replaceAll(template, "{{" + key + "}}", data2[key])
|
|
333
|
-
title = Util.replaceAll(title, "{{" + key + "}}", data2[key])
|
|
334
|
-
}
|
|
335
|
-
data.title = title;
|
|
336
|
-
var details = await connection.results({table: "zapprovals_details", where: {title_id: results[0].id}});
|
|
337
|
-
if (details.length) {
|
|
338
|
-
footers = await zRoute.approversFooter(details);
|
|
339
|
-
}
|
|
340
|
-
} catch (err) {
|
|
341
|
-
zDebug(req, res, err);
|
|
342
|
-
res.send("Err");
|
|
343
|
-
}
|
|
344
|
-
res.render(`layouts/blank`, {
|
|
345
|
-
template: template,
|
|
346
|
-
data: data,
|
|
347
|
-
footers: footers,
|
|
348
|
-
Util: Util,
|
|
349
|
-
details: details,
|
|
350
|
-
renderBody: 'index/zapprovals_preview.ejs'
|
|
351
|
-
});
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
router.post('/grid', access, zRoute.postGrid);
|
|
355
|
-
router.post('/reload', access, zRoute.postGridReload);
|
|
356
|
-
router.post('/chains', access, zRoute.chains);
|
|
357
|
-
router.post('/get_attributes',access, zRoute.getAttributes);
|
|
358
|
-
|
|
359
|
-
module.exports = router;
|
|
1
|
+
const router = require('express').Router();
|
|
2
|
+
const {csrf} = require('zet-lib');
|
|
3
|
+
const csrfProtection = csrf({cookie: true});
|
|
4
|
+
const {Util, access, connection, moduleLib, zDebug, zRoute, zRole, zDataTable, zForm} = require('zet-lib');
|
|
5
|
+
const MYMODEL = require('./../models/[[[TABLE_NAME]]]');
|
|
6
|
+
|
|
7
|
+
router.get('/', async (req, res) => {
|
|
8
|
+
await zRoute.attributeData(res, MYMODEL);
|
|
9
|
+
let dataTable = new zDataTable(res.locals);
|
|
10
|
+
dataTable.filterMODEL = await zRoute.dataTableFilterSync(req,res, MYMODEL, res.locals.gridFilter);
|
|
11
|
+
const levels = zRole.myLevel(req, res, MYMODEL.table);
|
|
12
|
+
dataTable.levels = levels;
|
|
13
|
+
res.render(`layouts/${layout}`, {
|
|
14
|
+
dataTable: dataTable,
|
|
15
|
+
levels : levels,
|
|
16
|
+
titleHeader: `<strong class="text text-info">${MYMODEL.title}</strong>`,
|
|
17
|
+
breadcrumb : zForm.breadcrumbIndex(),
|
|
18
|
+
myModal : zForm.modal({
|
|
19
|
+
attributeData: res.locals.attributeData,
|
|
20
|
+
visibles: res.locals.visibles,
|
|
21
|
+
invisibles: res.locals.invisibles,
|
|
22
|
+
routeName: MYMODEL.table,
|
|
23
|
+
}, LANGUAGE),
|
|
24
|
+
renderHead: `${MYMODEL.routeName}/indexcss.ejs`,
|
|
25
|
+
renderBody: `${MYMODEL.routeName}/index.ejs`,
|
|
26
|
+
renderEnd: `${MYMODEL.routeName}/indexjs.ejs`
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
router.post('/list', async (req, res) => {
|
|
31
|
+
const data = await zRoute.listData(req, res, MYMODEL, zRole);
|
|
32
|
+
res.json(data);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
router.get('/create', csrfProtection, async(req, res) => {
|
|
36
|
+
let data = MYMODEL.datas;
|
|
37
|
+
const zForms = await zRoute.formsFieldSync(req, res, MYMODEL, data);
|
|
38
|
+
res.render(`layouts/${layout}`, {
|
|
39
|
+
data: data,
|
|
40
|
+
zForms: zForms,
|
|
41
|
+
titleHeader: `${LANGUAGE['form_create']} : <strong><a href="/${MYMODEL.routeName}">${MYMODEL.title}</a></strong>`,
|
|
42
|
+
csrfToken: req.csrfToken(),
|
|
43
|
+
breadcrumb : zForm.breadcrumbCreate(MYMODEL.routeName),
|
|
44
|
+
renderBody: `${MYMODEL.routeName}/create.ejs`,
|
|
45
|
+
renderEnd: `${MYMODEL.routeName}/createjs.ejs`
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
router.post('/create', csrfProtection, async (req, res) => {
|
|
50
|
+
let json = Util.jsonSuccess(LANGUAGE['data_saved']);
|
|
51
|
+
try {
|
|
52
|
+
let data = zRoute.post(req,res, MYMODEL)[MYMODEL.routeName];
|
|
53
|
+
const validator = zRoute.validator(data, MYMODEL);
|
|
54
|
+
if(validator.status == 0) return res.json(validator.message);
|
|
55
|
+
const result = await zRoute.insertSQL(req, res, MYMODEL.table, data);
|
|
56
|
+
json.data=result;
|
|
57
|
+
} catch (err) {
|
|
58
|
+
if(Object.hasOwn(err,"sqlMessage")){
|
|
59
|
+
json = Util.flashError(err.sqlMessage);
|
|
60
|
+
} else {
|
|
61
|
+
json = Util.flashError(err.toString());
|
|
62
|
+
}
|
|
63
|
+
zDebug(req, res, err);
|
|
64
|
+
}
|
|
65
|
+
res.json(json);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
router.get('/update/:id', csrfProtection, async (req, res) => {
|
|
69
|
+
const id = parseInt(req.params.id);
|
|
70
|
+
const results = await connection.results({
|
|
71
|
+
select:zRoute.selectData(MYMODEL),
|
|
72
|
+
table: MYMODEL.table,
|
|
73
|
+
where: {
|
|
74
|
+
id: id,
|
|
75
|
+
company_id: res.locals.companyId
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
if (results.length == 0) {
|
|
79
|
+
req.session.sessionFlash = Util.flashError(LANGUAGE['data_not_found']);
|
|
80
|
+
return res.redirect('/' + MYMODEL.routeName);
|
|
81
|
+
}
|
|
82
|
+
let data = results[0];
|
|
83
|
+
const zForms = await zRoute.formsFieldSync(req, res, MYMODEL, data);
|
|
84
|
+
res.render(`layouts/${layout}`, {
|
|
85
|
+
data: data,
|
|
86
|
+
zForms: zForms,
|
|
87
|
+
titleHeader: `${LANGUAGE['form_update']} : <strong><a href="/${MYMODEL.routeName}">${MYMODEL.title}</a></strong>`,
|
|
88
|
+
csrfToken: req.csrfToken(),
|
|
89
|
+
breadcrumb : zForm.breadcrumbUpdate(MYMODEL.routeName, id),
|
|
90
|
+
renderBody: `${MYMODEL.routeName}/update.ejs`,
|
|
91
|
+
renderEnd: `${MYMODEL.routeName}/updatejs.ejs`,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
router.post('/update/:id', csrfProtection, async (req, res) => {
|
|
96
|
+
let json = Util.jsonSuccess(LANGUAGE['data_saved']);
|
|
97
|
+
try {
|
|
98
|
+
const id = parseInt(req.params.id);
|
|
99
|
+
let data = zRoute.post(req,res, MYMODEL)[MYMODEL.routeName];
|
|
100
|
+
const validator = zRoute.validator(data, MYMODEL);
|
|
101
|
+
if(validator.status == 0) return res.json(validator.message);
|
|
102
|
+
const result = await zRoute.updateSQL(req, res,MYMODEL.table, data, {id:id});
|
|
103
|
+
json.data=result;
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.log(err)
|
|
106
|
+
if(Object.hasOwn(err,"sqlMessage")){
|
|
107
|
+
json = Util.flashError(err.sqlMessage);
|
|
108
|
+
} else {
|
|
109
|
+
json = Util.flashError(err.toString());
|
|
110
|
+
}
|
|
111
|
+
zDebug(req, res, err)
|
|
112
|
+
}
|
|
113
|
+
res.json(json);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
router.delete('/delete', async (req, res) => {
|
|
117
|
+
const id = req.body.id;
|
|
118
|
+
let json = Util.jsonSuccess(LANGUAGE['data_delete']);
|
|
119
|
+
try {
|
|
120
|
+
await zRoute.deleteSQL(MYMODEL.table, id, res.locals.companyId);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
json = Util.flashError(err.toString());
|
|
123
|
+
zDebug(req, res, err)
|
|
124
|
+
}
|
|
125
|
+
res.json(json);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
router.get('/view/:id', async (req, res) => {
|
|
129
|
+
await zRoute.attributeData(res, MYMODEL);
|
|
130
|
+
const id = req.params.id;
|
|
131
|
+
let data = {};
|
|
132
|
+
let zForms = {};
|
|
133
|
+
try {
|
|
134
|
+
const results = await connection.results({select:zRoute.selectData(MYMODEL),table:MYMODEL.table,where:{id:id}});
|
|
135
|
+
if (results.length == 0) {
|
|
136
|
+
req.session.sessionFlash = Util.flashError(LANGUAGE['data_not_found']);
|
|
137
|
+
return res.redirect('/' + MYMODEL.routeName);
|
|
138
|
+
}
|
|
139
|
+
data = results[0];
|
|
140
|
+
zForms = await zRoute.viewFormsSync(req, res, MYMODEL, data);
|
|
141
|
+
} catch (err){
|
|
142
|
+
zDebug(req, res, err);
|
|
143
|
+
res.send("Err");
|
|
144
|
+
}
|
|
145
|
+
res.render(`layouts/${layout}`, {
|
|
146
|
+
data: data,
|
|
147
|
+
Util:Util,
|
|
148
|
+
zForms:zForms,
|
|
149
|
+
titleHeader: `View : <strong><a href="/${MYMODEL.routeName}">${MYMODEL.title}</a></strong>`,
|
|
150
|
+
breadcrumb : zForm.breadcrumbView(MYMODEL.routeName),
|
|
151
|
+
levels: zRole.myLevel(req, res, MYMODEL.table),
|
|
152
|
+
renderBody: `${MYMODEL.routeName}/view.ejs`
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
router.get('/excel-query', async (req,res) => {
|
|
157
|
+
await zRoute.excelQuery(req, res, MYMODEL);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
router.get('/excel', async (req, res) => {
|
|
161
|
+
const fields = MYMODEL.keysExcel;
|
|
162
|
+
const results = await connection.results({
|
|
163
|
+
table: MYMODEL.table,
|
|
164
|
+
where: {company_id: res.locals.companyId},
|
|
165
|
+
orderBy: ['id', 'DESC']
|
|
166
|
+
});
|
|
167
|
+
await zRoute.excel(req, res, MYMODEL, fields, results);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
router.get('/sample', async (req, res) => {
|
|
171
|
+
let fields = [];
|
|
172
|
+
for (let i = 0; i < MYMODEL.keysExcel.length; i++) {
|
|
173
|
+
if (!Util.in_array(MYMODEL.keysExcel[i], Util.nots)) {
|
|
174
|
+
fields.push(MYMODEL.keysExcel[i]);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const results = await connection.results({
|
|
178
|
+
table:MYMODEL.table,
|
|
179
|
+
where:{
|
|
180
|
+
company_id:res.locals.companyId
|
|
181
|
+
},
|
|
182
|
+
orderBy:["id","desc"], limit:10
|
|
183
|
+
});
|
|
184
|
+
let callback=(result,field)=> {
|
|
185
|
+
return MYMODEL.widgets[field] && MYMODEL.widgets[field].name == 'datepicker' ? Util.dateSql(result[field]) : result[field];
|
|
186
|
+
};
|
|
187
|
+
await zRoute.excel(req, res, MYMODEL, fields, results, callback);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
router.get('/import',csrfProtection, async (req, res) => {
|
|
191
|
+
const room = res.locals.token;
|
|
192
|
+
res.render(`layouts/${layout}`, {
|
|
193
|
+
room: room,
|
|
194
|
+
titleHeader: `Import / Upload data : <strong><a href="/${MYMODEL.routeName}">${MYMODEL.title}</a></strong>`,
|
|
195
|
+
csrfToken: req.csrfToken(),
|
|
196
|
+
breadcrumb : zForm.breadcrumbImport(MYMODEL.routeName),
|
|
197
|
+
renderBody: `${MYMODEL.routeName}/import.ejs`,
|
|
198
|
+
renderEnd: `${MYMODEL.routeName}/importjs.ejs`
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
router.post('/import', async(req, res) => {
|
|
204
|
+
res.json(await zRoute.import(req,res,MYMODEL));
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
router.get('/approval/:id', async (req, res) => {
|
|
208
|
+
await zRoute.attributeData(res, MYMODEL);
|
|
209
|
+
const id = req.params.id;
|
|
210
|
+
let data = {};
|
|
211
|
+
let data2 = {};
|
|
212
|
+
let zForms = {};
|
|
213
|
+
let zForms2 = {};
|
|
214
|
+
let result = {};
|
|
215
|
+
try {
|
|
216
|
+
result = await connection.result({table: MYMODEL.table, where: {id: id}});
|
|
217
|
+
if (!result.id) {
|
|
218
|
+
req.session.sessionFlash = Util.flashError(LANGUAGE['data_not_found']);
|
|
219
|
+
return res.redirect('/' + MYMODEL.routeName);
|
|
220
|
+
}
|
|
221
|
+
data=result;
|
|
222
|
+
moduleLib.editor(req, res);
|
|
223
|
+
zForms = await zRoute.viewFormsSync(req, res, MYMODEL, result);
|
|
224
|
+
const contentScript = `$("#submit_draft").on("click", function () {$("#status").val(1);ajaxPost("/zzapproval",$("#form-group").serializeArray(), function (data) {
|
|
225
|
+
if (data.status == 0) {
|
|
226
|
+
toastr.error(data.message, data.title);
|
|
227
|
+
} else {
|
|
228
|
+
toastr.success(data.title, data.message);
|
|
229
|
+
}
|
|
230
|
+
});});
|
|
231
|
+
$("#submit").on("click", function () {
|
|
232
|
+
$('[name="zapprovals[status]"]').val(2);
|
|
233
|
+
ajaxPost("/zzapproval",$("#form-group").serializeArray(), function (data) {
|
|
234
|
+
if (data.status == 0) {
|
|
235
|
+
toastr.error(data.message, data.title);
|
|
236
|
+
} else {
|
|
237
|
+
toastr.success(data.title, data.message);
|
|
238
|
+
location.href = "";
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
`;
|
|
243
|
+
await moduleLib.addScript(req, res, contentScript);
|
|
244
|
+
const MODEL_APROVALS = require("./../models/zapprovals");
|
|
245
|
+
const rows = await connection.results({
|
|
246
|
+
table: "zapprovals",
|
|
247
|
+
where: {table: MYMODEL.table, company_id: res.locals.companyId, id_data: id}
|
|
248
|
+
});
|
|
249
|
+
if (rows.length) {
|
|
250
|
+
data2 = rows[0];
|
|
251
|
+
} else {
|
|
252
|
+
let obj = {};
|
|
253
|
+
obj.token = Util.uuid();
|
|
254
|
+
obj.status = 1;
|
|
255
|
+
obj.table = MYMODEL.table;
|
|
256
|
+
obj.company_id = res.locals.companyId;
|
|
257
|
+
obj.id_data = id;
|
|
258
|
+
obj.created_by = res.locals.userId;
|
|
259
|
+
obj.updated_by = res.locals.userId;
|
|
260
|
+
obj.created_at = Util.now();
|
|
261
|
+
let zfields = await connection.result({table: "zfields", where: {table: MYMODEL.table}});
|
|
262
|
+
let jsonApproval = zfields.approval_json || {}
|
|
263
|
+
obj.template = jsonApproval.content || {}
|
|
264
|
+
let title = jsonApproval.title;
|
|
265
|
+
if (title) {
|
|
266
|
+
let titleData = title;
|
|
267
|
+
for (var key in data) {
|
|
268
|
+
titleData = Util.replaceAll(titleData, "{{" + key + "}}", data[key])
|
|
269
|
+
}
|
|
270
|
+
obj.title = titleData
|
|
271
|
+
}
|
|
272
|
+
if (jsonApproval.hasOwnProperty("approvers")) {
|
|
273
|
+
obj.approvers = JSON.stringify(jsonApproval.approvers);
|
|
274
|
+
obj.knowings = JSON.stringify(jsonApproval.knowings);
|
|
275
|
+
obj.type = 1;
|
|
276
|
+
}
|
|
277
|
+
//insert default data
|
|
278
|
+
await connection.insert({table: "zapprovals", data: obj});
|
|
279
|
+
data2 = obj;
|
|
280
|
+
}
|
|
281
|
+
zForms2 = await zRoute.formsFieldSync(req, res, MODEL_APROVALS, data2);
|
|
282
|
+
} catch (err) {
|
|
283
|
+
zDebug(req, res, err);
|
|
284
|
+
res.send("Err");
|
|
285
|
+
}
|
|
286
|
+
res.render(`layouts/${layout}`, {
|
|
287
|
+
zForms: zForms,
|
|
288
|
+
zForms2: zForms2,
|
|
289
|
+
data: result,
|
|
290
|
+
result:result,
|
|
291
|
+
data2: data2,
|
|
292
|
+
Util: Util,
|
|
293
|
+
levels: zRole.myLevel(req, res, MYMODEL.table),
|
|
294
|
+
breadcrumb : zForm.breadcrumbApproval(MYMODEL.routeName, id),
|
|
295
|
+
renderBody: `${MYMODEL.routeName}/approval.ejs`
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
router.get('/preview/:token', async (req, res) => {
|
|
300
|
+
let token = req.params.token;
|
|
301
|
+
let data = {}, data2 = {};
|
|
302
|
+
let template = "";
|
|
303
|
+
let footers = "";
|
|
304
|
+
try {
|
|
305
|
+
let zfields = await connection.result({table: "zfields", where: {table: MYMODEL.table}});
|
|
306
|
+
let jsonApproval = zfields.approval_json || {}
|
|
307
|
+
let mytemplate = jsonApproval.content || {}
|
|
308
|
+
let results = await connection.results({table: "zapprovals", where: {token: token}});
|
|
309
|
+
if (results.length == 0) {
|
|
310
|
+
req.session.sessionFlash = Util.flashError(LANGUAGE['data_not_found']);
|
|
311
|
+
return res.redirect('/' + MYMODEL.routeName);
|
|
312
|
+
}
|
|
313
|
+
if(results[0].status == 1){
|
|
314
|
+
await connection.update({
|
|
315
|
+
table : "zapprovals",
|
|
316
|
+
data: {
|
|
317
|
+
template : mytemplate
|
|
318
|
+
},
|
|
319
|
+
where : {
|
|
320
|
+
token:token
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
results = await connection.results({table: "zapprovals", where: {token: token}});
|
|
324
|
+
}
|
|
325
|
+
let MYMODEL2 = require("./../models/zapprovals")
|
|
326
|
+
data = await zRoute.viewTable(req, res, MYMODEL2, results[0], true);
|
|
327
|
+
let row = await connection.result({table: results[0].table, where: {id: results[0].id_data}});
|
|
328
|
+
data2 = await zRoute.viewTable(req, res, MYMODEL, row, true);
|
|
329
|
+
template = data.template;
|
|
330
|
+
let title = data.title || ""
|
|
331
|
+
for (var key in data2) {
|
|
332
|
+
template = Util.replaceAll(template, "{{" + key + "}}", data2[key])
|
|
333
|
+
title = Util.replaceAll(title, "{{" + key + "}}", data2[key])
|
|
334
|
+
}
|
|
335
|
+
data.title = title;
|
|
336
|
+
var details = await connection.results({table: "zapprovals_details", where: {title_id: results[0].id}});
|
|
337
|
+
if (details.length) {
|
|
338
|
+
footers = await zRoute.approversFooter(details);
|
|
339
|
+
}
|
|
340
|
+
} catch (err) {
|
|
341
|
+
zDebug(req, res, err);
|
|
342
|
+
res.send("Err");
|
|
343
|
+
}
|
|
344
|
+
res.render(`layouts/blank`, {
|
|
345
|
+
template: template,
|
|
346
|
+
data: data,
|
|
347
|
+
footers: footers,
|
|
348
|
+
Util: Util,
|
|
349
|
+
details: details,
|
|
350
|
+
renderBody: 'index/zapprovals_preview.ejs'
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
router.post('/grid', access, zRoute.postGrid);
|
|
355
|
+
router.post('/reload', access, zRoute.postGridReload);
|
|
356
|
+
router.post('/chains', access, zRoute.chains);
|
|
357
|
+
router.post('/get_attributes',access, zRoute.getAttributes);
|
|
358
|
+
|
|
359
|
+
module.exports = router;
|