@galadrim/galadmin 0.1.225 → 0.1.227

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/src/api.ts CHANGED
@@ -6,1024 +6,1338 @@ import * as crypto from "crypto";
6
6
  import * as mime from "mime";
7
7
 
8
8
  import { S3, Endpoint } from "aws-sdk";
9
- import sizeOf from "image-size"
9
+ import { Storage } from "@google-cloud/storage";
10
+ import sizeOf from "image-size";
10
11
  import * as serializeJs from "serialize-javascript";
11
- import * as SftpClient from 'ssh2-sftp-client';
12
+ import * as SftpClient from "ssh2-sftp-client";
12
13
 
13
14
  import * as rollup from "rollup";
14
- import babel from '@rollup/plugin-babel'
15
- import resolve from '@rollup/plugin-node-resolve'
15
+ import babel from "@rollup/plugin-babel";
16
+ import resolve from "@rollup/plugin-node-resolve";
16
17
  import axios from "axios";
17
- import { v4 as uuidv4 } from 'uuid';
18
+ import { v4 as uuidv4 } from "uuid";
18
19
 
19
20
  function generateUniqueIdForObject(obj) {
20
- const str = JSON.stringify(obj);
21
- const hash = crypto.createHash('sha256').update(str).digest('hex');
22
- return hash;
21
+ const str = JSON.stringify(obj);
22
+ const hash = crypto.createHash("sha256").update(str).digest("hex");
23
+ return hash;
23
24
  }
24
25
 
