@galadrim/galadmin 0.1.236 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.github/workflows/main.yml +34 -3
  2. package/README.md +65 -0
  3. package/dist/api.d.ts +16 -0
  4. package/dist/api.js +240 -189
  5. package/dist/api.js.map +1 -1
  6. package/dist/index.js +25 -3
  7. package/dist/index.js.map +1 -1
  8. package/dist/tsconfig.tsbuildinfo +1 -1
  9. package/dist/types/config.d.ts +6 -1
  10. package/dist/utils/dbManager.d.ts +5 -0
  11. package/dist/utils/dbManager.js +146 -11
  12. package/dist/utils/dbManager.js.map +1 -1
  13. package/dist/utils/imageDimensions.d.ts +5 -0
  14. package/dist/utils/imageDimensions.js +98 -0
  15. package/dist/utils/imageDimensions.js.map +1 -0
  16. package/jest.config.js +43 -0
  17. package/jest.integration.config.js +16 -0
  18. package/package.json +30 -10
  19. package/src/api.ts +284 -217
  20. package/src/front/src/App.test.js +28 -0
  21. package/src/front/src/components/OutsideClickHandler.test.js +53 -0
  22. package/src/front/src/components/RedirectToFirstView.test.js +38 -0
  23. package/src/front/src/utils/checkRequiredColumns.test.js +46 -0
  24. package/src/front/src/utils/evalExpression.test.js +29 -0
  25. package/src/index.ts +26 -3
  26. package/src/types/config.ts +13 -1
  27. package/src/utils/dbManager.ts +243 -102
  28. package/src/utils/imageDimensions.ts +112 -0
  29. package/tests/__mocks__/@aws-sdk/client-s3.js +6 -0
  30. package/tests/__mocks__/@aws-sdk/s3-request-presigner.js +3 -0
  31. package/tests/__mocks__/@google-cloud/storage.js +7 -0
  32. package/tests/__mocks__/argon2.js +3 -0
  33. package/tests/__mocks__/axios.js +7 -0
  34. package/tests/__mocks__/connect-pg-simple.js +24 -0
  35. package/tests/__mocks__/express-mysql-session.js +24 -0
  36. package/tests/__mocks__/mysql2/promise.js +3 -0
  37. package/tests/__mocks__/pg.js +3 -0
  38. package/tests/__mocks__/rollup.js +5 -0
  39. package/tests/__mocks__/ssh2-sftp-client.js +10 -0
  40. package/tests/api-helpers.test.ts +376 -0
  41. package/tests/dbManager.init.test.ts +129 -0
  42. package/tests/dbManager.test.ts +265 -0
  43. package/tests/edge-cases.test.ts +167 -0
  44. package/tests/helpers/app.ts +69 -0
  45. package/tests/helpers/fakeDb.ts +66 -0
  46. package/tests/helpers/fakePool.ts +76 -0
  47. package/tests/helpers/fixtures.ts +68 -0
  48. package/tests/imageDimensions.test.ts +111 -0
  49. package/tests/index.sessionStore.test.ts +19 -0
  50. package/tests/index.test.ts +412 -0
  51. package/tests/integration/__mocks__/axios.js +3 -0
  52. package/tests/integration/dbHarness.ts +381 -0
  53. package/tests/integration/dbManager.integration.test.ts +69 -0
  54. package/tests/integration/globalSetup.ts +43 -0
  55. package/tests/integration/globalTeardown.ts +42 -0
  56. package/tests/integration/routes.authorization.integration.test.ts +319 -0
  57. package/tests/integration/routes.config.integration.test.ts +189 -0
  58. package/tests/integration/routes.crud.integration.test.ts +805 -0
  59. package/tests/integration/routes.custom.integration.test.ts +376 -0
  60. package/tests/integration/routes.foreign.integration.test.ts +306 -0
  61. package/tests/integration/routes.importExport.integration.test.ts +404 -0
  62. package/tests/integration/routes.integration.test.ts +366 -0
  63. package/tests/integration/routes.postgresql.integration.test.ts +88 -0
  64. package/tests/integration/routes.records.integration.test.ts +634 -0
  65. package/tests/integration/routes.upload.integration.test.ts +425 -0
  66. package/tests/setup/silence-console.ts +15 -0
