@galadrim/galadmin 0.1.226 → 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,1027 +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
- const { rows: allRows } = await db.query(query)
86
- count = allRows.length
87
- } else {
88
- count = countRows?.[0]?.count || 0
89
- }
90
- }
91
-
92
- return {
93
- rows,
94
- queryString,
95
- input: {
96
- query,
97
- params
98
- },
99
- count
100
- }
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
+ };
101
91
  }
102
-
103
- async function findRecordByPosition(params) {
104
- return await getRecords(Object.assign(params, {
105
- page: parseInt(params.position) + parseInt(params.offset),
106
- limit: 1
107
- }));
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
+ }
108
103
  }
109
104
 
110
- app.get('/config', async (req, res) => {
111
- try {
112
- const { mysql, auth, sessionSecret, ...safeConfig } = config;
113
- res.send({
114
- config: serializeJs(safeConfig)
115
- })
116
- } catch (e) {
117
- console.error(e);
118
- res.sendStatus(500)
119
- }
120
- })
105
+ return {
106
+ rows,
107
+ queryString,
108
+ input: {
109
+ query,
110
+ params,
111
+ },
112
+ count,
113
+ };
114
+ }
121
115
 
122
- app.get("/customComponent", async (req, res) => {
123
- try {
124
- const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../src/front/package.json'), 'utf-8'));
125
- const external = Object.keys(pkg.dependencies || {});
126
- const bundle = await rollup.rollup({
127
- input: path.join(__dirname, "../../", req.query.componentPath as string),
128
- plugins: [
129
- resolve(),
130
- babel({
131
- configFile: path.join(__dirname, "../babel.config"),
132
- babelHelpers: "bundled"
133
- })
134
- ],
135
- external
136
- });
137
- const { output } = await bundle.generate({ format: 'cjs', exports: 'auto' });
138
- res.send(output[0].code)
139
- } catch (err) {
140
- console.error(err)
141
- }
142
- })
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
+ }
143
124
 
144
- app.post('/adjacentRecordId', async function (req, res, next) {
145
- const { rows, error } = await findRecordByPosition(req.body)
146
- if (error) {
147
- return res.status(500).send(error)
148
- }
149
- const recordId = rows[0] ? rows[0][req.body.primaryId] : null;
150
- res.send({
151
- recordId: recordId
152
- });
153
- });
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
+ });
154
136
 
155
- const sqlAnd = (conditions) => {
156
- const existingConditions = [];
157
- for (const condition of conditions) {
158
- if (condition && condition.length > 0) {
159
- existingConditions.push(condition)
160
- }
161
- }
162
- if (existingConditions.length <= 1) {
163
- return existingConditions[0] || null
164
- } else {
165
- let result = "";
166
- for (let i = 0; i < existingConditions.length; i++) {
167
- if (i > 0) {
168
- result += " AND "
169
- }
170
- result += "(" + existingConditions[i] + ")"
171
- }
172
- return result
173
- }
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);
174
168
  }
169
+ });
175
170
 
176
- app.post('/foreignSelectRecords', async (req, res, next) => {
177
- const view = config.views[req.body.viewIndex];
178
- const column = [
179
- ...view.columns,
180
- ...(view.recordViewPanels?.map(x => x?.createConfig?.columns)?.filter(x => x)?.flat() || [])
181
- ].find(x => x.__id__ === req.body.columnId)
182
- const queryEscaped = db.dbType === "POSTGRESQL" ? req.body.query?.replace(/'/g, "''") : req.body.query?.replace(/'/g, "\\'")
183
- const { rows: records, error } = await getRecords({
184
- table: column.table,
185
- page: 1,
186
- filters: sqlAnd([
187
- column.filters,
188
- req.body.query ? `${column.searchableName} LIKE '${column.wholeSearch ? '%' : ''}${queryEscaped}%'` : null,
189
- column.filtersFromUser ? column.filtersFromUser(user.userId, user.role) : null
190
- ]),
191
- join: column.join,
192
- selectExpression: column.selectExpression,
193
- orderBy: column.orderBy,
194
- limit: column.limit || 1000
195
- })
196
- if (error) {
197
- return res.status(500).send(error)
198
- }
199
- const options = await Promise.all(records.map(async (record) => {
200
- let label;
201
- if (column.formatForeignValue) {
202
- label = await column.formatForeignValue(record);
203
- } else if (column.formatValue) {
204
- label = await column.formatValue(record);
205
- } else if (column.formattedValue) {
206
- label = evalExpression(column.formattedValue, record)
207
- } else {
208
- label = record[column.name]
209
- }
210
- const value = record[column.foreignName] || record[column.name]
211
- return ({
212
- value,
213
- label
214
- })
215
- }))
216
- res.send(options)
217
- })
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
+ });
218
181
 