25
26
  export default (db, config, user) => {
26
- const app = express();
27
+ const app = express();
27
28
 
28
- app.use(async (req, res, next) => {
29
- if (!config.enriched) {
30
- await enrichConfig(config, db);
31
- }
32
- next();
33
- })
29
+ app.use(async (req, res, next) => {
30
+ if (!config.enriched) {
31
+ await enrichConfig(config, db);
32
+ }
33
+ next();
34
+ });
34
35
 
35
- async function getRecords(params, computeCount = false) {
36
- let queryString = "";
37
- if (params.filters) {
38
- queryString = " WHERE (" + params.filters + ")";
39
- }
40
- if (params.query) {
41
- const columns = params.columns
42
- let isNewCondition = true
43
- for (let i in columns) {
44
- if (columns[i].searchable !== false && (columns[i].searchableName || columns[i].name)) {
45
- if (queryString.length == 0) {
46
- queryString += " WHERE ("
47
- } else if (isNewCondition) {
48
- queryString += " AND ("
49
- } else {
50
- queryString += " OR "
51
- }
52
- if (columns[i].searchableName) {
53
- // on ne doit pas ajouter de backticks sur le searchableName car celui-ci peut être une fonction
54
- // exemple : searchableName = "CONCAT_WS(' ', firstname, lastname)"
55
- queryString += columns[i].searchableName
56
- } else if (columns[i].name) {
57
- queryString += columns[i].name.split(".").map(x => db.dbType === "POSTGRESQL" ? x : ("`" + x + "`")).join(".")
58
- }
59
- queryString += " LIKE '%" + params.query.replace(/'/g, "\\'") + "%'"
60
- isNewCondition = false;
61
- }
62
- }
63
- queryString += ")"
36
+ async function getRecords(params, computeCount = false) {
37
+ let queryString = "";
38
+ if (params.filters) {
39
+ queryString = " WHERE (" + params.filters + ")";
40
+ }
41
+ if (params.query) {
42
+ const columns = params.columns;
43
+ let isNewCondition = true;
44
+ for (let i in columns) {
45
+ if (
46
+ columns[i].searchable !== false &&
47
+ (columns[i].searchableName || columns[i].name)
48
+ ) {
49
+ if (queryString.length == 0) {
50
+ queryString += " WHERE (";
51
+ } else if (isNewCondition) {
52
+ queryString += " AND (";
53
+ } else {
54
+ queryString += " OR ";
55
+ }
56
+ if (columns[i].searchableName) {
57
+ // on ne doit pas ajouter de backticks sur le searchableName car celui-ci peut être une fonction
58
+ // exemple : searchableName = "CONCAT_WS(' ', firstname, lastname)"
59
+ queryString += columns[i].searchableName;
60
+ } else if (columns[i].name) {
61
+ queryString += columns[i].name
62
+ .split(".")
63
+ .map((x) => (db.dbType === "POSTGRESQL" ? x : "`" + x + "`"))
64
+ .join(".");
65
+ }
66
+ queryString += " LIKE '%" + params.query.replace(/'/g, "\\'") + "%'";
67
+ isNewCondition = false;
64
68
  }
65
- const limit = params.allRecords ? 999999 : parseInt(params.limit)
66
- const query = params.customQuery || `SELECT
69
+ }
70
+ queryString += ")";
71
+ }
72
+ const limit = params.allRecords ? 999999 : parseInt(params.limit);
73
+ const query =
74
+ params.customQuery ||
75
+ `SELECT
67
76
  ${params.selectExpression || "*"}
68
77
  FROM ${params.table}
69
78
  ${params.join || ""}
70
79
  ${queryString}
71
80
  ${params.groupBy ? "GROUP BY " + params.groupBy : ""}
72
81
  ${params.having ? " HAVING " + params.having : ""}
73
- ${params.orderBy || params.primaryId ? " ORDER BY " + (params.orderBy || `${params.table}.${params.primaryId}`) : ""}`
74
- const { err, rows } = await db.query(`${query} LIMIT ${limit} OFFSET ${Math.max((params.page - 1) * limit, 0)}`)
75
- if (err) {
76
- console.error(err)
77
- return {
78
- error: err
79
- }
80
- }
81
- let count = undefined;
82
- if (computeCount) {
83
- const { err: countErr, rows: countRows } = await db.query(`SELECT COUNT(*) AS count FROM (${query}) s`)
84
- if (countErr) {
85
- console.error(countErr)
86
- }
87
- count = countRows?.[0]?.count || 0
88
- }
89
- return {
90
- rows,
91
- queryString,
92
- input: {
93
- query,
94
- params
95
- },
96
- count
97
- }
82
+ ${params.orderBy || params.primaryId ? " ORDER BY " + (params.orderBy || `${params.table}.${params.primaryId}`) : ""}`;
83
+ const { err, rows } = await db.query(
84
+ `${query} LIMIT ${limit} OFFSET ${Math.max((params.page - 1) * limit, 0)}`
85
+ );
86
+ if (err) {
87
+ console.error(err);
88
+ return {
89
+ error: err,
90
+ };
98
91
  }
99
-
100
- async function findRecordByPosition(params) {
101
- return await getRecords(Object.assign(params, {
102
- page: parseInt(params.position) + parseInt(params.offset),
103
- limit: 1
104
- }));
92
+ let count = undefined;
93
+ if (computeCount) {
94
+ const { err: countErr, rows: countRows } = await db.query(
95
+ `SELECT COUNT(*) AS count FROM (${query}) s`
96
+ );
97
+ if (countErr) {
98
+ const { rows: allRows } = await db.query(query);
99
+ count = allRows.length;
100
+ } else {
101
+ count = countRows?.[0]?.count || 0;
102
+ }
105
103
  }
106
104
 
107
- app.get('/config', async (req, res) => {
108
- try {
109
- const { mysql, auth, sessionSecret, ...safeConfig } = config;
110
- res.send({
111
- config: serializeJs(safeConfig)
112
- })
113
- } catch (e) {
114
- console.error(e);
115
- res.sendStatus(500)
116
- }
117
- })
105
+ return {
106
+ rows,
107
+ queryString,
108
+ input: {
109
+ query,
110
+ params,
111
+ },
112
+ count,
113
+ };
114
+ }
118
115
 
119
- app.get("/customComponent", async (req, res) => {
120
- try {
121
- const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../src/front/package.json'), 'utf-8'));
122
- const external = Object.keys(pkg.dependencies || {});
123
- const bundle = await rollup.rollup({
124
- input: path.join(__dirname, "../../", req.query.componentPath as string),
125
- plugins: [
126
- resolve(),
127
- babel({
128
- configFile: path.join(__dirname, "../babel.config"),
129
- babelHelpers: "bundled"
130
- })
131
- ],
132
- external
133
- });
134
- const { output } = await bundle.generate({ format: 'cjs', exports: 'auto' });
135
- res.send(output[0].code)
136
- } catch (err) {
137
- console.error(err)
138
- }
139
- })
116
+ async function findRecordByPosition(params) {
117
+ return await getRecords(
118
+ Object.assign(params, {
119
+ page: parseInt(params.position) + parseInt(params.offset),
120
+ limit: 1,
121
+ })
122
+ );
123
+ }
140
124
 
141
- app.post('/adjacentRecordId', async function (req, res, next) {
142
- const { rows, error } = await findRecordByPosition(req.body)
143
- if (error) {
144
- return res.status(500).send(error)
145
- }
146
- const recordId = rows[0] ? rows[0][req.body.primaryId] : null;
147
- res.send({
148
- recordId: recordId
149
- });
150
- });
125
+ app.get("/config", async (req, res) => {
126
+ try {
127
+ const { mysql, auth, sessionSecret, ...safeConfig } = config;
128
+ res.send({
129
+ config: serializeJs(safeConfig),
130
+ });
131
+ } catch (e) {
132
+ console.error(e);
133
+ res.sendStatus(500);
134
+ }
135
+ });
151
136
 
152
- const sqlAnd = (conditions) => {
153
- const existingConditions = [];
154
- for (const condition of conditions) {
155
- if (condition && condition.length > 0) {
156
- existingConditions.push(condition)
157
- }
158
- }
159
- if (existingConditions.length <= 1) {
160
- return existingConditions[0] || null
161
- } else {
162
- let result = "";
163
- for (let i = 0; i < existingConditions.length; i++) {
164
- if (i > 0) {
165
- result += " AND "
166
- }
167
- result += "(" + existingConditions[i] + ")"
168
- }
169
- return result
170
- }
137
+ app.get("/customComponent", async (req, res) => {
138
+ try {
139
+ const pkg = JSON.parse(
140
+ fs.readFileSync(
141
+ path.join(__dirname, "../src/front/package.json"),
142
+ "utf-8"
143
+ )
144
+ );
145
+ const external = Object.keys(pkg.dependencies || {});
146
+ const bundle = await rollup.rollup({
147
+ input: path.join(
148
+ __dirname,
149
+ "../../",
150
+ req.query.componentPath as string
151
+ ),
152
+ plugins: [
153
+ resolve(),
154
+ babel({
155
+ configFile: path.join(__dirname, "../babel.config"),
156
+ babelHelpers: "bundled",
157
+ }),
158
+ ],
159
+ external,
160
+ });
161
+ const { output } = await bundle.generate({
162
+ format: "cjs",
163
+ exports: "auto",
164
+ });
165
+ res.send(output[0].code);
166
+ } catch (err) {
167
+ console.error(err);
171
168
  }
169
+ });
172
170
 
173
- app.post('/foreignSelectRecords', async (req, res, next) => {
174
- const view = config.views[req.body.viewIndex];
175
- const column = [
176
- ...view.columns,
177
- ...(view.recordViewPanels?.map(x => x?.createConfig?.columns)?.filter(x => x)?.flat() || [])
178
- ].find(x => x.__id__ === req.body.columnId)
179
- const queryEscaped = db.dbType === "POSTGRESQL" ? req.body.query?.replace(/'/g, "''") : req.body.query?.replace(/'/g, "\\'")
180
- const { rows: records, error } = await getRecords({
181
- table: column.table,
182
- page: 1,
183
- filters: sqlAnd([
184
- column.filters,
185
- req.body.query ? `${column.searchableName} LIKE '${column.wholeSearch ? '%' : ''}${queryEscaped}%'` : null,
186
- column.filtersFromUser ? column.filtersFromUser(user.userId, user.role) : null
187
- ]),
188
- join: column.join,
189
- selectExpression: column.selectExpression,
190
- orderBy: column.orderBy,
191
- limit: column.limit || 1000
192
- })
193
- if (error) {
194
- return res.status(500).send(error)
195
- }
196
- const options = await Promise.all(records.map(async (record) => {
197
- let label;
198
- if (column.formatForeignValue) {
199
- label = await column.formatForeignValue(record);
200
- } else if (column.formatValue) {
201
- label = await column.formatValue(record);
202
- } else if (column.formattedValue) {
203
- label = evalExpression(column.formattedValue, record)
204
- } else {
205
- label = record[column.name]
206
- }
207
- const value = record[column.foreignName] || record[column.name]
208
- return ({
209
- value,
210
- label
211
- })
212
- }))
213
- res.send(options)
214
- })
171
+ app.post("/adjacentRecordId", async function (req, res, next) {
172
+ const { rows, error } = await findRecordByPosition(req.body);
173
+ if (error) {
174
+ return res.status(500).send(error);
175
+ }
176
+ const recordId = rows[0] ? rows[0][req.body.primaryId] : null;
177
+ res.send({
178
+ recordId: recordId,
179
+ });
180
+ });
215
181
 
216
- app.post('/records', async function (req, res, next) {
217
- const view = config.views[req.body.viewIndex];
218
- const isDesc = req.body.isDesc === null ? view.isDesc : req.body.isDesc
219
- const subview = view.subviews && req.body.subviewIndex !== undefined ? view.subviews[req.body.subviewIndex] : null
220
- const orderBy = req.body.orderBy || subview?.orderBy || view.orderBy
221
- const { rows: records, input, error, count } = await getRecords({
222
- table: view.tableName,
223
- page: req.body.page,
224
- filters: sqlAnd([
225
- view.filters,
226
- subview?.filters,
227
- ...req.body.filters,
228
- view.filtersFromUser ? view.filtersFromUser(user.userId, user.role) : null
229
- ]),
230
- having: subview?.having,
231
- customQuery: view.customQuery,
232
- join: view.join,
233
- selectExpression: view.selectExpression,
234
- orderBy: [orderBy ? `${orderBy} ${isDesc ? "DESC" : "ASC"}` : null, view.primaryId].filter(x => x).join(", "),
235
- primaryId: view.primaryId,
236
- query: req.body.query,
237
- groupBy: view.groupBy,
238
- columns: view.columns,
239
- limit: view.limit || 10,
240
- }, true)
241
- if (error) {
242
- return res.status(500).send(error)
182
+ const sqlAnd = (conditions) => {
183
+ const existingConditions = [];
184
+ for (const condition of conditions) {
185
+ if (condition && condition.length > 0) {
186
+ existingConditions.push(condition);
187
+ }
188
+ }
189
+ if (existingConditions.length <= 1) {
190
+ return existingConditions[0] || null;
191
+ } else {
192
+ let result = "";
193
+ for (let i = 0; i < existingConditions.length; i++) {
194
+ if (i > 0) {
195
+ result += " AND ";
243
196
  }
197
+ result += "(" + existingConditions[i] + ")";
198
+ }
199
+ return result;
200
+ }
201
+ };
244
202
 
245
- for (const column of view.columns) {
246
- if (column.formatValue) {
247
- for (const record of records) {
248
- record[`__formatValue-${column.__id__}__`] = await column.formatValue(record)
249
- }
250
- }
203
+ app.post("/foreignSelectRecords", async (req, res, next) => {
204
+ const view = config.views[req.body.viewIndex];
205
+ const column = [
206
+ ...view.columns,
207
+ ...(view.recordViewPanels
208
+ ?.map((x) => x?.createConfig?.columns)
209
+ ?.filter((x) => x)
210
+ ?.flat() || []),
211
+ ].find((x) => x.__id__ === req.body.columnId);
212
+ const queryEscaped =
213
+ db.dbType === "POSTGRESQL"
214
+ ? req.body.query?.replace(/'/g, "''")
215
+ : req.body.query?.replace(/'/g, "\\'");
216
+ const { rows: records, error } = await getRecords({
217
+ table: column.table,
218
+ page: 1,
219
+ filters: sqlAnd([
220
+ column.filters,
221
+ req.body.query
222
+ ? `${column.searchableName} LIKE '${column.wholeSearch ? "%" : ""}${queryEscaped}%'`
223
+ : null,
224
+ column.filtersFromUser
225
+ ? column.filtersFromUser(user.userId, user.role)
226
+ : null,
227
+ ]),
228
+ join: column.join,
229
+ selectExpression: column.selectExpression,
230
+ orderBy: column.orderBy,
231
+ limit: column.limit || 1000,
232
+ });
233
+ if (error) {
234
+ return res.status(500).send(error);
235
+ }
236
+ const options = await Promise.all(
237
+ records.map(async (record) => {
238
+ let label;
239
+ if (column.formatForeignValue) {
240
+ label = await column.formatForeignValue(record);
241
+ } else if (column.formatValue) {
242
+ label = await column.formatValue(record);
243
+ } else if (column.formattedValue) {
244
+ label = evalExpression(column.formattedValue, record);
245
+ } else {
246
+ label = record[column.name];
251
247
  }
252
- res.send({
253
- records,
254
- input,
255
- count
256
- })
257
- })
258
-
259
- const formattedValueToFunction = {}
260
-
261
- async function getFormattedValue(column, record) {
262
- let value;
263
- try {
248
+ const value = record[column.foreignName] || record[column.name];
249
+ return {
250
+ value,
251
+ label,
252
+ };
253
+ })
254
+ );
255
+ res.send(options);
256
+ });
264
257
 
265
- record.options = column.options
266
- if (column.formatValueForExport) {
267
- value = await column.formatValueForExport(record)
268
- } else if (column.formattedValue) {
269
- value = evalExpression(column.formattedValue, record)
270
- } else if (column.formatValue) {
271
- value = await column.formatValue(record)
272
- } else if (column.dataType == "tinyint") {
273
- value = record[column.name] == 1 ? "Oui" : "Non"
274
- } else {
275
- value = record[column.name]
276
- }
277
- return value === null || typeof value == "undefined" ? "" : value
278
- } catch (err) {
279
- return value || ""
280
- }
258
+ app.post("/records", async function (req, res, next) {
259
+ const view = config.views[req.body.viewIndex];
260
+ const isDesc = req.body.isDesc === null ? view.isDesc : req.body.isDesc;
261
+ const subview =
262
+ view.subviews && req.body.subviewIndex !== undefined
263
+ ? view.subviews[req.body.subviewIndex]
264
+ : null;
265
+ const orderBy = req.body.orderBy || subview?.orderBy || view.orderBy;
266
+ const {
267
+ rows: records,
268
+ input,
269
+ error,
270
+ count,
271
+ } = await getRecords(
272
+ {
273
+ table: view.tableName,
274
+ page: req.body.page,
275
+ filters: sqlAnd([
276
+ view.filters,
277
+ subview?.filters,
278
+ ...req.body.filters,
279
+ view.filtersFromUser
280
+ ? view.filtersFromUser(user.userId, user.role)
281
+ : null,
282
+ ]),
283
+ having: subview?.having,
284
+ customQuery: view.customQuery,
285
+ join: view.join,
286
+ selectExpression: view.selectExpression,
287
+ orderBy: [
288
+ orderBy ? `${orderBy} ${isDesc ? "DESC" : "ASC"}` : null,
289
+ view.primaryId,
290
+ ]
291
+ .filter((x) => x)
292
+ .join(", "),
293
+ primaryId: view.primaryId,
294
+ query: req.body.query,
295
+ groupBy: view.groupBy,
296
+ columns: view.columns,
297
+ limit: view.limit || 10,
298
+ },
299
+ true
300
+ );
301
+ if (error) {
302
+ return res.status(500).send(error);
281
303
  }
282
304
 
283
- function evalExpression(expression, data) {
284
- let varString = '';
285
- let statement = '';
286
-
287
- try {
288
- for (let key in data) {
289
- statement = "var " + key + " = " + (typeof data[key] === "string" ? "'" + data[key].replace(/'/g, "\\'").replace(/\n/g, "").replace(/\r/g, "") + "'" : (typeof data[key] === "object" ? JSON.stringify(data[key]) : data[key])) + ";"
290
- varString += statement
291
- }
292
- const result = eval(varString + expression);
293
- return result;
294
- } catch (e) {
295
- console.log("Problème d'évaluation de l'expression", statement)
296
- console.error(e);
297
- return "Error"
305
+ for (const column of view.columns) {
306
+ if (column.formatValue) {
307
+ for (const record of records) {
308
+ record[`__formatValue-${column.__id__}__`] =
309
+ await column.formatValue(record);
298
310
  }
311
+ }
299
312
  }
313
+ res.send({
314
+ records,
315
+ input,
316
+ count,
317
+ });
318
+ });
300
319
 
301
- function formatValueForInsertion(value: any, dbType: string) {
302
- if (value !== 0 && !value) {
303
- return "NULL"
304
- }
305
- if (value instanceof Date) {
306
- return value.toISOString()
307
- }
308
- if (typeof value === "string") {
309
- if (dbType === "POSTGRESQL") {
310
- return "'" + value.replace(/'/g, "''") + "'";
311
- } else {
312
- return "'" + value.replace(/'/g, "\\'") + "'"
313
- }
314
- }
315
- if (typeof value === "object") {
316
- return "'" + JSON.stringify(value).replace(/'/g, "\\'") + "'"
317
- }
318
- if (typeof value === "boolean") {
319
- return value ? "TRUE" : "FALSE"
320
- }
321
- if (typeof value === "number") {
322
- return value
323
- }
320
+ const formattedValueToFunction = {};
324
321
 
325
- return value
322
+ async function getFormattedValue(column, record) {
323
+ let value;
324
+ try {
325
+ record.options = column.options;
326
+ if (column.formatValueForExport) {
327
+ value = await column.formatValueForExport(record);
328
+ } else if (column.formattedValue) {
329
+ value = evalExpression(column.formattedValue, record);
330
+ } else if (column.formatValue) {
331
+ value = await column.formatValue(record);
332
+ } else if (column.dataType == "tinyint") {
333
+ value = record[column.name] == 1 ? "Oui" : "Non";
334
+ } else {
335
+ value = record[column.name];
336
+ }
337
+ return value === null || typeof value == "undefined" ? "" : value;
338
+ } catch (err) {
339
+ return value || "";
326
340
  }
341
+ }
327
342
 
328
- app.post('/recordsExport', async function (req, res) {
329
- const view = config.views[req.body.viewIndex];
330
- const orderBy = req.body.orderBy || view.orderBy;
331
- const isDesc = req.body.isDesc === null ? view.isDesc : req.body.isDesc;
332
- const subview = view.subviews && req.body.subviewIndex !== undefined ? view.subviews[req.body.subviewIndex] : null
333
- const { rows: records, error } = await getRecords({
334
- table: view.tableName,
335
- page: 1,
336
- allRecords: true,
337
- filters: sqlAnd([view.filters, subview?.filters, req.body.filters]),
338
- having: subview?.having,
339
- customQuery: view.customQuery,
340
- join: view.join,
341
- selectExpression: view.selectExpression,
342
- orderBy: orderBy ? `${orderBy} ${isDesc ? "DESC" : "ASC"}` : null,
343
- primaryId: view.primaryId,
344
- query: req.body.query,
345
- groupBy: view.groupBy,
346
- columns: view.columns,
347
- limit: view.limit || 10,
348
- });
349
- if (error) {
350
- return res.status(500).send(error)
351
- }
352
- let output = "";
353
- const columns = view.columns
354
- for (let j = 0; j < columns.length; j++) {
355
- if (!columns[j].hideInTable || columns[j].includeInExport) {
356
- if (j > 0) output += ";"
357
- output += columns[j].label;
358
- }
359
- }
360
- for (let i in records) {
361
- const el = records[i];
362
- output += "\r\n"
363
- for (let j = 0; j < columns.length; j++) {
364
- if (!columns[j].hideInTable || columns[j].includeInExport) {
365
- if (j > 0) output += ";"
366
- if (columns[j].dataType == 'textarea') {
367
- output += '"'
368
- }
369
- output += await getFormattedValue(columns[j], el)
370
- if (columns[j].dataType == 'textarea') {
371
- output += '"'
372
- }
373
- }
374
- }
375
- }
376
- res.send({
377
- content: output
378
- })
379
- })
343
+ function evalExpression(expression, data) {
344
+ let varString = "";
345
+ let statement = "";
380
346
 
381
- app.post('/recordsImport', async function (req, res) {
382
- const form = new multiparty.Form();
383
- form.parse(req, async function (err, fields, files) {
384
- if (err) {
385
- return res.status(500).send(err);
386
- }
387
- // Get viewIndex in fields
388
- const view = config.views[fields.viewIndex[0]];
389
- if (typeof view.parseImportFile !== "function") {
390
- return res.status(500).send("\"parseImportFile\" doit être défini dans la configuration de la vue.");
391
- }
392
-
393
- const records = await view.parseImportFile(files.data[0]);
394
- const columns = [];
395
- if (records.length === 0) {
396
- return res.send({
397
- affectedRows: 0
398
- })
399
- }
347
+ try {
348
+ for (let key in data) {
349
+ statement =
350
+ "var " +
351
+ key +
352
+ " = " +
353
+ (typeof data[key] === "string"
354
+ ? "'" +
355
+ data[key]
356
+ .replace(/'/g, "\\'")
357
+ .replace(/\n/g, "")
358
+ .replace(/\r/g, "") +
359
+ "'"
360
+ : typeof data[key] === "object"
361
+ ? JSON.stringify(data[key])
362
+ : data[key]) +
363
+ ";";
364
+ varString += statement;
365
+ }
366
+ const result = eval(varString + expression);
367
+ return result;
368
+ } catch (e) {
369
+ console.log("Problème d'évaluation de l'expression", statement);
370
+ console.error(e);
371
+ return "Error";
372
+ }
373
+ }
400
374
 
401
- try {
402
- for (let record of records) {
403
- for (let key in record) {
404
- if (!columns.includes(key)) {
405
- columns.push(key)
406
- }
407
- }
408
- }
375
+ function formatValueForInsertion(value: any, dbType: string) {
376
+ if (value !== 0 && !value) {
377
+ return "NULL";
378
+ }
379
+ if (value instanceof Date) {
380
+ return value.toISOString();
381
+ }
382
+ if (typeof value === "string") {
383
+ if (dbType === "POSTGRESQL") {
384
+ return "'" + value.replace(/'/g, "''") + "'";
385
+ } else {
386
+ return "'" + value.replace(/'/g, "\\'") + "'";
387
+ }
388
+ }
389
+ if (typeof value === "object") {
390
+ return "'" + JSON.stringify(value).replace(/'/g, "\\'") + "'";
391
+ }
392
+ if (typeof value === "boolean") {
393
+ return value ? "TRUE" : "FALSE";
394
+ }
395
+ if (typeof value === "number") {
396
+ return value;
397
+ }
409
398
 
410
- let queryString;
411
- if (db.dbType === "POSTGRESQL") {
412
- queryString = `INSERT INTO ?? (${columns.map(x => `"${x}"`).join(", ")}) VALUES`;
399
+ return value;
400
+ }
413
401
 
414
- for (let record of records) {
415
- const valuesList = columns.map(x => formatValueForInsertion(record[x], db.dbType)).join(", ");
416
- queryString += ` (${valuesList}),`;
417
- }
418
- queryString = queryString.slice(0, -1) + ` ON CONFLICT (${view.importFileUniqueField}) DO UPDATE SET `;
402
+ app.post("/recordsExport", async function (req, res) {
403
+ const view = config.views[req.body.viewIndex];
404
+ const orderBy = req.body.orderBy || view.orderBy;
405
+ const isDesc = req.body.isDesc === null ? view.isDesc : req.body.isDesc;
406
+ const subview =
407
+ view.subviews && req.body.subviewIndex !== undefined
408
+ ? view.subviews[req.body.subviewIndex]
409
+ : null;
410
+ const { rows: records, error } = await getRecords({
411
+ table: view.tableName,
412
+ page: 1,
413
+ allRecords: true,
414
+ filters: sqlAnd([view.filters, subview?.filters, req.body.filters]),
415
+ having: subview?.having,
416
+ customQuery: view.customQuery,
417
+ join: view.join,
418
+ selectExpression: view.selectExpression,
419
+ orderBy: orderBy ? `${orderBy} ${isDesc ? "DESC" : "ASC"}` : null,
420
+ primaryId: view.primaryId,
421
+ query: req.body.query,
422
+ groupBy: view.groupBy,
423
+ columns: view.columns,
424
+ limit: view.limit || 10,
425
+ });
426
+ if (error) {
427
+ return res.status(500).send(error);
428
+ }
429
+ let output = "";
430
+ const columns = view.columns;
431
+ for (let j = 0; j < columns.length; j++) {
432
+ if (!columns[j].hideInTable || columns[j].includeInExport) {
433
+ if (j > 0) output += ";";
434
+ output += columns[j].label;
435
+ }
436
+ }
437
+ for (let i in records) {
438
+ const el = records[i];
439
+ output += "\r\n";
440
+ for (let j = 0; j < columns.length; j++) {
441
+ if (!columns[j].hideInTable || columns[j].includeInExport) {
442
+ if (j > 0) output += ";";
443
+ if (columns[j].dataType == "textarea") {
444
+ output += '"';
445
+ }
446
+ output += await getFormattedValue(columns[j], el);
447
+ if (columns[j].dataType == "textarea") {
448
+ output += '"';
449
+ }
450
+ }
451
+ }
452
+ }
453
+ res.send({
454
+ content: output,
455
+ });
456
+ });
419
457
 
420
- for (let column of columns) {
421
- queryString += `"${column}" = EXCLUDED."${column}",`;
422
- }
423
- } else {
424
- queryString = "INSERT INTO ?? (" + columns.map(x => "`" + x + "`").join(", ") + ") VALUES";
425
- for (let record of records) {
426
- queryString += " (" + columns.map(x => formatValueForInsertion(record[x], db.dbType)).join(", ") + "),";
427
- }
428
- queryString = queryString.slice(0, -1) + " ON DUPLICATE KEY UPDATE";
429
- for (let column of columns) {
430
- queryString += " `" + column + "` = VALUES(`" + column + "`),"
431
- }
432
- }
433
- queryString = queryString.slice(0, -1); // Remove the trailing comma
458
+ app.post("/recordsImport", async function (req, res) {
459
+ const form = new multiparty.Form();
460
+ form.parse(req, async function (err, fields, files) {
461
+ if (err) {
462
+ return res.status(500).send(err);
463
+ }
464
+ // Get viewIndex in fields
465
+ const view = config.views[fields.viewIndex[0]];
466
+ if (typeof view.parseImportFile !== "function") {
467
+ return res
468
+ .status(500)
469
+ .send(
470
+ '"parseImportFile" doit être défini dans la configuration de la vue.'
471
+ );
472
+ }
434
473
 
435
- const { err, rows: r } = await db.query(queryString, [view.tableName])
436
- if (err) {
437
- console.error(err)
438
- return res.status(500).send({
439
- err,
440
- message: "Une erreur est survenue."
441
- })
442
- }
443
- res.send({
444
- affectedRows: r.affectedRows
445
- })
446
- } catch (err) {
447
- console.error(err)
448
- res.status(500).send({
449
- err,
450
- message: "Une erreur est survenue."
451
- })
452
- }
453
- })
454
- })
455
-
456
- app.post('/subrecordsImport', async function (req, res) {
457
- const form = new multiparty.Form();
458
- form.parse(req, async function (err, fields, files) {
459
- if (err) {
460
- return res.status(500).send(err);
461
- }
462
- // Get viewIndex and panelIndex in fields
463
- const view = config.views[fields.viewIndex[0]];
464
- const panel = view.recordViewPanels[fields.panelIndex[0]];
465
- if (typeof panel.parseImportFile !== "function") {
466
- return res.status(500).send("\"parseImportFile\" doit être défini dans la configuration du panneau.");
467
- }
468
- if (!Boolean(panel.createConfig)) {
469
- return res.status(500).send("\"createConfig\" doit être défini dans la configuration du panneau.");
470
- }
474
+ const records = await view.parseImportFile(files.data[0]);
475
+ const columns = [];
476
+ if (records.length === 0) {
477
+ return res.send({
478
+ affectedRows: 0,
479
+ });
480
+ }
471
481
 
472
- const records = await panel.parseImportFile(files.data[0], fields.recordId[0]);
473
- const columns = [];
474
- if (records.length === 0) {
475
- return res.send({
476
- affectedRows: 0
477
- })
482
+ try {
483
+ for (let record of records) {
484
+ for (let key in record) {
485
+ if (!columns.includes(key)) {
486
+ columns.push(key);
478
487
  }
488
+ }
489
+ }
479
490
 
480
- try {
481
- for (let record of records) {
482
- for (let key in record) {
483
- if (!columns.includes(key)) {
484
- columns.push(key)
485
- }
486
- }
487
- }
491
+ let queryString;
492
+ if (db.dbType === "POSTGRESQL") {
493
+ queryString = `INSERT INTO ?? (${columns.map((x) => `"${x}"`).join(", ")}) VALUES`;
488
494
 
489
- let queryString = "INSERT IGNORE INTO ?? (`" + panel.createConfig.recordIdKey + "`, " + columns.map(x => "`" + x + "`").join(", ") + ") VALUES";
490
- for (let record of records) {
491
- queryString += " (" + fields.recordId[0] + ", " + columns.map(x => formatValueForInsertion(record[x], db.dbType)).join(", ") + "),";
492
- }
493
- queryString = queryString.slice(0, -1) + " ON DUPLICATE KEY UPDATE";
494
- for (let column of columns) {
495
- queryString += " `" + column + "` = VALUES(`" + column + "`),"
496
- }
497
- const { err, rows: r } = await db.query(queryString.slice(0, -1), [panel.tableName])
498
- if (err) {
499
- console.error(err)
500
- return res.status(500).send({
501
- err,
502
- message: "Une erreur est survenue."
503
- })
504
- }
505
- res.send({
506
- affectedRows: r.affectedRows
507
- })
508
- } catch (err) {
509
- console.error(err)
510
- res.status(500).send({
511
- err,
512
- message: "Une erreur est survenue."
513
- })
514
- }
515
- })
516
- })
495
+ for (let record of records) {
496
+ const valuesList = columns
497
+ .map((x) => formatValueForInsertion(record[x], db.dbType))
498
+ .join(", ");
499
+ queryString += ` (${valuesList}),`;
500
+ }
501
+ queryString =
502
+ queryString.slice(0, -1) +
503
+ ` ON CONFLICT (${view.importFileUniqueField}) DO UPDATE SET `;
517
504
 
518
- app.get('/record', async function (req, res) {
519
- const view = config.views[req.query.viewIndex as string];
520
- const { err, rows } = await db.query("SELECT " + (view.selectExpression || "*") + " FROM ?? " + (view.join || "") + " WHERE ?? = ?", [view.tableName, view.tableName + "." + view.primaryId, req.query.recordId])
521
- if (err) {
522
- return res.status(500).send(err);
523
- }
524
- if (rows.length > 0) {
525
- const record = rows[0]
526
- for (const column of view.columns) {
527
- if (column.formatValue) {
528
- record[`__formatValue-${column.__id__}__`] = await column.formatValue(record)
529
- }
530
- }
531
- res.send(record)
505
+ for (let column of columns) {
506
+ queryString += `"${column}" = EXCLUDED."${column}",`;
507
+ }
532
508
  } else {
533
- res.sendStatus(404)
509
+ queryString =
510
+ "INSERT INTO ?? (" +
511
+ columns.map((x) => "`" + x + "`").join(", ") +
512
+ ") VALUES";
513
+ for (let record of records) {
514
+ queryString +=
515
+ " (" +
516
+ columns
517
+ .map((x) => formatValueForInsertion(record[x], db.dbType))
518
+ .join(", ") +
519
+ "),";
520
+ }
521
+ queryString = queryString.slice(0, -1) + " ON DUPLICATE KEY UPDATE";
522
+ for (let column of columns) {
523
+ queryString += " `" + column + "` = VALUES(`" + column + "`),";
524
+ }
534
525
  }
535
- })
526
+ queryString = queryString.slice(0, -1); // Remove the trailing comma
536
527
 
537
- app.post('/updateRecord', async function (req, res, next) {
538
- const view = config.views[req.body.viewIndex];
539
- for (let key in req.body.data) {
540
- if (req.body.data[key] == "NULL" || req.body.data[key] === "" || req.body.data[key] === "Invalid date") {
541
- req.body.data[key] = null
542
- }
543
- }
544
- if (Object.keys(req.body.data).length === 0) {
545
- return res.sendStatus(200)
546
- }
547
- const queryExpression = Object.keys(req.body.data).map(x => `${x} = ?`).join(", ");
548
- const queryParameters = Object.keys(req.body.data).map(x => req.body.data[x]);
549
- const { err } = await db.query(`UPDATE ?? SET ${queryExpression} WHERE ?? = ?`, [view.tableName, ...queryParameters, view.primaryId, req.body.primaryValue])
528
+ const { err, rows: r } = await db.query(queryString, [view.tableName]);
550
529
  if (err) {
551
- return res.status(500).send(err);
552
- }
553
- if (view.editCallback) {
554
- try {
555
- await axios.post(view.editCallback, {
556
- id: req.body.primaryValue,
557
- data: req.body.data,
558
- previousData: req.body.previousData
559
- })
560
- } catch (e) { }
561
- }
562
- if (view.editCallbackFunction) {
563
- await view.editCallbackFunction({
564
- id: req.body.primaryValue,
565
- data: req.body.data
566
- })
530
+ console.error(err);
531
+ return res.status(500).send({
532
+ err,
533
+ message: "Une erreur est survenue.",
534
+ });
567
535
  }
568
- res.sendStatus(200)
569
- })
536
+ res.send({
537
+ affectedRows: r.affectedRows,
538
+ });
539
+ } catch (err) {
540
+ console.error(err);
541
+ res.status(500).send({
542
+ err,
543
+ message: "Une erreur est survenue.",
544
+ });
545
+ }
546
+ });
547
+ });
548
+
549
+ app.post("/subrecordsImport", async function (req, res) {
550
+ const form = new multiparty.Form();
551
+ form.parse(req, async function (err, fields, files) {
552
+ if (err) {
553
+ return res.status(500).send(err);
554
+ }
555
+ // Get viewIndex and panelIndex in fields
556
+ const view = config.views[fields.viewIndex[0]];
557
+ const panel = view.recordViewPanels[fields.panelIndex[0]];
558
+ if (typeof panel.parseImportFile !== "function") {
559
+ return res
560
+ .status(500)
561
+ .send(
562
+ '"parseImportFile" doit être défini dans la configuration du panneau.'
563
+ );
564
+ }
565
+ if (!Boolean(panel.createConfig)) {
566
+ return res
567
+ .status(500)
568
+ .send(
569
+ '"createConfig" doit être défini dans la configuration du panneau.'
570
+ );
571
+ }
572
+
573
+ const records = await panel.parseImportFile(
574
+ files.data[0],
575
+ fields.recordId[0]
576
+ );
577
+ const columns = [];
578
+ if (records.length === 0) {
579
+ return res.send({
580
+ affectedRows: 0,
581
+ });
582
+ }
570
583
 
571
- app.post('/customAction', async function (req, res, next) {
572
- const view = config.views[req.body.viewIndex];
573
- const action = view.customActions[req.body.actionIndex];
574
- for (const recordId of req.body.selectedRecordIds) {
575
- try {
576
- const { rows } = await db.query("SELECT " + (view.selectExpression || "*") + " FROM ?? " + (view.join || "") + " WHERE ?? = ?", [view.tableName, view.tableName + "." + view.primaryId, recordId])
577
- const record = rows[0]
578
- const actionResult = await action.action(record, user.userId);
579
- if (actionResult) {
580
- return res.send({
581
- success: true,
582
- message: actionResult
583
- })
584
- }
585
- } catch (err) {
586
- console.error(err)
587
- return res.send({
588
- success: false,
589
- error: err
590
- })
584
+ try {
585
+ for (let record of records) {
586
+ for (let key in record) {
587
+ if (!columns.includes(key)) {
588
+ columns.push(key);
591
589
  }
590
+ }
592
591
  }
593
- res.send({
594
- success: true,
595
- message: action.successMessage
596
- })
597
- })
598
592
 
599
- app.post('/deleteRecord', async function (req, res) {
600
- const view = config.views[req.body.viewIndex];
601
- const { rows, err: err2 } = await db.query("SELECT " + (view.selectExpression || "*") + " FROM ?? " + (view.join || "") + " WHERE ?? = ?", [view.tableName, view.tableName + "." + view.primaryId, req.body.primaryValue])
602
- if (err2) {
603
- return res.status(500).send(err2);
593
+ let queryString =
594
+ "INSERT IGNORE INTO ?? (`" +
595
+ panel.createConfig.recordIdKey +
596
+ "`, " +
597
+ columns.map((x) => "`" + x + "`").join(", ") +
598
+ ") VALUES";
599
+ for (let record of records) {
600
+ queryString +=
601
+ " (" +
602
+ fields.recordId[0] +
603
+ ", " +
604
+ columns
605
+ .map((x) => formatValueForInsertion(record[x], db.dbType))
606
+ .join(", ") +
607
+ "),";
604
608
  }
605
- const deletedRecord = rows[0];
606
- if (!deletedRecord) {
607
- return res.status(404).send("Record not found");
609
+ queryString = queryString.slice(0, -1) + " ON DUPLICATE KEY UPDATE";
610
+ for (let column of columns) {
611
+ queryString += " `" + column + "` = VALUES(`" + column + "`),";
608
612
  }
609
- const { err } = await db.query("DELETE FROM ?? WHERE ?? = ?", [view.tableName, view.primaryId, req.body.primaryValue])
613
+ const { err, rows: r } = await db.query(queryString.slice(0, -1), [
614
+ panel.tableName,
615
+ ]);
610
616
  if (err) {
611
- return res.status(500).send(err);
612
- }
613
- if (view.deleteCallback) {
614
- try {
615
- await axios.post(view.deleteCallback, {
616
- id: req.body.primaryValue
617
- })
618
- } catch (e) { }
617
+ console.error(err);
618
+ return res.status(500).send({
619
+ err,
620
+ message: "Une erreur est survenue.",
621
+ });
619
622
  }
620
- if (view.deleteCallbackFunction) {
621
- await view.deleteCallbackFunction({
622
- id: req.body.primaryValue,
623
- deletedRecord
624
- })
625
- }
626
- res.sendStatus(200)
627
- })
623
+ res.send({
624
+ affectedRows: r.affectedRows,
625
+ });
626
+ } catch (err) {
627
+ console.error(err);
628
+ res.status(500).send({
629
+ err,
630
+ message: "Une erreur est survenue.",
631
+ });
632
+ }
633
+ });
634
+ });
628
635
 
629
- app.post('/deleteRecordFromPanel', async function (req, res) {
630
- const view = config.views[req.body.viewIndex];
631
- const panel = view.recordViewPanels.find(x => x.__id__ === req.body.panelId)
632
- const { rows, err: err2 } = await db.query("SELECT * FROM ?? WHERE ?? = ?", [panel.tableName, panel.primaryId, req.body.primaryValue])
633
- if (err2) {
634
- return res.status(500).send(err2);
635
- }
636
- const deletedRecord = rows[0];
637
- const { err } = await db.query("DELETE FROM ?? WHERE ?? = ?", [panel.tableName, panel.primaryId, req.body.primaryValue])
638
- if (err) {
639
- return res.status(500).send(err);
640
- }
641
- if (panel.deleteCallback) {
642
- try {
643
- await axios.post(panel.deleteCallback, {
644
- id: req.body.primaryValue
645
- })
646
- } catch (e) { }
647
- }
648
- if (panel.deleteCallbackFunction) {
649
- await panel.deleteCallbackFunction({
650
- id: req.body.primaryValue,
651
- deletedRecord
652
- })
636
+ app.get("/record", async function (req, res) {
637
+ const view = config.views[req.query.viewIndex as string];
638
+ const { err, rows } = await db.query(
639
+ "SELECT " +
640
+ (view.selectExpression || "*") +
641
+ " FROM ?? " +
642
+ (view.join || "") +
643
+ " WHERE ?? = ?",
644
+ [
645
+ view.tableName,
646
+ view.tableName + "." + view.primaryId,
647
+ req.query.recordId,
648
+ ]
649
+ );
650
+ if (err) {
651
+ return res.status(500).send(err);
652
+ }
653
+ if (rows.length > 0) {
654
+ const record = rows[0];
655
+ for (const column of view.columns) {
656
+ if (column.formatValue) {
657
+ record[`__formatValue-${column.__id__}__`] =
658
+ await column.formatValue(record);
653
659
  }
654
- res.sendStatus(200)
655
- })
660
+ }
661
+ res.send(record);
662
+ } else {
663
+ res.sendStatus(404);
664
+ }
665
+ });
656
666
 
657
- app.post('/deleteRecords', async function (req, res, next) {
658
- const view = config.views[req.body.viewIndex];
659
- const { err } = await db.query(`DELETE FROM ?? WHERE ?? IN(${req.body.primaryValues.map(x => "'" + x + "'").join(",")})`, [view.tableName, view.primaryId])
660
- if (err) {
661
- return res.status(500).send(err);
667
+ app.post("/updateRecord", async function (req, res, next) {
668
+ const view = config.views[req.body.viewIndex];
669
+ for (let key in req.body.data) {
670
+ if (
671
+ req.body.data[key] == "NULL" ||
672
+ req.body.data[key] === "" ||
673
+ req.body.data[key] === "Invalid date"
674
+ ) {
675
+ req.body.data[key] = null;
676
+ }
677
+ }
678
+ if (Object.keys(req.body.data).length === 0) {
679
+ return res.sendStatus(200);
680
+ }
681
+ const queryExpression = Object.keys(req.body.data)
682
+ .map((x) => `${x} = ?`)
683
+ .join(", ");
684
+ const queryParameters = Object.keys(req.body.data).map(
685
+ (x) => req.body.data[x]
686
+ );
687
+ const { err } = await db.query(
688
+ `UPDATE ?? SET ${queryExpression} WHERE ?? = ?`,
689
+ [
690
+ view.tableName,
691
+ ...queryParameters,
692
+ view.primaryId,
693
+ req.body.primaryValue,
694
+ ]
695
+ );
696
+ if (err) {
697
+ return res.status(500).send(err);
698
+ }
699
+ if (view.editCallback) {
700
+ try {
701
+ await axios.post(view.editCallback, {
702
+ id: req.body.primaryValue,
703
+ data: req.body.data,
704
+ previousData: req.body.previousData,
705
+ });
706
+ } catch (e) {}
707
+ }
708
+ if (view.editCallbackFunction) {
709
+ await view.editCallbackFunction({
710
+ id: req.body.primaryValue,
711
+ data: req.body.data,
712
+ });
713
+ }
714
+ res.sendStatus(200);
715
+ });
716
+
717
+ app.post("/customAction", async function (req, res, next) {
718
+ const view = config.views[req.body.viewIndex];
719
+ const action = view.customActions[req.body.actionIndex];
720
+ for (const recordId of req.body.selectedRecordIds) {
721
+ try {
722
+ const { rows } = await db.query(
723
+ "SELECT " +
724
+ (view.selectExpression || "*") +
725
+ " FROM ?? " +
726
+ (view.join || "") +
727
+ " WHERE ?? = ?",
728
+ [view.tableName, view.tableName + "." + view.primaryId, recordId]
729
+ );
730
+ const record = rows[0];
731
+ const actionResult = await action.action(record, user.userId);
732
+ if (actionResult) {
733
+ return res.send({
734
+ success: true,
735
+ message: actionResult,
736
+ });
662
737
  }
663
- res.sendStatus(200)
664
- })
738
+ } catch (err) {
739
+ console.error(err);
740
+ return res.send({
741
+ success: false,
742
+ error: err,
743
+ });
744
+ }
745
+ }
746
+ res.send({
747
+ success: true,
748
+ message: action.successMessage,
749
+ });
750
+ });
665
751
 
666
- app.get('/notificationBadges', async function (req, res) {
667
- const notificationBadges = {}
752
+ app.post("/deleteRecord", async function (req, res) {
753
+ const view = config.views[req.body.viewIndex];
754
+ const { rows, err: err2 } = await db.query(
755
+ "SELECT " +
756
+ (view.selectExpression || "*") +
757
+ " FROM ?? " +
758
+ (view.join || "") +
759
+ " WHERE ?? = ?",
760
+ [
761
+ view.tableName,
762
+ view.tableName + "." + view.primaryId,
763
+ req.body.primaryValue,
764
+ ]
765
+ );
766
+ if (err2) {
767
+ return res.status(500).send(err2);
768
+ }
769
+ const deletedRecord = rows[0];
770
+ if (!deletedRecord) {
771
+ return res.status(404).send("Record not found");
772
+ }
773
+ const { err } = await db.query("DELETE FROM ?? WHERE ?? = ?", [
774
+ view.tableName,
775
+ view.primaryId,
776
+ req.body.primaryValue,
777
+ ]);
778
+ if (err) {
779
+ return res.status(500).send(err);
780
+ }
781
+ if (view.deleteCallback) {
782
+ try {
783
+ await axios.post(view.deleteCallback, {
784
+ id: req.body.primaryValue,
785
+ });
786
+ } catch (e) {}
787
+ }
788
+ if (view.deleteCallbackFunction) {
789
+ await view.deleteCallbackFunction({
790
+ id: req.body.primaryValue,
791
+ deletedRecord,
792
+ });
793
+ }
794
+ res.sendStatus(200);
795
+ });
668
796
 
669
- async function checkAndGetNotificationBadge(obj) {
670
- if (obj.notificationBadge) {
671
- const { err, rows } = await db.query(obj.notificationBadge.query);
672
- if (err) {
673
- console.error(err)
674
- } else if (rows.length > 0 && rows[0][obj.notificationBadge.attribute]) {
675
- const notificationBadge = rows[0][obj.notificationBadge.attribute];
676
- if (!obj.notificationBadge.displayCondition || obj.notificationBadge.displayCondition(notificationBadge)) {
677
- notificationBadges[obj.__id__] = notificationBadge;
678
- }
679
- }
680
- }
681
- }
797
+ app.post("/deleteRecordFromPanel", async function (req, res) {
798
+ const view = config.views[req.body.viewIndex];
799
+ const panel = view.recordViewPanels.find(
800
+ (x) => x.__id__ === req.body.panelId
801
+ );
802
+ const { rows, err: err2 } = await db.query(
803
+ "SELECT * FROM ?? WHERE ?? = ?",
804
+ [panel.tableName, panel.primaryId, req.body.primaryValue]
805
+ );
806
+ if (err2) {
807
+ return res.status(500).send(err2);
808
+ }
809
+ const deletedRecord = rows[0];
810
+ const { err } = await db.query("DELETE FROM ?? WHERE ?? = ?", [
811
+ panel.tableName,
812
+ panel.primaryId,
813
+ req.body.primaryValue,
814
+ ]);
815
+ if (err) {
816
+ return res.status(500).send(err);
817
+ }
818
+ if (panel.deleteCallback) {
819
+ try {
820
+ await axios.post(panel.deleteCallback, {
821
+ id: req.body.primaryValue,
822
+ });
823
+ } catch (e) {}
824
+ }
825
+ if (panel.deleteCallbackFunction) {
826
+ await panel.deleteCallbackFunction({
827
+ id: req.body.primaryValue,
828
+ deletedRecord,
829
+ });
830
+ }
831
+ res.sendStatus(200);
832
+ });
682
833
 
683
- for (const view of config.views) {
684
- await checkAndGetNotificationBadge(view)
685
- if (view.subviews) {
686
- for (const subview of view.subviews) {
687
- await checkAndGetNotificationBadge(subview)
688
- }
689
- }
690
- }
691
- res.send(notificationBadges)
692
- })
834
+ app.post("/deleteRecords", async function (req, res, next) {
835
+ const view = config.views[req.body.viewIndex];
836
+ const { err } = await db.query(
837
+ `DELETE FROM ?? WHERE ?? IN(${req.body.primaryValues.map((x) => "'" + x + "'").join(",")})`,
838
+ [view.tableName, view.primaryId]
839
+ );
840
+ if (err) {
841
+ return res.status(500).send(err);
842
+ }
843
+ res.sendStatus(200);
844
+ });
693
845
 
694
- app.post('/duplicateRecord', async function (req, res, next) {
695
- const primaryId = req.body.duplicateConfig.primaryId;
696
- const { err, rows } = await db.query("SELECT * FROM ?? WHERE ?? = ?", [req.body.table, primaryId, req.body.primaryValue])
846
+ app.get("/notificationBadges", async function (req, res) {
847
+ const notificationBadges = {};
848
+
849
+ async function checkAndGetNotificationBadge(obj) {
850
+ if (obj.notificationBadge) {
851
+ const { err, rows } = await db.query(obj.notificationBadge.query);
697
852
  if (err) {
698
- return res.status(500).send(err);
699
- }
700
- const record = rows[0];
701
- const data = {}
702
- for (let field of req.body.duplicateConfig.fields) {
703
- data[field] = record[field]
853
+ console.error(err);
854
+ } else if (
855
+ rows.length > 0 &&
856
+ rows[0][obj.notificationBadge.attribute]
857
+ ) {
858
+ const notificationBadge = rows[0][obj.notificationBadge.attribute];
859
+ if (
860
+ !obj.notificationBadge.displayCondition ||
861
+ obj.notificationBadge.displayCondition(notificationBadge)
862
+ ) {
863
+ notificationBadges[obj.__id__] = notificationBadge;
864
+ }
704
865
  }
705
- const { err: err2, rows: r } = await db.query("INSERT IGNORE INTO ?? SET ?", [req.body.table, data])
706
- if (err2) {
707
- return res.status(500).send(err);
866
+ }
867
+ }
868
+
869
+ for (const view of config.views) {
870
+ await checkAndGetNotificationBadge(view);
871
+ if (view.subviews) {
872
+ for (const subview of view.subviews) {
873
+ await checkAndGetNotificationBadge(subview);
708
874
  }
709
- const newRecordId = r.insertId;
710
- for (let foreignTable of req.body.duplicateConfig.foreignTables) {
711
- const { err, rows } = await db.query("SELECT * FROM ?? WHERE ?? = ?", [foreignTable.table, primaryId, req.body.primaryValue]);
712
- if (err) console.log(err);
713
- for (let record of rows) {
714
- const data = {};
715
- data[primaryId] = newRecordId;
716
- for (let field of foreignTable.fields) {
717
- data[field] = record[field]
718
- }
719
- await db.query("INSERT IGNORE INTO ?? SET ?", [foreignTable.table, data])
720
- }
875
+ }
876
+ }
877
+ res.send(notificationBadges);
878
+ });
879
+
880
+ app.post("/duplicateRecord", async function (req, res, next) {
881
+ const primaryId = req.body.duplicateConfig.primaryId;
882
+ const { err, rows } = await db.query("SELECT * FROM ?? WHERE ?? = ?", [
883
+ req.body.table,
884
+ primaryId,
885
+ req.body.primaryValue,
886
+ ]);
887
+ if (err) {
888
+ return res.status(500).send(err);
889
+ }
890
+ const record = rows[0];
891
+ const data = {};
892
+ for (let field of req.body.duplicateConfig.fields) {
893
+ data[field] = record[field];
894
+ }
895
+ const { err: err2, rows: r } = await db.query(
896
+ "INSERT IGNORE INTO ?? SET ?",
897
+ [req.body.table, data]
898
+ );
899
+ if (err2) {
900
+ return res.status(500).send(err);
901
+ }
902
+ const newRecordId = r.insertId;
903
+ for (let foreignTable of req.body.duplicateConfig.foreignTables) {
904
+ const { err, rows } = await db.query("SELECT * FROM ?? WHERE ?? = ?", [
905
+ foreignTable.table,
906
+ primaryId,
907
+ req.body.primaryValue,
908
+ ]);
909
+ if (err) console.log(err);
910
+ for (let record of rows) {
911
+ const data = {};
912
+ data[primaryId] = newRecordId;
913
+ for (let field of foreignTable.fields) {
914
+ data[field] = record[field];
721
915
  }
722
- res.sendStatus(200)
723
- })
916
+ await db.query("INSERT IGNORE INTO ?? SET ?", [
917
+ foreignTable.table,
918
+ data,
919
+ ]);
920
+ }
921
+ }
922
+ res.sendStatus(200);
923
+ });
724
924
 
725
- app.post('/createRecord', async function (req, res, next) {
726
- const view = config.views[req.body.viewIndex];
727
- const panel = req.body.panelIndex ? view.recordViewPanels[req.body.panelIndex] : null
925
+ app.post("/createRecord", async function (req, res, next) {
926
+ const view = config.views[req.body.viewIndex];
927
+ const panel = req.body.panelIndex
928
+ ? view.recordViewPanels[req.body.panelIndex]
929
+ : null;
930
+ try {
931
+ for (let key in req.body.data) {
932
+ if (
933
+ req.body.data[key] == "NULL" ||
934
+ req.body.data[key] === "Invalid date"
935
+ ) {
936
+ req.body.data[key] = null;
937
+ }
938
+ }
939
+ if (panel) {
940
+ req.body.data[panel.createConfig.recordIdKey] = req.body.recordId;
941
+ }
942
+ const primaryColumn = (view || panel)?.columns?.find(
943
+ (x) => x.name === (view || panel).primaryId
944
+ );
945
+ if (primaryColumn?.dataType == "uuid") {
946
+ req.body.data[primaryColumn.name] = uuidv4();
947
+ }
948
+ const createPayload = JSON.parse(JSON.stringify(req.body.data));
949
+ const columns = panel ? panel.createConfig.columns : view.columns;
950
+ for (let key in createPayload) {
951
+ if (
952
+ columns?.find((x) => x.name === key)?.includedInCreatePayload ===
953
+ false
954
+ ) {
955
+ delete createPayload[key];
956
+ }
957
+ }
958
+ const queryExpression = Object.keys(createPayload).join(", ");
959
+ const queryPlaceholders = Object.keys(createPayload)
960
+ .map((x) => "?")
961
+ .join(", ");
962
+ const queryParameters = Object.keys(createPayload).map(
963
+ (x) => createPayload[x]
964
+ );
965
+ const { err, rows: r } = await db.query(
966
+ `INSERT INTO ?? (${queryExpression}) VALUES(${queryPlaceholders})`,
967
+ [panel ? panel.tableName : view.tableName, ...queryParameters]
968
+ );
969
+ if (err) {
970
+ console.error(err);
971
+ return res.status(500).send({
972
+ err,
973
+ message:
974
+ err.code === "ER_DUP_ENTRY"
975
+ ? "Cet élément existe déjà."
976
+ : "Une erreur est survenue.",
977
+ });
978
+ }
979
+ if (panel ? panel.createCallback : view.createCallback) {
728
980
  try {
729
- for (let key in req.body.data) {
730
- if (req.body.data[key] == "NULL" || req.body.data[key] === "Invalid date") {
731
- req.body.data[key] = null
732
- }
733
- }
734
- if (panel) {
735
- req.body.data[panel.createConfig.recordIdKey] = req.body.recordId;
736
- }
737
- const primaryColumn = (view || panel)?.columns?.find(x => x.name === (view || panel).primaryId)
738
- if (primaryColumn?.dataType == "uuid") {
739
- req.body.data[primaryColumn.name] = uuidv4()
740
- }
741
- const createPayload = JSON.parse(JSON.stringify(req.body.data));
742
- const columns = panel ? panel.createConfig.columns : view.columns;
743
- for (let key in createPayload) {
744
- if (columns?.find(x => x.name === key)?.includedInCreatePayload === false) {
745
- delete createPayload[key]
746
- }
747
- }
748
- const queryExpression = Object.keys(createPayload).join(", ");
749
- const queryPlaceholders = Object.keys(createPayload).map(x => "?").join(", ");
750
- const queryParameters = Object.keys(createPayload).map(x => createPayload[x]);
751
- const { err, rows: r } = await db.query(`INSERT INTO ?? (${queryExpression}) VALUES(${queryPlaceholders})`, [panel ? panel.tableName : view.tableName, ...queryParameters])
752
- if (err) {
753
- console.error(err)
754
- return res.status(500).send({
755
- err,
756
- message: err.code === 'ER_DUP_ENTRY' ? "Cet élément existe déjà." : "Une erreur est survenue."
757
- })
758
- }
759
- if (panel ? panel.createCallback : view.createCallback) {
760
- try {
761
- await axios.post(panel ? panel.createCallback : view.createCallback, {
762
- id: r.insertId,
763
- data: req.body.data
764
- })
765
- } catch (e) { }
766
- }
767
- if (panel ? panel.createCallbackFunction : view.createCallbackFunction) {
768
- await (panel ? panel.createCallbackFunction : view.createCallbackFunction)({
769
- id: r.insertId,
770
- data: req.body.data
771
- })
772
- }
773
- res.send({
774
- id: r.insertId
775
- })
776
- } catch (err) {
777
- console.error(err)
778
- res.status(500).send({
779
- err,
780
- message: "Une erreur est survenue."
781
- })
981
+ await axios.post(panel ? panel.createCallback : view.createCallback, {
982
+ id: r.insertId,
983
+ data: req.body.data,
984
+ });
985
+ } catch (e) {}
986
+ }
987
+ if (panel ? panel.createCallbackFunction : view.createCallbackFunction) {
988
+ await (
989
+ panel ? panel.createCallbackFunction : view.createCallbackFunction
990
+ )({
991
+ id: r.insertId,
992
+ data: req.body.data,
993
+ });
994
+ }
995
+ res.send({
996
+ id: r.insertId,
997
+ });
998
+ } catch (err) {
999
+ console.error(err);
1000
+ res.status(500).send({
1001
+ err,
1002
+ message: "Une erreur est survenue.",
1003
+ });
1004
+ }
1005
+ });
1006
+
1007
+ app.get("/customQuery", async function (req, res, next) {
1008
+ const query = (req.query.query as string).replace(
1009
+ /\?/g,
1010
+ req.query.primaryValue as string
1011
+ );
1012
+ const { err, rows } = await db.query(query);
1013
+ if (err) {
1014
+ return res.status(500).send(err);
1015
+ }
1016
+ if (req.query.viewIndex && req.query.panelIndex) {
1017
+ const panel =
1018
+ config.views[req.query.viewIndex as string].recordViewPanels[
1019
+ req.query.panelIndex as string
1020
+ ];
1021
+ if (panel.formatValue) {
1022
+ for (const row of rows) {
1023
+ for (const key in panel.formatValue) {
1024
+ row[key] = await panel.formatValue[key](row);
1025
+ }
782
1026
  }
783
- })
1027
+ }
1028
+ }
1029
+ res.send(rows);
1030
+ });
784
1031
 
785
- app.get('/customQuery', async function (req, res, next) {
786
- const query = (req.query.query as string).replace(/\?/g, req.query.primaryValue as string)
787
- const { err, rows } = await db.query(query);
1032
+ function uploadFile(column, fileParamName, req, res) {
1033
+ if (column.s3Config || column.gcpConfig) {
1034
+ const form = new multiparty.Form();
1035
+ form.parse(req, function (err, fields, files) {
788
1036
  if (err) {
789
- return res.status(500).send(err);
790
- }
791
- if (req.query.viewIndex && req.query.panelIndex) {
792
- const panel = config.views[req.query.viewIndex as string].recordViewPanels[req.query.panelIndex as string];
793
- if (panel.formatValue) {
794
- for (const row of rows) {
795
- for (const key in panel.formatValue) {
796
- row[key] = await panel.formatValue[key](row);
797
- }
798
- }
799
- }
1037
+ return res.status(500).send(err);
800
1038
  }
801
- res.send(rows)
802
- })
1039
+ const originalFilename = files[fileParamName][0].originalFilename;
1040
+ const contentType =
1041
+ mime.getType(originalFilename) || "application/octet-stream";
803
1042
 
804
- function uploadFile(column, fileParamName, req, res) {
805
- if (column.s3Config) {
806
- const s3Config = column.s3Config;
807
- const form = new multiparty.Form();
808
- const s3 = new S3({
809
- endpoint: new Endpoint(s3Config.host),
810
- accessKeyId: s3Config.accessKeyId,
811
- secretAccessKey: s3Config.secretAccessKey,
812
- region: s3Config.region,
813
- signatureVersion: "v4",
814
- });
815
- form.parse(req, function (err, fields, files) {
816
- if (err) {
817
- return res.status(500).send(err);
818
- }
819
- const originalFilename = files[fileParamName][0].originalFilename;
820
- const contentType = mime.getType(originalFilename) || 'application/octet-stream';
1043
+ const extension = originalFilename.match(/(?:\.([^.]+))?$/)[1];
1044
+ let aspectRatio = null;
1045
+ if (["png", "jpeg", "jpg", "gif"].includes(extension)) {
1046
+ const dimensions = sizeOf(files[fileParamName][0].path);
1047
+ aspectRatio = dimensions.height / dimensions.width;
1048
+ }
1049
+ const originalFilenameWithoutExtension = originalFilename.replace(
1050
+ "." + extension,
1051
+ ""
1052
+ );
821
1053
 
822
- const extension = originalFilename.match(/(?:\.([^.]+))?$/)[1];
823
- let aspectRatio = null;
824
- if (["png", "jpeg", "jpg", "gif"].includes(extension)) {
825
- const dimensions = sizeOf(files[fileParamName][0].path);
826
- aspectRatio = dimensions.height / dimensions.width;
827
- }
828
- const originalFilenameWithoutExtension = originalFilename.replace("." + extension, "")
829
- const path = (s3Config.subfolder ? s3Config.subfolder + "/" : "") + originalFilenameWithoutExtension + "-" + Math.random().toString().substr(2) + "." + extension;
1054
+ if (column.gcpConfig) {
1055
+ const gcpConfig = column.gcpConfig;
1056
+ const path =
1057
+ (gcpConfig.subfolder ? gcpConfig.subfolder + "/" : "") +
1058
+ originalFilenameWithoutExtension +
1059
+ "-" +
1060
+ Math.random().toString().substr(2) +
1061
+ "." +
1062
+ extension;
1063
+ const storage = new Storage();
830
1064
 
831
- if (s3Config.signedUrlNeeded) {
832
- const signedUrl = s3.getSignedUrl('putObject', {
833
- Bucket: s3Config.bucket,
834
- Key: path,
835
- ContentType: contentType,
836
- Expires: 150,
837
- });
838
- axios.put(signedUrl, fs.readFileSync(files[fileParamName][0].path), {
839
- maxContentLength: 100000000000,
840
- maxBodyLength: 100000000000,
841
- headers: {
842
- "Content-Type": contentType
843
- }
844
- })
845
- .then(() => {
846
- res.status(200).send({
847
- path: s3Config.pathTransform ? s3Config.pathTransform(path) : path,
848
- aspectRatio,
849
- originalFilename,
850
- });
851
- })
852
- .catch(e => {
853
- res.status(500).send(e)
854
- })
855
- } else {
856
- s3.putObject({
857
- Body: fs.readFileSync(files[fileParamName][0].path),
858
- Bucket: s3Config.bucket,
859
- ContentType: contentType,
860
- ACL: 'public-read',
861
- Key: path
862
- }, function (err, data) {
863
- if (err) return res.status(500).send(err);
864
- res.send({
865
- url: `https://${s3Config.host}/${s3Config.bucket}/${path}`,
866
- path: s3Config.pathTransform ? s3Config.pathTransform(path) : path,
867
- aspectRatio,
868
- originalFilename
869
- })
870
- });
871
- }
1065
+ const bucketName = gcpConfig.bucket;
1066
+ storage
1067
+ .bucket(bucketName)
1068
+ .upload(files[fileParamName][0].path, {
1069
+ destination: path,
872
1070
  })
873
- } else if (column.sftpConfig) {
874
- if (!column.publicPath) {
875
- return res.status(500).send("You need to pass publicPath parameter when using sftpConfig");
876
- }
877
- const form = new multiparty.Form();
878
- form.parse(req, function (err, fields, files) {
879
- if (err) {
880
- return res.status(500).send(err);
881
- }
882
- const pathSplitted = files[fileParamName][0].path.split(/[\/\\]+/);
883
- const filename = pathSplitted[pathSplitted.length - 1];
884
- (async () => {
885
- const sftp = new SftpClient();
886
- await sftp.connect({
887
- host: column.sftpConfig.host,
888
- port: 22,
889
- username: column.sftpConfig.user,
890
- password: column.sftpConfig.password
891
- });
892
- await sftp.fastPut(files[fileParamName][0].path, column.publicPath + (column.subdirectory ? "/" + column.subdirectory : "") + "/" + filename);
893
- await sftp.end();
894
- res.send({
895
- path: filename,
896
- originalFilename: files[fileParamName][0].originalFilename
897
- })
898
- fs.unlinkSync(files[fileParamName][0].path)
899
- })();
1071
+ .then(() => {
1072
+ res.status(200).send({
1073
+ path: gcpConfig.pathTransform
1074
+ ? gcpConfig.pathTransform(path)
1075
+ : path,
1076
+ aspectRatio,
1077
+ originalFilename,
1078
+ });
900
1079
  })
1080
+ .catch((e) => {
1081
+ res.status(500).send(e);
1082
+ });
901
1083
  } else {
902
- const form = new multiparty.Form({
903
- uploadDir: (column.publicPath as string) + (column.subdirectory as string)
1084
+ const s3Config = column.s3Config;
1085
+ const path =
1086
+ (s3Config.subfolder ? s3Config.subfolder + "/" : "") +
1087
+ originalFilenameWithoutExtension +
1088
+ "-" +
1089
+ Math.random().toString().substr(2) +
1090
+ "." +
1091
+ extension;
1092
+ const s3 = new S3({
1093
+ endpoint: new Endpoint(s3Config.host),
1094
+ accessKeyId: s3Config.accessKeyId,
1095
+ secretAccessKey: s3Config.secretAccessKey,
1096
+ region: s3Config.region,
1097
+ signatureVersion: "v4",
1098
+ });
1099
+ if (s3Config.signedUrlNeeded) {
1100
+ const signedUrl = s3.getSignedUrl("putObject", {
1101
+ Bucket: s3Config.bucket,
1102
+ Key: path,
1103
+ ContentType: contentType,
1104
+ Expires: 150,
904
1105
  });
905
- form.parse(req, function (err, fields, files) {
906
- if (err) {
907
- return res.status(500).send(err);
908
- }
1106
+ axios
1107
+ .put(signedUrl, fs.readFileSync(files[fileParamName][0].path), {
1108
+ maxContentLength: 100000000000,
1109
+ maxBodyLength: 100000000000,
1110
+ headers: {
1111
+ "Content-Type": contentType,
1112
+ },
1113
+ })
1114
+ .then(() => {
1115
+ res.status(200).send({
1116
+ path: s3Config.pathTransform
1117
+ ? s3Config.pathTransform(path)
1118
+ : path,
1119
+ aspectRatio,
1120
+ originalFilename,
1121
+ });
1122
+ })
1123
+ .catch((e) => {
1124
+ res.status(500).send(e);
1125
+ });
1126
+ } else {
1127
+ s3.putObject(
1128
+ {
1129
+ Body: fs.readFileSync(files[fileParamName][0].path),
1130
+ Bucket: s3Config.bucket,
1131
+ ContentType: contentType,
1132
+ ACL: "public-read",
1133
+ Key: path,
1134
+ },
1135
+ function (err, data) {
1136
+ if (err) return res.status(500).send(err);
909
1137
  res.send({
910
- path: files[fileParamName][0].path.replace(column.publicPath, ""),
911
- originalFilename: files[fileParamName][0].originalFilename
912
- })
913
- })
1138
+ url: `https://${s3Config.host}/${s3Config.bucket}/${path}`,
1139
+ path: s3Config.pathTransform
1140
+ ? s3Config.pathTransform(path)
1141
+ : path,
1142
+ aspectRatio,
1143
+ originalFilename,
1144
+ });
1145
+ }
1146
+ );
1147
+ }
1148
+ }
1149
+ });
1150
+ } else if (column.sftpConfig) {
1151
+ if (!column.publicPath) {
1152
+ return res
1153
+ .status(500)
1154
+ .send("You need to pass publicPath parameter when using sftpConfig");
1155
+ }
1156
+ const form = new multiparty.Form();
1157
+ form.parse(req, function (err, fields, files) {
1158
+ if (err) {
1159
+ return res.status(500).send(err);
914
1160
  }
1161
+ const pathSplitted = files[fileParamName][0].path.split(/[\/\\]+/);
1162
+ const filename = pathSplitted[pathSplitted.length - 1];
1163
+ (async () => {
1164
+ const sftp = new SftpClient();
1165
+ await sftp.connect({
1166
+ host: column.sftpConfig.host,
1167
+ port: 22,
1168
+ username: column.sftpConfig.user,
1169
+ password: column.sftpConfig.password,
1170
+ });
1171
+ await sftp.fastPut(
1172
+ files[fileParamName][0].path,
1173
+ column.publicPath +
1174
+ (column.subdirectory ? "/" + column.subdirectory : "") +
1175
+ "/" +
1176
+ filename
1177
+ );
1178
+ await sftp.end();
1179
+ res.send({
1180
+ path: filename,
1181
+ originalFilename: files[fileParamName][0].originalFilename,
1182
+ });
1183
+ fs.unlinkSync(files[fileParamName][0].path);
1184
+ })();
1185
+ });
1186
+ } else {
1187
+ const form = new multiparty.Form({
1188
+ uploadDir:
1189
+ (column.publicPath as string) + (column.subdirectory as string),
1190
+ });
1191
+ form.parse(req, function (err, fields, files) {
1192
+ if (err) {
1193
+ return res.status(500).send(err);
1194
+ }
1195
+ res.send({
1196
+ path: files[fileParamName][0].path.replace(column.publicPath, ""),
1197
+ originalFilename: files[fileParamName][0].originalFilename,
1198
+ });
1199
+ });
915
1200
  }
1201
+ }
916
1202
 
917
- app.post('/uploadFileFromBetterRichTextInput', async function (req, res, next) {
918
- const fileUploadProperties = JSON.parse(req.headers['file-upload-properties'] as string);
919
- uploadFile(fileUploadProperties, "upload", req, res);
920
- })
1203
+ app.post(
1204
+ "/uploadFileFromBetterRichTextInput",
1205
+ async function (req, res, next) {
1206
+ const fileUploadProperties = JSON.parse(
1207
+ req.headers["file-upload-properties"] as string
1208
+ );
1209
+ uploadFile(fileUploadProperties, "upload", req, res);
1210
+ }
1211
+ );
921
1212
 
922
- app.post('/uploadFile', async function (req, res, next) {
923
- const view = config.views[req.query.viewIndex as string];
924
- const column = [
925
- ...view.columns,
926
- ...(view.recordViewPanels?.map(x => x?.createConfig?.columns)?.filter(x => x)?.flat() || [])
927
- ].find(x => x.__id__ == req.query.columnId)
928
- uploadFile(column, "file", req, res);
929
- })
1213
+ app.post("/uploadFile", async function (req, res, next) {
1214
+ const view = config.views[req.query.viewIndex as string];
1215
+ const column = [
1216
+ ...view.columns,
1217
+ ...(view.recordViewPanels
1218
+ ?.map((x) => x?.createConfig?.columns)
1219
+ ?.filter((x) => x)
1220
+ ?.flat() || []),
1221
+ ].find((x) => x.__id__ == req.query.columnId);
1222
+ uploadFile(column, "file", req, res);
1223
+ });
930
1224
 
931
- app.get('/searchCompanies', async (req, res) => {
932
- const processedQuery = (req.query.query as any).replaceAll(" ", "-")
933
- try {
934
- const response = await axios.get(`https://public.opendatasoft.com/api/explore/v2.1/catalog/datasets/economicref-france-sirene-v3/records`, {
935
- params: {
936
- where: `denominationunitelegale LIKE '${processedQuery}*' OR siret LIKE '${processedQuery}*'`,
937
- limit: 100
938
- }
939
- });
940
- const results = response.data.results
941
- .filter((etablissement) => etablissement.etatadministratifetablissement === "Actif")
942
- .map((etablissement) => ({
943
- id: Math.random().toString().slice(2),
944
- companyName: etablissement.denominationunitelegale,
945
- siret: etablissement.siret,
946
- siren: etablissement.siren,
947
- address: `${etablissement.numerovoieetablissement || ""} ${etablissement.typevoieetablissement || ""} ${etablissement.libellevoieetablissement || ""}`,
948
- city: etablissement.libellecommuneetablissement,
949
- zipCode: etablissement.codepostaletablissement,
950
- activityNumber: etablissement.activiteprincipaleunitelegale,
951
- }))
952
- .slice(0, 30)
953
- res.send(results)
954
- } catch (e) {
955
- res.send([])
1225
+ app.get("/searchCompanies", async (req, res) => {
1226
+ const processedQuery = (req.query.query as any).replaceAll(" ", "-");
1227
+ try {
1228
+ const response = await axios.get(
1229
+ `https://public.opendatasoft.com/api/explore/v2.1/catalog/datasets/economicref-france-sirene-v3/records`,
1230
+ {
1231
+ params: {
1232
+ where: `denominationunitelegale LIKE '${processedQuery}*' OR siret LIKE '${processedQuery}*'`,
1233
+ limit: 100,
1234
+ },
956
1235
  }
957
- })
958
-
959
- return app
960
- }
961
-
1236
+ );
1237
+ const results = response.data.results
1238
+ .filter(
1239
+ (etablissement) =>
1240
+ etablissement.etatadministratifetablissement === "Actif"
1241
+ )
1242
+ .map((etablissement) => ({
1243
+ id: Math.random().toString().slice(2),
1244
+ companyName: etablissement.denominationunitelegale,
1245
+ siret: etablissement.siret,
1246
+ siren: etablissement.siren,
1247
+ address: `${etablissement.numerovoieetablissement || ""} ${etablissement.typevoieetablissement || ""} ${etablissement.libellevoieetablissement || ""}`,
1248
+ city: etablissement.libellecommuneetablissement,
1249
+ zipCode: etablissement.codepostaletablissement,
1250
+ activityNumber: etablissement.activiteprincipaleunitelegale,
1251
+ }))
1252
+ .slice(0, 30);
1253
+ res.send(results);
1254
+ } catch (e) {
1255
+ res.send([]);
1256
+ }
1257
+ });
962
1258
 
1259
+ return app;
1260
+ };
963
1261
 
964
1262
  async function enrichConfig(config, db) {
965
- const { rows: info, err } = await db.query("SELECT * FROM information_schema.columns WHERE table_schema = ?", [config.mysql?.database || config.postgresql?.database])
966
- if (err) {
967
- console.error(err)
968
- return
969
- }
970
- for (const view of config.views) {
971
- if (view.type == "table") {
972
- const tableName = view.tableName;
973
- for (const infoEl of info) {
974
- if (infoEl.TABLE_NAME == tableName && infoEl.COLUMN_KEY == "PRI") {
975
- view.primaryId = infoEl.COLUMN_NAME;
976
- }
977
- }
978
- for (const column of view.columns) {
979
- enrichColumn(column, tableName, info)
980
- }
981
- if (view.recordViewPanels) {
982
- for (const panel of view.recordViewPanels) {
983
- if (panel.createConfig) {
984
- for (const column of panel.createConfig.columns) {
985
- enrichColumn(column, tableName, info)
986
- }
987
- }
988
- if (!panel.__id__) {
989
- panel.__id__ = generateUniqueIdForObject(panel)
990
- }
991
- }
992
- }
1263
+ const { rows: info, err } = await db.query(
1264
+ "SELECT * FROM information_schema.columns WHERE table_schema = ?",
1265
+ [config.mysql?.database || config.postgresql?.database]
1266
+ );
1267
+ if (err) {
1268
+ console.error(err);
1269
+ return;
1270
+ }
1271
+ for (const view of config.views) {
1272
+ if (view.type == "table") {
1273
+ const tableName = view.tableName;
1274
+ for (const infoEl of info) {
1275
+ if (infoEl.TABLE_NAME == tableName && infoEl.COLUMN_KEY == "PRI") {
1276
+ view.primaryId = infoEl.COLUMN_NAME;
993
1277
  }
994
- if (view.subviews) {
995
- for (const subview of view.subviews) {
996
- if (!subview.__id__) {
997
- subview.__id__ = generateUniqueIdForObject(subview)
998
- }
1278
+ }
1279
+ for (const column of view.columns) {
1280
+ enrichColumn(column, tableName, info);
1281
+ }
1282
+ if (view.recordViewPanels) {
1283
+ for (const panel of view.recordViewPanels) {
1284
+ if (panel.createConfig) {
1285
+ for (const column of panel.createConfig.columns) {
1286
+ enrichColumn(column, tableName, info);
999
1287
  }
1288
+ }
1289
+ if (!panel.__id__) {
1290
+ panel.__id__ = generateUniqueIdForObject(panel);
1291
+ }
1000
1292
  }
1001
- if (!view.__id__) {
1002
- view.__id__ = generateUniqueIdForObject(view)
1293
+ }
1294
+ }
1295
+ if (view.subviews) {
1296
+ for (const subview of view.subviews) {
1297
+ if (!subview.__id__) {
1298
+ subview.__id__ = generateUniqueIdForObject(subview);
1003
1299
  }
1300
+ }
1301
+ }
1302
+ if (!view.__id__) {
1303
+ view.__id__ = generateUniqueIdForObject(view);
1004
1304
  }
1005
- config.enriched = true;
1305
+ }
1306
+ config.enriched = true;
1006
1307
  }
1007
1308
 
1008
1309
  function enrichColumn(column, tableName, info) {
1009
- if (!column.__id__) {
1010
- column.__id__ = generateUniqueIdForObject(column)
1011
- }
1012
- const infoEl = info.find(x => x.TABLE_NAME == tableName && x.COLUMN_NAME == column.name)
1013
- if (infoEl?.IS_NULLABLE == "NO" && infoEl?.DATA_TYPE != 'text' && (column.required !== true && column.required !== false)) {
1014
- column.required = true;
1015
- }
1016
- if (!column.dataType && infoEl) {
1017
- column.dataType = infoEl.DATA_TYPE;
1018
- if (column.dataType == "enum") {
1019
- column.dataType = "select";
1020
- column.options = JSON.parse(infoEl.COLUMN_TYPE.replace("enum(", "[").replace(")", "]").replace(/'/g, '"'))
1021
- }
1022
- }
1023
- if (column.dataType == "richText") {
1024
- column.name = column.contentField
1025
- }
1026
- if (column.dataType == "file" && column.s3Config) {
1027
- column.baseUrl = `https://${column.s3Config.host}/${column.s3Config.bucket}/`
1310
+ if (!column.__id__) {
1311
+ column.__id__ = generateUniqueIdForObject(column);
1312
+ }
1313
+ const infoEl = info.find(
1314
+ (x) => x.TABLE_NAME == tableName && x.COLUMN_NAME == column.name
1315
+ );
1316
+ if (
1317
+ infoEl?.IS_NULLABLE == "NO" &&
1318
+ infoEl?.DATA_TYPE != "text" &&
1319
+ column.required !== true &&
1320
+ column.required !== false
1321
+ ) {
1322
+ column.required = true;
1323
+ }
1324
+ if (!column.dataType && infoEl) {
1325
+ column.dataType = infoEl.DATA_TYPE;
1326
+ if (column.dataType == "enum") {
1327
+ column.dataType = "select";
1328
+ column.options = JSON.parse(
1329
+ infoEl.COLUMN_TYPE.replace("enum(", "[")
1330
+ .replace(")", "]")
1331
+ .replace(/'/g, '"')
1332
+ );
1028
1333
  }
1334
+ }
1335
+ if (column.dataType == "richText") {
1336
+ column.name = column.contentField;
1337
+ }
1338
+ if (column.dataType == "file" && (column.s3Config || column.gcpConfig)) {
1339
+ const host = column.s3Config?.host || column.gcpConfig?.host;
1340
+ const bucket = column.s3Config?.bucket || column.gcpConfig?.bucket;
1341
+ column.baseUrl = `https://${host}/${bucket}/`;
1342
+ }
1029
1343
  }