@galadrim/galadmin 0.1.234 → 0.1.236
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/dist/api.js +39 -29
- package/dist/api.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/dbManager.d.ts +1 -0
- package/dist/utils/dbManager.js +5 -0
- package/dist/utils/dbManager.js.map +1 -1
- package/package.json +1 -1
- package/src/api.ts +137 -104
- package/src/front/build/asset-manifest.json +3 -3
- package/src/front/build/index.html +1 -1
- package/src/front/build/static/js/{main.f024ade6.js → main.70ad8e58.js} +3 -3
- package/src/front/build/static/js/{main.f024ade6.js.map → main.70ad8e58.js.map} +1 -1
- package/src/front/ckeditor5-build/node_modules/.bin/semver +12 -0
- package/src/front/ckeditor5-build/node_modules/.bin/semver.cmd +17 -0
- package/src/front/ckeditor5-build/node_modules/.bin/semver.ps1 +28 -0
- package/src/front/ckeditor5-build/node_modules/semver/bin/semver.js +0 -0
- package/src/front/src/components/panels/ChatPanel.js +1 -1
- package/src/utils/dbManager.ts +5 -0
- /package/src/front/build/static/js/{main.f024ade6.js.LICENSE.txt → main.70ad8e58.js.LICENSE.txt} +0 -0
package/src/api.ts
CHANGED
|
@@ -18,6 +18,8 @@ import resolve from "@rollup/plugin-node-resolve";
|
|
|
18
18
|
import axios from "axios";
|
|
19
19
|
import { v4 as uuidv4 } from "uuid";
|
|
20
20
|
|
|
21
|
+
import { quoteIdentifier } from "./utils/dbManager";
|
|
22
|
+
|
|
21
23
|
function generateUniqueIdForObject(obj) {
|
|
22
24
|
const str = JSON.stringify(obj);
|
|
23
25
|
const hash = crypto.createHash("sha256").update(str).digest("hex");
|
|
@@ -39,34 +41,47 @@ export default (db, config) => {
|
|
|
39
41
|
req.config = {
|
|
40
42
|
...config,
|
|
41
43
|
views: config.views
|
|
42
|
-
.filter(
|
|
43
|
-
|
|
44
|
+
.filter(
|
|
45
|
+
(x) =>
|
|
46
|
+
!x.roles ||
|
|
47
|
+
(req.session.user &&
|
|
48
|
+
[...(x.roles || []), ...(x.readRoles || [])].includes(
|
|
49
|
+
req.session.user.role,
|
|
50
|
+
)),
|
|
44
51
|
)
|
|
45
|
-
.map(view => {
|
|
46
|
-
const readOnly =
|
|
52
|
+
.map((view) => {
|
|
53
|
+
const readOnly =
|
|
54
|
+
req.session.user &&
|
|
55
|
+
view.roles &&
|
|
56
|
+
!view.roles.includes(req.session.user.role);
|
|
47
57
|
if (readOnly) {
|
|
48
58
|
return {
|
|
49
59
|
...view,
|
|
50
60
|
canEdit: false,
|
|
51
61
|
canCreate: false,
|
|
52
62
|
canDelete: false,
|
|
53
|
-
clickToEdit: false
|
|
54
|
-
}
|
|
63
|
+
clickToEdit: false,
|
|
64
|
+
};
|
|
55
65
|
} else {
|
|
56
66
|
return view;
|
|
57
67
|
}
|
|
58
68
|
})
|
|
59
|
-
.map(view => {
|
|
69
|
+
.map((view) => {
|
|
60
70
|
if (view.type === "table") {
|
|
61
71
|
return {
|
|
62
72
|
...view,
|
|
63
|
-
columns: view.columns.filter(
|
|
64
|
-
|
|
73
|
+
columns: view.columns.filter(
|
|
74
|
+
(column) =>
|
|
75
|
+
!column.roles ||
|
|
76
|
+
(req.session.user &&
|
|
77
|
+
column.roles.includes(req.session.user.role)),
|
|
78
|
+
),
|
|
79
|
+
};
|
|
65
80
|
} else {
|
|
66
81
|
return view;
|
|
67
82
|
}
|
|
68
|
-
})
|
|
69
|
-
}
|
|
83
|
+
}),
|
|
84
|
+
};
|
|
70
85
|
next();
|
|
71
86
|
});
|
|
72
87
|
|
|
@@ -88,7 +103,8 @@ export default (db, config) => {
|
|
|
88
103
|
// This block re-checks every sensitive route against the view's declared roles.
|
|
89
104
|
// ---------------------------------------------------------------------------
|
|
90
105
|
const ROOT_ROLE = "root";
|
|
91
|
-
const roleOf = (req) =>
|
|
106
|
+
const roleOf = (req) =>
|
|
107
|
+
req.session && req.session.user && req.session.user.role;
|
|
92
108
|
// Write access = the view's `roles` (root always allowed).
|
|
93
109
|
const canWriteView = (view, role) =>
|
|
94
110
|
role === ROOT_ROLE || !view || !view.roles || view.roles.includes(role);
|
|
@@ -98,8 +114,8 @@ export default (db, config) => {
|
|
|
98
114
|
req.body && req.body.viewIndex !== undefined
|
|
99
115
|
? req.body.viewIndex
|
|
100
116
|
: req.query && req.query.viewIndex !== undefined
|
|
101
|
-
|
|
102
|
-
|
|
117
|
+
? req.query.viewIndex
|
|
118
|
+
: undefined;
|
|
103
119
|
if (idx === undefined || !req.config || !req.config.views) return undefined;
|
|
104
120
|
return req.config.views[idx];
|
|
105
121
|
};
|
|
@@ -122,8 +138,7 @@ export default (db, config) => {
|
|
|
122
138
|
// Column names are interpolated into INSERT/UPDATE statements; only accept plain
|
|
123
139
|
// SQL identifiers so a crafted key cannot break out of the column list.
|
|
124
140
|
const areSafeColumnNames = (data) =>
|
|
125
|
-
!data ||
|
|
126
|
-
Object.keys(data).every((k) => /^[A-Za-z0-9_]+$/.test(k));
|
|
141
|
+
!data || Object.keys(data).every((k) => /^[A-Za-z0-9_]+$/.test(k));
|
|
127
142
|
|
|
128
143
|
// Tables the current role is allowed to write, gathered from its writable views
|
|
129
144
|
// and their creatable panels — used to bound /duplicateRecord's target table.
|
|
@@ -135,7 +150,8 @@ export default (db, config) => {
|
|
|
135
150
|
if (view.tableName) tables.add(view.tableName);
|
|
136
151
|
for (const panel of view.recordViewPanels || []) {
|
|
137
152
|
if (panel.tableName) tables.add(panel.tableName);
|
|
138
|
-
if (panel.createConfig?.tableName)
|
|
153
|
+
if (panel.createConfig?.tableName)
|
|
154
|
+
tables.add(panel.createConfig.tableName);
|
|
139
155
|
}
|
|
140
156
|
}
|
|
141
157
|
return tables;
|
|
@@ -147,7 +163,7 @@ export default (db, config) => {
|
|
|
147
163
|
const looksLikeSqlBreakout = (frag) =>
|
|
148
164
|
typeof frag === "string" &&
|
|
149
165
|
/(;|--|\/\*|\bunion\b|\binto\s+(out|dump)file\b|\bsleep\s*\(|\bbenchmark\s*\(|\bload_file\s*\()/i.test(
|
|
150
|
-
frag
|
|
166
|
+
frag,
|
|
151
167
|
);
|
|
152
168
|
|
|
153
169
|
app.use((req, res, next) => {
|
|
@@ -198,7 +214,7 @@ export default (db, config) => {
|
|
|
198
214
|
} else if (columns[i].name) {
|
|
199
215
|
queryString += columns[i].name
|
|
200
216
|
.split(".")
|
|
201
|
-
.map((x) => (db.dbType
|
|
217
|
+
.map((x) => quoteIdentifier(x, db.dbType))
|
|
202
218
|
.join(".");
|
|
203
219
|
}
|
|
204
220
|
queryString += " LIKE '%" + params.query.replace(/'/g, "\\'") + "%'";
|
|
@@ -210,16 +226,16 @@ export default (db, config) => {
|
|
|
210
226
|
const limit = params.allRecords ? 999999 : parseInt(params.limit);
|
|
211
227
|
const query =
|
|
212
228
|
params.customQuery ||
|
|
213
|
-
`SELECT
|
|
214
|
-
${params.selectExpression || "*"}
|
|
215
|
-
FROM ${params.table}
|
|
216
|
-
${params.join || ""}
|
|
217
|
-
${queryString}
|
|
218
|
-
${params.groupBy ? "GROUP BY " + params.groupBy : ""}
|
|
219
|
-
${params.having ? " HAVING " + params.having : ""}
|
|
229
|
+
`SELECT
|
|
230
|
+
${params.selectExpression || "*"}
|
|
231
|
+
FROM ${params.table}
|
|
232
|
+
${params.join || ""}
|
|
233
|
+
${queryString}
|
|
234
|
+
${params.groupBy ? "GROUP BY " + params.groupBy : ""}
|
|
235
|
+
${params.having ? " HAVING " + params.having : ""}
|
|
220
236
|
${params.orderBy || params.primaryId ? " ORDER BY " + (params.orderBy || `${params.table}.${params.primaryId}`) : ""}`;
|
|
221
237
|
const { err, rows } = await db.query(
|
|
222
|
-
`${query} LIMIT ${limit} OFFSET ${Math.max((params.page - 1) * limit, 0)}
|
|
238
|
+
`${query} LIMIT ${limit} OFFSET ${Math.max((params.page - 1) * limit, 0)}`,
|
|
223
239
|
);
|
|
224
240
|
if (err) {
|
|
225
241
|
console.error(err);
|
|
@@ -230,7 +246,7 @@ export default (db, config) => {
|
|
|
230
246
|
let count = undefined;
|
|
231
247
|
if (computeCount) {
|
|
232
248
|
const { err: countErr, rows: countRows } = await db.query(
|
|
233
|
-
`SELECT COUNT(*) AS count FROM (${query}) s
|
|
249
|
+
`SELECT COUNT(*) AS count FROM (${query}) s`,
|
|
234
250
|
);
|
|
235
251
|
if (countErr) {
|
|
236
252
|
const { rows: allRows } = await db.query(query);
|
|
@@ -256,7 +272,7 @@ export default (db, config) => {
|
|
|
256
272
|
Object.assign(params, {
|
|
257
273
|
page: parseInt(params.position) + parseInt(params.offset),
|
|
258
274
|
limit: 1,
|
|
259
|
-
})
|
|
275
|
+
}),
|
|
260
276
|
);
|
|
261
277
|
}
|
|
262
278
|
|
|
@@ -277,15 +293,15 @@ export default (db, config) => {
|
|
|
277
293
|
const pkg = JSON.parse(
|
|
278
294
|
fs.readFileSync(
|
|
279
295
|
path.join(__dirname, "../src/front/package.json"),
|
|
280
|
-
"utf-8"
|
|
281
|
-
)
|
|
296
|
+
"utf-8",
|
|
297
|
+
),
|
|
282
298
|
);
|
|
283
299
|
const external = Object.keys(pkg.dependencies || {});
|
|
284
300
|
const bundle = await rollup.rollup({
|
|
285
301
|
input: path.join(
|
|
286
302
|
__dirname,
|
|
287
303
|
"../../",
|
|
288
|
-
req.query.componentPath as string
|
|
304
|
+
req.query.componentPath as string,
|
|
289
305
|
),
|
|
290
306
|
plugins: [
|
|
291
307
|
resolve(),
|
|
@@ -360,7 +376,10 @@ export default (db, config) => {
|
|
|
360
376
|
? `${column.searchableName} LIKE '${column.wholeSearch ? "%" : ""}${queryEscaped}%'`
|
|
361
377
|
: null,
|
|
362
378
|
column.filtersFromUser
|
|
363
|
-
? column.filtersFromUser(
|
|
379
|
+
? column.filtersFromUser(
|
|
380
|
+
req.session.user.userId,
|
|
381
|
+
req.session.user.role,
|
|
382
|
+
)
|
|
364
383
|
: null,
|
|
365
384
|
]),
|
|
366
385
|
join: column.join,
|
|
@@ -388,7 +407,7 @@ export default (db, config) => {
|
|
|
388
407
|
value,
|
|
389
408
|
label,
|
|
390
409
|
};
|
|
391
|
-
})
|
|
410
|
+
}),
|
|
392
411
|
);
|
|
393
412
|
res.send(options);
|
|
394
413
|
});
|
|
@@ -415,7 +434,10 @@ export default (db, config) => {
|
|
|
415
434
|
subview?.filters,
|
|
416
435
|
...req.body.filters,
|
|
417
436
|
view.filtersFromUser
|
|
418
|
-
? view.filtersFromUser(
|
|
437
|
+
? view.filtersFromUser(
|
|
438
|
+
req.session.user.userId,
|
|
439
|
+
req.session.user.role,
|
|
440
|
+
)
|
|
419
441
|
: null,
|
|
420
442
|
]),
|
|
421
443
|
having: subview?.having,
|
|
@@ -434,7 +456,7 @@ export default (db, config) => {
|
|
|
434
456
|
columns: view.columns,
|
|
435
457
|
limit: view.limit || 10,
|
|
436
458
|
},
|
|
437
|
-
true
|
|
459
|
+
true,
|
|
438
460
|
);
|
|
439
461
|
if (error) {
|
|
440
462
|
return res.status(500).send(error);
|
|
@@ -490,11 +512,11 @@ export default (db, config) => {
|
|
|
490
512
|
" = " +
|
|
491
513
|
(typeof data[key] === "string"
|
|
492
514
|
? "'" +
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
515
|
+
data[key]
|
|
516
|
+
.replace(/'/g, "\\'")
|
|
517
|
+
.replace(/\n/g, "")
|
|
518
|
+
.replace(/\r/g, "") +
|
|
519
|
+
"'"
|
|
498
520
|
: typeof data[key] === "object"
|
|
499
521
|
? JSON.stringify(data[key])
|
|
500
522
|
: data[key]) +
|
|
@@ -605,7 +627,7 @@ export default (db, config) => {
|
|
|
605
627
|
return res
|
|
606
628
|
.status(500)
|
|
607
629
|
.send(
|
|
608
|
-
'"parseImportFile" doit être défini dans la configuration de la vue.'
|
|
630
|
+
'"parseImportFile" doit être défini dans la configuration de la vue.',
|
|
609
631
|
);
|
|
610
632
|
}
|
|
611
633
|
|
|
@@ -628,7 +650,7 @@ export default (db, config) => {
|
|
|
628
650
|
|
|
629
651
|
let queryString;
|
|
630
652
|
if (db.dbType === "POSTGRESQL") {
|
|
631
|
-
queryString = `INSERT INTO ?? (${columns.map((x) =>
|
|
653
|
+
queryString = `INSERT INTO ?? (${columns.map((x) => quoteIdentifier(x, db.dbType)).join(", ")}) VALUES`;
|
|
632
654
|
|
|
633
655
|
for (let record of records) {
|
|
634
656
|
const valuesList = columns
|
|
@@ -641,12 +663,12 @@ export default (db, config) => {
|
|
|
641
663
|
` ON CONFLICT (${view.importFileUniqueField}) DO UPDATE SET `;
|
|
642
664
|
|
|
643
665
|
for (let column of columns) {
|
|
644
|
-
queryString +=
|
|
666
|
+
queryString += `${quoteIdentifier(column, db.dbType)} = EXCLUDED.${quoteIdentifier(column, db.dbType)},`;
|
|
645
667
|
}
|
|
646
668
|
} else {
|
|
647
669
|
queryString =
|
|
648
670
|
"INSERT INTO ?? (" +
|
|
649
|
-
columns.map((x) =>
|
|
671
|
+
columns.map((x) => quoteIdentifier(x, db.dbType)).join(", ") +
|
|
650
672
|
") VALUES";
|
|
651
673
|
for (let record of records) {
|
|
652
674
|
queryString +=
|
|
@@ -658,7 +680,7 @@ export default (db, config) => {
|
|
|
658
680
|
}
|
|
659
681
|
queryString = queryString.slice(0, -1) + " ON DUPLICATE KEY UPDATE";
|
|
660
682
|
for (let column of columns) {
|
|
661
|
-
queryString +=
|
|
683
|
+
queryString += ` ${quoteIdentifier(column, db.dbType)} = VALUES(${quoteIdentifier(column, db.dbType)}),`;
|
|
662
684
|
}
|
|
663
685
|
}
|
|
664
686
|
queryString = queryString.slice(0, -1); // Remove the trailing comma
|
|
@@ -697,20 +719,20 @@ export default (db, config) => {
|
|
|
697
719
|
return res
|
|
698
720
|
.status(500)
|
|
699
721
|
.send(
|
|
700
|
-
'"parseImportFile" doit être défini dans la configuration du panneau.'
|
|
722
|
+
'"parseImportFile" doit être défini dans la configuration du panneau.',
|
|
701
723
|
);
|
|
702
724
|
}
|
|
703
725
|
if (!Boolean(panel.createConfig)) {
|
|
704
726
|
return res
|
|
705
727
|
.status(500)
|
|
706
728
|
.send(
|
|
707
|
-
'"createConfig" doit être défini dans la configuration du panneau.'
|
|
729
|
+
'"createConfig" doit être défini dans la configuration du panneau.',
|
|
708
730
|
);
|
|
709
731
|
}
|
|
710
732
|
|
|
711
733
|
const records = await panel.parseImportFile(
|
|
712
734
|
files.data[0],
|
|
713
|
-
fields.recordId[0]
|
|
735
|
+
fields.recordId[0],
|
|
714
736
|
);
|
|
715
737
|
const columns = [];
|
|
716
738
|
if (records.length === 0) {
|
|
@@ -729,10 +751,10 @@ export default (db, config) => {
|
|
|
729
751
|
}
|
|
730
752
|
|
|
731
753
|
let queryString =
|
|
732
|
-
"INSERT IGNORE INTO ?? (
|
|
733
|
-
panel.createConfig.recordIdKey +
|
|
734
|
-
"
|
|
735
|
-
columns.map((x) =>
|
|
754
|
+
"INSERT IGNORE INTO ?? (" +
|
|
755
|
+
quoteIdentifier(panel.createConfig.recordIdKey, db.dbType) +
|
|
756
|
+
", " +
|
|
757
|
+
columns.map((x) => quoteIdentifier(x, db.dbType)).join(", ") +
|
|
736
758
|
") VALUES";
|
|
737
759
|
for (let record of records) {
|
|
738
760
|
queryString +=
|
|
@@ -746,7 +768,7 @@ export default (db, config) => {
|
|
|
746
768
|
}
|
|
747
769
|
queryString = queryString.slice(0, -1) + " ON DUPLICATE KEY UPDATE";
|
|
748
770
|
for (let column of columns) {
|
|
749
|
-
queryString +=
|
|
771
|
+
queryString += ` ${quoteIdentifier(column, db.dbType)} = VALUES(${quoteIdentifier(column, db.dbType)}),`;
|
|
750
772
|
}
|
|
751
773
|
const { err, rows: r } = await db.query(queryString.slice(0, -1), [
|
|
752
774
|
panel.tableName,
|
|
@@ -775,15 +797,15 @@ export default (db, config) => {
|
|
|
775
797
|
const view = req.config.views[req.query.viewIndex as string];
|
|
776
798
|
const { err, rows } = await db.query(
|
|
777
799
|
"SELECT " +
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
800
|
+
(view.selectExpression || "*") +
|
|
801
|
+
" FROM ?? " +
|
|
802
|
+
(view.join || "") +
|
|
803
|
+
" WHERE ?? = ?",
|
|
782
804
|
[
|
|
783
805
|
view.tableName,
|
|
784
806
|
view.tableName + "." + view.primaryId,
|
|
785
807
|
req.query.recordId,
|
|
786
|
-
]
|
|
808
|
+
],
|
|
787
809
|
);
|
|
788
810
|
if (err) {
|
|
789
811
|
return res.status(500).send(err);
|
|
@@ -820,10 +842,10 @@ export default (db, config) => {
|
|
|
820
842
|
return res.sendStatus(200);
|
|
821
843
|
}
|
|
822
844
|
const queryExpression = Object.keys(req.body.data)
|
|
823
|
-
.map((x) => `${x} = ?`)
|
|
845
|
+
.map((x) => `${quoteIdentifier(x, db.dbType)} = ?`)
|
|
824
846
|
.join(", ");
|
|
825
847
|
const queryParameters = Object.keys(req.body.data).map(
|
|
826
|
-
(x) => req.body.data[x]
|
|
848
|
+
(x) => req.body.data[x],
|
|
827
849
|
);
|
|
828
850
|
const { err } = await db.query(
|
|
829
851
|
`UPDATE ?? SET ${queryExpression} WHERE ?? = ?`,
|
|
@@ -832,7 +854,7 @@ export default (db, config) => {
|
|
|
832
854
|
...queryParameters,
|
|
833
855
|
view.primaryId,
|
|
834
856
|
req.body.primaryValue,
|
|
835
|
-
]
|
|
857
|
+
],
|
|
836
858
|
);
|
|
837
859
|
if (err) {
|
|
838
860
|
return res.status(500).send(err);
|
|
@@ -844,7 +866,7 @@ export default (db, config) => {
|
|
|
844
866
|
data: req.body.data,
|
|
845
867
|
previousData: req.body.previousData,
|
|
846
868
|
});
|
|
847
|
-
} catch (e) {
|
|
869
|
+
} catch (e) {}
|
|
848
870
|
}
|
|
849
871
|
if (view.editCallbackFunction) {
|
|
850
872
|
await view.editCallbackFunction({
|
|
@@ -862,14 +884,17 @@ export default (db, config) => {
|
|
|
862
884
|
try {
|
|
863
885
|
const { rows } = await db.query(
|
|
864
886
|
"SELECT " +
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
[view.tableName, view.tableName + "." + view.primaryId, recordId]
|
|
887
|
+
(view.selectExpression || "*") +
|
|
888
|
+
" FROM ?? " +
|
|
889
|
+
(view.join || "") +
|
|
890
|
+
" WHERE ?? = ?",
|
|
891
|
+
[view.tableName, view.tableName + "." + view.primaryId, recordId],
|
|
870
892
|
);
|
|
871
893
|
const record = rows[0];
|
|
872
|
-
const actionResult = await action.action(
|
|
894
|
+
const actionResult = await action.action(
|
|
895
|
+
record,
|
|
896
|
+
req.session.user.userId,
|
|
897
|
+
);
|
|
873
898
|
if (actionResult) {
|
|
874
899
|
return res.send({
|
|
875
900
|
success: true,
|
|
@@ -894,15 +919,15 @@ export default (db, config) => {
|
|
|
894
919
|
const view = req.config.views[req.body.viewIndex];
|
|
895
920
|
const { rows, err: err2 } = await db.query(
|
|
896
921
|
"SELECT " +
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
922
|
+
(view.selectExpression || "*") +
|
|
923
|
+
" FROM ?? " +
|
|
924
|
+
(view.join || "") +
|
|
925
|
+
" WHERE ?? = ?",
|
|
901
926
|
[
|
|
902
927
|
view.tableName,
|
|
903
928
|
view.tableName + "." + view.primaryId,
|
|
904
929
|
req.body.primaryValue,
|
|
905
|
-
]
|
|
930
|
+
],
|
|
906
931
|
);
|
|
907
932
|
if (err2) {
|
|
908
933
|
return res.status(500).send(err2);
|
|
@@ -924,7 +949,7 @@ export default (db, config) => {
|
|
|
924
949
|
await axios.post(view.deleteCallback, {
|
|
925
950
|
id: req.body.primaryValue,
|
|
926
951
|
});
|
|
927
|
-
} catch (e) {
|
|
952
|
+
} catch (e) {}
|
|
928
953
|
}
|
|
929
954
|
if (view.deleteCallbackFunction) {
|
|
930
955
|
await view.deleteCallbackFunction({
|
|
@@ -938,11 +963,11 @@ export default (db, config) => {
|
|
|
938
963
|
app.post("/deleteRecordFromPanel", async function (req, res) {
|
|
939
964
|
const view = req.config.views[req.body.viewIndex];
|
|
940
965
|
const panel = view.recordViewPanels.find(
|
|
941
|
-
(x) => x.__id__ === req.body.panelId
|
|
966
|
+
(x) => x.__id__ === req.body.panelId,
|
|
942
967
|
);
|
|
943
968
|
const { rows, err: err2 } = await db.query(
|
|
944
969
|
"SELECT * FROM ?? WHERE ?? = ?",
|
|
945
|
-
[panel.tableName, panel.primaryId, req.body.primaryValue]
|
|
970
|
+
[panel.tableName, panel.primaryId, req.body.primaryValue],
|
|
946
971
|
);
|
|
947
972
|
if (err2) {
|
|
948
973
|
return res.status(500).send(err2);
|
|
@@ -961,7 +986,7 @@ export default (db, config) => {
|
|
|
961
986
|
await axios.post(panel.deleteCallback, {
|
|
962
987
|
id: req.body.primaryValue,
|
|
963
988
|
});
|
|
964
|
-
} catch (e) {
|
|
989
|
+
} catch (e) {}
|
|
965
990
|
}
|
|
966
991
|
if (panel.deleteCallbackFunction) {
|
|
967
992
|
await panel.deleteCallbackFunction({
|
|
@@ -976,7 +1001,7 @@ export default (db, config) => {
|
|
|
976
1001
|
const view = req.config.views[req.body.viewIndex];
|
|
977
1002
|
const { err } = await db.query(
|
|
978
1003
|
`DELETE FROM ?? WHERE ?? IN(${req.body.primaryValues.map((x) => "'" + x + "'").join(",")})`,
|
|
979
|
-
[view.tableName, view.primaryId]
|
|
1004
|
+
[view.tableName, view.primaryId],
|
|
980
1005
|
);
|
|
981
1006
|
if (err) {
|
|
982
1007
|
return res.status(500).send(err);
|
|
@@ -1044,7 +1069,7 @@ export default (db, config) => {
|
|
|
1044
1069
|
}
|
|
1045
1070
|
const { err: err2, rows: r } = await db.query(
|
|
1046
1071
|
"INSERT IGNORE INTO ?? SET ?",
|
|
1047
|
-
[req.body.table, data]
|
|
1072
|
+
[req.body.table, data],
|
|
1048
1073
|
);
|
|
1049
1074
|
if (err2) {
|
|
1050
1075
|
return res.status(500).send(err);
|
|
@@ -1090,7 +1115,7 @@ export default (db, config) => {
|
|
|
1090
1115
|
req.body.data[panel.createConfig.recordIdKey] = req.body.recordId;
|
|
1091
1116
|
}
|
|
1092
1117
|
const primaryColumn = (view || panel)?.columns?.find(
|
|
1093
|
-
(x) => x.name === (view || panel).primaryId
|
|
1118
|
+
(x) => x.name === (view || panel).primaryId,
|
|
1094
1119
|
);
|
|
1095
1120
|
if (primaryColumn?.dataType == "uuid") {
|
|
1096
1121
|
req.body.data[primaryColumn.name] = uuidv4();
|
|
@@ -1108,16 +1133,18 @@ export default (db, config) => {
|
|
|
1108
1133
|
if (!areSafeColumnNames(createPayload)) {
|
|
1109
1134
|
return res.status(400).send({ error: "Invalid column name" });
|
|
1110
1135
|
}
|
|
1111
|
-
const queryExpression = Object.keys(createPayload)
|
|
1136
|
+
const queryExpression = Object.keys(createPayload)
|
|
1137
|
+
.map((x) => quoteIdentifier(x, db.dbType))
|
|
1138
|
+
.join(", ");
|
|
1112
1139
|
const queryPlaceholders = Object.keys(createPayload)
|
|
1113
1140
|
.map((x) => "?")
|
|
1114
1141
|
.join(", ");
|
|
1115
1142
|
const queryParameters = Object.keys(createPayload).map(
|
|
1116
|
-
(x) => createPayload[x]
|
|
1143
|
+
(x) => createPayload[x],
|
|
1117
1144
|
);
|
|
1118
1145
|
const { err, rows: r } = await db.query(
|
|
1119
1146
|
`INSERT INTO ?? (${queryExpression}) VALUES(${queryPlaceholders})`,
|
|
1120
|
-
[panel ? panel.tableName : view.tableName, ...queryParameters]
|
|
1147
|
+
[panel ? panel.tableName : view.tableName, ...queryParameters],
|
|
1121
1148
|
);
|
|
1122
1149
|
if (err) {
|
|
1123
1150
|
console.error(err);
|
|
@@ -1135,7 +1162,7 @@ export default (db, config) => {
|
|
|
1135
1162
|
id: r.insertId,
|
|
1136
1163
|
data: req.body.data,
|
|
1137
1164
|
});
|
|
1138
|
-
} catch (e) {
|
|
1165
|
+
} catch (e) {}
|
|
1139
1166
|
}
|
|
1140
1167
|
if (panel ? panel.createCallbackFunction : view.createCallbackFunction) {
|
|
1141
1168
|
await (
|
|
@@ -1169,7 +1196,10 @@ export default (db, config) => {
|
|
|
1169
1196
|
}
|
|
1170
1197
|
for (const key of Object.keys(node)) {
|
|
1171
1198
|
const value = node[key];
|
|
1172
|
-
if (
|
|
1199
|
+
if (
|
|
1200
|
+
typeof value === "string" &&
|
|
1201
|
+
(key === "query" || /Query$/.test(key))
|
|
1202
|
+
) {
|
|
1173
1203
|
acc.add(value.trim());
|
|
1174
1204
|
} else if (value && typeof value === "object") {
|
|
1175
1205
|
collectAllowedQueries(value, acc);
|
|
@@ -1188,7 +1218,8 @@ export default (db, config) => {
|
|
|
1188
1218
|
app.get("/customQuery", (req, res) => res.sendStatus(404));
|
|
1189
1219
|
} else {
|
|
1190
1220
|
app.get("/customQuery", async function (req, res, next) {
|
|
1191
|
-
const template =
|
|
1221
|
+
const template =
|
|
1222
|
+
typeof req.query.query === "string" ? req.query.query : "";
|
|
1192
1223
|
// SECURITY: only run whitelisted, read-only templates, and bind the record id
|
|
1193
1224
|
// as a parameter instead of substituting it into the SQL string.
|
|
1194
1225
|
const allowed = collectAllowedQueries(req.config.views);
|
|
@@ -1196,7 +1227,9 @@ export default (db, config) => {
|
|
|
1196
1227
|
return res.status(403).send({ error: "Query not allowed" });
|
|
1197
1228
|
}
|
|
1198
1229
|
if (!/^\s*\(?\s*(select|with)\b/i.test(template) || /;/.test(template)) {
|
|
1199
|
-
return res
|
|
1230
|
+
return res
|
|
1231
|
+
.status(400)
|
|
1232
|
+
.send({ error: "Only read-only queries are allowed" });
|
|
1200
1233
|
}
|
|
1201
1234
|
const placeholderCount = (template.match(/\?/g) || []).length;
|
|
1202
1235
|
const params = new Array(placeholderCount).fill(req.query.primaryValue);
|
|
@@ -1207,7 +1240,7 @@ export default (db, config) => {
|
|
|
1207
1240
|
if (req.query.viewIndex && req.query.panelIndex) {
|
|
1208
1241
|
const panel =
|
|
1209
1242
|
req.config.views[req.query.viewIndex as string].recordViewPanels[
|
|
1210
|
-
|
|
1243
|
+
req.query.panelIndex as string
|
|
1211
1244
|
];
|
|
1212
1245
|
if (panel.formatValue) {
|
|
1213
1246
|
for (const row of rows) {
|
|
@@ -1240,7 +1273,7 @@ export default (db, config) => {
|
|
|
1240
1273
|
}
|
|
1241
1274
|
const originalFilenameWithoutExtension = originalFilename.replace(
|
|
1242
1275
|
"." + extension,
|
|
1243
|
-
""
|
|
1276
|
+
"",
|
|
1244
1277
|
);
|
|
1245
1278
|
|
|
1246
1279
|
if (column.gcpConfig) {
|
|
@@ -1302,7 +1335,7 @@ export default (db, config) => {
|
|
|
1302
1335
|
Key: path,
|
|
1303
1336
|
ContentType: contentType,
|
|
1304
1337
|
}),
|
|
1305
|
-
{ expiresIn: 150 }
|
|
1338
|
+
{ expiresIn: 150 },
|
|
1306
1339
|
)
|
|
1307
1340
|
.then((signedUrl) =>
|
|
1308
1341
|
axios.put(
|
|
@@ -1314,8 +1347,8 @@ export default (db, config) => {
|
|
|
1314
1347
|
headers: {
|
|
1315
1348
|
"Content-Type": contentType,
|
|
1316
1349
|
},
|
|
1317
|
-
}
|
|
1318
|
-
)
|
|
1350
|
+
},
|
|
1351
|
+
),
|
|
1319
1352
|
)
|
|
1320
1353
|
.then(() => {
|
|
1321
1354
|
res.status(200).send({
|
|
@@ -1337,7 +1370,7 @@ export default (db, config) => {
|
|
|
1337
1370
|
ContentType: contentType,
|
|
1338
1371
|
ACL: "public-read",
|
|
1339
1372
|
Key: path,
|
|
1340
|
-
})
|
|
1373
|
+
}),
|
|
1341
1374
|
)
|
|
1342
1375
|
.then(() => {
|
|
1343
1376
|
res.send({
|
|
@@ -1377,9 +1410,9 @@ export default (db, config) => {
|
|
|
1377
1410
|
await sftp.fastPut(
|
|
1378
1411
|
files[fileParamName][0].path,
|
|
1379
1412
|
column.publicPath +
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1413
|
+
(column.subdirectory ? "/" + column.subdirectory : "") +
|
|
1414
|
+
"/" +
|
|
1415
|
+
filename,
|
|
1383
1416
|
);
|
|
1384
1417
|
await sftp.end();
|
|
1385
1418
|
res.send({
|
|
@@ -1410,10 +1443,10 @@ export default (db, config) => {
|
|
|
1410
1443
|
"/uploadFileFromBetterRichTextInput",
|
|
1411
1444
|
async function (req, res, next) {
|
|
1412
1445
|
const fileUploadProperties = JSON.parse(
|
|
1413
|
-
req.headers["file-upload-properties"] as string
|
|
1446
|
+
req.headers["file-upload-properties"] as string,
|
|
1414
1447
|
);
|
|
1415
1448
|
uploadFile(fileUploadProperties, "upload", req, res);
|
|
1416
|
-
}
|
|
1449
|
+
},
|
|
1417
1450
|
);
|
|
1418
1451
|
|
|
1419
1452
|
app.post("/uploadFile", async function (req, res, next) {
|
|
@@ -1438,12 +1471,12 @@ export default (db, config) => {
|
|
|
1438
1471
|
where: `denominationunitelegale LIKE '${processedQuery}*' OR siret LIKE '${processedQuery}*'`,
|
|
1439
1472
|
limit: 100,
|
|
1440
1473
|
},
|
|
1441
|
-
}
|
|
1474
|
+
},
|
|
1442
1475
|
);
|
|
1443
1476
|
const results = response.data.results
|
|
1444
1477
|
.filter(
|
|
1445
1478
|
(etablissement) =>
|
|
1446
|
-
etablissement.etatadministratifetablissement === "Actif"
|
|
1479
|
+
etablissement.etatadministratifetablissement === "Actif",
|
|
1447
1480
|
)
|
|
1448
1481
|
.map((etablissement) => ({
|
|
1449
1482
|
id: Math.random().toString().slice(2),
|
|
@@ -1468,7 +1501,7 @@ export default (db, config) => {
|
|
|
1468
1501
|
async function enrichConfig(config, db) {
|
|
1469
1502
|
const { rows: info, err } = await db.query(
|
|
1470
1503
|
"SELECT * FROM information_schema.columns WHERE table_schema = ?",
|
|
1471
|
-
[config.mysql?.database || config.postgresql?.database]
|
|
1504
|
+
[config.mysql?.database || config.postgresql?.database],
|
|
1472
1505
|
);
|
|
1473
1506
|
if (err) {
|
|
1474
1507
|
console.error(err);
|
|
@@ -1517,7 +1550,7 @@ function enrichColumn(column, tableName, info) {
|
|
|
1517
1550
|
column.__id__ = generateUniqueIdForObject(column);
|
|
1518
1551
|
}
|
|
1519
1552
|
const infoEl = info.find(
|
|
1520
|
-
(x) => x.TABLE_NAME == tableName && x.COLUMN_NAME == column.name
|
|
1553
|
+
(x) => x.TABLE_NAME == tableName && x.COLUMN_NAME == column.name,
|
|
1521
1554
|
);
|
|
1522
1555
|
if (
|
|
1523
1556
|
infoEl?.IS_NULLABLE == "NO" &&
|
|
@@ -1534,7 +1567,7 @@ function enrichColumn(column, tableName, info) {
|
|
|
1534
1567
|
column.options = JSON.parse(
|
|
1535
1568
|
infoEl.COLUMN_TYPE.replace("enum(", "[")
|
|
1536
1569
|
.replace(")", "]")
|
|
1537
|
-
.replace(/'/g, '"')
|
|
1570
|
+
.replace(/'/g, '"'),
|
|
1538
1571
|
);
|
|
1539
1572
|
}
|
|
1540
1573
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": {
|
|
3
3
|
"main.css": "/_HOMEPAGE_/static/css/main.eacc50f1.css",
|
|
4
|
-
"main.js": "/_HOMEPAGE_/static/js/main.
|
|
4
|
+
"main.js": "/_HOMEPAGE_/static/js/main.70ad8e58.js",
|
|
5
5
|
"static/media/gt-eesti-medium.woff": "/_HOMEPAGE_/static/media/gt-eesti-medium.a48c02fa57e83ab6ddcd.woff",
|
|
6
6
|
"static/media/gt-eesti-regular.woff": "/_HOMEPAGE_/static/media/gt-eesti-regular.304a7ae06ee4a4d5d04a.woff",
|
|
7
7
|
"static/media/logo.png": "/_HOMEPAGE_/static/media/logo.e407b9ca7e302466a07d.png",
|
|
8
8
|
"static/media/cubano.woff": "/_HOMEPAGE_/static/media/cubano.4eaa47ef94222121a371.woff",
|
|
9
9
|
"index.html": "/_HOMEPAGE_/index.html",
|
|
10
10
|
"main.eacc50f1.css.map": "/_HOMEPAGE_/static/css/main.eacc50f1.css.map",
|
|
11
|
-
"main.
|
|
11
|
+
"main.70ad8e58.js.map": "/_HOMEPAGE_/static/js/main.70ad8e58.js.map"
|
|
12
12
|
},
|
|
13
13
|
"entrypoints": [
|
|
14
14
|
"static/css/main.eacc50f1.css",
|
|
15
|
-
"static/js/main.
|
|
15
|
+
"static/js/main.70ad8e58.js"
|
|
16
16
|
]
|
|
17
17
|
}
|