package/dist/api.js CHANGED
@@ -11,6 +11,15 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  return t;
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.looksLikeSqlBreakout = exports.writableTables = exports.areSafeColumnNames = exports.MUTATING_VIEW_ROUTES = exports.viewFromRequest = exports.canWriteView = exports.roleOf = exports.ROOT_ROLE = void 0;
15
+ exports.generateUniqueIdForObject = generateUniqueIdForObject;
16
+ exports.sqlAnd = sqlAnd;
17
+ exports.getFormattedValue = getFormattedValue;
18
+ exports.evalExpression = evalExpression;
19
+ exports.formatValueForInsertion = formatValueForInsertion;
20
+ exports.collectAllowedQueries = collectAllowedQueries;
21
+ exports.enrichConfig = enrichConfig;
22
+ exports.enrichColumn = enrichColumn;
14
23
  const express = require("express");
15
24
  const multiparty = require("multiparty");
16
25
  const fs = require("fs");
@@ -20,7 +29,7 @@ const mime = require("mime");
20
29
  const client_s3_1 = require("@aws-sdk/client-s3");
21
30
  const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
22
31
  const storage_1 = require("@google-cloud/storage");
23
- const image_size_1 = require("image-size");
32
+ const imageDimensions_1 = require("./utils/imageDimensions");
24
33
  const serializeJs = require("serialize-javascript");
25
34
  const SftpClient = require("ssh2-sftp-client");
26
35
  const rollup = require("rollup");
@@ -34,6 +43,158 @@ function generateUniqueIdForObject(obj) {
34
43
  const hash = crypto.createHash("sha256").update(str).digest("hex");
35
44
  return hash;
36
45
  }