219
- app.post('/records', async function (req, res, next) {
220
- const view = config.views[req.body.viewIndex];
221
- const isDesc = req.body.isDesc === null ? view.isDesc : req.body.isDesc
222
- const subview = view.subviews && req.body.subviewIndex !== undefined ? view.subviews[req.body.subviewIndex] : null
223
- const orderBy = req.body.orderBy || subview?.orderBy || view.orderBy
224
- const { rows: records, input, error, count } = await getRecords({
225
- table: view.tableName,
226
- page: req.body.page,
227
- filters: sqlAnd([
228
- view.filters,
229
- subview?.filters,
230
- ...req.body.filters,
231
- view.filtersFromUser ? view.filtersFromUser(user.userId, user.role) : null
232
- ]),
233
- having: subview?.having,
234
- customQuery: view.customQuery,
235
- join: view.join,
236
- selectExpression: view.selectExpression,
237
- orderBy: [orderBy ? `${orderBy} ${isDesc ? "DESC" : "ASC"}` : null, view.primaryId].filter(x => x).join(", "),
238
- primaryId: view.primaryId,
239
- query: req.body.query,
240
- groupBy: view.groupBy,
241
- columns: view.columns,
242
- limit: view.limit || 10,
243
- }, true)
244
- if (error) {
245
- 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 ";
246
196
  }
197
+ result += "(" + existingConditions[i] + ")";
198
+ }
199
+ return result;
200
+ }
201
+ };
247
202
 
248
- for (const column of view.columns) {
249
- if (column.formatValue) {
250
- for (const record of records) {
251
- record[`__formatValue-${column.__id__}__`] = await column.formatValue(record)
252
- }
253
- }
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];
254
247
  }
255
- res.send({
256
- records,
257
- input,
258
- count
259
- })
260
- })
261
-
262
- const formattedValueToFunction = {}
263
-
264
- async function getFormattedValue(column, record) {
265
- let value;
266
- try {
248
+ const value = record[column.foreignName] || record[column.name];
249
+ return {
250
+ value,
251
+ label,
252
+ };
253
+ })
254
+ );
255
+ res.send(options);
256
+ });
267
257
 
268
- record.options = column.options
269
- if (column.formatValueForExport) {
270
- value = await column.formatValueForExport(record)
271
- } else if (column.formattedValue) {
272
- value = evalExpression(column.formattedValue, record)
273
- } else if (column.formatValue) {
274
- value = await column.formatValue(record)
275
- } else if (column.dataType == "tinyint") {
276
- value = record[column.name] == 1 ? "Oui" : "Non"
277
- } else {
278
- value = record[column.name]
279
- }
280
- return value === null || typeof value == "undefined" ? "" : value
281
- } catch (err) {
282
- return value || ""
283
- }
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);
284
303
  }
285
304
 
286
- function evalExpression(expression, data) {
287
- let varString = '';
288
- let statement = '';
289
-
290
- try {
291
- for (let key in data) {
292
- 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])) + ";"
293
- varString += statement
294
- }
295
- const result = eval(varString + expression);
296
- return result;
297
- } catch (e) {
298
- console.log("Problème d'évaluation de l'expression", statement)
299
- console.error(e);
300
- 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);
301
310
  }
311
+ }
302
312
  }
313
+ res.send({
314
+ records,
315
+ input,
316
+ count,
317
+ });
318
+ });
303
319
 
