@opengis/fastify-table 1.4.18 → 1.4.20
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/package.json +1 -1
- package/redactionList.js +1 -1
- package/server/migrations/properties.sql +1 -0
- package/server/plugins/crud/funcs/dataInsert.js +88 -60
- package/server/plugins/crud/funcs/dataUpdate.js +87 -37
- package/server/plugins/crud/funcs/utils/getInsertQuery.js +44 -0
- package/server/plugins/crud/funcs/utils/logChanges.js +4 -2
- package/server/plugins/extra/extraData.js +27 -14
- package/server/plugins/extra/extraDataGet.js +19 -4
- package/server/routes/crud/controllers/insert.js +0 -17
- package/server/routes/crud/controllers/update.js +0 -21
- package/server/routes/logger/controllers/logger.file.js +97 -93
- package/server/routes/logger/controllers/utils/checkUserAccess.js +24 -19
- package/server/routes/logger/index.js +9 -7
- package/server/routes/table/functions/getData.js +1 -1
- package/utils.js +2 -0
package/package.json
CHANGED
package/redactionList.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
|
|
3
|
-
const redactionList = ['clientSecret', 'clientId'];
|
|
3
|
+
const redactionList = ['clientSecret', 'clientId', 'password'];
|
|
4
4
|
|
|
5
5
|
const customRedactionList = fs.existsSync('redactionList.json') ? JSON.parse(fs.readFileSync('redactionList.json')) : [];
|
|
6
6
|
|
|
@@ -120,6 +120,7 @@ ALTER TABLE crm.extra_data ADD COLUMN IF NOT EXISTS property_entity text;
|
|
|
120
120
|
ALTER TABLE crm.extra_data ADD COLUMN IF NOT EXISTS object_id text;
|
|
121
121
|
ALTER TABLE crm.extra_data ADD COLUMN IF NOT EXISTS value_text text;
|
|
122
122
|
ALTER TABLE crm.extra_data ADD COLUMN IF NOT EXISTS value_date timestamp without time zone;
|
|
123
|
+
ALTER TABLE crm.extra_data ADD COLUMN IF NOT EXISTS value_array text[];
|
|
123
124
|
ALTER TABLE crm.extra_data ADD COLUMN IF NOT EXISTS uid text;
|
|
124
125
|
ALTER TABLE crm.extra_data ALTER COLUMN uid SET DEFAULT '1'::text;
|
|
125
126
|
ALTER TABLE crm.extra_data ADD COLUMN IF NOT EXISTS cdate timestamp without time zone;
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import getPG from '../../pg/funcs/getPG.js';
|
|
2
|
-
import getMeta from '../../pg/funcs/getMeta.js';
|
|
3
2
|
import getRedis from '../../redis/funcs/getRedis.js';
|
|
4
3
|
import pgClients from '../../pg/pgClients.js';
|
|
4
|
+
import getTemplate from '../../table/funcs/getTemplate.js';
|
|
5
5
|
import config from '../../../../config.js';
|
|
6
6
|
|
|
7
7
|
import logChanges from './utils/logChanges.js';
|
|
8
|
+
import getInsertQuery from './utils/getInsertQuery.js';
|
|
8
9
|
import logger from '../../logger/getLogger.js';
|
|
9
10
|
import extraData from '../../extra/extraData.js';
|
|
10
11
|
|
|
11
12
|
const rclient = getRedis();
|
|
12
13
|
|
|
13
14
|
export default async function dataInsert({
|
|
14
|
-
id, table
|
|
15
|
+
id, table: table1, referer, data, pg: pg1, uid, tokenData = {},
|
|
15
16
|
}) {
|
|
16
17
|
const pg = pg1 || getPG({ name: 'client' });
|
|
17
18
|
|
|
@@ -24,63 +25,90 @@ export default async function dataInsert({
|
|
|
24
25
|
pg.pk = pgClients.client?.pk;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
28
|
+
const { unittest } = tokenData || {};
|
|
29
|
+
|
|
30
|
+
if (config.trace || unittest) console.log('form', tokenData?.form);
|
|
31
|
+
|
|
32
|
+
const table = pg.pk[table1] ? table1 : table1.replace(/"/g, '');
|
|
33
|
+
|
|
34
|
+
const { insertQuery, args = [] } = await getInsertQuery({
|
|
35
|
+
pg, table, data, id, uid,
|
|
36
|
+
}) || {};
|
|
37
|
+
|
|
38
|
+
if (!insertQuery || !args.length) return null;
|
|
39
|
+
|
|
40
|
+
// for transactions
|
|
41
|
+
const client = pg?._connected ? pg : await pg.connect();
|
|
42
|
+
|
|
43
|
+
client.options = pg?.options;
|
|
44
|
+
client.tlist = pg?.tlist;
|
|
45
|
+
client.pgType = pg?.pgType;
|
|
46
|
+
client.relkinds = pg?.relkinds;
|
|
47
|
+
client.pk = pg?.pk;
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
await client.query('begin;');
|
|
51
|
+
const res = await client.query(insertQuery, args).then(el => el || {});
|
|
52
|
+
|
|
53
|
+
const id1 = res.rows?.[0]?.[pg.pk[table1]];
|
|
54
|
+
|
|
55
|
+
if (!id1) return null;
|
|
56
|
+
|
|
57
|
+
await extraData({
|
|
58
|
+
table,
|
|
59
|
+
form: tokenData?.form,
|
|
60
|
+
id: id1,
|
|
61
|
+
data,
|
|
62
|
+
uid,
|
|
63
|
+
row: res.rows[0],
|
|
64
|
+
}, client);
|
|
65
|
+
|
|
66
|
+
// foreign key dataTable (table + parent_id)
|
|
67
|
+
const formData = tokenData?.form ? (await getTemplate('form', tokenData.form) || {}) : {};
|
|
68
|
+
const schema = formData?.schema || formData;
|
|
69
|
+
const parentKeys = Object.keys(schema || {})?.filter((key) => data[key]?.length && Array.isArray(data[key]) && schema?.[key]?.table && schema?.[key]?.parent_id);
|
|
70
|
+
|
|
71
|
+
if (parentKeys?.length) {
|
|
72
|
+
await Promise.all(parentKeys?.map(async (key) => {
|
|
73
|
+
const parentKey = schema[key].parent_id;
|
|
74
|
+
const objId = data[parentKey] || data?.id || res?.rows?.[0]?.[parentKey] || id1;
|
|
75
|
+
const parentRows = await Promise.all(data[key].map(async (row) => {
|
|
76
|
+
Object.assign(row, { [parentKey]: objId });
|
|
77
|
+
const parentRes = await getInsertQuery({
|
|
78
|
+
pg: client, table: schema[key].table, data: row, uid,
|
|
79
|
+
}) || {};
|
|
80
|
+
if (!parentRes?.insertQuery || !parentRes?.args?.length) return null;
|
|
81
|
+
|
|
82
|
+
const { rows = [] } = await client.query(parentRes.insertQuery, parentRes.args);
|
|
83
|
+
return rows[0];
|
|
84
|
+
}));
|
|
85
|
+
Object.assign(res.rows[0], { [key]: parentRows.filter(Boolean) });
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await logChanges({
|
|
90
|
+
pg: client,
|
|
91
|
+
table,
|
|
92
|
+
tokenData,
|
|
93
|
+
referer,
|
|
94
|
+
data,
|
|
95
|
+
id: id1,
|
|
96
|
+
uid,
|
|
97
|
+
type: 'INSERT',
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (config.redis) { rclient.incr(`pg:${table}:crud`); }
|
|
101
|
+
await client.query('commit;');
|
|
102
|
+
return res;
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
logger.file('crud/insert', {
|
|
106
|
+
error: err.toString(), stack: err.stack, table, id, referer, uid, form: tokenData?.form,
|
|
107
|
+
});
|
|
108
|
+
await client.query('rollback;');
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
client.release();
|
|
71
113
|
}
|
|
72
|
-
|
|
73
|
-
await logChanges({
|
|
74
|
-
pg,
|
|
75
|
-
table,
|
|
76
|
-
tokenData,
|
|
77
|
-
referer,
|
|
78
|
-
data,
|
|
79
|
-
id: res.rows?.[0]?.[pg.pk[table1]],
|
|
80
|
-
uid,
|
|
81
|
-
type: 'INSERT',
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
if (config.redis) { rclient.incr(`pg:${table}:crud`); }
|
|
85
|
-
return res;
|
|
86
114
|
}
|
|
@@ -2,11 +2,13 @@ import getPG from '../../pg/funcs/getPG.js';
|
|
|
2
2
|
import getMeta from '../../pg/funcs/getMeta.js';
|
|
3
3
|
import getRedis from '../../redis/funcs/getRedis.js';
|
|
4
4
|
import pgClients from '../../pg/pgClients.js';
|
|
5
|
+
import getTemplate from '../../table/funcs/getTemplate.js';
|
|
5
6
|
import config from '../../../../config.js';
|
|
6
7
|
|
|
7
8
|
import extraData from '../../extra/extraData.js';
|
|
8
9
|
import logChanges from './utils/logChanges.js';
|
|
9
10
|
import logger from '../../logger/getLogger.js';
|
|
11
|
+
import getInsertQuery from './utils/getInsertQuery.js';
|
|
10
12
|
|
|
11
13
|
const rclient = getRedis();
|
|
12
14
|
const srids = {};
|
|
@@ -71,41 +73,89 @@ export default async function dataUpdate({
|
|
|
71
73
|
${filterData?.map((key, i) => assignValue(key, i, srids[table]?.[key] || 4326, pg.pgType?.[columns.find(col => col.name === key)?.dataTypeID || '']))?.join(',')}
|
|
72
74
|
WHERE ${pk}::text = $1::text returning *`;
|
|
73
75
|
// console.log(updateQuery, filterValue);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
76
|
+
|
|
77
|
+
// for transactions
|
|
78
|
+
const client = pg?._connected ? pg : await pg.connect();
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
await client.query('begin;');
|
|
82
|
+
const res = await client.query(updateQuery, [id, ...filterValue])
|
|
83
|
+
.catch(err => {
|
|
84
|
+
logger.file('crud/update', {
|
|
85
|
+
error: err.toString(),
|
|
86
|
+
stack: err.stack,
|
|
87
|
+
table,
|
|
88
|
+
id,
|
|
89
|
+
referer,
|
|
90
|
+
uid,
|
|
91
|
+
data,
|
|
92
|
+
q: updateQuery,
|
|
93
|
+
});
|
|
94
|
+
throw err;
|
|
95
|
+
})
|
|
96
|
+
.then(el => el?.rows?.[0]) || {};
|
|
97
|
+
|
|
98
|
+
await extraData({
|
|
99
|
+
table,
|
|
100
|
+
form: tokenData?.form,
|
|
101
|
+
id,
|
|
102
|
+
data,
|
|
103
|
+
uid,
|
|
104
|
+
row: res,
|
|
105
|
+
}, client);
|
|
106
|
+
|
|
107
|
+
// foreign key dataTable (table + parent_id)
|
|
108
|
+
const formData = tokenData?.form ? (await getTemplate('form', tokenData.form) || {}) : {};
|
|
109
|
+
const schema = formData?.schema || formData;
|
|
110
|
+
|
|
111
|
+
const parentKeys = Object.keys(schema || {})?.filter((key) => Array.isArray(data[key]) && schema?.[key]?.table && schema?.[key]?.parent_id /* && body[key].length */);
|
|
112
|
+
if (parentKeys?.length) {
|
|
113
|
+
await Promise.all(parentKeys?.map(async (key) => {
|
|
114
|
+
const objId = data[schema[key].parent_id] || data?.id || res?.[schema[key]?.parent_id] || res?.[pg.pk?.[table] || ''];
|
|
115
|
+
// delete old extra data
|
|
116
|
+
await client.query(`delete from ${schema[key].table} where ${schema[key].parent_id}=$1`, [objId]); // rewrite?
|
|
117
|
+
// insert new extra data
|
|
118
|
+
if (Array.isArray(data[key]) && data[key]?.length) {
|
|
119
|
+
const parentKey = schema[key].parent_id;
|
|
120
|
+
const extraRows = await Promise.all(data[key]?.map?.(async (row) => {
|
|
121
|
+
Object.assign(row, { [parentKey]: objId });
|
|
122
|
+
const parentRes = await getInsertQuery({
|
|
123
|
+
pg: client, table: schema[key].table, data: row, uid,
|
|
124
|
+
});
|
|
125
|
+
if (!parentRes?.insertQuery || !parentRes?.args?.length) return null;
|
|
126
|
+
|
|
127
|
+
const { rows = [] } = await client.query(parentRes.insertQuery, parentRes.args);
|
|
128
|
+
return rows[0];
|
|
129
|
+
}));
|
|
130
|
+
Object.assign(res, { [key]: extraRows.filter(Boolean) });
|
|
131
|
+
}
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
await logChanges({
|
|
136
|
+
pg,
|
|
137
|
+
table,
|
|
138
|
+
tokenData,
|
|
139
|
+
referer,
|
|
140
|
+
data,
|
|
141
|
+
id,
|
|
142
|
+
uid,
|
|
143
|
+
type: 'UPDATE',
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (config.redis) { rclient.incr(`pg:${table}:crud`); }
|
|
147
|
+
|
|
148
|
+
await client.query('commit;');
|
|
149
|
+
return res || {};
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
logger.file('crud/update', {
|
|
153
|
+
error: err.toString(), stack: err.stack, table, id, referer, uid, form: tokenData?.form,
|
|
154
|
+
});
|
|
155
|
+
await client.query('rollback;');
|
|
156
|
+
throw err;
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
client.release();
|
|
160
|
+
}
|
|
111
161
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import getMeta from '../../../pg/funcs/getMeta.js';
|
|
2
|
+
|
|
3
|
+
export default async function getInsertQuery({
|
|
4
|
+
pg, table, data, id, uid,
|
|
5
|
+
}) {
|
|
6
|
+
if (!pg?.pk || !data) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { columns } = await getMeta({ pg, table });
|
|
11
|
+
|
|
12
|
+
if (!columns) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const names = columns.map((el) => el.name);
|
|
17
|
+
|
|
18
|
+
Object.assign(data, {
|
|
19
|
+
...(id && pg.pk?.[table] ? { [pg.pk?.[table]]: id } : {}),
|
|
20
|
+
...(table !== 'admin.users' ? { uid } : {}),
|
|
21
|
+
editor_id: uid,
|
|
22
|
+
created_by: uid,
|
|
23
|
+
updated_by: uid,
|
|
24
|
+
// editor_id: uid,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const systemColumns = ['cdate', 'editor_date', 'created_at', 'updated_at'].filter((el) => names.includes(el)).map((el) => [el, 'now()']);
|
|
28
|
+
const systemColumnNames = systemColumns.map(el => el[0]);
|
|
29
|
+
|
|
30
|
+
const filterData = Object.keys(data)
|
|
31
|
+
.filter((el) => !systemColumnNames.includes(el) && (typeof data[el] === 'boolean' ? true : data[el]) && names.includes(el)).map((el) => [el, data[el]]);
|
|
32
|
+
|
|
33
|
+
const insertQuery = `insert into ${table}
|
|
34
|
+
|
|
35
|
+
( ${filterData?.map((key) => `"${key[0]}"`).concat(systemColumnNames).join(',')})
|
|
36
|
+
|
|
37
|
+
values (${filterData?.map((key, i) => (key[0] === 'geom' ? `st_setsrid(st_geomfromgeojson($${i + 1}::json),4326)` : `$${i + 1}`)).concat(systemColumns.map((el) => el[1])).join(',')})
|
|
38
|
+
|
|
39
|
+
returning *`;
|
|
40
|
+
|
|
41
|
+
const args = [...filterData.map((el) => (typeof el[1] === 'object' && (!Array.isArray(el[1]) || typeof el[1]?.[0] === 'object') ? JSON.stringify(el[1]) : el[1]))];
|
|
42
|
+
|
|
43
|
+
return { insertQuery, args };
|
|
44
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
|
|
3
|
-
import getTemplate from '
|
|
3
|
+
import getTemplate from '../../../table/funcs/getTemplate.js';
|
|
4
4
|
import metaFormat from '../../../table/funcs/metaFormat/index.js';
|
|
5
5
|
|
|
6
6
|
const defaultTitles = {
|
|
@@ -84,7 +84,9 @@ export default async function logChanges({
|
|
|
84
84
|
.filter(el => schema[el]?.data)
|
|
85
85
|
.reduce((acc, curr) => Object.assign(acc, { [curr]: schema[curr].data }), {});
|
|
86
86
|
|
|
87
|
-
const data1 = data ? await metaFormat({
|
|
87
|
+
const data1 = data ? await metaFormat({
|
|
88
|
+
rows: [data], cls, sufix: false, reassign: false,
|
|
89
|
+
}, pg) : null;
|
|
88
90
|
|
|
89
91
|
const newObj = Object.fromEntries(Object.entries(data1?.[0] || {}).map(el => ([[titles[el[0]] || defaultTitles[el[0]] || el[0]], el[1]])));
|
|
90
92
|
const changesData = Object.keys(newObj || {}).map(el => ({
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import config from '../../../config.js';
|
|
2
|
+
import getTemplate from '../table/funcs/getTemplate.js';
|
|
3
|
+
import getMeta from '../pg/funcs/getMeta.js';
|
|
4
|
+
import pgClients from '../pg/pgClients.js';
|
|
5
|
+
import getInsertQuery from '../crud/funcs/utils/getInsertQuery.js';
|
|
2
6
|
|
|
3
7
|
const defaultTable = 'crm.extra_data';
|
|
4
8
|
|
|
@@ -11,7 +15,7 @@ function format(key, value, schema) {
|
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
export default async function extraData({
|
|
14
|
-
table, form, id, data, uid,
|
|
18
|
+
table, form, id, data, uid, row = {},
|
|
15
19
|
}, pg = pgClients.client) {
|
|
16
20
|
if (!id || !table) {
|
|
17
21
|
return null;
|
|
@@ -20,9 +24,9 @@ export default async function extraData({
|
|
|
20
24
|
const loadTemplate = await getTemplate('form', form);
|
|
21
25
|
if (!loadTemplate?.extra) return null;
|
|
22
26
|
|
|
23
|
-
const
|
|
27
|
+
const extraTable = config.extraData?.[table]
|
|
24
28
|
|| config.extraData?.[table.split('.').shift()]
|
|
25
|
-
|| config.extraData?.
|
|
29
|
+
|| config.extraData?.default
|
|
26
30
|
|| config.extraData
|
|
27
31
|
|| defaultTable;
|
|
28
32
|
|
|
@@ -32,33 +36,42 @@ export default async function extraData({
|
|
|
32
36
|
return { error: `table pk not found: ${table}`, status: 404 };
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
if (!pg.pk?.[
|
|
36
|
-
return { error: `extra table pk not found: ${
|
|
39
|
+
if (!pg.pk?.[extraTable]) {
|
|
40
|
+
return { error: `extra table pk not found: ${extraTable}`, status: 404 };
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
Object.assign(data || {}, { object_id: id });
|
|
40
44
|
|
|
41
|
-
const deleteRes = await pg.query(`delete from ${
|
|
45
|
+
const deleteRes = await pg.query(`delete from ${extraTable} where object_id=$1 and property_key = any($2::text[]) returning *`, [id, Object.keys(loadTemplate?.schema || {})]);
|
|
42
46
|
|
|
43
47
|
if (!data) {
|
|
44
|
-
return deleteRes?.rows?.reduce?.((acc, curr) => Object.assign(acc, { [curr.property_key]: format(curr.property_key, curr.value_text, loadTemplate?.schema) }), {});
|
|
48
|
+
return deleteRes?.rows?.reduce?.((acc, curr) => Object.assign(acc, { [curr.property_key]: format(curr.property_key, curr.value_text, curr.value_array, loadTemplate?.schema) }), {});
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
const rows = Object.keys(data || {})
|
|
48
52
|
.filter(key => Object.keys(loadTemplate?.schema || {}).includes(key))
|
|
49
53
|
.filter(key => !mainColumns.map(el => el.name).concat('id', 'token').includes(key))
|
|
50
|
-
.filter(key => Array.isArray(data[key]) ? data[key].length : true)
|
|
54
|
+
.filter(key => (Array.isArray(data[key]) ? data[key].length : true))
|
|
51
55
|
.filter(key => !(loadTemplate?.schema?.[key]?.table && loadTemplate?.schema?.[key]?.parent_id))
|
|
52
56
|
.map(key => ({
|
|
53
57
|
object_id: id,
|
|
54
58
|
property_key: key,
|
|
55
59
|
property_entity: table,
|
|
56
|
-
value_text: data[key],
|
|
60
|
+
value_text: Array.isArray(data[key]) ? null : data[key],
|
|
61
|
+
value_array: Array.isArray(data[key]) ? data[key] : null,
|
|
57
62
|
}));
|
|
58
63
|
|
|
59
|
-
const res = await Promise.all(rows.map(async (
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
const res = await Promise.all(rows.map(async (dataRow) => {
|
|
65
|
+
const { insertQuery, args = [] } = await getInsertQuery({
|
|
66
|
+
pg, table: extraTable, data: dataRow, uid,
|
|
67
|
+
});
|
|
68
|
+
if (!insertQuery || !args?.length) return {};
|
|
69
|
+
return pg.query(insertQuery, args).then(el => el.rows?.[0] || {});
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
Object.assign(row, {
|
|
73
|
+
...res.reduce((acc, curr) => Object.assign(acc, { [curr.property_key]: format(curr.property_key, curr.value_text, curr.value_array, loadTemplate?.schema) }), {}),
|
|
62
74
|
id: res?.[0]?.object_id,
|
|
63
|
-
};
|
|
75
|
+
});
|
|
76
|
+
return row;
|
|
64
77
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import config from '../../../config.js';
|
|
2
|
+
import pgClients from '../pg/pgClients.js';
|
|
3
|
+
import getTemplate from '../table/funcs/getTemplate.js';
|
|
2
4
|
|
|
3
5
|
const defaultTable = 'crm.extra_data';
|
|
4
6
|
|
|
@@ -43,9 +45,22 @@ export default async function extraDataGet({
|
|
|
43
45
|
Object.assign(row, {
|
|
44
46
|
...extraRows
|
|
45
47
|
.filter(el => el.object_id === row.id)
|
|
46
|
-
.reduce((acc, curr) =>
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
.reduce((acc, curr) => {
|
|
49
|
+
let value = null;
|
|
50
|
+
|
|
51
|
+
if (curr.value_text !== null) {
|
|
52
|
+
value = curr.value_text;
|
|
53
|
+
}
|
|
54
|
+
else if (curr.value_array !== null) {
|
|
55
|
+
value = curr.value_array;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (value !== null) {
|
|
59
|
+
acc[curr.property_key] = format(curr.property_key, value, loadTemplate?.schema);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return acc;
|
|
63
|
+
}, {}),
|
|
49
64
|
});
|
|
50
65
|
});
|
|
51
66
|
}
|
|
@@ -97,23 +97,6 @@ export default async function insert(req, reply) {
|
|
|
97
97
|
await applyHook('afterInsert', {
|
|
98
98
|
pg, table, token: params?.table, body, payload: res, user,
|
|
99
99
|
});
|
|
100
|
-
// form DataTable
|
|
101
|
-
// to do: rewrite as single transaction
|
|
102
|
-
const extraKeys = Object.keys(schema || {})?.filter((key) => body[key]?.length && Array.isArray(body[key]) && schema?.[key]?.table && schema?.[key]?.parent_id);
|
|
103
|
-
const pkey = res?.rows?.[0]?.[loadTemplate?.key || pg.pk?.[loadTemplate?.table || table]];
|
|
104
|
-
if (extraKeys?.length) {
|
|
105
|
-
res.extra = {};
|
|
106
|
-
await Promise.all(extraKeys?.map(async (key) => {
|
|
107
|
-
const objId = body[schema[key].parent_id] || req.body?.id || res?.rows?.[0]?.[schema[key].parent_id] || pkey;
|
|
108
|
-
const extraRows = await Promise.all(body[key].map(async (row) => {
|
|
109
|
-
const extraRes = await dataInsert({
|
|
110
|
-
pg, table: schema[key].table, data: { ...row, [schema[key].parent_id]: objId }, uid: user?.uid, tokenData, referer,
|
|
111
|
-
});
|
|
112
|
-
return extraRes?.rows?.[0];
|
|
113
|
-
}));
|
|
114
|
-
Object.assign(res.extra, { [key]: extraRows.filter((el) => el) });
|
|
115
|
-
}));
|
|
116
|
-
}
|
|
117
100
|
|
|
118
101
|
const pk = pg.pk?.[loadTemplate?.table || table];
|
|
119
102
|
return reply.status(200).send({ id: res?.rows?.[0]?.[pk], rows: res.rows, extra: res.extra });
|
|
@@ -99,26 +99,5 @@ export default async function update(req, reply) {
|
|
|
99
99
|
pg, table: params?.table, body, payload: res, user,
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
// form DataTable
|
|
103
|
-
const extraKeys = Object.keys(schema || {})?.filter((key) => Array.isArray(body[key]) && schema?.[key]?.table && schema?.[key]?.parent_id /* && body[key].length */);
|
|
104
|
-
if (extraKeys?.length) {
|
|
105
|
-
res.extra = {};
|
|
106
|
-
await Promise.all(extraKeys?.map(async (key) => {
|
|
107
|
-
const objId = body[schema[key].parent_id] || body?.id || res?.[schema[key]?.parent_id] || res?.[pg.pk?.[loadTemplate?.table || table] || ''];
|
|
108
|
-
// delete old extra data
|
|
109
|
-
await pg.query(`delete from ${schema[key].table} where ${schema[key].parent_id}=$1`, [objId]); // rewrite?
|
|
110
|
-
// insert new extra data
|
|
111
|
-
if (Array.isArray(body[key]) && body[key]?.length) {
|
|
112
|
-
const extraRows = await Promise.all(body[key]?.map?.(async (row) => {
|
|
113
|
-
const extraRes = await dataInsert({
|
|
114
|
-
pg, table: schema[key].table, data: { ...row, [schema[key].parent_id]: objId }, uid, tokenData, referer,
|
|
115
|
-
});
|
|
116
|
-
return extraRes?.rows?.[0];
|
|
117
|
-
}));
|
|
118
|
-
Object.assign(res.extra, { [key]: extraRows.filter((el) => el) });
|
|
119
|
-
}
|
|
120
|
-
}));
|
|
121
|
-
}
|
|
122
|
-
|
|
123
102
|
return reply.status(200).send(res);
|
|
124
103
|
}
|
|
@@ -1,93 +1,97 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { lstat, readdir, readFile } from 'node:fs/promises';
|
|
3
|
-
import { createReadStream, existsSync } from 'node:fs';
|
|
4
|
-
import readline from 'node:readline';
|
|
5
|
-
|
|
6
|
-
import checkUserAccess from './utils/checkUserAccess.js';
|
|
7
|
-
import getRootDir from './utils/getRootDir.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { lstat, readdir, readFile } from 'node:fs/promises';
|
|
3
|
+
import { createReadStream, existsSync } from 'node:fs';
|
|
4
|
+
import readline from 'node:readline';
|
|
5
|
+
|
|
6
|
+
import checkUserAccess from './utils/checkUserAccess.js';
|
|
7
|
+
import getRootDir from './utils/getRootDir.js';
|
|
8
|
+
|
|
9
|
+
const limit = 200000;
|
|
10
|
+
const rootDir = getRootDir();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @method GET
|
|
15
|
+
* @summary API для перегляду логів
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export default async function loggerFile(req, reply) {
|
|
20
|
+
const {
|
|
21
|
+
params = {}, user = {}, query = {}, originalUrl,
|
|
22
|
+
} = req;
|
|
23
|
+
|
|
24
|
+
const access = checkUserAccess({ user, token: query.token });
|
|
25
|
+
|
|
26
|
+
if (access?.status !== 200) return reply.status(access.status).send(access.message);
|
|
27
|
+
|
|
28
|
+
// absolute / relative path
|
|
29
|
+
|
|
30
|
+
const filepath = path.join(rootDir, params['*'] || '');
|
|
31
|
+
|
|
32
|
+
if (!existsSync(filepath)) {
|
|
33
|
+
return reply.status(404).send('file not exists');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const stat = await lstat(filepath);
|
|
37
|
+
const isFile = stat.isFile();
|
|
38
|
+
|
|
39
|
+
if (query.download && isFile) {
|
|
40
|
+
const buffer = await readFile(filepath, { buffer: true });
|
|
41
|
+
return buffer;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (query.full && isFile) {
|
|
45
|
+
if (stat.size > 20 * 1000 * 1000) {
|
|
46
|
+
return { message: 'file size > 20MB' };
|
|
47
|
+
}
|
|
48
|
+
const buffer = await readFile(filepath, { buffer: true });
|
|
49
|
+
return buffer;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (isFile) {
|
|
53
|
+
const ext = path.extname(filepath);
|
|
54
|
+
|
|
55
|
+
const lines = await new Promise((resolve) => {
|
|
56
|
+
const rl = readline.createInterface({
|
|
57
|
+
input: createReadStream(filepath, { start: stat.size > limit ? stat.size - limit : 0 }),
|
|
58
|
+
});
|
|
59
|
+
const lines1 = [];
|
|
60
|
+
rl.on('close', () => resolve(lines1));
|
|
61
|
+
rl.on('line', (line) => lines1.push(line));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (ext === '.html') {
|
|
65
|
+
const buffer = await readFile(filepath, { buffer: true });
|
|
66
|
+
reply.headers({ 'Content-type': 'text/html; charset=UTF-8' });
|
|
67
|
+
return buffer;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
reply.headers({ 'Content-type': 'text/plain; charset=UTF-8' });
|
|
71
|
+
return stat.size > limit && lines.length > 1
|
|
72
|
+
? lines.reverse().slice(0, -1).join('\n')
|
|
73
|
+
: lines.reverse().join('\n');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// dir
|
|
77
|
+
const files = await readdir(filepath);
|
|
78
|
+
|
|
79
|
+
if (query.dir) {
|
|
80
|
+
return files.filter((el) => !['backup', 'marker_icon', 'error', 'migration'].includes(el));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const lstatsArr = await Promise.all(files.map(async (file) => [file, await lstat(path.join(filepath, file))]));
|
|
84
|
+
const lstats = Object.fromEntries(lstatsArr);
|
|
85
|
+
|
|
86
|
+
const relpaceable = query.token ? `?token=${query.token}` : '';
|
|
87
|
+
const relpath = query.token ? originalUrl.replace(relpaceable, '') : originalUrl;
|
|
88
|
+
const message = (params['*'] ? '<a href="/logger-file/">...</a><br>' : '')
|
|
89
|
+
+ files.map((file) => `<a href="${relpath}/${file}${relpaceable}">${file}</a> (${lstats[file].size} bytes)`).join('</br>');
|
|
90
|
+
|
|
91
|
+
reply.headers({
|
|
92
|
+
'Content-Type': 'text/html; charset=UTF-8',
|
|
93
|
+
'Content-Security-Policy': "default-src 'none'",
|
|
94
|
+
'X-Content-Type-Options': 'nosniff',
|
|
95
|
+
});
|
|
96
|
+
return message;
|
|
97
|
+
}
|
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
import config from '../../../../../config.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
import config from '../../../../../config.js';
|
|
2
|
+
|
|
3
|
+
const { accessToken = '0NWcGQxKRP8AsRxD' } = config.auth || {};
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @summary check user access to logger interface - per admin user type or user group
|
|
8
|
+
* @returns {Object} message, status
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export default function checkUserAccess({ user = {}, token }) {
|
|
12
|
+
if (token && token === accessToken) {
|
|
13
|
+
return { message: 'access granted', status: 200 };
|
|
14
|
+
}
|
|
15
|
+
// console.log(user);
|
|
16
|
+
if (user.user_type !== 'admin' && !config?.local && !config.auth?.disable) {
|
|
17
|
+
return { message: 'access restricted', status: 403 };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* if (!['admin', 'superadmin']?.includes(user.user_type) && count === '0') {
|
|
21
|
+
return { message: 'access restricted', status: 403 };
|
|
22
|
+
} */
|
|
23
|
+
return { message: 'access granted', status: 200 };
|
|
24
|
+
}
|
|
@@ -3,18 +3,20 @@ import loggerFile from './controllers/logger.file.js';
|
|
|
3
3
|
// import loggerTest from './controllers/logger.test.api.js';
|
|
4
4
|
|
|
5
5
|
const loggerSchema = {
|
|
6
|
-
|
|
6
|
+
querystring: {
|
|
7
|
+
type: 'object',
|
|
7
8
|
properties: {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
download: { type: 'string', pattern: '^(\\d)$' },
|
|
10
|
+
full: { type: 'string', pattern: '^(\\d)$' },
|
|
11
|
+
dir: { type: 'string', pattern: '^(\\d)$' },
|
|
12
|
+
token: { type: 'string' },
|
|
13
|
+
},
|
|
14
|
+
additionalProperties: false,
|
|
13
15
|
},
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
async function plugin(app) {
|
|
17
|
-
app.get('/logger-file/*', { config: { policy: ['
|
|
19
|
+
app.get('/logger-file/*', { config: { policy: ['public'] }, schema: loggerSchema }, loggerFile);
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export default plugin;
|
|
@@ -295,7 +295,7 @@ export default async function dataAPI(req, reply, called) {
|
|
|
295
295
|
// title, count
|
|
296
296
|
panels?.filter(el => el.items).forEach(async el => {
|
|
297
297
|
const filtered1 = el.items.filter(item => item.count?.toLowerCase?.().includes('select'));
|
|
298
|
-
const data = await Promise.all(filtered1.map(item => pg.query(item.count).then(item1 => item1.rows[0] || {})));
|
|
298
|
+
const data = await Promise.all(filtered1.map(item => pg.query(item.count.replace(/{{id}}/g, params.id)).then(item1 => item1.rows[0] || {})));
|
|
299
299
|
filtered1.forEach((el1, i) => {
|
|
300
300
|
Object.assign(el1, data[i] || {}, data[i].count ? {} : { count: undefined });
|
|
301
301
|
});
|
package/utils.js
CHANGED
|
@@ -45,6 +45,7 @@ import getFilter from './server/plugins/table/funcs/getFilter.js';
|
|
|
45
45
|
import dataInsert from './server/plugins/crud/funcs/dataInsert.js';
|
|
46
46
|
import dataUpdate from './server/plugins/crud/funcs/dataUpdate.js';
|
|
47
47
|
import dataDelete from './server/plugins/crud/funcs/dataDelete.js';
|
|
48
|
+
import getInsertQuery from './server/plugins/crud/funcs/utils/getInsertQuery.js';
|
|
48
49
|
import getToken from './server/plugins/crud/funcs/getToken.js';
|
|
49
50
|
import setToken from './server/plugins/crud/funcs/setToken.js';
|
|
50
51
|
import getOpt from './server/plugins/crud/funcs/getOpt.js';
|
|
@@ -149,6 +150,7 @@ export {
|
|
|
149
150
|
dataInsert,
|
|
150
151
|
dataUpdate,
|
|
151
152
|
dataDelete,
|
|
153
|
+
getInsertQuery,
|
|
152
154
|
|
|
153
155
|
// table
|
|
154
156
|
autoIndex,
|