46
+ exports.ROOT_ROLE = "root";
47
+ const roleOf = (req) => req.session && req.session.user && req.session.user.role;
48
+ exports.roleOf = roleOf;
49
+ const canWriteView = (view, role) => role === exports.ROOT_ROLE || !view || !view.roles || view.roles.includes(role);
50
+ exports.canWriteView = canWriteView;
51
+ const viewFromRequest = (req) => {
52
+ const idx = req.body && req.body.viewIndex !== undefined
53
+ ? req.body.viewIndex
54
+ : req.query && req.query.viewIndex !== undefined
55
+ ? req.query.viewIndex
56
+ : undefined;
57
+ if (idx === undefined || !req.config || !req.config.views)
58
+ return undefined;
59
+ return req.config.views[idx];
60
+ };
61
+ exports.viewFromRequest = viewFromRequest;
62
+ exports.MUTATING_VIEW_ROUTES = new Set([
63
+ "/updateRecord",
64
+ "/createRecord",
65
+ "/deleteRecord",
66
+ "/deleteRecords",
67
+ "/deleteRecordFromPanel",
68
+ "/customAction",
69
+ "/uploadFile",
70
+ ]);
71
+ const areSafeColumnNames = (data) => !data || Object.keys(data).every((k) => /^[A-Za-z0-9_]+$/.test(k));
72
+ exports.areSafeColumnNames = areSafeColumnNames;
73
+ const writableTables = (req) => {
74
+ var _a, _b;
75
+ const role = (0, exports.roleOf)(req);
76
+ const tables = new Set();
77
+ for (const view of ((_a = req.config) === null || _a === void 0 ? void 0 : _a.views) || []) {
78
+ if (!(0, exports.canWriteView)(view, role))
79
+ continue;
80
+ if (view.tableName)
81
+ tables.add(view.tableName);
82
+ for (const panel of view.recordViewPanels || []) {
83
+ if (panel.tableName)
84
+ tables.add(panel.tableName);
85
+ if ((_b = panel.createConfig) === null || _b === void 0 ? void 0 : _b.tableName)
86
+ tables.add(panel.createConfig.tableName);
87
+ }
88
+ }
89
+ return tables;
90
+ };
91
+ exports.writableTables = writableTables;
92
+ const looksLikeSqlBreakout = (frag) => typeof frag === "string" &&
93
+ /(;|--|\/\*|\bunion\b|\binto\s+(out|dump)file\b|\bsleep\s*\(|\bbenchmark\s*\(|\bload_file\s*\()/i.test(frag);
94
+ exports.looksLikeSqlBreakout = looksLikeSqlBreakout;
95
+ function sqlAnd(conditions) {
96
+ const existingConditions = [];
97
+ for (const condition of conditions) {
98
+ if (condition && condition.length > 0) {
99
+ existingConditions.push(condition);
100
+ }
101
+ }
102
+ if (existingConditions.length <= 1) {
103
+ return existingConditions[0] || null;
104
+ }
105
+ else {
106
+ let result = "";
107
+ for (let i = 0; i < existingConditions.length; i++) {
108
+ if (i > 0) {
109
+ result += " AND ";
110
+ }
111
+ result += "(" + existingConditions[i] + ")";
112
+ }
113
+ return result;
114
+ }
115
+ }
116
+ async function getFormattedValue(column, record) {
117
+ let value;
118
+ try {
119
+ record.options = column.options;
120
+ if (column.formatValueForExport) {
121
+ value = await column.formatValueForExport(record);
122
+ }
123
+ else if (column.formattedValue) {
124
+ value = evalExpression(column.formattedValue, record);
125
+ }
126
+ else if (column.formatValue) {
127
+ value = await column.formatValue(record);
128
+ }
129
+ else if (column.dataType == "tinyint") {
130
+ value = record[column.name] == 1 ? "Oui" : "Non";
131
+ }
132
+ else {
133
+ value = record[column.name];
134
+ }
135
+ return value === null || typeof value == "undefined" ? "" : value;
136
+ }
137
+ catch (err) {
138
+ return value || "";
139
+ }
140
+ }
141
+ function evalExpression(expression, data) {
142
+ let varString = "";
143
+ let statement = "";
144
+ try {
145
+ for (let key in data) {
146
+ statement =
147
+ "var " +
148
+ key +
149
+ " = " +
150
+ (typeof data[key] === "string"
151
+ ? "'" +
152
+ data[key]
153
+ .replace(/'/g, "\\'")
154
+ .replace(/\n/g, "")
155
+ .replace(/\r/g, "") +
156
+ "'"
157
+ : typeof data[key] === "object"
158
+ ? JSON.stringify(data[key])
159
+ : data[key]) +
160
+ ";";
161
+ varString += statement;
162
+ }
163
+ const result = eval(varString + expression);
164
+ return result;
165
+ }
166
+ catch (e) {
167
+ console.log("Problème d'évaluation de l'expression", statement);
168
+ console.error(e);
169
+ return "Error";
170
+ }
171
+ }
172
+ function formatValueForInsertion(value, dbType) {
173
+ if (value !== 0 && !value) {
174
+ return "NULL";
175
+ }
176
+ if (value instanceof Date) {
177
+ return value.toISOString();
178
+ }
179
+ if (typeof value === "string") {
180
+ if (dbType === "POSTGRESQL") {
181
+ return "'" + value.replace(/'/g, "''") + "'";
182
+ }
183
+ else {
184
+ return "'" + value.replace(/'/g, "\\'") + "'";
185
+ }
186
+ }
187
+ if (typeof value === "object") {
188
+ return "'" + JSON.stringify(value).replace(/'/g, "\\'") + "'";
189
+ }
190
+ if (typeof value === "boolean") {
191
+ return value ? "TRUE" : "FALSE";
192
+ }
193
+ if (typeof value === "number") {
194
+ return value;
195
+ }
196
+ return value;
197
+ }
37
198
  exports.default = (db, config) => {
38
199
  const app = express();
39
200
  app.use((req, res, next) => {
@@ -70,59 +231,16 @@ exports.default = (db, config) => {
70
231
  }
71
232
  next();
72
233
  });
73
- const ROOT_ROLE = "root";
74
- const roleOf = (req) => req.session && req.session.user && req.session.user.role;
75
- const canWriteView = (view, role) => role === ROOT_ROLE || !view || !view.roles || view.roles.includes(role);
76
- const viewFromRequest = (req) => {
77
- const idx = req.body && req.body.viewIndex !== undefined
78
- ? req.body.viewIndex
79
- : req.query && req.query.viewIndex !== undefined
80
- ? req.query.viewIndex
81
- : undefined;
82
- if (idx === undefined || !req.config || !req.config.views)
83
- return undefined;
84
- return req.config.views[idx];
85
- };
86
- const MUTATING_VIEW_ROUTES = new Set([
87
- "/updateRecord",
88
- "/createRecord",
89
- "/deleteRecord",
90
- "/deleteRecords",
91
- "/deleteRecordFromPanel",
92
- "/customAction",
93
- "/uploadFile",
94
- ]);
95
- const areSafeColumnNames = (data) => !data || Object.keys(data).every((k) => /^[A-Za-z0-9_]+$/.test(k));
96
- const writableTables = (req) => {
97
- var _a, _b;
98
- const role = roleOf(req);
99
- const tables = new Set();
100
- for (const view of ((_a = req.config) === null || _a === void 0 ? void 0 : _a.views) || []) {
101
- if (!canWriteView(view, role))
102
- continue;
103
- if (view.tableName)
104
- tables.add(view.tableName);
105
- for (const panel of view.recordViewPanels || []) {
106
- if (panel.tableName)
107
- tables.add(panel.tableName);
108
- if ((_b = panel.createConfig) === null || _b === void 0 ? void 0 : _b.tableName)
109
- tables.add(panel.createConfig.tableName);
110
- }
111
- }
112
- return tables;
113
- };
114
- const looksLikeSqlBreakout = (frag) => typeof frag === "string" &&
115
- /(;|--|\/\*|\bunion\b|\binto\s+(out|dump)file\b|\bsleep\s*\(|\bbenchmark\s*\(|\bload_file\s*\()/i.test(frag);
116
234
  app.use((req, res, next) => {
117
- const role = roleOf(req);
235
+ const role = (0, exports.roleOf)(req);
118
236
  if (req.body && Array.isArray(req.body.filters)) {
119
- if (req.body.filters.some(looksLikeSqlBreakout)) {
237
+ if (req.body.filters.some(exports.looksLikeSqlBreakout)) {
120
238
  return res.status(400).send({ error: "Invalid filter" });
121
239
  }
122
240
  }
123
- if (MUTATING_VIEW_ROUTES.has(req.path)) {
124
- const view = viewFromRequest(req);
125
- if (!view || !canWriteView(view, role)) {
241
+ if (exports.MUTATING_VIEW_ROUTES.has(req.path)) {
242
+ const view = (0, exports.viewFromRequest)(req);
243
+ if (!view || !(0, exports.canWriteView)(view, role)) {
126
244
  return res.sendStatus(403);
127
245
  }
128
246
  }
@@ -158,7 +276,10 @@ exports.default = (db, config) => {
158
276
  .map((x) => (0, dbManager_1.quoteIdentifier)(x, db.dbType))
159
277
  .join(".");
160
278
  }
161
- queryString += " LIKE '%" + params.query.replace(/'/g, "\\'") + "%'";
279
+ const searchEscaped = db.dbType === "POSTGRESQL"
280
+ ? params.query.replace(/'/g, "''")
281
+ : params.query.replace(/'/g, "\\'");
282
+ queryString += " LIKE '%" + searchEscaped + "%'";
162
283
  isNewCondition = false;
163
284
  }
164
285
  }
@@ -243,6 +364,7 @@ exports.default = (db, config) => {
243
364
  }
244
365
  catch (err) {
245
366
  console.error(err);
367
+ res.sendStatus(500);
246
368
  }
247
369
  });
248
370
  app.post("/adjacentRecordId", async function (req, res, next) {
@@ -255,27 +377,6 @@ exports.default = (db, config) => {
255
377
  recordId: recordId,
256
378
  });
257
379
  });
258
- const sqlAnd = (conditions) => {
259
- const existingConditions = [];
260
- for (const condition of conditions) {
261
- if (condition && condition.length > 0) {
262
- existingConditions.push(condition);
263
- }
264
- }
265
- if (existingConditions.length <= 1) {
266
- return existingConditions[0] || null;
267
- }
268
- else {
269
- let result = "";
270
- for (let i = 0; i < existingConditions.length; i++) {
271
- if (i > 0) {
272
- result += " AND ";
273
- }
274
- result += "(" + existingConditions[i] + ")";
275
- }
276
- return result;
277
- }
278
- };
279
380
  app.post("/foreignSelectRecords", async (req, res, next) => {
280
381
  var _a, _b, _c, _d, _e;
281
382
  const view = req.config.views[req.body.viewIndex];
@@ -380,88 +481,6 @@ exports.default = (db, config) => {
380
481
  });
381
482
  });
382
483
  const formattedValueToFunction = {};
383
- async function getFormattedValue(column, record) {
384
- let value;
385
- try {
386
- record.options = column.options;
387
- if (column.formatValueForExport) {
388
- value = await column.formatValueForExport(record);
389
- }
390
- else if (column.formattedValue) {
391
- value = evalExpression(column.formattedValue, record);
392
- }
393
- else if (column.formatValue) {
394
- value = await column.formatValue(record);
395
- }
396
- else if (column.dataType == "tinyint") {
397
- value = record[column.name] == 1 ? "Oui" : "Non";
398
- }
399
- else {
400
- value = record[column.name];
401
- }
402
- return value === null || typeof value == "undefined" ? "" : value;
403
- }
404
- catch (err) {
405
- return value || "";
406
- }
407
- }
408
- function evalExpression(expression, data) {
409
- let varString = "";
410
- let statement = "";
411
- try {
412
- for (let key in data) {
413
- statement =
414
- "var " +
415
- key +
416
- " = " +
417
- (typeof data[key] === "string"
418
- ? "'" +
419
- data[key]
420
- .replace(/'/g, "\\'")
421
- .replace(/\n/g, "")
422
- .replace(/\r/g, "") +
423
- "'"
424
- : typeof data[key] === "object"
425
- ? JSON.stringify(data[key])
426
- : data[key]) +
427
- ";";
428
- varString += statement;
429
- }
430
- const result = eval(varString + expression);
431
- return result;
432
- }
433
- catch (e) {
434
- console.log("Problème d'évaluation de l'expression", statement);
435
- console.error(e);
436
- return "Error";
437
- }
438
- }
439
- function formatValueForInsertion(value, dbType) {
440
- if (value !== 0 && !value) {
441
- return "NULL";
442
- }
443
- if (value instanceof Date) {
444
- return value.toISOString();
445
- }
446
- if (typeof value === "string") {
447
- if (dbType === "POSTGRESQL") {
448
- return "'" + value.replace(/'/g, "''") + "'";
449
- }
450
- else {
451
- return "'" + value.replace(/'/g, "\\'") + "'";
452
- }
453
- }
454
- if (typeof value === "object") {
455
- return "'" + JSON.stringify(value).replace(/'/g, "\\'") + "'";
456
- }
457
- if (typeof value === "boolean") {
458
- return value ? "TRUE" : "FALSE";
459
- }
460
- if (typeof value === "number") {
461
- return value;
462
- }
463
- return value;
464
- }
465
484
  app.post("/recordsExport", async function (req, res) {
466
485
  const view = req.config.views[req.body.viewIndex];
467
486
  const orderBy = req.body.orderBy || view.orderBy;
@@ -473,7 +492,14 @@ exports.default = (db, config) => {
473
492
  table: view.tableName,
474
493
  page: 1,
475
494
  allRecords: true,
476
- filters: sqlAnd([view.filters, subview === null || subview === void 0 ? void 0 : subview.filters, req.body.filters]),
495
+ filters: sqlAnd([
496
+ view.filters,
497
+ subview === null || subview === void 0 ? void 0 : subview.filters,
498
+ req.body.filters,
499
+ view.filtersFromUser
500
+ ? view.filtersFromUser(req.session.user.userId, req.session.user.role)
501
+ : null,
502
+ ]),
477
503
  having: subview === null || subview === void 0 ? void 0 : subview.having,
478
504
  customQuery: view.customQuery,
479
505
  join: view.join,
@@ -525,6 +551,9 @@ exports.default = (db, config) => {
525
551
  return res.status(500).send(err);
526
552
  }
527
553
  const view = req.config.views[fields.viewIndex[0]];
554
+ if (!view || !(0, exports.canWriteView)(view, (0, exports.roleOf)(req))) {
555
+ return res.sendStatus(403);
556
+ }
528
557
  if (typeof view.parseImportFile !== "function") {
529
558
  return res
530
559
  .status(500)
@@ -608,7 +637,15 @@ exports.default = (db, config) => {
608
637
  return res.status(500).send(err);
609
638
  }
610
639
  const view = req.config.views[fields.viewIndex[0]];
611
- const panel = view.recordViewPanels[fields.panelIndex[0]];
640
+ const panel = view
641
+ ? view.recordViewPanels[fields.panelIndex[0]]
642
+ : undefined;
643
+ if (!view ||
644
+ !(0, exports.canWriteView)(view, (0, exports.roleOf)(req)) ||
645
+ !panel ||
646
+ !(0, exports.canWriteView)(panel, (0, exports.roleOf)(req))) {
647
+ return res.sendStatus(403);
648
+ }
612
649
  if (typeof panel.parseImportFile !== "function") {
613
650
  return res
614
651
  .status(500)
@@ -706,7 +743,7 @@ exports.default = (db, config) => {
706
743
  });
707
744
  app.post("/updateRecord", async function (req, res, next) {
708
745
  const view = req.config.views[req.body.viewIndex];
709
- if (!areSafeColumnNames(req.body.data)) {
746
+ if (!(0, exports.areSafeColumnNames)(req.body.data)) {
710
747
  return res.status(400).send({ error: "Invalid column name" });
711
748
  }
712
749
  for (let key in req.body.data) {
@@ -858,7 +895,8 @@ exports.default = (db, config) => {
858
895
  });
859
896
  app.post("/deleteRecords", async function (req, res, next) {
860
897
  const view = req.config.views[req.body.viewIndex];
861
- const { err } = await db.query(`DELETE FROM ?? WHERE ?? IN(${req.body.primaryValues.map((x) => "'" + x + "'").join(",")})`, [view.tableName, view.primaryId]);
898
+ const placeholders = req.body.primaryValues.map(() => "?").join(", ");
899
+ const { err } = await db.query(`DELETE FROM ?? WHERE ?? IN(${placeholders})`, [view.tableName, view.primaryId, ...req.body.primaryValues]);
862
900
  if (err) {
863
901
  return res.status(500).send(err);
864
902
  }
@@ -894,7 +932,7 @@ exports.default = (db, config) => {
894
932
  });
895
933
  app.post("/duplicateRecord", async function (req, res, next) {
896
934
  var _a;
897
- const allowedTables = writableTables(req);
935
+ const allowedTables = (0, exports.writableTables)(req);
898
936
  const requestedTables = [
899
937
  req.body.table,
900
938
  ...(((_a = req.body.duplicateConfig) === null || _a === void 0 ? void 0 : _a.foreignTables) || []).map((t) => t.table),
@@ -946,9 +984,8 @@ exports.default = (db, config) => {
946
984
  app.post("/createRecord", async function (req, res, next) {
947
985
  var _a, _b, _c;
948
986
  const view = req.config.views[req.body.viewIndex];
949
- const panel = req.body.panelIndex
950
- ? view.recordViewPanels[req.body.panelIndex]
951
- : null;
987
+ const hasPanel = req.body.panelIndex !== undefined && req.body.panelIndex !== null;
988
+ const panel = hasPanel ? view.recordViewPanels[req.body.panelIndex] : null;
952
989
  try {
953
990
  for (let key in req.body.data) {
954
991
  if (req.body.data[key] == "NULL" ||
@@ -971,7 +1008,7 @@ exports.default = (db, config) => {
971
1008
  delete createPayload[key];
972
1009
  }
973
1010
  }
974
- if (!areSafeColumnNames(createPayload)) {
1011
+ if (!(0, exports.areSafeColumnNames)(createPayload)) {
975
1012
  return res.status(400).send({ error: "Invalid column name" });
976
1013
  }
977
1014
  const queryExpression = Object.keys(createPayload)
@@ -1001,10 +1038,15 @@ exports.default = (db, config) => {
1001
1038
  catch (e) { }
1002
1039
  }
1003
1040
  if (panel ? panel.createCallbackFunction : view.createCallbackFunction) {
1004
- await (panel ? panel.createCallbackFunction : view.createCallbackFunction)({
1005
- id: r.insertId,
1006
- data: req.body.data,
1007
- });
1041
+ try {
1042
+ await (panel ? panel.createCallbackFunction : view.createCallbackFunction)({
1043
+ id: r.insertId,
1044
+ data: req.body.data,
1045
+ });
1046
+ }
1047
+ catch (callbackErr) {
1048
+ console.error(callbackErr);
1049
+ }
1008
1050
  }
1009
1051
  res.send({
1010
1052
  id: r.insertId,
@@ -1018,26 +1060,6 @@ exports.default = (db, config) => {
1018
1060
  });
1019
1061
  }
1020
1062
  });
1021
- function collectAllowedQueries(node, acc = new Set()) {
1022
- if (!node || typeof node !== "object")
1023
- return acc;
1024
- if (Array.isArray(node)) {
1025
- for (const item of node)
1026
- collectAllowedQueries(item, acc);
1027
- return acc;
1028
- }
1029
- for (const key of Object.keys(node)) {
1030
- const value = node[key];
1031
- if (typeof value === "string" &&
1032
- (key === "query" || /Query$/.test(key))) {
1033
- acc.add(value.trim());
1034
- }
1035
- else if (value && typeof value === "object") {
1036
- collectAllowedQueries(value, acc);
1037
- }
1038
- }
1039
- return acc;
1040
- }
1041
1063
  const CONFIG_USES_CUSTOM_QUERY = collectAllowedQueries(config.views).size > 0;
1042
1064
  if (!CONFIG_USES_CUSTOM_QUERY) {
1043
1065
  app.get("/customQuery", (req, res) => res.sendStatus(404));
@@ -1076,7 +1098,7 @@ exports.default = (db, config) => {
1076
1098
  function uploadFile(column, fileParamName, req, res) {
1077
1099
  if (column.s3Config || column.gcpConfig) {
1078
1100
  const form = new multiparty.Form();
1079
- form.parse(req, function (err, fields, files) {
1101
+ form.parse(req, async function (err, fields, files) {
1080
1102
  if (err) {
1081
1103
  return res.status(500).send(err);
1082
1104
  }
@@ -1085,8 +1107,14 @@ exports.default = (db, config) => {
1085
1107
  const extension = originalFilename.match(/(?:\.([^.]+))?$/)[1];
1086
1108
  let aspectRatio = null;
1087
1109
  if (["png", "jpeg", "jpg", "gif"].includes(extension)) {
1088
- const dimensions = (0, image_size_1.default)(files[fileParamName][0].path);
1089
- aspectRatio = dimensions.height / dimensions.width;
1110
+ try {
1111
+ const dimensions = await (0, imageDimensions_1.getImageDimensions)(files[fileParamName][0].path);
1112
+ if (dimensions) {
1113
+ aspectRatio = dimensions.height / dimensions.width;
1114
+ }
1115
+ }
1116
+ catch (_a) {
1117
+ }
1090
1118
  }
1091
1119
  const originalFilenameWithoutExtension = originalFilename.replace("." + extension, "");
1092
1120
  if (column.gcpConfig) {
@@ -1237,6 +1265,10 @@ exports.default = (db, config) => {
1237
1265
  }
1238
1266
  }
1239
1267
  app.post("/uploadFileFromBetterRichTextInput", async function (req, res, next) {
1268
+ const role = (0, exports.roleOf)(req);
1269
+ if (!req.config.views.some((view) => (0, exports.canWriteView)(view, role))) {
1270
+ return res.sendStatus(403);
1271
+ }
1240
1272
  const fileUploadProperties = JSON.parse(req.headers["file-upload-properties"]);
1241
1273
  uploadFile(fileUploadProperties, "upload", req, res);
1242
1274
  });
@@ -1279,6 +1311,25 @@ exports.default = (db, config) => {
1279
1311
  });
1280
1312
  return app;
1281
1313
  };
1314
+ function collectAllowedQueries(node, acc = new Set()) {
1315
+ if (!node || typeof node !== "object")
1316
+ return acc;
1317
+ if (Array.isArray(node)) {
1318
+ for (const item of node)
1319
+ collectAllowedQueries(item, acc);
1320
+ return acc;
1321
+ }
1322
+ for (const key of Object.keys(node)) {
1323
+ const value = node[key];
1324
+ if (typeof value === "string" && (key === "query" || /Query$/.test(key))) {
1325
+ acc.add(value.trim());
1326
+ }
1327
+ else if (value && typeof value === "object") {
1328
+ collectAllowedQueries(value, acc);
1329
+ }
1330
+ }
1331
+ return acc;
1332
+ }
1282
1333
  async function enrichConfig(config, db) {
1283
1334
  var _a, _b;
1284
1335
  const { rows: info, err } = await db.query("SELECT * FROM information_schema.columns WHERE table_schema = ?", [((_a = config.mysql) === null || _a === void 0 ? void 0 : _a.database) || ((_b = config.postgresql) === null || _b === void 0 ? void 0 : _b.database)]);