304
- function formatValueForInsertion(value: any, dbType: string) {
305
- if (value !== 0 && !value) {
306
- return "NULL"
307
- }
308
- if (value instanceof Date) {
309
- return value.toISOString()
310
- }
311
- if (typeof value === "string") {
312
- if (dbType === "POSTGRESQL") {
313
- return "'" + value.replace(/'/g, "''") + "'";
314
- } else {
315
- return "'" + value.replace(/'/g, "\\'") + "'"
316
- }
317
- }
318
- if (typeof value === "object") {
319
- return "'" + JSON.stringify(value).replace(/'/g, "\\'") + "'"
320
- }
321
- if (typeof value === "boolean") {
322
- return value ? "TRUE" : "FALSE"
323
- }
324
- if (typeof value === "number") {
325
- return value
326
- }
320
+ const formattedValueToFunction = {};
327
321
 
328
- 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 || "";
329
340
  }
341
+ }
330
342
 
331
- app.post('/recordsExport', async function (req, res) {
332
- const view = config.views[req.body.viewIndex];
333
- const orderBy = req.body.orderBy || view.orderBy;
334
- const isDesc = req.body.isDesc === null ? view.isDesc : req.body.isDesc;
335
- const subview = view.subviews && req.body.subviewIndex !== undefined ? view.subviews[req.body.subviewIndex] : null
336
- const { rows: records, error } = await getRecords({
337
- table: view.tableName,
338
- page: 1,
339
- allRecords: true,
340
- filters: sqlAnd([view.filters, subview?.filters, req.body.filters]),
341
- having: subview?.having,
342
- customQuery: view.customQuery,
343
- join: view.join,
344
- selectExpression: view.selectExpression,
345
- orderBy: orderBy ? `${orderBy} ${isDesc ? "DESC" : "ASC"}` : null,
346
- primaryId: view.primaryId,
347
- query: req.body.query,
348
- groupBy: view.groupBy,
349
- columns: view.columns,
350
- limit: view.limit || 10,
351
- });
352
- if (error) {
353
- return res.status(500).send(error)
354
- }
355
- let output = "";
356
- const columns = view.columns
357
- for (let j = 0; j < columns.length; j++) {
358
- if (!columns[j].hideInTable || columns[j].includeInExport) {
359
- if (j > 0) output += ";"
360
- output += columns[j].label;
361
- }
362
- }
363
- for (let i in records) {
364
- const el = records[i];
365
- output += "\r\n"
366
- for (let j = 0; j < columns.length; j++) {
367
- if (!columns[j].hideInTable || columns[j].includeInExport) {
368
- if (j > 0) output += ";"
369
- if (columns[j].dataType == 'textarea') {
370
- output += '"'
371
- }
372
- output += await getFormattedValue(columns[j], el)
373
- if (columns[j].dataType == 'textarea') {
374
- output += '"'
375
- }
376
- }
377
- }
378
- }
379
- res.send({
380
- content: output
381
- })
382
- })
383
-
384
- app.post('/recordsImport', async function (req, res) {
385
- const form = new multiparty.Form();
386
- form.parse(req, async function (err, fields, files) {
387
- if (err) {
388
- return res.status(500).send(err);
389
- }
390
- // Get viewIndex in fields
391
- const view = config.views[fields.viewIndex[0]];
392
- if (typeof view.parseImportFile !== "function") {
393
- return res.status(500).send("\"parseImportFile\" doit être défini dans la configuration de la vue.");
394
- }
343
+ function evalExpression(expression, data) {
344
+ let varString = "";
345
+ let statement = "";
395
346
 
396
- const records = await view.parseImportFile(files.data[0]);
397
- const columns = [];
398
- if (records.length === 0) {
399
- return res.send({
400
- affectedRows: 0
401
- })
402
- }
403
-
404
- try {
405
- for (let record of records) {
406
- for (let key in record) {
407
- if (!columns.includes(key)) {
408
- columns.push(key)
409
- }
410
- }
411
- }
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
+ }
412
374
 
413
- let queryString;
414
- if (db.dbType === "POSTGRESQL") {
415
- queryString = `INSERT INTO ?? (${columns.map(x => `"${x}"`).join(", ")}) VALUES`;
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
+ }
416
398
 
417
- for (let record of records) {
418
- const valuesList = columns.map(x => formatValueForInsertion(record[x], db.dbType)).join(", ");
419
- queryString += ` (${valuesList}),`;
420
- }
421
- queryString = queryString.slice(0, -1) + ` ON CONFLICT (${view.importFileUniqueField}) DO UPDATE SET `;
399
+ return value;
400
+ }
422
401
 
423
- for (let column of columns) {
424
- queryString += `"${column}" = EXCLUDED."${column}",`;
425
- }
426
- } else {
427
- queryString = "INSERT INTO ?? (" + columns.map(x => "`" + x + "`").join(", ") + ") VALUES";
428
- for (let record of records) {
429
- queryString += " (" + columns.map(x => formatValueForInsertion(record[x], db.dbType)).join(", ") + "),";
430
- }
431
- queryString = queryString.slice(0, -1) + " ON DUPLICATE KEY UPDATE";
432
- for (let column of columns) {
433
- queryString += " `" + column + "` = VALUES(`" + column + "`),"
434
- }
435
- }
436
- queryString = queryString.slice(0, -1); // Remove the trailing comma
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
+ });
437
457
 
438
- const { err, rows: r } = await db.query(queryString, [view.tableName])
439
- if (err) {
440
- console.error(err)
441
- return res.status(500).send({
442
- err,
443
- message: "Une erreur est survenue."
444
- })
445
- }
446
- res.send({
447
- affectedRows: r.affectedRows
448
- })
449
- } catch (err) {
450
- console.error(err)
451
- res.status(500).send({
452
- err,
453
- message: "Une erreur est survenue."
454
- })
455
- }
456
- })
457
- })
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
+ }
458
473
 
