@opengis/fastify-table 1.1.48 → 1.1.49
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/Changelog.md +2 -2
- package/README.md +26 -26
- package/config.js +10 -10
- package/cron/controllers/cronApi.js +22 -22
- package/cron/controllers/utils/cronList.js +1 -1
- package/cron/index.js +10 -10
- package/crud/controllers/deleteCrud.js +4 -9
- package/crud/controllers/insert.js +7 -8
- package/crud/controllers/update.js +10 -13
- package/crud/controllers/utils/xssInjection.js +72 -72
- package/crud/funcs/getAccess.js +24 -11
- package/crud/funcs/getToken.js +27 -27
- package/crud/funcs/isFileExists.js +13 -13
- package/crud/funcs/setToken.js +53 -53
- package/crud/funcs/utils/getFolder.js +9 -0
- package/package.json +6 -7
- package/redis/funcs/getRedis.js +23 -23
- package/server/migrations/log.sql +80 -80
- package/table/controllers/data.js +24 -40
- package/table/controllers/table.js +22 -26
- package/table/index.js +50 -3
- package/test/config.example +18 -18
- package/test/funcs/pg.test.js +34 -34
- package/test/funcs/redis.test.js +19 -19
- package/test/templates/cls/test.json +9 -9
- package/test/templates/form/cp_building.form.json +32 -32
- package/test/templates/select/account_id.json +3 -3
- package/test/templates/select/storage.data.json +2 -2
- package/test/templates/table/gis.dataset.table.json +20 -20
- package/util/controllers/next.id.js +4 -4
- package/util/controllers/properties.get.js +19 -19
- package/util/index.js +23 -23
- package/utils.js +2 -0
- package/table/schema.js +0 -54
package/crud/funcs/setToken.js
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'crypto';
|
|
2
|
-
|
|
3
|
-
import config from '../../config.js';
|
|
4
|
-
import getRedis from '../../redis/funcs/getRedis.js';
|
|
5
|
-
|
|
6
|
-
const generateCodes = (ids, userToken) => {
|
|
7
|
-
const token = userToken || randomUUID();
|
|
8
|
-
const notNullIds = ids.filter((el) => el);
|
|
9
|
-
const obj = {};
|
|
10
|
-
const codes = notNullIds.reduce((acc, id) => {
|
|
11
|
-
const newToken = createHash('sha1').update(token + id).digest('base64url').replace(/-/g, '');
|
|
12
|
-
acc[newToken] = id; obj[id] = newToken;
|
|
13
|
-
return acc;
|
|
14
|
-
}, {});
|
|
15
|
-
return { codes, obj };
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
function setToken({
|
|
19
|
-
ids: idsOrigin, mode = 'r', uid, array,
|
|
20
|
-
}) {
|
|
21
|
-
const rclient2 = getRedis({ db: 0 });
|
|
22
|
-
// const rclient5 = getRedis({ db: 0, funcs });
|
|
23
|
-
|
|
24
|
-
if (!uid) return { user: 'empty' };
|
|
25
|
-
if (!Object.keys(idsOrigin).length) return { ids: 'empty' };
|
|
26
|
-
|
|
27
|
-
const ids = idsOrigin.map((el) => (typeof el === 'object' ? JSON.stringify(el) : el));
|
|
28
|
-
// update/delete
|
|
29
|
-
|
|
30
|
-
if (mode === 'r') return null;
|
|
31
|
-
|
|
32
|
-
// TODO generate salt
|
|
33
|
-
const { codes, obj } = generateCodes(ids, uid);
|
|
34
|
-
|
|
35
|
-
if (!Object.keys(codes).length) return { ids: 'empty' };
|
|
36
|
-
|
|
37
|
-
rclient2.hmset(`${config.pg.database}:token:${{
|
|
38
|
-
e: 'exec', r: 'view', w: 'edit', a: 'add',
|
|
39
|
-
}[mode]}:${uid}`, codes);
|
|
40
|
-
|
|
41
|
-
// log token for debug. add extra data - uid, mode, date
|
|
42
|
-
/* const dt = new Date().toISOString();
|
|
43
|
-
const codesLog = Object.keys(codes).reduce((acc, key) => {
|
|
44
|
-
acc[key] = `{"referer": "${referer}" ,"uid":"${uid}","mode":"${mode}","date":"${dt}",${codes[key].substr(1)}`;
|
|
45
|
-
return acc;
|
|
46
|
-
}, {});
|
|
47
|
-
rclient5.hmset(`${config.pg.database}:token:edit`, codesLog); // 'EX', 64800 */
|
|
48
|
-
|
|
49
|
-
// TODO дополнительно писать в hset token -> uid
|
|
50
|
-
return array ? Object.values(obj) : obj;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export default setToken;
|
|
1
|
+
import { createHash, randomUUID } from 'crypto';
|
|
2
|
+
|
|
3
|
+
import config from '../../config.js';
|
|
4
|
+
import getRedis from '../../redis/funcs/getRedis.js';
|
|
5
|
+
|
|
6
|
+
const generateCodes = (ids, userToken) => {
|
|
7
|
+
const token = userToken || randomUUID();
|
|
8
|
+
const notNullIds = ids.filter((el) => el);
|
|
9
|
+
const obj = {};
|
|
10
|
+
const codes = notNullIds.reduce((acc, id) => {
|
|
11
|
+
const newToken = createHash('sha1').update(token + id).digest('base64url').replace(/-/g, '');
|
|
12
|
+
acc[newToken] = id; obj[id] = newToken;
|
|
13
|
+
return acc;
|
|
14
|
+
}, {});
|
|
15
|
+
return { codes, obj };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function setToken({
|
|
19
|
+
ids: idsOrigin, mode = 'r', uid, referer, array,
|
|
20
|
+
}) {
|
|
21
|
+
const rclient2 = getRedis({ db: 0 });
|
|
22
|
+
// const rclient5 = getRedis({ db: 0, funcs });
|
|
23
|
+
|
|
24
|
+
if (!uid) return { user: 'empty' };
|
|
25
|
+
if (!Object.keys(idsOrigin).length) return { ids: 'empty' };
|
|
26
|
+
|
|
27
|
+
const ids = idsOrigin.map((el) => (typeof el === 'object' ? JSON.stringify(el) : el));
|
|
28
|
+
// update/delete
|
|
29
|
+
|
|
30
|
+
if (mode === 'r') return null;
|
|
31
|
+
|
|
32
|
+
// TODO generate salt
|
|
33
|
+
const { codes, obj } = generateCodes(ids, uid);
|
|
34
|
+
|
|
35
|
+
if (!Object.keys(codes).length) return { ids: 'empty' };
|
|
36
|
+
|
|
37
|
+
rclient2.hmset(`${config.pg.database}:token:${{
|
|
38
|
+
e: 'exec', r: 'view', w: 'edit', a: 'add',
|
|
39
|
+
}[mode]}:${uid}`, codes);
|
|
40
|
+
|
|
41
|
+
// log token for debug. add extra data - uid, mode, date
|
|
42
|
+
/* const dt = new Date().toISOString();
|
|
43
|
+
const codesLog = Object.keys(codes).reduce((acc, key) => {
|
|
44
|
+
acc[key] = `{"referer": "${referer}" ,"uid":"${uid}","mode":"${mode}","date":"${dt}",${codes[key].substr(1)}`;
|
|
45
|
+
return acc;
|
|
46
|
+
}, {});
|
|
47
|
+
rclient5.hmset(`${config.pg.database}:token:edit`, codesLog); // 'EX', 64800 */
|
|
48
|
+
|
|
49
|
+
// TODO дополнительно писать в hset token -> uid
|
|
50
|
+
return array ? Object.values(obj) : obj;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default setToken;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import config from '../../../config.js';
|
|
3
|
+
|
|
4
|
+
export default function getFolder(req, type = 'server') {
|
|
5
|
+
if (!['server', 'local'].includes(type)) throw new Error('params type is invalid');
|
|
6
|
+
const types = { local: req.root || config.root, server: req.mapServerRoot || config.mapServerRoot };
|
|
7
|
+
const filepath = path.posix.join(types[type] || `/data/local/${req.pg?.options?.database || ''}`, req.folder || config.folder || '');
|
|
8
|
+
return filepath;
|
|
9
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengis/fastify-table",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.49",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "core-plugins",
|
|
6
6
|
"main": "index.js",
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
"@fastify/sensible": "^5.0.0",
|
|
16
16
|
"@fastify/url-data": "^5.4.0",
|
|
17
17
|
"@opengis/fastify-hb": "^1.4.2",
|
|
18
|
-
"@opengis/fastify-table": "^1.1.47",
|
|
19
18
|
"fastify": "^4.26.1",
|
|
20
19
|
"fastify-plugin": "^4.0.0",
|
|
21
20
|
"ioredis": "^5.3.2",
|
|
@@ -26,17 +25,17 @@
|
|
|
26
25
|
},
|
|
27
26
|
"devDependencies": {
|
|
28
27
|
"@panzoom/panzoom": "^4.5.1",
|
|
29
|
-
"eslint": "^8.49.0",
|
|
30
|
-
"eslint-config-airbnb": "^19.0.4",
|
|
31
28
|
"markdown-it-abbr": "^2.0.0",
|
|
32
|
-
"mermaid": "
|
|
29
|
+
"mermaid": "10.9.1",
|
|
33
30
|
"sass": "1.72.0",
|
|
34
31
|
"vitepress": "^1.3.4",
|
|
35
32
|
"vitepress-plugin-mermaid": "^2.0.16",
|
|
36
33
|
"vitepress-plugin-tabs": "^0.5.0",
|
|
37
34
|
"vitepress-sidebar": "^1.25.0",
|
|
38
|
-
"vue": "^3.4.27"
|
|
35
|
+
"vue": "^3.4.27",
|
|
36
|
+
"eslint": "^8.49.0",
|
|
37
|
+
"eslint-config-airbnb": "^19.0.4"
|
|
39
38
|
},
|
|
40
39
|
"author": "Softpro",
|
|
41
40
|
"license": "ISC"
|
|
42
|
-
}
|
|
41
|
+
}
|
package/redis/funcs/getRedis.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import Redis from 'ioredis';
|
|
2
|
-
import config from '../../config.js';
|
|
3
|
-
import redisClients from './redisClients.js';
|
|
4
|
-
|
|
5
|
-
function getRedis({ db } = { db: 0 }) {
|
|
6
|
-
if (!config.redis) return null;
|
|
7
|
-
if (redisClients[db]) return redisClients[db];
|
|
8
|
-
|
|
9
|
-
const redisConfig = {
|
|
10
|
-
db,
|
|
11
|
-
keyPrefix: `${config.db}:`,
|
|
12
|
-
host: config.redis?.host || '127.0.0.1',
|
|
13
|
-
port: config.redis?.port || 6379, // Redis port
|
|
14
|
-
family: 4, // 4 (IPv4) or 6 (IPv6)
|
|
15
|
-
closeClient: true,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
redisClients[db] = new Redis(redisConfig);
|
|
19
|
-
|
|
20
|
-
return redisClients[db];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default getRedis;
|
|
1
|
+
import Redis from 'ioredis';
|
|
2
|
+
import config from '../../config.js';
|
|
3
|
+
import redisClients from './redisClients.js';
|
|
4
|
+
|
|
5
|
+
function getRedis({ db } = { db: 0 }) {
|
|
6
|
+
if (!config.redis) return null;
|
|
7
|
+
if (redisClients[db]) return redisClients[db];
|
|
8
|
+
|
|
9
|
+
const redisConfig = {
|
|
10
|
+
db,
|
|
11
|
+
keyPrefix: `${config.db}:`,
|
|
12
|
+
host: config.redis?.host || '127.0.0.1',
|
|
13
|
+
port: config.redis?.port || 6379, // Redis port
|
|
14
|
+
family: 4, // 4 (IPv4) or 6 (IPv6)
|
|
15
|
+
closeClient: true,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
redisClients[db] = new Redis(redisConfig);
|
|
19
|
+
|
|
20
|
+
return redisClients[db];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default getRedis;
|
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
create schema if not exists log;
|
|
2
|
-
|
|
3
|
-
-- DROP TABLE IF EXISTS log.table_changes cascade;
|
|
4
|
-
CREATE TABLE IF NOT EXISTS log.table_changes();
|
|
5
|
-
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS change_id text NOT NULL DEFAULT next_id();
|
|
6
|
-
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS change_type text;
|
|
7
|
-
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS change_date date;
|
|
8
|
-
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS change_user_id text;
|
|
9
|
-
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS entity_type text; -- table_name
|
|
10
|
-
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS entity_id text; -- object_id
|
|
11
|
-
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS uid text;
|
|
12
|
-
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT (now())::timestamp without time zone;
|
|
13
|
-
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS editor_id text;
|
|
14
|
-
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
|
|
15
|
-
|
|
16
|
-
-- DROP TABLE IF EXISTS log.table_changes_data;
|
|
17
|
-
CREATE TABLE IF NOT EXISTS log.table_changes_data();
|
|
18
|
-
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS change_data_id text NOT NULL DEFAULT next_id();
|
|
19
|
-
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS change_id text not null;
|
|
20
|
-
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS entity_key text; -- column_name
|
|
21
|
-
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS value_old text;
|
|
22
|
-
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS value_new text;
|
|
23
|
-
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS uid text;
|
|
24
|
-
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT (now())::timestamp without time zone;
|
|
25
|
-
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS editor_id text;
|
|
26
|
-
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
|
|
27
|
-
|
|
28
|
-
-- DROP TABLE IF EXISTS log.user_auth;
|
|
29
|
-
CREATE TABLE IF NOT EXISTS log.user_auth();
|
|
30
|
-
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS user_auth_id text NOT NULL DEFAULT next_id();
|
|
31
|
-
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS user_id text;
|
|
32
|
-
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS auth_date timestamp without time zone;
|
|
33
|
-
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS auth_type text;
|
|
34
|
-
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS uid text;
|
|
35
|
-
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT (now())::timestamp without time zone;
|
|
36
|
-
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS editor_id text;
|
|
37
|
-
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
|
|
38
|
-
|
|
39
|
-
COMMENT ON TABLE log.table_changes IS 'Логи подій змін в БД';
|
|
40
|
-
COMMENT ON COLUMN log.table_changes.change_type IS 'Тип події (insert / update / delete)';
|
|
41
|
-
COMMENT ON COLUMN log.table_changes.change_date IS 'Дата внесення змін до БД';
|
|
42
|
-
COMMENT ON COLUMN log.table_changes.entity_type IS 'Таблиця, до якої вносяться зміни';
|
|
43
|
-
COMMENT ON COLUMN log.table_changes.entity_id IS 'ID строки, до якої вносяться зміни';
|
|
44
|
-
COMMENT ON COLUMN log.table_changes.change_user_id IS 'Ініціатор внесення змін';
|
|
45
|
-
|
|
46
|
-
COMMENT ON TABLE log.table_changes_data IS 'Логи змін в таблицях БД';
|
|
47
|
-
COMMENT ON COLUMN log.table_changes_data.change_id IS 'ID події зміни в БД';
|
|
48
|
-
COMMENT ON COLUMN log.table_changes_data.entity_key IS 'Колонка таблиці, до якої вносяться зміни';
|
|
49
|
-
COMMENT ON COLUMN log.table_changes_data.value_old IS 'Старе значення';
|
|
50
|
-
COMMENT ON COLUMN log.table_changes_data.value_new IS 'Нове значення';
|
|
51
|
-
|
|
52
|
-
COMMENT ON TABLE log.user_auth IS 'Логи авторизації';
|
|
53
|
-
COMMENT ON COLUMN log.user_auth.user_id IS 'ID користувача';
|
|
54
|
-
COMMENT ON COLUMN log.user_auth.auth_date IS 'Дата авторизації';
|
|
55
|
-
COMMENT ON COLUMN log.user_auth.auth_type IS 'Тип авторизації';
|
|
56
|
-
|
|
57
|
-
ALTER TABLE log.table_changes DROP CONSTRAINT IF EXISTS log_table_changes_pkey cascade;
|
|
58
|
-
ALTER TABLE log.table_changes_data DROP CONSTRAINT IF EXISTS log_table_changes_data_pkey;
|
|
59
|
-
ALTER TABLE log.table_changes_data DROP CONSTRAINT IF EXISTS log_table_changes_data_change_id_fkey;
|
|
60
|
-
ALTER TABLE log.user_auth DROP CONSTRAINT IF EXISTS log_user_auth_pkey;
|
|
61
|
-
ALTER TABLE log.user_auth DROP CONSTRAINT IF EXISTS log_user_auth_user_id_fkey;
|
|
62
|
-
|
|
63
|
-
ALTER TABLE log.table_changes ADD CONSTRAINT log_table_changes_pkey PRIMARY KEY (change_id);
|
|
64
|
-
ALTER TABLE log.table_changes_data ADD CONSTRAINT log_table_changes_data_pkey PRIMARY KEY (change_data_id);
|
|
65
|
-
ALTER TABLE log.table_changes_data ADD CONSTRAINT log_table_changes_data_change_id_fkey FOREIGN KEY (change_id)
|
|
66
|
-
REFERENCES log.table_changes (change_id);
|
|
67
|
-
ALTER TABLE log.user_auth ADD CONSTRAINT log_user_auth_pkey PRIMARY KEY (user_auth_id);
|
|
68
|
-
-- ALTER TABLE log.user_auth ADD CONSTRAINT log_user_auth_user_id_fkey FOREIGN KEY (user_id) REFERENCES admin.users (uid) MATCH SIMPLE;
|
|
69
|
-
|
|
70
|
-
/* drop old columns */
|
|
71
|
-
alter table log.table_changes drop column if exists date_new;
|
|
72
|
-
alter table log.table_changes drop column if exists date_old;
|
|
73
|
-
alter table log.table_changes drop column if exists number_new;
|
|
74
|
-
alter table log.table_changes drop column if exists number_old;
|
|
75
|
-
alter table log.table_changes drop column if exists json_new;
|
|
76
|
-
alter table log.table_changes drop column if exists json_old;
|
|
77
|
-
alter table log.table_changes drop column if exists text_new;
|
|
78
|
-
alter table log.table_changes drop column if exists text_old;
|
|
79
|
-
alter table log.table_changes drop column if exists bool_new;
|
|
80
|
-
alter table log.table_changes drop column if exists bool_old;
|
|
1
|
+
create schema if not exists log;
|
|
2
|
+
|
|
3
|
+
-- DROP TABLE IF EXISTS log.table_changes cascade;
|
|
4
|
+
CREATE TABLE IF NOT EXISTS log.table_changes();
|
|
5
|
+
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS change_id text NOT NULL DEFAULT next_id();
|
|
6
|
+
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS change_type text;
|
|
7
|
+
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS change_date date;
|
|
8
|
+
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS change_user_id text;
|
|
9
|
+
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS entity_type text; -- table_name
|
|
10
|
+
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS entity_id text; -- object_id
|
|
11
|
+
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS uid text;
|
|
12
|
+
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT (now())::timestamp without time zone;
|
|
13
|
+
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS editor_id text;
|
|
14
|
+
ALTER TABLE log.table_changes ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
|
|
15
|
+
|
|
16
|
+
-- DROP TABLE IF EXISTS log.table_changes_data;
|
|
17
|
+
CREATE TABLE IF NOT EXISTS log.table_changes_data();
|
|
18
|
+
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS change_data_id text NOT NULL DEFAULT next_id();
|
|
19
|
+
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS change_id text not null;
|
|
20
|
+
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS entity_key text; -- column_name
|
|
21
|
+
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS value_old text;
|
|
22
|
+
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS value_new text;
|
|
23
|
+
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS uid text;
|
|
24
|
+
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT (now())::timestamp without time zone;
|
|
25
|
+
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS editor_id text;
|
|
26
|
+
ALTER TABLE log.table_changes_data ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
|
|
27
|
+
|
|
28
|
+
-- DROP TABLE IF EXISTS log.user_auth;
|
|
29
|
+
CREATE TABLE IF NOT EXISTS log.user_auth();
|
|
30
|
+
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS user_auth_id text NOT NULL DEFAULT next_id();
|
|
31
|
+
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS user_id text;
|
|
32
|
+
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS auth_date timestamp without time zone;
|
|
33
|
+
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS auth_type text;
|
|
34
|
+
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS uid text;
|
|
35
|
+
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT (now())::timestamp without time zone;
|
|
36
|
+
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS editor_id text;
|
|
37
|
+
ALTER TABLE log.user_auth ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
|
|
38
|
+
|
|
39
|
+
COMMENT ON TABLE log.table_changes IS 'Логи подій змін в БД';
|
|
40
|
+
COMMENT ON COLUMN log.table_changes.change_type IS 'Тип події (insert / update / delete)';
|
|
41
|
+
COMMENT ON COLUMN log.table_changes.change_date IS 'Дата внесення змін до БД';
|
|
42
|
+
COMMENT ON COLUMN log.table_changes.entity_type IS 'Таблиця, до якої вносяться зміни';
|
|
43
|
+
COMMENT ON COLUMN log.table_changes.entity_id IS 'ID строки, до якої вносяться зміни';
|
|
44
|
+
COMMENT ON COLUMN log.table_changes.change_user_id IS 'Ініціатор внесення змін';
|
|
45
|
+
|
|
46
|
+
COMMENT ON TABLE log.table_changes_data IS 'Логи змін в таблицях БД';
|
|
47
|
+
COMMENT ON COLUMN log.table_changes_data.change_id IS 'ID події зміни в БД';
|
|
48
|
+
COMMENT ON COLUMN log.table_changes_data.entity_key IS 'Колонка таблиці, до якої вносяться зміни';
|
|
49
|
+
COMMENT ON COLUMN log.table_changes_data.value_old IS 'Старе значення';
|
|
50
|
+
COMMENT ON COLUMN log.table_changes_data.value_new IS 'Нове значення';
|
|
51
|
+
|
|
52
|
+
COMMENT ON TABLE log.user_auth IS 'Логи авторизації';
|
|
53
|
+
COMMENT ON COLUMN log.user_auth.user_id IS 'ID користувача';
|
|
54
|
+
COMMENT ON COLUMN log.user_auth.auth_date IS 'Дата авторизації';
|
|
55
|
+
COMMENT ON COLUMN log.user_auth.auth_type IS 'Тип авторизації';
|
|
56
|
+
|
|
57
|
+
ALTER TABLE log.table_changes DROP CONSTRAINT IF EXISTS log_table_changes_pkey cascade;
|
|
58
|
+
ALTER TABLE log.table_changes_data DROP CONSTRAINT IF EXISTS log_table_changes_data_pkey;
|
|
59
|
+
ALTER TABLE log.table_changes_data DROP CONSTRAINT IF EXISTS log_table_changes_data_change_id_fkey;
|
|
60
|
+
ALTER TABLE log.user_auth DROP CONSTRAINT IF EXISTS log_user_auth_pkey;
|
|
61
|
+
ALTER TABLE log.user_auth DROP CONSTRAINT IF EXISTS log_user_auth_user_id_fkey;
|
|
62
|
+
|
|
63
|
+
ALTER TABLE log.table_changes ADD CONSTRAINT log_table_changes_pkey PRIMARY KEY (change_id);
|
|
64
|
+
ALTER TABLE log.table_changes_data ADD CONSTRAINT log_table_changes_data_pkey PRIMARY KEY (change_data_id);
|
|
65
|
+
ALTER TABLE log.table_changes_data ADD CONSTRAINT log_table_changes_data_change_id_fkey FOREIGN KEY (change_id)
|
|
66
|
+
REFERENCES log.table_changes (change_id);
|
|
67
|
+
ALTER TABLE log.user_auth ADD CONSTRAINT log_user_auth_pkey PRIMARY KEY (user_auth_id);
|
|
68
|
+
-- ALTER TABLE log.user_auth ADD CONSTRAINT log_user_auth_user_id_fkey FOREIGN KEY (user_id) REFERENCES admin.users (uid) MATCH SIMPLE;
|
|
69
|
+
|
|
70
|
+
/* drop old columns */
|
|
71
|
+
alter table log.table_changes drop column if exists date_new;
|
|
72
|
+
alter table log.table_changes drop column if exists date_old;
|
|
73
|
+
alter table log.table_changes drop column if exists number_new;
|
|
74
|
+
alter table log.table_changes drop column if exists number_old;
|
|
75
|
+
alter table log.table_changes drop column if exists json_new;
|
|
76
|
+
alter table log.table_changes drop column if exists json_old;
|
|
77
|
+
alter table log.table_changes drop column if exists text_new;
|
|
78
|
+
alter table log.table_changes drop column if exists text_old;
|
|
79
|
+
alter table log.table_changes drop column if exists bool_new;
|
|
80
|
+
alter table log.table_changes drop column if exists bool_old;
|
|
81
81
|
alter table log.table_changes drop column if exists table_change_id;
|
|
@@ -13,7 +13,6 @@ export default async function dataAPI(req) {
|
|
|
13
13
|
const {
|
|
14
14
|
pg, params, query = {}, user,
|
|
15
15
|
} = req;
|
|
16
|
-
|
|
17
16
|
const time = Date.now();
|
|
18
17
|
|
|
19
18
|
const uid = user?.uid || query.uid || 0;
|
|
@@ -25,20 +24,25 @@ export default async function dataAPI(req) {
|
|
|
25
24
|
return { message: hookData?.message, status: hookData?.status };
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
const {
|
|
28
|
+
actions = [], scope, my, query: access,
|
|
29
|
+
} = await getAccess({
|
|
30
|
+
table: hookData?.table || params.table,
|
|
31
|
+
id: hookData?.id || params?.id,
|
|
32
|
+
user,
|
|
33
|
+
}) || {};
|
|
34
|
+
|
|
35
|
+
if (!actions.includes('get') || (scope === 'my' && !my)) {
|
|
35
36
|
return { message: 'access restricted', status: 403 };
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
const loadTable = await getTemplate('table', hookData?.table || params.table);
|
|
40
|
+
|
|
41
|
+
if (!loadTable) { return { message: 'template not found', status: 404 }; }
|
|
42
|
+
|
|
38
43
|
const {
|
|
39
|
-
table, columns, sql, cardSql, filters, form, meta, sqlColumns,
|
|
44
|
+
table, columns, sql, cardSql, filters, form, meta, sqlColumns, ispublic,
|
|
40
45
|
} = loadTable;
|
|
41
|
-
|
|
42
46
|
const tableMeta = await getMeta(table);
|
|
43
47
|
if (tableMeta?.view) {
|
|
44
48
|
if (!loadTable?.key) return { message: `key not found: ${table}`, status: 404 };
|
|
@@ -87,59 +91,39 @@ export default async function dataAPI(req) {
|
|
|
87
91
|
const queryPolyline = meta?.bbox && query?.polyline ? `ST_Contains(ST_MakePolygon(ST_LineFromEncodedPolyline('${query?.polyline}')),${meta.bbox})` : undefined;
|
|
88
92
|
const bbox = meta?.bbox && queryBbox.filter((el) => !Number.isNaN(el))?.length === 4 ? `${meta.bbox} && 'box(${queryBbox[0]} ${queryBbox[1]},${queryBbox[2]} ${queryBbox[3]})'::box2d ` : undefined;
|
|
89
93
|
|
|
90
|
-
const where = [(hookData?.id || params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, search,
|
|
91
|
-
|
|
94
|
+
const where = [(hookData?.id || params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, search, access?.query || '1=1', bbox, queryPolyline].filter((el) => el);
|
|
92
95
|
const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
|
|
93
|
-
const q = `select ${pk ? `"${pk}" as id,` : ''}
|
|
94
|
-
${columnList.includes('geom') ? 'st_asgeojson(geom)::json as geom,' : ''}
|
|
95
|
-
${query.id || query.key ? '*' : sqlColumns || cols || '*'}
|
|
96
|
-
${metaCols}
|
|
97
|
-
${cardColumns}
|
|
98
|
-
from ${table} t ${sqlTable} ${cardSqlTable}
|
|
99
|
-
where ${where.join(' and ') || 'true'}
|
|
100
|
-
${order} ${offset} limit ${limit}`
|
|
101
|
-
.replace(/{{uid}}/g, user.uid);
|
|
96
|
+
const q = `select ${pk ? `"${pk}" as id,` : ''} ${columnList.includes('geom') ? 'st_asgeojson(geom)::json as geom,' : ''} ${query.id || query.key ? '*' : sqlColumns || cols || '*'} ${metaCols} ${cardColumns} from ${table} t ${sqlTable} ${cardSqlTable} where ${where.join(' and ') || 'true'} ${order} ${offset} limit ${limit}`;
|
|
102
97
|
|
|
103
98
|
if (query.sql === '1') { return q; }
|
|
104
99
|
|
|
105
100
|
const { rows } = await pg.query(q, (hookData?.id || params.id ? [hookData?.id || params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
|
|
106
101
|
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
const qCount = `select
|
|
110
|
-
count(*)::int as total,
|
|
111
|
-
count(*) FILTER(WHERE ${filterWhere.filter(el => el).join(' and ') || 'true'})::int as filtered
|
|
112
|
-
from ${table} t ${sqlTable}
|
|
113
|
-
where ${[loadTable.query, accessQuery].filter(el => el).join(' and ') || 'true'} `
|
|
114
|
-
.replace(/{{uid}}/g, user.uid);
|
|
115
|
-
|
|
116
|
-
const { total, filtered } = keyQuery || hookData?.id || params.id ? rows.length
|
|
117
|
-
: await pg.queryCache(qCount).then((el) => el?.rows[0]);
|
|
102
|
+
const total = keyQuery || hookData?.id || params.id ? rows.length : await pg.queryCache(`select count(*) from ${table} t ${sqlTable} where ${where.join(' and ') || 'true'}`).then((el) => (el?.rows[0]?.count || 0) - 0);
|
|
118
103
|
|
|
119
104
|
await metaFormat({ rows, table: params.table });
|
|
120
105
|
const res = {
|
|
121
|
-
time: Date.now() - time,
|
|
106
|
+
time: Date.now() - time, card: loadTable.card, actions: loadTable.actions, access, total, count: rows.length, pk, form, rows, meta, columns, filters,
|
|
122
107
|
};
|
|
123
108
|
|
|
124
|
-
|
|
125
|
-
if (user.uid && actions.includes('add')) {
|
|
109
|
+
if (!config?.security?.disableToken) {
|
|
126
110
|
const addTokens = setToken({
|
|
127
|
-
ids: [JSON.stringify({
|
|
111
|
+
ids: [JSON.stringify({ add: loadTable.table, form: loadTable.form })],
|
|
128
112
|
mode: 'a',
|
|
129
|
-
uid,
|
|
113
|
+
uid: config?.auth?.disable || ispublic ? '1' : uid,
|
|
130
114
|
array: 1,
|
|
131
115
|
});
|
|
132
116
|
Object.assign(res, { addToken: addTokens[0] });
|
|
133
117
|
|
|
134
|
-
|
|
118
|
+
rows.forEach((row) => {
|
|
135
119
|
const editTokens = setToken({
|
|
136
120
|
ids: [JSON.stringify({ id: row.id, table: loadTable.table, form: loadTable.form })],
|
|
137
121
|
mode: 'w',
|
|
138
|
-
uid: config?.auth?.disable ? '1' : uid,
|
|
122
|
+
uid: config?.auth?.disable || ispublic ? '1' : uid,
|
|
139
123
|
array: 1,
|
|
140
124
|
});
|
|
141
125
|
Object.assign(row, { token: editTokens[0] });
|
|
142
|
-
});
|
|
126
|
+
});
|
|
143
127
|
}
|
|
144
128
|
|
|
145
129
|
const result = await applyHook('afterData', {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import setToken from '../../crud/funcs/setToken.js';
|
|
2
1
|
import getTemplate from './utils/getTemplate.js';
|
|
3
2
|
import getMeta from '../../pg/funcs/getMeta.js';
|
|
4
3
|
import applyHook from '../../hook/funcs/applyHook.js';
|
|
@@ -19,23 +18,27 @@ export default async function tableAPI(req) {
|
|
|
19
18
|
if (!params?.id && !hookData?.id) {
|
|
20
19
|
return { message: 'not enough params', status: 400 };
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
const loadTable = await getTemplate('table',
|
|
24
|
-
if (!loadTable
|
|
25
|
-
return { message: 'not found', status: 404 };
|
|
21
|
+
|
|
22
|
+
const loadTable = await getTemplate('table', hookData?.table || params.table) || {};
|
|
23
|
+
if (!loadTable) {
|
|
24
|
+
if (!pg.pk?.[hookData?.table || params.table]) { return { message: 'not found', status: 404 }; }
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
const {
|
|
27
|
+
const {
|
|
28
|
+
actions = [], scope, my,
|
|
29
|
+
} = await getAccess({
|
|
29
30
|
table: hookData?.table || params.table,
|
|
30
31
|
id: hookData?.id || params?.id,
|
|
31
32
|
user,
|
|
32
33
|
}) || {};
|
|
33
34
|
|
|
34
|
-
if (!actions.includes('
|
|
35
|
+
if (!actions.includes('get') || (scope === 'my' && !my)) {
|
|
35
36
|
return { message: 'access restricted', status: 403 };
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
const {
|
|
39
|
+
const {
|
|
40
|
+
table, /* columns, */ form,
|
|
41
|
+
} = loadTable;
|
|
39
42
|
|
|
40
43
|
const { pk, columns: dbColumns = [] } = await getMeta(table || hookData?.table || params.table);
|
|
41
44
|
if (!pk) return { message: `table not found: ${table}`, status: 404 };
|
|
@@ -51,33 +54,26 @@ export default async function tableAPI(req) {
|
|
|
51
54
|
const cols = loadTable?.table
|
|
52
55
|
? Object.keys(schema || {}).filter((col) => columnList.includes(col) && !extraKeys.includes(col))?.map((col) => (col?.includes('geom') ? `st_asgeojson(${col})::json as "${col}"` : `"${col}"`))?.join(',')
|
|
53
56
|
: fields.map((el) => (el?.name?.includes('geom') ? `st_asgeojson(${el.name})::json as "${el.name}"` : `"${el?.name}"`)).join(',');
|
|
54
|
-
const where = [`"${pk}" = $1`, loadTable.query
|
|
57
|
+
const where = [`"${pk}" = $1`, loadTable.query].filter((el) => el);
|
|
55
58
|
const geom = columnList.includes('geom') ? ',st_asgeojson(geom)::json as geom' : '';
|
|
56
59
|
const q = `select "${pk}" as id, ${cols || '*'} ${geom} from ${table || hookData?.table || params.table} t where ${where.join(' and ') || 'true'} limit 1`;
|
|
57
60
|
|
|
58
61
|
if (query?.sql === '1') return q;
|
|
59
62
|
|
|
60
|
-
const
|
|
61
|
-
if (!data) return { message: 'not found', status: 404 };
|
|
63
|
+
const { rows } = await pg.query(q, [hookData?.id || params.id]);
|
|
62
64
|
|
|
63
65
|
if (extraKeys?.length) {
|
|
64
|
-
await Promise.all(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
await Promise.all(rows?.map(async (row) => {
|
|
67
|
+
await Promise.all(extraKeys?.map(async (key) => {
|
|
68
|
+
const { colModel, table: extraTable, parent_id: parentId } = schema[key];
|
|
69
|
+
const { rows: extraRows } = await pg.query(`select ${parentId} as parent, ${colModel.map((col) => col.name).join(',')} from ${extraTable} a where ${parentId}=$1`, [row.id]);
|
|
70
|
+
Object.assign(row, { [key]: extraRows });
|
|
71
|
+
}));
|
|
68
72
|
}));
|
|
69
73
|
}
|
|
70
|
-
|
|
71
|
-
const [token] = setToken({
|
|
72
|
-
ids: [JSON.stringify({ id: params?.id, table: params.table || loadTable.table, form: loadTable.form })],
|
|
73
|
-
mode: 'w',
|
|
74
|
-
uid: user.uid,
|
|
75
|
-
array: 1,
|
|
76
|
-
});
|
|
77
|
-
data.token = token;
|
|
78
|
-
}
|
|
74
|
+
|
|
79
75
|
const res = await applyHook('afterTable', {
|
|
80
|
-
table: loadTable?.table, payload:
|
|
76
|
+
table: loadTable?.table, payload: rows, user,
|
|
81
77
|
});
|
|
82
|
-
return res ||
|
|
78
|
+
return res || rows?.[0] || {};
|
|
83
79
|
}
|
package/table/index.js
CHANGED
|
@@ -12,9 +12,56 @@ import getSelect from './controllers/utils/getSelect.js';
|
|
|
12
12
|
|
|
13
13
|
import loadTemplatePath from './controllers/utils/loadTemplatePath.js';
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const tableSchema = {
|
|
16
|
+
querystring1: {
|
|
17
|
+
page: { type: 'string', pattern: '^(\\d+)$' },
|
|
18
|
+
order: { type: 'string', pattern: '^(\\d+)$' },
|
|
19
|
+
filter: { type: 'string', pattern: '^([\\w\\d_-]+)=([\\w\\d_-]+)$' },
|
|
20
|
+
},
|
|
21
|
+
params: {
|
|
22
|
+
id: { type: 'string', pattern: '^([\\d\\w]+)$' },
|
|
23
|
+
table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const searchSchema = {
|
|
28
|
+
querystring: {
|
|
29
|
+
page: { type: 'string', pattern: '^(\\d+)$' },
|
|
30
|
+
limit: { type: 'string', pattern: '^(\\d+)$' },
|
|
31
|
+
order: { type: 'string', pattern: '^([\\w_.]+)$' },
|
|
32
|
+
desc: { type: 'string', pattern: '^(desc)|(asc)$' },
|
|
33
|
+
key: { type: 'string', pattern: '^([\\w\\d_]+)$' },
|
|
34
|
+
table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
|
|
35
|
+
sql: { type: 'string', pattern: '^(\\d)$' },
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const suggestSchema = {
|
|
40
|
+
querystring: {
|
|
41
|
+
lang: { type: 'string', pattern: '^([\\w.]+)$' },
|
|
42
|
+
// parent: { type: 'string', pattern: '^([\\w,./]+)$' },
|
|
43
|
+
sel: { type: 'string', pattern: '^([\\w,./]+)$' },
|
|
44
|
+
name: { type: 'string', pattern: '^([\\w,./]+)$' },
|
|
45
|
+
// key: { type: 'string', pattern: '^([\\w\\d_]+)$' },
|
|
46
|
+
// val: { type: 'string', pattern: '^([\\w.,]+)$' },
|
|
47
|
+
sql: { type: 'string', pattern: '^(\\d)$' },
|
|
48
|
+
},
|
|
49
|
+
params: {
|
|
50
|
+
id: { type: 'string', pattern: '^([\\d\\w]+)$' },
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const formSchema = {
|
|
55
|
+
params: {
|
|
56
|
+
form: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const filterSchema = {
|
|
61
|
+
params: {
|
|
62
|
+
table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
|
|
63
|
+
},
|
|
64
|
+
};
|
|
18
65
|
|
|
19
66
|
async function plugin(fastify, config = {}) {
|
|
20
67
|
const prefix = config.prefix || '/api';
|