@opengis/fastify-table 1.4.29 → 1.4.31

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.
@@ -1,11 +1,5 @@
1
- import getExtraProperties from './controllers/properties.get.js';
2
- import addExtraProperties from './controllers/properties.add.js';
3
-
4
- import getSettingsApp from './controllers/admin.properties.get.js';
5
- import postSettingsApp from './controllers/admin.properties.post.js';
6
-
7
- import getSettingsTable from './controllers/table.properties.get.js';
8
- import postSettingsTable from './controllers/table.properties.post.js';
1
+ import getAppSettings from './controllers/properties.get.js';
2
+ import postAppSettings from './controllers/properties.post.js';
9
3
 
10
4
  const propertiesSchema = {
11
5
  type: 'object',
@@ -24,14 +18,8 @@ const policy = ['site']; // cms etc.
24
18
 
25
19
  async function plugin(app, config = {}) {
26
20
  const { prefix = '/api' } = config;
27
- app.get(`${prefix}/properties/:id`, { config: { policy }, schema: propertiesSchema }, getExtraProperties);
28
- app.post(`${prefix}/properties/:id`, { config: { policy }, schema: propertiesSchema }, addExtraProperties);
29
-
30
- app.get(`${prefix}/settings-app/:key?`, { config: { policy }, schema: propertiesSchema }, getSettingsApp);
31
- app.post(`${prefix}/settings-app`, { config: { policy: ['admin'] } }, postSettingsApp);
32
-
33
- app.get(`${prefix}/settings-table/:table/:entity?`, { config: { policy } }, getSettingsTable);
34
- app.post(`${prefix}/settings-table/:table`, { config: { policy } }, postSettingsTable);
21
+ app.get(`${prefix}/settings-app/:entity?`, { config: { policy }, schema: propertiesSchema }, getAppSettings);
22
+ app.post(`${prefix}/settings-app/:entity?`, { config: { policy: ['admin'] } }, postAppSettings);
35
23
  }
36
24
 
37
25
  export default plugin;
@@ -1,87 +0,0 @@
1
- import path from 'node:path';
2
- import { rename, readFile, writeFile } from 'node:fs/promises';
3
-
4
- import { formidable } from 'formidable';
5
- import { imageSize } from 'image-size';
6
-
7
- import config from '../../../../config.js';
8
-
9
- import { images } from './allowedExtensions.js';
10
- import grpc from '../../grpc/grpc.js';
11
-
12
- const { resizeImage } = grpc();
13
- const { maxFileSize = 512 } = config;
14
-
15
- async function handleImages(file) {
16
- const ext = path.extname(file.originalFilename).toLowerCase();
17
- if (images.find(el => el === ext.substring(1)) && file.size >= 5000000) {
18
- const fileData = await readFile(file.filepath, { buffer: true });
19
- const { width = 320, height = 240 } = imageSize(fileData) || {};
20
-
21
- const ratio = width / height;
22
-
23
- const resizeWidth = Math.min(width, 2048);
24
- const resizeHeight = resizeWidth / ratio;
25
-
26
- const { result } = await resizeImage({
27
- base64: Buffer.from(fileData).toString('base64'),
28
- width: resizeWidth,
29
- height: resizeHeight,
30
- quality: 75,
31
- });
32
-
33
- await rename(file.filepath, `${file.filepath}_original${ext}`);
34
- await writeFile(file.filepath + ext, Buffer.from(result, 'base64'));
35
-
36
- file.originalFilepath = `${file.filepath}_original${ext}`;
37
- file.filepath += ext;
38
-
39
- return file;
40
- }
41
- return null;
42
- }
43
-
44
- function promisify(func) {
45
- // eslint-disable-next-line func-names
46
- return async function (request) {
47
- return new Promise((resolve, reject) => {
48
- func(request, (err, fields, files) => {
49
- if (err) { reject(err); }
50
- resolve({ fields, files });
51
- });
52
- });
53
- };
54
- }
55
- /*
56
- [{
57
- filepath,
58
- mimetype,
59
- originalFilename,
60
- size
61
- }]
62
- */
63
- async function upload({ req, folder }) {
64
- const form = formidable({ uploadDir: folder, keepExtensions: false, maxFileSize: maxFileSize * 1024 * 1024 });
65
- const parse = promisify(form.parse.bind(form));
66
-
67
- const files = await parse(req.raw).then((data) => data.files);
68
-
69
- if (!files?.file?.[0]) return {};
70
- const file = files.file[0];
71
- const ext = path.extname(file.originalFilename).toLowerCase();
72
-
73
- // resize big images
74
- const result = await handleImages(file);
75
- if (result) {
76
- return result;
77
- }
78
-
79
- // rename with ext, keepExtensions has bug if file with .
80
- await rename(file.filepath, file.filepath + ext);
81
-
82
- file.filepath += ext;
83
-
84
- return file;
85
- }
86
-
87
- export default upload;
@@ -1,27 +0,0 @@
1
- /* eslint-disable camelcase */
2
- const table = 'admin.properties';
3
-
4
- import { pgClients, getTemplatePath, getTemplate } from '../../../../utils.js';
5
-
6
- export default async function getSettingsApp({
7
- pg = pgClients.client,
8
- }, reply) {
9
- const time = Date.now();
10
-
11
- if (!pg) {
12
- return reply.status(500).send('empty pg');
13
- }
14
-
15
- if (!pg?.pk?.[table]) {
16
- return reply.status(404).send('table not found');
17
- }
18
-
19
- const { rows = [] } = await pg.query('select property_key as key, property_text, property_json from admin.properties');
20
-
21
- const settings = rows.reduce((acc, { key, property_text, property_json }) => ({ ...acc, [key]: property_text || property_json }), {});
22
-
23
- const forms = await Promise.all(getTemplatePath('setting')
24
- .map(async (el) => ({ name: el[0], body: await getTemplate('setting', el[0]) })));
25
-
26
- return reply.status(200).send({ time: Date.now() - time, forms, settings });
27
- }
@@ -1,54 +0,0 @@
1
- const table = 'admin.properties';
2
-
3
- import { dataInsert, pgClients } from '../../../../utils.js';
4
-
5
- function checkValueType(val) {
6
- if (val) {
7
- if (typeof val === 'object') {
8
- return 'property_json';
9
- }
10
- if (typeof val === 'number' && (!/\D/.test(val.toString()) && val.toString().length < 10)) {
11
- return 'property_int';
12
- }
13
- }
14
- return 'property_text';
15
- }
16
-
17
- export default async function postSettingsApp({
18
- pg = pgClients.client, body = {}, user = {}, uid = user?.uid,
19
- }, reply) {
20
- if (!user?.user_type?.includes?.('admin')) {
21
- return reply.status(403).send('access restricted');
22
- }
23
- const { key, val } = body;
24
-
25
- if ((!key || !val) && !Object.keys(body).length) {
26
- return reply.status(400).send('not enough body params');
27
- }
28
-
29
- if (!pg) {
30
- return reply.status(500).send('empty pg');
31
- }
32
-
33
- if (!pg?.pk?.[table]) {
34
- return reply.status(404).send('table not found');
35
- }
36
-
37
- const keys = Object.keys(body);
38
- await pg.query(`delete from ${table} where property_key=any($1)`, [keys]);
39
-
40
- await Promise.all(keys.filter(el => body[el]).map(async (el) => {
41
- const columnType = table === 'admin.user_properties'
42
- ? 'property_json'
43
- : checkValueType(body[el]);
44
-
45
- await dataInsert({
46
- pg,
47
- table,
48
- data: { property_key: el, [columnType]: body[el] },
49
- uid,
50
- });
51
- }));
52
-
53
- return reply.status(200).send('ok');
54
- }
@@ -1,55 +0,0 @@
1
- import { dataInsert } from '../../../../utils.js';
2
-
3
- const table = 'crm.properties';
4
-
5
- function checkKeyType({ body, key }) {
6
- if (typeof body[key] === 'number' && (!/\D/.test(body[key].toString()) && body[key].toString().length < 10)) {
7
- return { [key]: 'int' };
8
- } if (typeof body[key] === 'object') {
9
- return { [key]: 'json' };
10
- }
11
- if (Date.parse(body[key], 'yyyy/MM/ddTHH:mm:ss.000Z')) {
12
- return { [key]: 'date' };
13
- }
14
- return { [key]: 'text' };
15
- }
16
-
17
- export default async function addExtraProperties({
18
- pg, params = {}, body = {}, user = {},
19
- }) {
20
- const { id } = params;
21
- const { uid } = user;
22
- if (!uid) {
23
- return { message: 'access restricted: uid', status: 403 };
24
- }
25
- if (!id) {
26
- return { message: 'not enougn params: 1', status: 400 };
27
- }
28
- const extraProperties = Object.keys(body);
29
- if (!extraProperties.length) {
30
- return { message: 'not enougn params: 2', status: 400 };
31
- }
32
-
33
- if (!pg.pk?.[table]) {
34
- return { message: 'table not found: crm.properties', status: 400 };
35
- }
36
-
37
- await pg.query('delete from crm.properties where object_id=$1', [id]); // rewrite?
38
- const keyTypeMatch = extraProperties.filter((key) => body[key]).reduce((acc, curr) => Object.assign(acc, checkKeyType({ body, key: curr })), {});
39
- const res = await Promise.all(Object.keys(keyTypeMatch).map(async (key) => {
40
- const propertyType = keyTypeMatch[key];
41
- const { rows = [] } = await dataInsert({
42
- pg,
43
- table,
44
- data: {
45
- property_type: propertyType,
46
- property_key: key,
47
- object_id: id,
48
- [`property_${propertyType}`]: body[key],
49
- },
50
- uid,
51
- });
52
- return { id: rows[0]?.property_id, type: propertyType, value: body[key] };
53
- }));
54
- return { message: { rows: res }, status: 200 };
55
- }
@@ -1,116 +0,0 @@
1
- import { pgClients, getMeta, getTemplate } from '../../../../utils.js';
2
-
3
- import getColumnMetaData from '../utils/getColumnMetaData.js';
4
-
5
- /**
6
- *
7
- * @method GET
8
- * @priority 4
9
- * @alias getSettingsTable
10
- * @type api
11
- * @tag custom
12
-
13
- * @returns {String} message and status
14
- */
15
-
16
- export default async function getSettingsTable({
17
- pg = pgClients.client, params = {}, user = {}, query = {},
18
- }, reply) {
19
- const { table, entity } = params;
20
- const { uid } = user;
21
- const time = [];
22
-
23
- if (!uid) {
24
- return reply.status(401).send('unauthorized');
25
- }
26
-
27
- if (!table) {
28
- return reply.status(400).send('not enough params: table');
29
- }
30
-
31
- if (!pg) {
32
- return reply.status(500).send('empty pg');
33
- }
34
-
35
- const loadTable = await getTemplate('table', table);
36
-
37
- const tableName = loadTable?.table || table;
38
- const meta = await getMeta({ pg, table: tableName });
39
- const { view, pk } = meta || {};
40
- time.push(Date.now());
41
-
42
- if (!pk && !view) {
43
- return reply.status(400).send(`table primary key not found: ${tableName}`);
44
- }
45
-
46
- const { rows: geometry } = meta?.geom
47
- ? await pg.queryCache(`select st_geometrytype(${meta?.geom}) as type ,count(*)
48
- from ${tableName} group by st_geometrytype(${meta?.geom})`, { table: tableName }) : [];
49
- time.push(Date.now());
50
-
51
- const stat = !view ? await getColumnMetaData({ pg, table: tableName }) : {};
52
- time.push(Date.now());
53
-
54
- const { total = 0 } = await pg.queryCache(`select count(*) as total from ${tableName}`, { table: tableName })
55
- .then(el => el.rows?.[0] || {});
56
- time.push(Date.now());
57
-
58
- const { property_json: customColumns = [] } = await pg.query(`select property_json::json from setting.property
59
- where property_entity='customColumn' and object_id=$1`, [table]).then(el => el.rows?.[0] || {});
60
-
61
- const { property_json: tableSettingColumns } = await pg.query(`select property_json::json from setting.property
62
- where property_entity='column' and object_id=$1`, [table]).then(el => el.rows?.[0] || {});
63
-
64
- const columns = (tableSettingColumns || loadTable?.columns || meta?.columns)?.map((el) => ({ ...el, title: el.title || el.name }))
65
- ?.filter((value, index, array) => array?.map((el) => el.name).indexOf(value.name) === index); // get unique columns
66
-
67
- const cls = columns?.filter((el) => ['badge', 'select'].includes(el.format) && (el.option || el.data))
68
- ?.map((el) => (el.option || el.data))
69
- ?.filter((value, index, array) => array.indexOf(value) === index); // get unique cls
70
-
71
- const { property_json: userFilterList } = await pg.query(`select property_json::json from setting.property
72
- where property_entity='filter' and object_id=$1`, [table]).then(el => el.rows?.[0] || {});
73
-
74
- const { property_json: customQuery } = await pg.query(`select json_agg(json_build_object('id',property_id,'name', property_key,
75
- 'query', property_text, 'disabled', coalesce((property_json::json->>'disabled')::boolean,false) )) as property_json
76
- from setting.property where 1=1 and property_entity='customQuery' and object_id=$1`, [table]).then(el => el.rows?.[0] || {});
77
-
78
- time.push(Date.now());
79
-
80
- const debug = user?.user_type?.includes('admin') && query?.debug
81
- ? {
82
- view,
83
- tableName,
84
- metaColumns: meta?.columns,
85
- columnProperties: tableSettingColumns,
86
- }
87
- : undefined;
88
-
89
- const res = {
90
- time: {
91
- geom: time[1] - time[0],
92
- stat: time[2] - time[1],
93
- count: time[3] - time[2],
94
- total: time[4] - time[0],
95
- },
96
- debug,
97
- model: tableName,
98
- total,
99
- geometry,
100
- stat,
101
- cls,
102
- columns,
103
- filters: userFilterList?.length
104
- ? userFilterList
105
- : loadTable?.filters?.map((el) => ({ ...el, title: el.title || el.ua })),
106
- customQuery: customQuery?.length ? customQuery : [],
107
- customColumns,
108
- customFilters: userFilterList || [],
109
- };
110
-
111
- if (entity) {
112
- return reply.status(200).send({ [entity]: res[entity] });
113
- }
114
-
115
- return reply.status(200).send(res);
116
- }
@@ -1,125 +0,0 @@
1
- import { pgClients, getRedis } from '../../../../utils.js';
2
-
3
- import refreshData from '../utils/refreshData.js';
4
-
5
- const rclient = getRedis();
6
-
7
- /**
8
- *
9
- * @method POST
10
- * @priority 4
11
- * @alias postSettingsTable
12
- * @type api
13
- * @tag custom
14
- * @returns {String} message and status
15
- */
16
-
17
- export default async function postTablpostSettingsTableeProperties({
18
- pg = pgClients.client, params = {}, body = {}, user = {},
19
- }, reply) {
20
- const { table } = params;
21
- const { uid } = user;
22
-
23
- if (!uid) {
24
- return reply.status(401).send('unauthorized');
25
- }
26
-
27
- if (!table) {
28
- return reply.status(400).send('not enough params: table');
29
- }
30
-
31
- if (!pg) {
32
- return reply.status(500).send('empty pg');
33
- }
34
-
35
- const {
36
- deleted = [],
37
- columns = [],
38
- filters = [],
39
- customColumns = [],
40
- customQuery = [],
41
- } = body;
42
-
43
- const cacheKey = `${pg.options?.database}:filter-list:${table}`;
44
-
45
- if (Array.isArray(deleted) && deleted?.length) {
46
- const sqlList = {
47
- columns: 'delete from setting.property where property_entity=\'column\' and object_id=$1',
48
- filters: 'delete from setting.property where property_entity=\'filter\' and object_id=$1',
49
- customColumns: `delete from setting.custom_data where attr in (select cf_id from setting.custom_field where object_id=$1);
50
- delete from setting.custom_field where object_id=$1;
51
- delete from setting.property where property_entity='customColumn' and object_id=$1;`,
52
- customQuery: 'delete from setting.property where property_entity=\'customQuery\' and object_id=$1;',
53
- };
54
- const q = deleted.map((item) => sqlList[item])?.filter((el) => el)?.join(' ');
55
- const { rowCount = 0 } = await pg.query(q, [table]);
56
-
57
- if (['customColumn', 'column'].find((el) => deleted.includes(el))) {
58
- // await funcs.applyHook('preTemplate', { pg, name: table, type: 'table' });
59
- }
60
- if (['customQuery', 'filter'].find((el) => deleted.includes(el))) {
61
- await rclient.del(cacheKey);
62
- }
63
-
64
- if (rowCount === 0) {
65
- return reply.status(200).send('nothing to restore');
66
- }
67
-
68
- return reply.status(200).send({
69
- command: 'DELETE',
70
- deleted,
71
- table,
72
- result: 'success',
73
- });
74
- }
75
-
76
- const result = {};
77
- // custom filter query (sql)
78
- if (customQuery?.length) {
79
- const res = await refreshData({
80
- pg, entity: 'customQuery', table, uid, data: customQuery,
81
- });
82
- if (res?.error) return res;
83
- Object.assign(result, { customQuery: res });
84
- }
85
-
86
- // columns
87
- if (columns?.length) {
88
- const res = await refreshData({
89
- pg, entity: 'column', table, uid, data: columns,
90
- });
91
- if (res?.error) return res;
92
- Object.assign(result, { columns: res });
93
- }
94
-
95
- // custom columns
96
- if (customColumns?.length) {
97
- const colModel = await pg.query(`select property_json::json from setting.property
98
- where property_entity='customColumn' and object_id=$1`, [table]).then((el) => el.rows?.[0]?.property_json);
99
-
100
- const res = await refreshData({
101
- pg, entity: 'customColumn', table, uid, data: customColumns, colModel,
102
- });
103
- if (res?.error) return res;
104
- Object.assign(result, { customColumns: res });
105
- }
106
-
107
- // filters
108
- if (filters?.length) {
109
- const res = await refreshData({
110
- pg, entity: 'filter', table, uid, data: filters,
111
- });
112
- if (res?.error) return res;
113
-
114
- Object.assign(result, { filters: res });
115
- }
116
-
117
- if (columns?.length || customColumns?.length) {
118
- // await funcs.applyHook('preTemplate', { pg, type: 'table', name: table });
119
- }
120
- if (filters?.length || customQuery?.length) {
121
- await rclient.del(cacheKey);
122
- }
123
-
124
- return reply.status(200).send(result);
125
- }
@@ -1,26 +0,0 @@
1
- import { getMeta, getPG } from '../../../../utils.js';
2
-
3
- export default async function dataInsert({
4
- table, data, pg: pg1,
5
- }) {
6
- const pg = pg1 || getPG({ name: 'client' });
7
- if (!data) return null;
8
- const { columns } = await getMeta(table);
9
- if (!columns) return null;
10
-
11
- const names = columns.map((el) => el.name);
12
- const filterData = Object.keys(data)
13
- .filter((el) => data[el] && names.includes(el)).map((el) => [el, data[el]]);
14
-
15
- const insertQuery = `insert into ${table}
16
-
17
- ( ${filterData?.map((key) => `"${key[0]}"`).join(',')})
18
-
19
- values (${filterData?.map((key, i) => (key[0] === 'property_json' ? `to_json($${i + 1}::${key[1] && typeof key[1] === 'object' ? 'json' : 'text'})` : `$${i + 1}`)).join(',')})
20
-
21
- returning *`;
22
-
23
- const res = await pg.query(insertQuery, [...filterData.map((el) => (typeof el[1] === 'object' && (!Array.isArray(el[1]) || typeof el[1]?.[0] === 'object') ? JSON.stringify(el[1]) : el[1]))]) || {};
24
-
25
- return res;
26
- }
@@ -1,19 +0,0 @@
1
- import { pgClients, getMeta } from '../../../../utils.js';
2
-
3
- const systemColumns = ['files', 'uid', 'editor_date', 'editor_id', 'cdate', 'created_at', 'updated_at', 'created_by', 'updated_by'];
4
-
5
- export default async function getColumnMetaData({
6
- pg = pgClients.client, table,
7
- }) {
8
- if (!table) return null;
9
-
10
- const { columns = [] } = await getMeta({ pg, table });
11
- const columnList = columns.filter(el => !systemColumns.includes(el.name)).map(el => el.name);
12
-
13
- const sql = `select count(*) as total, ${columnList.map((item) => `(count(*) filter(where ${item} is not null)) as "${item}"`).join(',')} from ${table}`;
14
- const res = await pg.queryCache(sql, { table }).then(el => el.rows?.[0] || {});
15
-
16
- return columns
17
- .filter(el => !systemColumns.includes(el.name))
18
- .map((row) => Object.assign(row, { percent: ((+res[row.name] / +res.total) * 100).toFixed(0) }));
19
- }
@@ -1,129 +0,0 @@
1
- import { randomUUID, createHash } from 'node:crypto';
2
-
3
- import { dataInsert, pgClients } from '../../../../utils.js';
4
-
5
- function prepareResult(res) {
6
- const arr = (res.rows || (Array.isArray(res) ? res : Object.values(res || {})));
7
- return {
8
- command: 'INSERT',
9
- count: res?.rowCount || arr.filter((el) => el.command === 'INSERT')
10
- ?.map((el) => ({ result: 'success', count: el.rowCount }))
11
- ?.reduce((acc, curr) => acc + curr.count, 0),
12
- };
13
- }
14
-
15
- export default async function refreshData({
16
- pg = pgClients.client, entity, table, uid, data,
17
- }) {
18
- if (!['filter', 'column', 'customQuery', 'customColumn'].includes(entity)) {
19
- return { message: 'invalid params: entity', status: 400 };
20
- }
21
-
22
- const client = await pg.connect();
23
-
24
- /* getMeta support */
25
- client.options = pg.options;
26
- client.tlist = pg.tlist;
27
- client.pgType = pg.pgType;
28
- client.pk = pg.pk;
29
- client.relkinds = pg.relkinds;
30
-
31
- const result = {};
32
-
33
- try {
34
- await client.query('BEGIN');
35
-
36
- if (['filter', 'column'].includes(entity)) {
37
- data?.forEach((el) => Object.assign(el, { id: el.id || randomUUID() }));
38
- await client.query('delete from setting.property where object_id=$1 and property_entity=$2', [table, entity]);
39
-
40
- const res = await dataInsert({
41
- pg: client,
42
- table: 'setting.property',
43
- data: {
44
- property_entity: entity,
45
- object_id: table,
46
- property_json: JSON.stringify(data), // ?.replace(/'+/g, "'")?.replace(/'/g, "''")
47
- },
48
- uid,
49
- });
50
- Object.assign(result, res);
51
- }
52
-
53
- if (['customQuery'].includes(entity)) {
54
- await client.query('delete from setting.property where object_id=$1 and property_entity=$2', [table, entity]);
55
-
56
- const res = await Promise.all(data?.map(async (el) => dataInsert({
57
- pg: client,
58
- table: 'setting.property',
59
- data: {
60
- property_entity: entity,
61
- object_id: table,
62
- property_key: el.name,
63
- property_text: el.query,
64
- property_json: { disabled: el.disabled },
65
- },
66
- uid,
67
- })));
68
- Object.assign(result, res);
69
- }
70
-
71
- if (entity === 'customColumn') {
72
- const prefix = createHash('md5').update(table).digest('hex').substr(0, 10);
73
- const data1 = [];
74
-
75
- await Promise.all(data.map(async (el, index) => {
76
- const obj = {
77
- cf_id: `col_${prefix}_${index}`,
78
- cf_name: el.title || el.ua || el.name,
79
- cf_type: el.format || el.type,
80
- cf_default: el.default,
81
- cf_notnull: el.notnull,
82
- object_id: table,
83
- tablename: table,
84
- };
85
-
86
- data1.push({
87
- name: obj.cf_id,
88
- title: obj.cf_name,
89
- format: obj.cf_type,
90
- option: data[index]?.option,
91
- hidden: data[index]?.hidden,
92
- custom: true,
93
- });
94
-
95
- await dataInsert({
96
- pg: client,
97
- table: 'setting.custom_field',
98
- data: obj,
99
- uid,
100
- });
101
- }));
102
-
103
- await client.query('delete from setting.custom_field where $1 in (tablename, object_id)', [table]);
104
- await client.query('delete from setting.property where object_id=$1 and property_entity=$2', [table, entity]);
105
-
106
- const data2 = JSON.stringify(data1?.filter((value, index, array) => array.indexOf(value) === index)); // ?.replace(/'+/g, "'")?.replace(/'/g, "''")
107
- const res = await dataInsert({
108
- pg: client,
109
- table: 'setting.property',
110
- data: {
111
- property_entity: 'customColumn',
112
- object_id: table,
113
- property_json: data2,
114
- },
115
- uid,
116
- });
117
- Object.assign(result, res);
118
- }
119
- await client.query('COMMIT');
120
- return prepareResult(result);
121
- }
122
- catch (err) {
123
- await client.query('ROLLBACK');
124
- return { error: err.toString(), status: 500 };
125
- }
126
- finally {
127
- client.release();
128
- }
129
- }