459
- app.post('/subrecordsImport', async function (req, res) {
460
- const form = new multiparty.Form();
461
- form.parse(req, async function (err, fields, files) {
462
- if (err) {
463
- return res.status(500).send(err);
464
- }
465
- // Get viewIndex and panelIndex in fields
466
- const view = config.views[fields.viewIndex[0]];
467
- const panel = view.recordViewPanels[fields.panelIndex[0]];
468
- if (typeof panel.parseImportFile !== "function") {
469
- return res.status(500).send("\"parseImportFile\" doit être défini dans la configuration du panneau.");
470
- }
471
- if (!Boolean(panel.createConfig)) {
472
- return res.status(500).send("\"createConfig\" doit être défini dans la configuration du panneau.");
473
- }
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
+ }
474
481
 
475
- const records = await panel.parseImportFile(files.data[0], fields.recordId[0]);
476
- const columns = [];
477
- if (records.length === 0) {
478
- return res.send({
479
- affectedRows: 0
480
- })
482
+ try {
483
+ for (let record of records) {
484
+ for (let key in record) {
485
+ if (!columns.includes(key)) {
486
+ columns.push(key);
481
487
  }
488
+ }
489
+ }
482
490
 
483
- try {
484
- for (let record of records) {
485
- for (let key in record) {
486
- if (!columns.includes(key)) {
487
- columns.push(key)
488
- }
489
- }
490
- }
491
+ let queryString;
492
+ if (db.dbType === "POSTGRESQL") {
493
+ queryString = `INSERT INTO ?? (${columns.map((x) => `"${x}"`).join(", ")}) VALUES`;
491
494
 
492
- let queryString = "INSERT IGNORE INTO ?? (`" + panel.createConfig.recordIdKey + "`, " + columns.map(x => "`" + x + "`").join(", ") + ") VALUES";
493
- for (let record of records) {
494
- queryString += " (" + fields.recordId[0] + ", " + columns.map(x => formatValueForInsertion(record[x], db.dbType)).join(", ") + "),";
495
- }
496
- queryString = queryString.slice(0, -1) + " ON DUPLICATE KEY UPDATE";
497
- for (let column of columns) {
498
- queryString += " `" + column + "` = VALUES(`" + column + "`),"
499
- }
500
- const { err, rows: r } = await db.query(queryString.slice(0, -1), [panel.tableName])
501
- if (err) {
502
- console.error(err)
503
- return res.status(500).send({
504
- err,
505
- message: "Une erreur est survenue."
506
- })
507
- }
508
- res.send({
509
- affectedRows: r.affectedRows
510
- })
511
- } catch (err) {
512
- console.error(err)
513
- res.status(500).send({
514
- err,
515
- message: "Une erreur est survenue."
516
- })
517
- }
518
- })
519
- })
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 `;
520
504
 
521
- app.get('/record', async function (req, res) {
522
- const view = config.views[req.query.viewIndex as string];
523
- const { err, rows } = await db.query("SELECT " + (view.selectExpression || "*") + " FROM ?? " + (view.join || "") + " WHERE ?? = ?", [view.tableName, view.tableName + "." + view.primaryId, req.query.recordId])
524
- if (err) {
525
- return res.status(500).send(err);
526
- }
527
- if (rows.length > 0) {
528
- const record = rows[0]
529
- for (const column of view.columns) {
530
- if (column.formatValue) {
531
- record[`__formatValue-${column.__id__}__`] = await column.formatValue(record)
532
- }
533
- }
534
- res.send(record)
505
+ for (let column of columns) {
506
+ queryString += `"${column}" = EXCLUDED."${column}",`;
507
+ }
535
508
  } else {
536
- 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
+ }
537
525
  }
538
- })
526
+ queryString = queryString.slice(0, -1); // Remove the trailing comma
539
527
 
540
- app.post('/updateRecord', async function (req, res, next) {
541
- const view = config.views[req.body.viewIndex];
542
- for (let key in req.body.data) {
543
- if (req.body.data[key] == "NULL" || req.body.data[key] === "" || req.body.data[key] === "Invalid date") {
544
- req.body.data[key] = null
545
- }
546
- }
547
- if (Object.keys(req.body.data).length === 0) {
548
- return res.sendStatus(200)
549
- }
550
- const queryExpression = Object.keys(req.body.data).map(x => `${x} = ?`).join(", ");
551
- const queryParameters = Object.keys(req.body.data).map(x => req.body.data[x]);
552
- 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]);
553
529
  if (err) {
554
- return res.status(500).send(err);
530
+ console.error(err);
531
+ return res.status(500).send({
532
+ err,
533
+ message: "Une erreur est survenue.",
534
+ });
555
535
  }
556
- if (view.editCallback) {
557
- try {
558
- await axios.post(view.editCallback, {
559
- id: req.body.primaryValue,
560
- data: req.body.data,
561
- previousData: req.body.previousData
562
- })
563
- } catch (e) { }
564
- }
565
- if (view.editCallbackFunction) {
566
- await view.editCallbackFunction({
567
- id: req.body.primaryValue,
568
- data: req.body.data
569
- })
570
- }
571
- res.sendStatus(200)
572
- })
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
+ });
573
548
 
574
- app.post('/customAction', async function (req, res, next) {
575
- const view = config.views[req.body.viewIndex];
576
- const action = view.customActions[req.body.actionIndex];
577
- for (const recordId of req.body.selectedRecordIds) {
578
- try {
579
- const { rows } = await db.query("SELECT " + (view.selectExpression || "*") + " FROM ?? " + (view.join || "") + " WHERE ?? = ?", [view.tableName, view.tableName + "." + view.primaryId, recordId])
580
- const record = rows[0]
581
- const actionResult = await action.action(record, user.userId);
582
- if (actionResult) {
583
- return res.send({
584
- success: true,
585
- message: actionResult
586
- })
587
- }
588
- } catch (err) {
589
- console.error(err)
590
- return res.send({
591
- success: false,
592
- error: err
593
- })
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
+ }
583
+
584
+ try {
585
+ for (let record of records) {
586
+ for (let key in record) {
587
+ if (!columns.includes(key)) {
588
+ columns.push(key);
594
589
  }
590
+ }
595
591
  }
596
- res.send({
597
- success: true,
598
- message: action.successMessage
599
- })
600
- })
601
592
 
602
- app.post('/deleteRecord', async function (req, res) {
603
- const view = config.views[req.body.viewIndex];
604
- const { rows, err: err2 } = await db.query("SELECT " + (view.selectExpression || "*") + " FROM ?? " + (view.join || "") + " WHERE ?? = ?", [view.tableName, view.tableName + "." + view.primaryId, req.body.primaryValue])
605
- if (err2) {
606
- 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
+ "),";
607
608
  }
608
- const deletedRecord = rows[0];
609
- if (!deletedRecord) {
610
- 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 + "`),";
611
612
  }
