@opengis/fastify-table 1.0.81 → 1.0.83
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 +8 -0
- package/index.js +97 -97
- package/package.json +1 -1
- package/server/migrations/0.sql +14 -0
- package/server/templates/select/test.storage.data.json +3 -2
- package/server/templates/select/test.storage.data.sql +1 -1
- package/server/templates/select/test.suggest.ato_new.json +3 -0
- package/server/templates/select/test.suggest.ato_new.sql +26 -0
- package/server/templates/select/test.suggest.data.json +5 -0
- package/server/templates/select/test.suggest.data.sql +1 -0
- package/server/templates/select/test.suggest.parent.sql +3 -0
- package/table/controllers/data.js +97 -96
- package/table/controllers/utils/getSelect.js +20 -20
- package/table/index.js +80 -78
- package/test/api/suggest.test.js +66 -0
- package/test/config.example +1 -1
package/Changelog.md
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# fastify-table
|
|
2
2
|
|
|
3
|
+
## 1.0.83 - 20.08.2024
|
|
4
|
+
|
|
5
|
+
- code optimization
|
|
6
|
+
|
|
3
7
|
## 1.0.81 - 15.08.2024
|
|
4
8
|
|
|
5
9
|
- refactor /table API
|
|
6
10
|
|
|
11
|
+
## 1.0.78 - 14.08.2024
|
|
12
|
+
|
|
13
|
+
- add sqlColumns to /api/data/:table/?:id
|
|
14
|
+
|
|
7
15
|
## 1.0.77 - 13.08.2024
|
|
8
16
|
|
|
9
17
|
- add statusMonitor API
|
package/index.js
CHANGED
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
3
|
-
|
|
4
|
-
import fp from 'fastify-plugin';
|
|
5
|
-
import config from './config.js';
|
|
6
|
-
// import rclient from './redis/client.js';
|
|
7
|
-
|
|
8
|
-
import redisPlugin from './redis/index.js';
|
|
9
|
-
import pgPlugin from './pg/index.js';
|
|
10
|
-
import tablePlugin from './table/index.js';
|
|
11
|
-
import notificationPlugin from './notification/index.js';
|
|
12
|
-
import widgetPlugin from './widget/index.js';
|
|
13
|
-
import crudPlugin from './crud/index.js';
|
|
14
|
-
import policyPlugin from './policy/index.js';
|
|
15
|
-
import utilPlugin from './util/index.js';
|
|
16
|
-
import cronPlugin from './cron/index.js';
|
|
17
|
-
|
|
18
|
-
import pgClients from './pg/pgClients.js';
|
|
19
|
-
|
|
20
|
-
import execMigrations from './migration/exec.migrations.js';
|
|
21
|
-
|
|
22
|
-
async function plugin(fastify, opt) {
|
|
23
|
-
// console.log(opt);
|
|
24
|
-
config.pg = opt.pg;
|
|
25
|
-
config.redis = opt.redis;
|
|
26
|
-
config.root = opt.root;
|
|
27
|
-
config.mapServerRoot = opt.mapServerRoot;
|
|
28
|
-
|
|
29
|
-
// independent npm start / unit test
|
|
30
|
-
if (!fastify.config) {
|
|
31
|
-
fastify.decorate('config', config);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
fastify.register(import('@fastify/sensible'), {
|
|
35
|
-
errorHandler: false,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
fastify.register(import('@fastify/url-data'), {
|
|
39
|
-
errorHandler: false,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
fastify.register(import('@opengis/fastify-hb'));
|
|
43
|
-
fastify.decorate('getFolder', (req, type = 'server') => {
|
|
44
|
-
if (!['server', 'local'].includes(type)) throw new Error('params type is invalid');
|
|
45
|
-
const types = { local: req.root, server: req.mapServerRoot };
|
|
46
|
-
const filepath = path.posix.join(types[type] || '/data/local', req.folder || config.folder || '');
|
|
47
|
-
return filepath;
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
fastify.addHook('onListen', async () => {
|
|
51
|
-
const { client } = pgClients;
|
|
52
|
-
if (client?.pk?.['crm.cls']) {
|
|
53
|
-
const clsDir = path.join(process.cwd(), 'server/templates/cls');
|
|
54
|
-
const files = existsSync(clsDir) ? readdirSync(clsDir) : [];
|
|
55
|
-
if (files.length) {
|
|
56
|
-
const res = await Promise.all(files.map(async (filename) => {
|
|
57
|
-
const filepath = path.join(clsDir, filename);
|
|
58
|
-
const data = JSON.parse(readFileSync(filepath));
|
|
59
|
-
return { name: path.parse(filename).name, data };
|
|
60
|
-
}));
|
|
61
|
-
await client.query('truncate table crm.cls');
|
|
62
|
-
const { rows } = await client.query(`insert into crm.cls(name, type)
|
|
63
|
-
select value->>'name', 'json' from json_array_elements($1) returning cls_id as id, name`, [JSON.stringify(res).replace(/'/g, "''")]);
|
|
64
|
-
rows.forEach((row) => Object.assign(row, { data: res.find((cls) => row.name === cls.name)?.data }));
|
|
65
|
-
const sql = `insert into crm.cls(code, name, parent)
|
|
66
|
-
select json_array_elements(value->'data')->>'id', json_array_elements(value->'data')->>'text', value->>'name' from json_array_elements($1)`;
|
|
67
|
-
await client.query(sql, [JSON.stringify(rows).replace(/'/g, "''")]);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
// call from another repo / project
|
|
71
|
-
fastify.execMigrations = execMigrations;
|
|
72
|
-
// execute core migrations
|
|
73
|
-
await fastify.execMigrations();
|
|
74
|
-
});
|
|
75
|
-
if (!fastify.funcs) {
|
|
76
|
-
fastify.addHook('onRequest', async (req) => {
|
|
77
|
-
req.funcs = fastify;
|
|
78
|
-
if (!req.user && req.session?.passport?.user) {
|
|
79
|
-
const { user } = req.session?.passport || {};
|
|
80
|
-
req.user = user;
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
// fastify.decorateRequest('funcs', fastify);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
policyPlugin(fastify);
|
|
87
|
-
redisPlugin(fastify);
|
|
88
|
-
await pgPlugin(fastify, opt);
|
|
89
|
-
tablePlugin(fastify, opt);
|
|
90
|
-
crudPlugin(fastify, opt);
|
|
91
|
-
notificationPlugin(fastify, opt);
|
|
92
|
-
widgetPlugin(fastify, opt);
|
|
93
|
-
utilPlugin(fastify, opt);
|
|
94
|
-
cronPlugin(fastify, opt);
|
|
95
|
-
}
|
|
96
|
-
export default fp(plugin);
|
|
97
|
-
// export { rclient };
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
3
|
+
|
|
4
|
+
import fp from 'fastify-plugin';
|
|
5
|
+
import config from './config.js';
|
|
6
|
+
// import rclient from './redis/client.js';
|
|
7
|
+
|
|
8
|
+
import redisPlugin from './redis/index.js';
|
|
9
|
+
import pgPlugin from './pg/index.js';
|
|
10
|
+
import tablePlugin from './table/index.js';
|
|
11
|
+
import notificationPlugin from './notification/index.js';
|
|
12
|
+
import widgetPlugin from './widget/index.js';
|
|
13
|
+
import crudPlugin from './crud/index.js';
|
|
14
|
+
import policyPlugin from './policy/index.js';
|
|
15
|
+
import utilPlugin from './util/index.js';
|
|
16
|
+
import cronPlugin from './cron/index.js';
|
|
17
|
+
|
|
18
|
+
import pgClients from './pg/pgClients.js';
|
|
19
|
+
|
|
20
|
+
import execMigrations from './migration/exec.migrations.js';
|
|
21
|
+
|
|
22
|
+
async function plugin(fastify, opt) {
|
|
23
|
+
// console.log(opt);
|
|
24
|
+
config.pg = opt.pg;
|
|
25
|
+
config.redis = opt.redis;
|
|
26
|
+
config.root = opt.root;
|
|
27
|
+
config.mapServerRoot = opt.mapServerRoot;
|
|
28
|
+
|
|
29
|
+
// independent npm start / unit test
|
|
30
|
+
if (!fastify.config) {
|
|
31
|
+
fastify.decorate('config', config);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fastify.register(import('@fastify/sensible'), {
|
|
35
|
+
errorHandler: false,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
fastify.register(import('@fastify/url-data'), {
|
|
39
|
+
errorHandler: false,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
fastify.register(import('@opengis/fastify-hb'));
|
|
43
|
+
fastify.decorate('getFolder', (req, type = 'server') => {
|
|
44
|
+
if (!['server', 'local'].includes(type)) throw new Error('params type is invalid');
|
|
45
|
+
const types = { local: req.root || config.root, server: req.mapServerRoot || config.mapServerRoot };
|
|
46
|
+
const filepath = path.posix.join(types[type] || '/data/local', req.folder || config.folder || '');
|
|
47
|
+
return filepath;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
fastify.addHook('onListen', async () => {
|
|
51
|
+
const { client } = pgClients;
|
|
52
|
+
if (client?.pk?.['crm.cls']) {
|
|
53
|
+
const clsDir = path.join(process.cwd(), 'server/templates/cls');
|
|
54
|
+
const files = existsSync(clsDir) ? readdirSync(clsDir) : [];
|
|
55
|
+
if (files.length) {
|
|
56
|
+
const res = await Promise.all(files.map(async (filename) => {
|
|
57
|
+
const filepath = path.join(clsDir, filename);
|
|
58
|
+
const data = JSON.parse(readFileSync(filepath));
|
|
59
|
+
return { name: path.parse(filename).name, data };
|
|
60
|
+
}));
|
|
61
|
+
await client.query('truncate table crm.cls');
|
|
62
|
+
const { rows } = await client.query(`insert into crm.cls(name, type)
|
|
63
|
+
select value->>'name', 'json' from json_array_elements($1) returning cls_id as id, name`, [JSON.stringify(res).replace(/'/g, "''")]);
|
|
64
|
+
rows.forEach((row) => Object.assign(row, { data: res.find((cls) => row.name === cls.name)?.data }));
|
|
65
|
+
const sql = `insert into crm.cls(code, name, parent)
|
|
66
|
+
select json_array_elements(value->'data')->>'id', json_array_elements(value->'data')->>'text', value->>'name' from json_array_elements($1)`;
|
|
67
|
+
await client.query(sql, [JSON.stringify(rows).replace(/'/g, "''")]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// call from another repo / project
|
|
71
|
+
fastify.execMigrations = execMigrations;
|
|
72
|
+
// execute core migrations
|
|
73
|
+
await fastify.execMigrations();
|
|
74
|
+
});
|
|
75
|
+
if (!fastify.funcs) {
|
|
76
|
+
fastify.addHook('onRequest', async (req) => {
|
|
77
|
+
req.funcs = fastify;
|
|
78
|
+
if (!req.user && req.session?.passport?.user) {
|
|
79
|
+
const { user } = req.session?.passport || {};
|
|
80
|
+
req.user = user;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
// fastify.decorateRequest('funcs', fastify);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
policyPlugin(fastify);
|
|
87
|
+
redisPlugin(fastify);
|
|
88
|
+
await pgPlugin(fastify, opt);
|
|
89
|
+
tablePlugin(fastify, opt);
|
|
90
|
+
crudPlugin(fastify, opt);
|
|
91
|
+
notificationPlugin(fastify, opt);
|
|
92
|
+
widgetPlugin(fastify, opt);
|
|
93
|
+
utilPlugin(fastify, opt);
|
|
94
|
+
cronPlugin(fastify, opt);
|
|
95
|
+
}
|
|
96
|
+
export default fp(plugin);
|
|
97
|
+
// export { rclient };
|
package/package.json
CHANGED
package/server/migrations/0.sql
CHANGED
|
@@ -59,6 +59,20 @@ if (_returnType != 'text') then
|
|
|
59
59
|
|
|
60
60
|
else
|
|
61
61
|
raise notice 'skip default reassign';
|
|
62
|
+
|
|
63
|
+
CREATE EXTENSION if not exists "uuid-ossp";
|
|
64
|
+
|
|
65
|
+
CREATE OR REPLACE FUNCTION next_id()
|
|
66
|
+
RETURNS text AS
|
|
67
|
+
$BODY$
|
|
68
|
+
DECLARE
|
|
69
|
+
|
|
70
|
+
BEGIN
|
|
71
|
+
return replace(uuid_generate_v4()::text, '-', '');
|
|
72
|
+
END;
|
|
73
|
+
$BODY$
|
|
74
|
+
LANGUAGE plpgsql VOLATILE
|
|
75
|
+
COST 100;
|
|
62
76
|
end if;
|
|
63
77
|
|
|
64
78
|
end $$
|
|
@@ -1 +1 @@
|
|
|
1
|
-
select dataset_id,dataset_name from
|
|
1
|
+
select dataset_id, dataset_name, table_name from gis.dataset
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
select
|
|
2
|
+
codifier,
|
|
3
|
+
case
|
|
4
|
+
when object_type = 'A' then name_ua
|
|
5
|
+
else case
|
|
6
|
+
when object_type in ('P', 'H', 'O', 'B') then name_ua || ' ' || prefix_ua
|
|
7
|
+
else prefix_ua || ' ' || name_ua
|
|
8
|
+
end
|
|
9
|
+
end as title
|
|
10
|
+
from
|
|
11
|
+
ato_new.ato_new_all
|
|
12
|
+
left join ato_new.ato_settings_city_and_terrytory on ato_type = object_type
|
|
13
|
+
where
|
|
14
|
+
codifier is not null
|
|
15
|
+
union
|
|
16
|
+
all
|
|
17
|
+
select
|
|
18
|
+
country_id,
|
|
19
|
+
name_ua as title
|
|
20
|
+
from
|
|
21
|
+
ato_new.country
|
|
22
|
+
where
|
|
23
|
+
country_id = '2845832997045798794'
|
|
24
|
+
union
|
|
25
|
+
all
|
|
26
|
+
select 'UA00000000000000000', 'Україна'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
select dataset_id, dataset_name, table_name from gis.dataset
|
|
@@ -1,96 +1,97 @@
|
|
|
1
|
-
import getTemplate from './utils/getTemplate.js';
|
|
2
|
-
import getFilterSQL from '../funcs/getFilterSQL/index.js';
|
|
3
|
-
import getMeta from '../../pg/funcs/getMeta.js';
|
|
4
|
-
import metaFormat from '../funcs/metaFormat/index.js';
|
|
5
|
-
import getAccess from '../../crud/funcs/getAccess.js';
|
|
6
|
-
import setToken from '../../crud/funcs/setToken.js';
|
|
7
|
-
import gisIRColumn from './utils/gisIRColumn.js';
|
|
8
|
-
|
|
9
|
-
const maxLimit = 100;
|
|
10
|
-
export default async function dataAPI(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
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
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
1
|
+
import getTemplate from './utils/getTemplate.js';
|
|
2
|
+
import getFilterSQL from '../funcs/getFilterSQL/index.js';
|
|
3
|
+
import getMeta from '../../pg/funcs/getMeta.js';
|
|
4
|
+
import metaFormat from '../funcs/metaFormat/index.js';
|
|
5
|
+
import getAccess from '../../crud/funcs/getAccess.js';
|
|
6
|
+
import setToken from '../../crud/funcs/setToken.js';
|
|
7
|
+
import gisIRColumn from './utils/gisIRColumn.js';
|
|
8
|
+
|
|
9
|
+
const maxLimit = 100;
|
|
10
|
+
export default async function dataAPI({
|
|
11
|
+
pg, params, funcs = {}, query = {}, opt = {}, uid: uid1, req, session,
|
|
12
|
+
}) {
|
|
13
|
+
const time = Date.now();
|
|
14
|
+
|
|
15
|
+
const uid = session?.passport?.user?.uid || uid1 || query.uid || 0;
|
|
16
|
+
|
|
17
|
+
const loadTable = await getTemplate('table', params.table);
|
|
18
|
+
|
|
19
|
+
if (!loadTable) { return { message: 'template not found', status: 404 }; }
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
table, columns, sql, cardSql, filters, form, meta, sqlColumns, ispublic,
|
|
23
|
+
} = loadTable;
|
|
24
|
+
const { pk, columns: dbColumns = [] } = await getMeta(table);
|
|
25
|
+
|
|
26
|
+
if (!pk) return { message: `table not found: ${table}`, status: 404 };
|
|
27
|
+
|
|
28
|
+
const cols = columns.filter((el) => el.name !== 'geom').map((el) => el.name || el).join(',');
|
|
29
|
+
const columnList = dbColumns.map((el) => el.name || el).join(',');
|
|
30
|
+
const sqlTable = sql?.filter?.((el) => !el?.disabled && el?.sql?.replace).map((el, i) => ` left join lateral (${el.sql.replace('{{uid}}', uid)}) ${el.name || `t${i}`} on 1=1 `)?.join('') || '';
|
|
31
|
+
const cardSqlFiltered = opt?.id || params.id ? (cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) || []) : [];
|
|
32
|
+
const cardSqlTable = cardSqlFiltered.length ? cardSqlFiltered.map((el, i) => ` left join lateral (select json_agg(row_to_json(q)) as ${el.name} from (${el.sql})q) ct${i} on 1=1 `).join('') || '' : '';
|
|
33
|
+
|
|
34
|
+
if (params.id && columnList.includes(params.id)) {
|
|
35
|
+
return gisIRColumn({
|
|
36
|
+
pg, funcs, layer: params.table, column: params.id, sql: query.sql,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const fData = query.filter || query.search ? await getFilterSQL({
|
|
41
|
+
filter: query.filter,
|
|
42
|
+
search: query.search,
|
|
43
|
+
table: params.table,
|
|
44
|
+
json: 1,
|
|
45
|
+
}) : {};
|
|
46
|
+
|
|
47
|
+
const keyQuery = query.key && loadTable.key && !(opt?.id || params.id) ? `${loadTable.key}=$1` : null;
|
|
48
|
+
|
|
49
|
+
const limit = Math.min(maxLimit, +(query.limit || 20));
|
|
50
|
+
|
|
51
|
+
const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
|
|
52
|
+
// id, query, filter
|
|
53
|
+
const [orderColumn, orderDir] = (query.order || loadTable.order || '').split(/[- ]/);
|
|
54
|
+
|
|
55
|
+
const order = columnList.includes(orderColumn) && orderColumn?.length ? `order by ${orderColumn} ${query.desc || orderDir === 'desc' ? 'desc' : ''}` : '';
|
|
56
|
+
const state = loadTable.filterState && query.state ? loadTable.filterState[query.state]?.sql : null;
|
|
57
|
+
const custom = loadTable.filterCustom && query.custom ? loadTable.filterCustom[query.custom]?.sql : null;
|
|
58
|
+
const search = loadTable.meta?.search && query.search ? `(${loadTable.meta?.search.split(',').map(el => `${el} ilike '%${query.search}%'`).join(' or ')})` : null;
|
|
59
|
+
|
|
60
|
+
const access = await getAccess(req, params.table);
|
|
61
|
+
const where = [(opt?.id || params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, state, custom, search, access?.query || '1=1'].filter((el) => el);
|
|
62
|
+
const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
|
|
63
|
+
const q = `select ${pk ? `"${pk}" as id,` : ''} ${columnList.includes('geom') ? 'st_asgeojson(geom)::json as geom,' : ''} ${query.id || query.key ? '*' : sqlColumns || cols || '*'} ${cardColumns} from ${table} t ${sqlTable} ${cardSqlTable} where ${where.join(' and ') || 'true'} ${order} ${offset} limit ${limit}`;
|
|
64
|
+
|
|
65
|
+
if (query.sql === '1') { return q; }
|
|
66
|
+
|
|
67
|
+
const { rows } = await pg.query(q, (opt?.id || params.id ? [opt?.id || params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
|
|
68
|
+
|
|
69
|
+
const total = keyQuery || opt?.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);
|
|
70
|
+
|
|
71
|
+
await metaFormat({ rows, table: params.table });
|
|
72
|
+
const res = {
|
|
73
|
+
time: Date.now() - time, card: loadTable.card, actions: loadTable.actions, access, total, count: rows.length, pk, form, rows, meta, columns, filters,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if (!funcs.config?.security?.disableToken) {
|
|
77
|
+
const addTokens = setToken({
|
|
78
|
+
ids: [JSON.stringify({ add: loadTable.table, form: loadTable.form })],
|
|
79
|
+
mode: 'a',
|
|
80
|
+
uid: funcs.config?.auth?.disable || ispublic ? '1' : uid,
|
|
81
|
+
array: 1,
|
|
82
|
+
});
|
|
83
|
+
Object.assign(res, { addToken: addTokens[0] });
|
|
84
|
+
|
|
85
|
+
rows.forEach((row) => {
|
|
86
|
+
const editTokens = setToken({
|
|
87
|
+
ids: [JSON.stringify({ id: row.id, table: loadTable.table, form: loadTable.form })],
|
|
88
|
+
mode: 'w',
|
|
89
|
+
uid: funcs.config?.auth?.disable || ispublic ? '1' : uid,
|
|
90
|
+
array: 1,
|
|
91
|
+
});
|
|
92
|
+
Object.assign(row, { token: editTokens[0] });
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return res;
|
|
97
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import getTemplate from './getTemplate.js';
|
|
2
|
-
|
|
3
|
-
const loadCls = {};
|
|
4
|
-
|
|
5
|
-
export default async function
|
|
6
|
-
if (loadCls[name]) return loadCls[name];
|
|
7
|
-
|
|
8
|
-
const clsData = await getTemplate('cls', name);
|
|
9
|
-
|
|
10
|
-
if (clsData) {
|
|
11
|
-
loadCls[name] = { arr: clsData };
|
|
12
|
-
return loadCls[name];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const selectData = await getTemplate('select', name);
|
|
16
|
-
if (!selectData) { return null; }
|
|
17
|
-
|
|
18
|
-
loadCls[name] = selectData;
|
|
19
|
-
return loadCls[name];
|
|
20
|
-
}
|
|
1
|
+
import getTemplate from './getTemplate.js';
|
|
2
|
+
|
|
3
|
+
const loadCls = {};
|
|
4
|
+
|
|
5
|
+
export default async function getSelect(name) {
|
|
6
|
+
if (loadCls[name]) return loadCls[name];
|
|
7
|
+
|
|
8
|
+
const clsData = await getTemplate('cls', name);
|
|
9
|
+
|
|
10
|
+
if (clsData) {
|
|
11
|
+
loadCls[name] = { arr: clsData };
|
|
12
|
+
return loadCls[name];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const selectData = await getTemplate('select', name);
|
|
16
|
+
if (!selectData) { return null; }
|
|
17
|
+
|
|
18
|
+
loadCls[name] = selectData;
|
|
19
|
+
return loadCls[name];
|
|
20
|
+
}
|
package/table/index.js
CHANGED
|
@@ -1,78 +1,80 @@
|
|
|
1
|
-
import suggest from './controllers/suggest.js';
|
|
2
|
-
import data from './controllers/data.js';
|
|
3
|
-
import table from './controllers/table.js';
|
|
4
|
-
import card from './controllers/card.js';
|
|
5
|
-
import search from './controllers/search.js';
|
|
6
|
-
import filter from './controllers/filter.js';
|
|
7
|
-
import form from './controllers/form.js';
|
|
8
|
-
import metaFormat from './funcs/metaFormat/index.js';
|
|
9
|
-
import getFilterSQL from './funcs/getFilterSQL/index.js';
|
|
10
|
-
import getTemplate from './controllers/utils/getTemplate.js';
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
fastify.decorate('
|
|
67
|
-
fastify.decorate('
|
|
68
|
-
|
|
69
|
-
fastify.
|
|
70
|
-
|
|
71
|
-
fastify.get(`${prefix}/
|
|
72
|
-
fastify.get(`${prefix}/
|
|
73
|
-
fastify.get(`${prefix}/
|
|
74
|
-
fastify.get(`${prefix}/
|
|
75
|
-
fastify.get(`${prefix}/
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
1
|
+
import suggest from './controllers/suggest.js';
|
|
2
|
+
import data from './controllers/data.js';
|
|
3
|
+
import table from './controllers/table.js';
|
|
4
|
+
import card from './controllers/card.js';
|
|
5
|
+
import search from './controllers/search.js';
|
|
6
|
+
import filter from './controllers/filter.js';
|
|
7
|
+
import form from './controllers/form.js';
|
|
8
|
+
import metaFormat from './funcs/metaFormat/index.js';
|
|
9
|
+
import getFilterSQL from './funcs/getFilterSQL/index.js';
|
|
10
|
+
import getTemplate from './controllers/utils/getTemplate.js';
|
|
11
|
+
import getSelect from './controllers/utils/getSelect.js';
|
|
12
|
+
|
|
13
|
+
const tableSchema = {
|
|
14
|
+
querystring: {
|
|
15
|
+
page: { type: 'string', pattern: '^(\\d+)$' },
|
|
16
|
+
order: { type: 'string', pattern: '^(\\d+)$' },
|
|
17
|
+
filter: { type: 'string', pattern: '^([\\w\\d_-]+)=([\\w\\d_-]+)$' },
|
|
18
|
+
},
|
|
19
|
+
params: {
|
|
20
|
+
id: { type: 'string', pattern: '^([\\d\\w]+)$' },
|
|
21
|
+
table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const searchSchema = {
|
|
26
|
+
querystring: {
|
|
27
|
+
page: { type: 'string', pattern: '^(\\d+)$' },
|
|
28
|
+
limit: { type: 'string', pattern: '^(\\d+)$' },
|
|
29
|
+
order: { type: 'string', pattern: '^([\\w_.]+)$' },
|
|
30
|
+
desc: { type: 'string', pattern: '^(desc)|(asc)$' },
|
|
31
|
+
key: { type: 'string', pattern: '^([\\w\\d_]+)$' },
|
|
32
|
+
table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
|
|
33
|
+
sql: { type: 'string', pattern: '^(\\d)$' },
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const suggestSchema = {
|
|
38
|
+
querystring: {
|
|
39
|
+
lang: { type: 'string', pattern: '^([\\w.]+)$' },
|
|
40
|
+
// parent: { type: 'string', pattern: '^([\\w,./]+)$' },
|
|
41
|
+
sel: { type: 'string', pattern: '^([\\w,./]+)$' },
|
|
42
|
+
name: { type: 'string', pattern: '^([\\w,./]+)$' },
|
|
43
|
+
// key: { type: 'string', pattern: '^([\\w\\d_]+)$' },
|
|
44
|
+
// val: { type: 'string', pattern: '^([\\w.,]+)$' },
|
|
45
|
+
sql: { type: 'string', pattern: '^(\\d)$' },
|
|
46
|
+
},
|
|
47
|
+
params: {
|
|
48
|
+
id: { type: 'string', pattern: '^([\\d\\w]+)$' },
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const formSchema = {
|
|
53
|
+
params: {
|
|
54
|
+
form: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const filterSchema = {
|
|
59
|
+
params: {
|
|
60
|
+
table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
async function plugin(fastify, config = {}) {
|
|
65
|
+
const prefix = config.prefix || '/api';
|
|
66
|
+
fastify.decorate('metaFormat', metaFormat);
|
|
67
|
+
fastify.decorate('getFilterSQL', getFilterSQL);
|
|
68
|
+
fastify.decorate('getTemplate', getTemplate);
|
|
69
|
+
fastify.decorate('getSelect', getSelect);
|
|
70
|
+
|
|
71
|
+
fastify.get(`${prefix}/suggest/:data`, { schema: suggestSchema }, suggest);
|
|
72
|
+
fastify.get(`${prefix}/data/:table/:id?`, { schema: tableSchema }, data); // vs.crm.data.api с node
|
|
73
|
+
fastify.get(`${prefix}/table/:table/:id`, { schema: tableSchema }, table);
|
|
74
|
+
fastify.get(`${prefix}/card/:table/:id`, { schema: tableSchema }, card);
|
|
75
|
+
fastify.get(`${prefix}/search`, { schema: searchSchema }, search);
|
|
76
|
+
fastify.get(`${prefix}/filter/:table`, { schema: filterSchema }, filter);
|
|
77
|
+
fastify.get(`${prefix}/form/:form`, { schema: formSchema }, form);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default plugin;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
|
|
4
|
+
import build from '../../helper.js';
|
|
5
|
+
|
|
6
|
+
test('api suggest', async (t) => {
|
|
7
|
+
const app = await build(t);
|
|
8
|
+
|
|
9
|
+
await t.test('GET /suggest', async () => {
|
|
10
|
+
const res = await app.inject({
|
|
11
|
+
method: 'GET',
|
|
12
|
+
url: `/api/suggest/test.storage.data`,
|
|
13
|
+
});
|
|
14
|
+
const rep = JSON.parse(res?.body);
|
|
15
|
+
// console.log(rep);
|
|
16
|
+
assert.equal(res?.statusCode, 200);
|
|
17
|
+
assert.ok(rep?.count);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
await t.test('GET /suggest key query', async () => {
|
|
21
|
+
const key = 'Новокиївка';
|
|
22
|
+
const res = await app.inject({
|
|
23
|
+
method: 'GET',
|
|
24
|
+
url: `/api/suggest/test.suggest.ato_new?key=${key}`,
|
|
25
|
+
});
|
|
26
|
+
const rep = JSON.parse(res?.body);
|
|
27
|
+
// console.log(rep);
|
|
28
|
+
assert.equal(res?.statusCode, 200);
|
|
29
|
+
assert.ok(rep?.count);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
await t.test('GET /suggest key searchColumn', async () => {
|
|
33
|
+
const key = 'data_address.addr_city';
|
|
34
|
+
const res = await app.inject({
|
|
35
|
+
method: 'GET',
|
|
36
|
+
url: `/api/suggest/test.storage.data?key=${key}`,
|
|
37
|
+
});
|
|
38
|
+
const rep = JSON.parse(res?.body);
|
|
39
|
+
// console.log(rep);
|
|
40
|
+
assert.equal(res?.statusCode, 200);
|
|
41
|
+
assert.ok(rep?.count);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
await t.test('GET /suggest інша db', async () => {
|
|
45
|
+
const res = await app.inject({
|
|
46
|
+
method: 'GET',
|
|
47
|
+
url: `/api/suggest/test.suggest.data`,
|
|
48
|
+
});
|
|
49
|
+
const rep = JSON.parse(res?.body);
|
|
50
|
+
// console.log(rep);
|
|
51
|
+
assert.equal(res?.statusCode, 200);
|
|
52
|
+
assert.ok(rep?.count);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await t.test('GET /suggest parent', async () => {
|
|
56
|
+
const parent = '3206158274160231699';
|
|
57
|
+
const res = await app.inject({
|
|
58
|
+
method: 'GET',
|
|
59
|
+
url: `/api/suggest/test.suggest.parent?parent=${parent}`,
|
|
60
|
+
});
|
|
61
|
+
const rep = JSON.parse(res?.body);
|
|
62
|
+
// console.log(rep);
|
|
63
|
+
assert.equal(res?.statusCode, 200);
|
|
64
|
+
assert.ok(rep?.count);
|
|
65
|
+
});
|
|
66
|
+
})
|