612
- 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
+ ]);
613
616
  if (err) {
614
- return res.status(500).send(err);
617
+ console.error(err);
618
+ return res.status(500).send({
619
+ err,
620
+ message: "Une erreur est survenue.",
621
+ });
615
622
  }
616
- if (view.deleteCallback) {
617
- try {
618
- await axios.post(view.deleteCallback, {
619
- id: req.body.primaryValue
620
- })
621
- } catch (e) { }
622
- }
623
- if (view.deleteCallbackFunction) {
624
- await view.deleteCallbackFunction({
625
- id: req.body.primaryValue,
626
- deletedRecord
627
- })
628
- }
629
- res.sendStatus(200)
630
- })
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
+ });
631
635
 
632
- app.post('/deleteRecordFromPanel', async function (req, res) {
633
- const view = config.views[req.body.viewIndex];
634
- const panel = view.recordViewPanels.find(x => x.__id__ === req.body.panelId)
635
- const { rows, err: err2 } = await db.query("SELECT * FROM ?? WHERE ?? = ?", [panel.tableName, panel.primaryId, req.body.primaryValue])
636
- if (err2) {
637
- return res.status(500).send(err2);
638
- }
639
- const deletedRecord = rows[0];
640
- const { err } = await db.query("DELETE FROM ?? WHERE ?? = ?", [panel.tableName, panel.primaryId, req.body.primaryValue])
641
- if (err) {
642
- return res.status(500).send(err);
643
- }
644
- if (panel.deleteCallback) {
645
- try {
646
- await axios.post(panel.deleteCallback, {
647
- id: req.body.primaryValue
648
- })
649
- } catch (e) { }
650
- }
651
- if (panel.deleteCallbackFunction) {
652
- await panel.deleteCallbackFunction({
653
- id: req.body.primaryValue,
654
- deletedRecord
655
- })
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);
656
659
  }
657
- res.sendStatus(200)
658
- })
660
+ }
661
+ res.send(record);
662
+ } else {
663
+ res.sendStatus(404);
664
+ }
665
+ });
659
666
 
660
- app.post('/deleteRecords', async function (req, res, next) {
661
- const view = config.views[req.body.viewIndex];
662
- const { err } = await db.query(`DELETE FROM ?? WHERE ?? IN(${req.body.primaryValues.map(x => "'" + x + "'").join(",")})`, [view.tableName, view.primaryId])
663
- if (err) {
664
- 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
+ });
665
737
  }
666
- res.sendStatus(200)
667
- })
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
+ });
668
751
 
669
- app.get('/notificationBadges', async function (req, res) {
670
- 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
+ });
671
796
 
672
- async function checkAndGetNotificationBadge(obj) {
673
- if (obj.notificationBadge) {
674
- const { err, rows } = await db.query(obj.notificationBadge.query);
675
- if (err) {
676
- console.error(err)
677
- } else if (rows.length > 0 && rows[0][obj.notificationBadge.attribute]) {
678
- const notificationBadge = rows[0][obj.notificationBadge.attribute];
679
- if (!obj.notificationBadge.displayCondition || obj.notificationBadge.displayCondition(notificationBadge)) {
680
- notificationBadges[obj.__id__] = notificationBadge;
681
- }
682
- }
683
- }
684
- }
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
+ });
685
833
 
686
- for (const view of config.views) {
687
- await checkAndGetNotificationBadge(view)
688
- if (view.subviews) {
689
- for (const subview of view.subviews) {
690
- await checkAndGetNotificationBadge(subview)
691
- }
692
- }
693
- }
694
- res.send(notificationBadges)
695
- })
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
+ });
845
+
846
+ app.get("/notificationBadges", async function (req, res) {
847
+ const notificationBadges = {};
696
848
 
697
- app.post('/duplicateRecord', async function (req, res, next) {
698
- const primaryId = req.body.duplicateConfig.primaryId;
699
- const { err, rows } = await db.query("SELECT * FROM ?? WHERE ?? = ?", [req.body.table, primaryId, req.body.primaryValue])
849
+ async function checkAndGetNotificationBadge(obj) {
850
+ if (obj.notificationBadge) {
851
+ const { err, rows } = await db.query(obj.notificationBadge.query);
700
852
  if (err) {
701
- return res.status(500).send(err);
702
- }
703
- const record = rows[0];
704
- const data = {}
705
- for (let field of req.body.duplicateConfig.fields) {
706
- 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
+ }
707
865
  }
708
- const { err: err2, rows: r } = await db.query("INSERT IGNORE INTO ?? SET ?", [req.body.table, data])
709
- if (err2) {
710
- 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);
711
874
  }
712
- const newRecordId = r.insertId;
713
- for (let foreignTable of req.body.duplicateConfig.foreignTables) {
714
- const { err, rows } = await db.query("SELECT * FROM ?? WHERE ?? = ?", [foreignTable.table, primaryId, req.body.primaryValue]);
715
- if (err) console.log(err);
716
- for (let record of rows) {
717
- const data = {};
718
- data[primaryId] = newRecordId;
719
- for (let field of foreignTable.fields) {
720
- data[field] = record[field]
721
- }
722
- await db.query("INSERT IGNORE INTO ?? SET ?", [foreignTable.table, data])
723
- }
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];
724
915
  }
725
- res.sendStatus(200)
726
- })
916
+ await db.query("INSERT IGNORE INTO ?? SET ?", [
917
+ foreignTable.table,
918
+ data,
919
+ ]);
920
+ }
921
+ }
922
+ res.sendStatus(200);
923
+ });
727
924
 
728
- app.post('/createRecord', async function (req, res, next) {
729
- const view = config.views[req.body.viewIndex];
730
- 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) {
731
980
  try {
732
- for (let key in req.body.data) {
733
- if (req.body.data[key] == "NULL" || req.body.data[key] === "Invalid date") {
734
- req.body.data[key] = null
735
- }
736
- }
737
- if (panel) {
738
- req.body.data[panel.createConfig.recordIdKey] = req.body.recordId;
739
- }
740
- const primaryColumn = (view || panel)?.columns?.find(x => x.name === (view || panel).primaryId)
741
- if (primaryColumn?.dataType == "uuid") {
742
- req.body.data[primaryColumn.name] = uuidv4()
743
- }
744
- const createPayload = JSON.parse(JSON.stringify(req.body.data));
745
- const columns = panel ? panel.createConfig.columns : view.columns;
746
- for (let key in createPayload) {
747
- if (columns?.find(x => x.name === key)?.includedInCreatePayload === false) {
748
- delete createPayload[key]
749
- }
750
- }
751
- const queryExpression = Object.keys(createPayload).join(", ");
752
- const queryPlaceholders = Object.keys(createPayload).map(x => "?").join(", ");
753
- const queryParameters = Object.keys(createPayload).map(x => createPayload[x]);
754
- const { err, rows: r } = await db.query(`INSERT INTO ?? (${queryExpression}) VALUES(${queryPlaceholders})`, [panel ? panel.tableName : view.tableName, ...queryParameters])
755
- if (err) {
756
- console.error(err)
757
- return res.status(500).send({
758
- err,
759
- message: err.code === 'ER_DUP_ENTRY' ? "Cet élément existe déjà." : "Une erreur est survenue."
760
- })
761
- }
762
- if (panel ? panel.createCallback : view.createCallback) {
763
- try {
764
- await axios.post(panel ? panel.createCallback : view.createCallback, {
765
- id: r.insertId,
766
- data: req.body.data
767
- })
768
- } catch (e) { }
769
- }
770
- if (panel ? panel.createCallbackFunction : view.createCallbackFunction) {
771
- await (panel ? panel.createCallbackFunction : view.createCallbackFunction)({
772
- id: r.insertId,
773
- data: req.body.data
774
- })
775
- }
776
- res.send({
777
- id: r.insertId
778
- })
779
- } catch (err) {
780
- console.error(err)
781
- res.status(500).send({
782
- err,
783
- message: "Une erreur est survenue."
784
- })
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
+ }
785
1026
  }
786
- })
1027
+ }
1028
+ }
1029
+ res.send(rows);
1030
+ });
787
1031
 
788
- app.get('/customQuery', async function (req, res, next) {
789
- const query = (req.query.query as string).replace(/\?/g, req.query.primaryValue as string)
790
- 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) {
791
1036
  if (err) {
792
- return res.status(500).send(err);
793
- }
794
- if (req.query.viewIndex && req.query.panelIndex) {
795
- const panel = config.views[req.query.viewIndex as string].recordViewPanels[req.query.panelIndex as string];
796
- if (panel.formatValue) {
797
- for (const row of rows) {
798
- for (const key in panel.formatValue) {
799
- row[key] = await panel.formatValue[key](row);
800
- }
801
- }
802
- }
1037
+ return res.status(500).send(err);
803
1038
  }
804
- res.send(rows)
805
- })
1039
+ const originalFilename = files[fileParamName][0].originalFilename;
1040
+ const contentType =
1041
+ mime.getType(originalFilename) || "application/octet-stream";
806
1042
 
807
- function uploadFile(column, fileParamName, req, res) {
808
- if (column.s3Config) {
809
- const s3Config = column.s3Config;
810
- const form = new multiparty.Form();
811
- const s3 = new S3({
812
- endpoint: new Endpoint(s3Config.host),
813
- accessKeyId: s3Config.accessKeyId,
814
- secretAccessKey: s3Config.secretAccessKey,
815
- region: s3Config.region,
816
- signatureVersion: "v4",
817
- });
818
- form.parse(req, function (err, fields, files) {
819
- if (err) {
820
- return res.status(500).send(err);
821
- }
822
- const originalFilename = files[fileParamName][0].originalFilename;
823
- 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
+ );
824
1053
 
825
- const extension = originalFilename.match(/(?:\.([^.]+))?$/)[1];
826
- let aspectRatio = null;
827
- if (["png", "jpeg", "jpg", "gif"].includes(extension)) {
828
- const dimensions = sizeOf(files[fileParamName][0].path);
829
- aspectRatio = dimensions.height / dimensions.width;
830
- }
831
- const originalFilenameWithoutExtension = originalFilename.replace("." + extension, "")
832
- 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();
833
1064
 
834
- if (s3Config.signedUrlNeeded) {
835
- const signedUrl = s3.getSignedUrl('putObject', {
836
- Bucket: s3Config.bucket,
837
- Key: path,
838
- ContentType: contentType,
839
- Expires: 150,
840
- });
841
- axios.put(signedUrl, fs.readFileSync(files[fileParamName][0].path), {
842
- maxContentLength: 100000000000,
843
- maxBodyLength: 100000000000,
844
- headers: {
845
- "Content-Type": contentType
846
- }
847
- })
848
- .then(() => {
849
- res.status(200).send({
850
- path: s3Config.pathTransform ? s3Config.pathTransform(path) : path,
851
- aspectRatio,
852
- originalFilename,
853
- });
854
- })
855
- .catch(e => {
856
- res.status(500).send(e)
857
- })
858
- } else {
859
- s3.putObject({
860
- Body: fs.readFileSync(files[fileParamName][0].path),
861
- Bucket: s3Config.bucket,
862
- ContentType: contentType,
863
- ACL: 'public-read',
864
- Key: path
865
- }, function (err, data) {
866
- if (err) return res.status(500).send(err);
867
- res.send({
868
- url: `https://${s3Config.host}/${s3Config.bucket}/${path}`,
869
- path: s3Config.pathTransform ? s3Config.pathTransform(path) : path,
870
- aspectRatio,
871
- originalFilename
872
- })
873
- });
874
- }
1065
+ const bucketName = gcpConfig.bucket;
1066
+ storage
1067
+ .bucket(bucketName)
1068
+ .upload(files[fileParamName][0].path, {
1069
+ destination: path,
875
1070
  })
876
- } else if (column.sftpConfig) {
877
- if (!column.publicPath) {
878
- return res.status(500).send("You need to pass publicPath parameter when using sftpConfig");
879
- }
880
- const form = new multiparty.Form();
881
- form.parse(req, function (err, fields, files) {
882
- if (err) {
883
- return res.status(500).send(err);
884
- }
885
- const pathSplitted = files[fileParamName][0].path.split(/[\/\\]+/);
886
- const filename = pathSplitted[pathSplitted.length - 1];
887
- (async () => {
888
- const sftp = new SftpClient();
889
- await sftp.connect({
890
- host: column.sftpConfig.host,
891
- port: 22,
892
- username: column.sftpConfig.user,
893
- password: column.sftpConfig.password
894
- });
895
- await sftp.fastPut(files[fileParamName][0].path, column.publicPath + (column.subdirectory ? "/" + column.subdirectory : "") + "/" + filename);
896
- await sftp.end();
897
- res.send({
898
- path: filename,
899
- originalFilename: files[fileParamName][0].originalFilename
900
- })
901
- fs.unlinkSync(files[fileParamName][0].path)
902
- })();
1071
+ .then(() => {
1072
+ res.status(200).send({
1073
+ path: gcpConfig.pathTransform
1074
+ ? gcpConfig.pathTransform(path)
1075
+ : path,
1076
+ aspectRatio,
1077
+ originalFilename,
1078
+ });
903
1079
  })
1080
+ .catch((e) => {
1081
+ res.status(500).send(e);
1082
+ });
904
1083
  } else {
905
- const form = new multiparty.Form({
906
- 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,
907
1105
  });
908
- form.parse(req, function (err, fields, files) {
909
- if (err) {
910
- return res.status(500).send(err);
911
- }
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);
912
1137
  res.send({
913
- path: files[fileParamName][0].path.replace(column.publicPath, ""),
914
- originalFilename: files[fileParamName][0].originalFilename
915
- })
916
- })
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);
917
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
+ });
918
1200
  }
1201
+ }
919
1202
 
920
- app.post('/uploadFileFromBetterRichTextInput', async function (req, res, next) {
921
- const fileUploadProperties = JSON.parse(req.headers['file-upload-properties'] as string);
922
- uploadFile(fileUploadProperties, "upload", req, res);
923
- })
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
+ );
924
1212
 
925
- app.post('/uploadFile', async function (req, res, next) {
926
- const view = config.views[req.query.viewIndex as string];
927
- const column = [
928
- ...view.columns,
929
- ...(view.recordViewPanels?.map(x => x?.createConfig?.columns)?.filter(x => x)?.flat() || [])
930
- ].find(x => x.__id__ == req.query.columnId)
931
- uploadFile(column, "file", req, res);
932
- })
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
+ });
933
1224
 
934
- app.get('/searchCompanies', async (req, res) => {
935
- const processedQuery = (req.query.query as any).replaceAll(" ", "-")
936
- try {
937
- const response = await axios.get(`https://public.opendatasoft.com/api/explore/v2.1/catalog/datasets/economicref-france-sirene-v3/records`, {
938
- params: {
939
- where: `denominationunitelegale LIKE '${processedQuery}*' OR siret LIKE '${processedQuery}*'`,
940
- limit: 100
941
- }
942
- });
943
- const results = response.data.results
944
- .filter((etablissement) => etablissement.etatadministratifetablissement === "Actif")
945
- .map((etablissement) => ({
946
- id: Math.random().toString().slice(2),
947
- companyName: etablissement.denominationunitelegale,
948
- siret: etablissement.siret,
949
- siren: etablissement.siren,
950
- address: `${etablissement.numerovoieetablissement || ""} ${etablissement.typevoieetablissement || ""} ${etablissement.libellevoieetablissement || ""}`,
951
- city: etablissement.libellecommuneetablissement,
952
- zipCode: etablissement.codepostaletablissement,
953
- activityNumber: etablissement.activiteprincipaleunitelegale,
954
- }))
955
- .slice(0, 30)
956
- res.send(results)
957
- } catch (e) {
958
- 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
+ },
959
1235
  }
960
- })
961
-
962
- return app
963
- }
964
-
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
+ });
965
1258
 
1259
+ return app;
1260
+ };
966
1261
 
967
1262
  async function enrichConfig(config, db) {
968
- const { rows: info, err } = await db.query("SELECT * FROM information_schema.columns WHERE table_schema = ?", [config.mysql?.database || config.postgresql?.database])
969
- if (err) {
970
- console.error(err)
971
- return
972
- }
973
- for (const view of config.views) {
974
- if (view.type == "table") {
975
- const tableName = view.tableName;
976
- for (const infoEl of info) {
977
- if (infoEl.TABLE_NAME == tableName && infoEl.COLUMN_KEY == "PRI") {
978
- view.primaryId = infoEl.COLUMN_NAME;
979
- }
980
- }
981
- for (const column of view.columns) {
982
- enrichColumn(column, tableName, info)
983
- }
984
- if (view.recordViewPanels) {
985
- for (const panel of view.recordViewPanels) {
986
- if (panel.createConfig) {
987
- for (const column of panel.createConfig.columns) {
988
- enrichColumn(column, tableName, info)
989
- }
990
- }
991
- if (!panel.__id__) {
992
- panel.__id__ = generateUniqueIdForObject(panel)
993
- }
994
- }
995
- }
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;
996
1277
  }
997
- if (view.subviews) {
998
- for (const subview of view.subviews) {
999
- if (!subview.__id__) {
1000
- subview.__id__ = generateUniqueIdForObject(subview)
1001
- }
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);
1002
1287
  }
1288
+ }
1289
+ if (!panel.__id__) {
1290
+ panel.__id__ = generateUniqueIdForObject(panel);
1291
+ }
1003
1292
  }
1004
- if (!view.__id__) {
1005
- 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);
1006
1299
  }
1300
+ }
1301
+ }
1302
+ if (!view.__id__) {
1303
+ view.__id__ = generateUniqueIdForObject(view);
1007
1304
  }
1008
- config.enriched = true;
1305
+ }
1306
+ config.enriched = true;
1009
1307
  }
1010
1308
 
1011
1309
  function enrichColumn(column, tableName, info) {
1012
- if (!column.__id__) {
1013
- column.__id__ = generateUniqueIdForObject(column)
1014
- }
1015
- const infoEl = info.find(x => x.TABLE_NAME == tableName && x.COLUMN_NAME == column.name)
1016
- if (infoEl?.IS_NULLABLE == "NO" && infoEl?.DATA_TYPE != 'text' && (column.required !== true && column.required !== false)) {
1017
- column.required = true;
1018
- }
1019
- if (!column.dataType && infoEl) {
1020
- column.dataType = infoEl.DATA_TYPE;
1021
- if (column.dataType == "enum") {
1022
- column.dataType = "select";
1023
- column.options = JSON.parse(infoEl.COLUMN_TYPE.replace("enum(", "[").replace(")", "]").replace(/'/g, '"'))
1024
- }
1025
- }
1026
- if (column.dataType == "richText") {
1027
- column.name = column.contentField
1028
- }
1029
- if (column.dataType == "file" && column.s3Config) {
1030
- 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
+ );
1031
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
+ }
1032
1343
  }