@opengis/admin 0.2.122 → 0.2.123
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/{add-page-D9BweG1f.js → add-page-CNaov3n_.js} +1 -1
- package/dist/{admin-interface-Bq8kf59d.js → admin-interface-Bfl5z6ck.js} +582 -732
- package/dist/{admin-view-pmWjnncn.js → admin-view-DYePX_Un.js} +1 -1
- package/dist/admin.js +2 -2
- package/dist/admin.umd.cjs +50 -50
- package/dist/{card-view-DNKItKZ_.js → card-view-OkM8SWMi.js} +1 -1
- package/dist/{edit-page-DmanLFQC.js → edit-page-BXf-hnHj.js} +1 -1
- package/dist/{import-file-B3hz_TTe.js → import-file-Cds3w-U5.js} +15024 -14822
- package/dist/{profile-page-CnXrMOV_.js → profile-page-B7ofAYKr.js} +1 -1
- package/dist/style.css +1 -1
- package/module/settings/menu.json +21 -13
- package/package.json +3 -3
- package/server/plugins/hook.js +13 -1
- package/server/routes/menu/controllers/getMenu.js +9 -3
- package/server/routes/print/controllers/printTemplate.add.js +37 -0
- package/server/routes/print/controllers/printTemplate.delete.js +29 -0
- package/server/routes/print/controllers/printTemplate.edit.js +42 -0
- package/server/routes/print/controllers/printTemplate.js +24 -79
- package/server/routes/print/controllers/printTemplateList.js +20 -0
- package/server/routes/print/controllers/printTemplatePreview.js +81 -0
- package/server/routes/print/index.mjs +14 -2
- package/server/routes/properties/controllers/admin.properties.post.js +2 -2
- package/server/routes/properties/index.mjs +1 -1
- package/server/routes/report/controllers/data.js +76 -0
- package/server/routes/report/controllers/list.js +18 -0
- package/server/routes/report/index.mjs +7 -0
- package/server/routes/report/utils/formatValue.js +179 -0
- package/server/routes/report/utils/getFilterQuery.js +67 -0
@@ -39,26 +39,34 @@
|
|
39
39
|
"icon": "IconSettingsCog",
|
40
40
|
"menu": [
|
41
41
|
{
|
42
|
-
"path": "admin.
|
43
|
-
"
|
44
|
-
"component": "user-cls-page"
|
45
|
-
},
|
46
|
-
{
|
47
|
-
"path": "admin.properties",
|
48
|
-
"table": "admin.properties.table",
|
42
|
+
"path": "admin.settings",
|
43
|
+
"component": "admin-properties-page" ,
|
49
44
|
"ua": "Налаштування",
|
50
45
|
"en": "Settings"
|
51
46
|
},
|
52
47
|
{
|
53
|
-
"path": "admin.
|
54
|
-
"ua": "
|
48
|
+
"path": "admin.templates",
|
49
|
+
"ua": "Друк ",
|
50
|
+
"component": "template-print-page"
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"path": "admin.interfaces",
|
54
|
+
"ua": "Таблиці",
|
55
55
|
"component": "table-settings-page"
|
56
56
|
},
|
57
57
|
{
|
58
|
-
"path": "admin.
|
59
|
-
"ua": "
|
60
|
-
"component": "
|
61
|
-
}
|
58
|
+
"path": "admin.user-cls",
|
59
|
+
"ua": "Довідники ",
|
60
|
+
"component": "user-cls-page"
|
61
|
+
},
|
62
|
+
{
|
63
|
+
"path": "admin.reports",
|
64
|
+
"ua": "Звіти ",
|
65
|
+
"component": "reports-page"
|
66
|
+
},
|
67
|
+
{ "path": "admin.admin_properties",
|
68
|
+
"ua": "Налаштування адміна",
|
69
|
+
"table": "admin.properties.table"}
|
62
70
|
]
|
63
71
|
}
|
64
72
|
]
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@opengis/admin",
|
3
|
-
"version": "0.2.
|
3
|
+
"version": "0.2.123",
|
4
4
|
"description": "This project Softpro Admin",
|
5
5
|
"main": "dist/admin.js",
|
6
6
|
"type": "module",
|
@@ -48,9 +48,9 @@
|
|
48
48
|
"@fullcalendar/vue3": "^6.1.15",
|
49
49
|
"@opengis/fastify-auth": "^1.0.70",
|
50
50
|
"@opengis/fastify-file": "^1.0.42",
|
51
|
-
"@opengis/fastify-table": "^1.2.
|
51
|
+
"@opengis/fastify-table": "^1.2.11",
|
52
52
|
"@opengis/v3-core": "^0.3.45",
|
53
|
-
"@opengis/v3-filter": "^0.0.
|
53
|
+
"@opengis/v3-filter": "^0.0.71",
|
54
54
|
"@tabler/icons-vue": "^3.28.1",
|
55
55
|
"@tiptap/core": "^2.8.0",
|
56
56
|
"@tiptap/extension-color": "^2.8.0",
|
package/server/plugins/hook.js
CHANGED
@@ -83,10 +83,22 @@ export default async function plugin(fastify) {
|
|
83
83
|
const printTemplateList = getTemplatePath('print');
|
84
84
|
printTemplateList.filter(el => el[2] === 'json').map((el) => {
|
85
85
|
const settings = JSON.parse(readFileSync(el[1]) || '{}');
|
86
|
+
const htmlPath = printTemplateList.find(item => item[0] === el[0] && ['hbs', 'html'].includes(item[2]))?.[1];
|
87
|
+
const html = htmlPath ? readFileSync(htmlPath, 'utf-8') : null;
|
88
|
+
Object.assign(settings, { html });
|
86
89
|
printTemplates[el[0]] = settings;
|
87
90
|
});
|
88
91
|
if (client?.pk?.['admin.templates']) {
|
89
|
-
|
92
|
+
const arr = Object.keys(printTemplates || {}).map(el => ({ name: el, ...printTemplates?.[el] || {} }));
|
93
|
+
|
94
|
+
const { rowsCount = 0 } = await pgClients.client.query(`delete from admin.templates where not (name=any($1::text[])) and type = 'demo'`, [arr.map(el => el.name)]);
|
95
|
+
console.log('delete deprecated templates', 'ok', rowsCount);
|
96
|
+
const { rowsCount: empty = 0 } = await pgClients.client.query('delete from admin.templates where body is null');
|
97
|
+
console.log('delete empty templates', 'ok', empty);
|
98
|
+
|
99
|
+
const q = arr.map(el => `insert into admin.templates(name, route_id, title, type, body) values ('${el.name.replace(/'/g, "''")}', '${el.route.replace(/'/g, "''")}', '${el.title.replace(/'/g, "''")}', 'demo', '${(el.html || '').replace(/'/g, "''")}') on conflict(name, type) do update set route_id=excluded.route_id, title=excluded.title`).join(';');
|
100
|
+
const res = await pgClients.client.query(q);
|
101
|
+
console.log('insert print templates', 'ok', (Array.isArray(res) ? res : [res]).length);
|
90
102
|
}
|
91
103
|
});
|
92
104
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
|
2
2
|
import { join } from 'path';
|
3
|
-
import { userTemplateDir, config,
|
3
|
+
import { userTemplateDir, config, pgClients } from '@opengis/fastify-table/utils.js';
|
4
4
|
|
5
5
|
import { existsSync, readdirSync, readFileSync } from 'fs';
|
6
6
|
const menuCache = [];
|
@@ -38,8 +38,14 @@ export default async function adminMenu({ user = {}, session, pg = pgClients.cli
|
|
38
38
|
|
39
39
|
if (session && user?.uid && !config.local && !config.admin && !user.user_type?.includes?.('admin') && !user.type?.includes?.('admin') && pg.pk['admin.role_access']) {
|
40
40
|
const { type, gl = [], routes = [] } = await pg.query(`select user_type as type, b.gl,routes from admin.users a
|
41
|
-
left join lateral (
|
42
|
-
|
41
|
+
left join lateral (
|
42
|
+
select array_agg(role_id) as gl from admin.user_roles
|
43
|
+
where user_uid=a.uid
|
44
|
+
and role_id in ( select role_id from admin.roles where enabled)
|
45
|
+
)b on 1=1
|
46
|
+
left join lateral (
|
47
|
+
select array_agg(route_id) as routes from admin.role_access where role_id=any(b.gl)
|
48
|
+
)r on 1=1
|
43
49
|
where uid=$1`, [user.uid]).then(el => el.rows[0] || {});
|
44
50
|
/* const { interfaces = [] } = await pg.query(`select array_agg(route_id) as interfaces from admin.role_access
|
45
51
|
where user_uid=$1 or role_id=any($2::text[])`, [user.uid, gl]).then((res) => res.rows?.[0] || {}); */
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import { pgClients, dataInsert } from '@opengis/fastify-table/utils.js';
|
2
|
+
|
3
|
+
export default async function printTemplateAdd({
|
4
|
+
pg = pgClients.client, body = {}, user = {},
|
5
|
+
}) {
|
6
|
+
if (!user?.uid) {
|
7
|
+
return { message: 'access restricted', status: 403 };
|
8
|
+
}
|
9
|
+
|
10
|
+
const { name, title, route, html } = body;
|
11
|
+
|
12
|
+
if (!name) {
|
13
|
+
return { message: 'not enough body params: name', status: 400 };
|
14
|
+
}
|
15
|
+
|
16
|
+
if (!html) {
|
17
|
+
return { message: 'not enough body params: html', status: 400 };
|
18
|
+
}
|
19
|
+
|
20
|
+
const id = await pg.query(`select template_id as id from admin.templates where $1 in (template_id, name)`, [name])
|
21
|
+
.then(el => el.rows?.[0]?.id);
|
22
|
+
|
23
|
+
if (id) {
|
24
|
+
return { message: 'access restricted: template exists', status: 403 };
|
25
|
+
}
|
26
|
+
|
27
|
+
const res = await dataInsert({
|
28
|
+
pg,
|
29
|
+
table: 'admin.templates',
|
30
|
+
data: { name, title, route_id: route, body: html, type: 'user' },
|
31
|
+
uid: user?.uid,
|
32
|
+
})
|
33
|
+
|
34
|
+
const data = res.rows?.[0];
|
35
|
+
|
36
|
+
return { id: data?.template_id, data };
|
37
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { pgClients, dataDelete } from '@opengis/fastify-table/utils.js';
|
2
|
+
|
3
|
+
export default async function printTemplateDelete({
|
4
|
+
pg = pgClients.client, params = {}, user = {},
|
5
|
+
}) {
|
6
|
+
if (!user?.uid) {
|
7
|
+
return { message: 'access restricted', status: 403 };
|
8
|
+
}
|
9
|
+
|
10
|
+
const { id, type } = await pg.query(`select template_id as id, type from admin.templates where enabled and $1 in (template_id, name)`, [params.id])
|
11
|
+
.then(el => el.rows?.[0] || {});
|
12
|
+
|
13
|
+
if (!id) {
|
14
|
+
return { message: 'template not found', status: 404 };
|
15
|
+
}
|
16
|
+
|
17
|
+
if (type === 'demo') {
|
18
|
+
return { message: 'access restricted: demo', status: 403 };
|
19
|
+
}
|
20
|
+
|
21
|
+
const res = await dataDelete({
|
22
|
+
pg,
|
23
|
+
table: 'admin.templates',
|
24
|
+
id,
|
25
|
+
uid: user?.uid,
|
26
|
+
});
|
27
|
+
|
28
|
+
return { id: res?.template_id, data: res };
|
29
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import { pgClients, dataInsert } from '@opengis/fastify-table/utils.js';
|
2
|
+
|
3
|
+
export default async function printTemplateEdit({
|
4
|
+
pg = pgClients.client, params = {}, body = {}, user = {},
|
5
|
+
}) {
|
6
|
+
if (!user?.uid) {
|
7
|
+
return { message: 'access restricted', status: 403 };
|
8
|
+
}
|
9
|
+
|
10
|
+
if (!params?.id) {
|
11
|
+
return { message: 'not enough params: id', status: 400 };
|
12
|
+
}
|
13
|
+
|
14
|
+
const { html, route, title } = body;
|
15
|
+
|
16
|
+
if (!html) {
|
17
|
+
return { message: 'not enough body params: html', status: 400 };
|
18
|
+
}
|
19
|
+
|
20
|
+
const { id, name } = await pg.query(`select template_id as id, name from admin.templates where $1 in (template_id, name)`, [params.id])
|
21
|
+
.then(el => el.rows?.[0] || {});
|
22
|
+
|
23
|
+
if (!id) {
|
24
|
+
return { message: 'template not found', status: 404 };
|
25
|
+
}
|
26
|
+
|
27
|
+
await pg.query(`delete from admin.templates where $1 in (template_id, name)`, [id]);
|
28
|
+
|
29
|
+
const data = { name, body: html, type: 'user' };
|
30
|
+
if (title) Object.assign(data, { title });
|
31
|
+
if (route) Object.assign(data, { route_id: route });
|
32
|
+
|
33
|
+
const res = await dataInsert({
|
34
|
+
pg,
|
35
|
+
table: 'admin.templates',
|
36
|
+
id,
|
37
|
+
data,
|
38
|
+
uid: user?.uid,
|
39
|
+
});
|
40
|
+
|
41
|
+
return { id: res.rows?.[0]?.template_id, data: res.rows?.[0] };
|
42
|
+
}
|
@@ -3,8 +3,6 @@ import { createHash } from 'node:crypto';
|
|
3
3
|
import { config, getFilterSQL, getTemplate, handlebars, pgClients } from '@opengis/fastify-table/utils.js';
|
4
4
|
import { grpc } from '@opengis/fastify-file/utils.js';
|
5
5
|
|
6
|
-
import printTemplates from './printTemplates.js';
|
7
|
-
|
8
6
|
const { htmlToPdf } = grpc();
|
9
7
|
|
10
8
|
export default async function printTemplate(req, reply) {
|
@@ -12,110 +10,57 @@ export default async function printTemplate(req, reply) {
|
|
12
10
|
pg = pgClients.client,
|
13
11
|
params = {},
|
14
12
|
query = {},
|
15
|
-
user = {},
|
16
13
|
} = req;
|
17
14
|
|
18
15
|
if (!params?.name) {
|
19
16
|
return { message: 'not enough params: name', status: 400 };
|
20
17
|
}
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
/* -- params.name === interface -- */
|
25
|
-
if (!printBody?.route) {
|
26
|
-
const where = user?.user_type?.includes('admin')
|
27
|
-
? '1=1'
|
28
|
-
: `coalesce(b.user_uid, d.user_uid)='${user?.uid?.replace(/'/g, "''")}'::text`;
|
29
|
-
|
30
|
-
const { route_id: routeId, alias } = await pg.query(`select a.route_id, alias
|
31
|
-
from admin.routes a
|
32
|
-
left join admin.role_access b on
|
33
|
-
a.route_id=b.route_id
|
34
|
-
left join admin.roles c on
|
35
|
-
b.role_id=c.role_id
|
36
|
-
and c.enabled
|
37
|
-
left join admin.user_roles d on
|
38
|
-
c.role_id=d.role_id
|
39
|
-
and ( case when
|
40
|
-
d.expiration is not null
|
41
|
-
then d.expiration > CURRENT_DATE
|
42
|
-
else 1=1
|
43
|
-
end )
|
44
|
-
where $1 in (a.route_id, a.alias, a.table_name) and ${where}`, [params.name])
|
45
|
-
.then(el => el.rows?.[0] || {});
|
46
|
-
|
47
|
-
if (!routeId || !alias) {
|
48
|
-
return { message: `access restricted: route (${params.name}/${routeId})`, status: 403 };
|
49
|
-
}
|
50
|
-
|
51
|
-
const rows = Object.keys(printTemplates)
|
52
|
-
.filter(key => printTemplates[key]?.route === routeId)
|
53
|
-
.map(key => ({ key, title: printTemplates[key]?.title }));
|
54
|
-
|
55
|
-
return { rows };
|
19
|
+
if (!params?.id) {
|
20
|
+
return { message: 'not enough params: id', status: 400 };
|
56
21
|
}
|
57
22
|
|
58
|
-
|
59
|
-
|
23
|
+
const { id, route, body = '' } = await pg.query(`select template_id as id, body, route_id as route from admin.templates where $1 in (template_id,name)`, [params.name])
|
24
|
+
.then(el => el.rows?.[0] || {});
|
25
|
+
|
26
|
+
if (!id) {
|
27
|
+
return reply.status(404).send('template not found');
|
60
28
|
}
|
61
29
|
|
62
30
|
/* -- params.name === document template -- */
|
63
31
|
const format = query.format || (config.debug ? 'html' : 'pdf');
|
64
32
|
|
65
33
|
const hash = createHash('md5')
|
66
|
-
.update([
|
34
|
+
.update([params?.name, params?.id].join())
|
67
35
|
.digest('hex');
|
68
36
|
|
69
37
|
const headers = format === 'pdf'
|
70
38
|
? { 'Content-Disposition': `inline; filename=${hash}.pdf`, 'Content-Type': 'application/pdf' }
|
71
39
|
: { 'Content-Type': 'text/html; charset=utf-8' };
|
72
40
|
|
73
|
-
const
|
74
|
-
|
75
|
-
if (query?.preview) {
|
76
|
-
const matches = pt.match(/{{(?!\!)([^}]*)}}/g);
|
77
|
-
const preview = `<style> #toggle { background: yellow; } </style>`
|
78
|
-
+ matches.reduce((acc, curr) => acc.replace(curr, curr.replace(/{{(?!\!)([^}]*)}}/g, `<div id="toggle">${curr.replace(/{/g, '%7B').replace(/}/g, '%7D')}</div>`)), pt);
|
79
|
-
|
80
|
-
const html = await handlebars.compile(preview)({});
|
41
|
+
const table = await pg.query(`select alias from admin.routes where route_id=$1`, [route])
|
42
|
+
.then(el => el.rows?.[0]?.alias);
|
81
43
|
|
82
|
-
|
83
|
-
|
84
|
-
}
|
85
|
-
|
86
|
-
const result = await htmlToPdf({ html: html.replace(/%7B/g, '{').replace(/%7D/g, '}') });
|
87
|
-
const buffer = Buffer.from(result.result, 'base64');
|
88
|
-
return reply.headers(headers).send(buffer);
|
44
|
+
if (!table) {
|
45
|
+
return { message: 'route table not found', status: 404 };
|
89
46
|
}
|
90
47
|
|
91
|
-
|
92
|
-
|
93
|
-
.then(el => el.rows?.[0]?.alias);
|
94
|
-
|
95
|
-
if (!table) {
|
96
|
-
return { message: 'route table not found', status: 404 };
|
97
|
-
}
|
98
|
-
|
99
|
-
const loadTable = await getTemplate('table', table);
|
100
|
-
const { optimizedSQL } = await getFilterSQL({ table, pg });
|
48
|
+
const loadTable = await getTemplate('table', table);
|
49
|
+
const { optimizedSQL } = await getFilterSQL({ table, pg });
|
101
50
|
|
102
|
-
|
103
|
-
? ' 1=1 limit 1'
|
104
|
-
: `${loadTable?.key || pg.pk?.[loadTable?.table || table]}=$1`;
|
51
|
+
const where = `${loadTable?.key || pg.pk?.[loadTable?.table || table]}=$1`;
|
105
52
|
|
106
|
-
|
53
|
+
const q = `select * from (${optimizedSQL})q where ${loadTable?.query || '1=1'} and ${where}`;
|
107
54
|
|
108
|
-
|
109
|
-
.then(el => el.rows?.[0] || {});
|
55
|
+
const obj = await pg.query(q, [params.id]).then(el => el.rows?.[0] || {});
|
110
56
|
|
111
|
-
|
57
|
+
const html = await handlebars.compile(body)(obj);
|
112
58
|
|
113
|
-
|
114
|
-
|
115
|
-
}
|
116
|
-
|
117
|
-
const result = await htmlToPdf({ html });
|
118
|
-
const buffer = Buffer.from(result.result, 'base64');
|
119
|
-
return reply.headers(headers).send(buffer);
|
59
|
+
if (format == 'html') {
|
60
|
+
return reply.headers(headers).send(html);
|
120
61
|
}
|
62
|
+
|
63
|
+
const result = await htmlToPdf({ html });
|
64
|
+
const buffer = Buffer.from(result.result, 'base64');
|
65
|
+
return reply.headers(headers).send(buffer);
|
121
66
|
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { pgClients } from "@opengis/fastify-table/utils.js";
|
2
|
+
|
3
|
+
const maxLimit = 100;
|
4
|
+
|
5
|
+
export default async function printTemplateList({
|
6
|
+
pg = pgClients.client, query = {},
|
7
|
+
}) {
|
8
|
+
const { page = 1 } = query;
|
9
|
+
|
10
|
+
const limit = Math.min(maxLimit, +(query.limit || 20));
|
11
|
+
|
12
|
+
const offset = page && page > 0 ? (page - 1) * limit : '0';
|
13
|
+
|
14
|
+
const q = `select template_id as id, name, title, route_id as route, type from admin.templates
|
15
|
+
where enabled limit ${limit} offset ${offset}`;
|
16
|
+
|
17
|
+
const { rows = [] } = pg.pk?.['admin.templates'] ? await pg.query(q) : {};
|
18
|
+
|
19
|
+
return { page, limit, rows };
|
20
|
+
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import { createHash } from 'node:crypto';
|
2
|
+
|
3
|
+
import { config, getFilterSQL, getTemplate, handlebars, pgClients } from '@opengis/fastify-table/utils.js';
|
4
|
+
import { grpc } from '@opengis/fastify-file/utils.js';
|
5
|
+
|
6
|
+
const { htmlToPdf } = grpc();
|
7
|
+
|
8
|
+
export default async function printTemplate(req, reply) {
|
9
|
+
const {
|
10
|
+
pg = pgClients.client,
|
11
|
+
params = {},
|
12
|
+
query = {},
|
13
|
+
} = req;
|
14
|
+
|
15
|
+
if (!params?.name) {
|
16
|
+
return reply.status(404).send('not enough params: name');
|
17
|
+
}
|
18
|
+
|
19
|
+
const { id, name, title, body = '', route } = await pg.query(`select template_id as id, name, title, body, route_id as route from admin.templates where $1 in (template_id,name)`, [params.name])
|
20
|
+
.then(el => el.rows?.[0] || {});
|
21
|
+
|
22
|
+
if (!id) {
|
23
|
+
return reply.status(404).send('template not found');
|
24
|
+
}
|
25
|
+
|
26
|
+
/* -- params.name === document template -- */
|
27
|
+
const format = query.format || (config.debug ? 'html' : 'pdf');
|
28
|
+
|
29
|
+
const hash = createHash('md5')
|
30
|
+
.update([query?.preview, query?.demo, params?.name].join())
|
31
|
+
.digest('hex');
|
32
|
+
|
33
|
+
const headers = format === 'pdf'
|
34
|
+
? { 'Content-Disposition': `inline; filename=${hash}.pdf`, 'Content-Type': 'application/pdf' }
|
35
|
+
: { 'Content-Type': 'text/html; charset=utf-8' };
|
36
|
+
|
37
|
+
if (query?.preview) {
|
38
|
+
const matches = body?.match?.(/{{(?!\!)([^}]*)}}/g) || [];
|
39
|
+
const preview = `<style> #toggle { background: yellow; } </style>`
|
40
|
+
+ matches.reduce((acc, curr) => acc.replace(curr, curr.replace(/{{(?!\!)([^}]*)}}/g, `<div id="toggle">${curr.replace(/{/g, '%7B').replace(/}/g, '%7D')}</div>`)), body);
|
41
|
+
|
42
|
+
const html = await handlebars.compile(preview)({});
|
43
|
+
|
44
|
+
if (format == 'html') {
|
45
|
+
return reply.headers(headers).send(html.replace(/%7B/g, '{').replace(/%7D/g, '}'));
|
46
|
+
}
|
47
|
+
|
48
|
+
const result = await htmlToPdf({ html: html.replace(/%7B/g, '{').replace(/%7D/g, '}') });
|
49
|
+
const buffer = Buffer.from(result.result, 'base64');
|
50
|
+
return reply.headers(headers).send(buffer);
|
51
|
+
}
|
52
|
+
|
53
|
+
if (query?.demo) {
|
54
|
+
const table = await pg.query(`select alias from admin.routes where route_id=$1`, [route])
|
55
|
+
.then(el => el.rows?.[0]?.alias);
|
56
|
+
|
57
|
+
if (!table) {
|
58
|
+
return reply.status(404).send('route table not found');
|
59
|
+
}
|
60
|
+
|
61
|
+
const loadTable = await getTemplate('table', table);
|
62
|
+
const { optimizedSQL } = await getFilterSQL({ table, pg });
|
63
|
+
|
64
|
+
const q = `select * from (${optimizedSQL})q where ${loadTable?.query || '1=1'} limit 1`;
|
65
|
+
|
66
|
+
const obj = await pg.query(q).then(el => el.rows?.[0] || {});
|
67
|
+
|
68
|
+
const html = await handlebars.compile(body)(obj);
|
69
|
+
|
70
|
+
if (format == 'html') {
|
71
|
+
return reply.headers(headers).send(html);
|
72
|
+
}
|
73
|
+
|
74
|
+
const result = await htmlToPdf({ html });
|
75
|
+
const buffer = Buffer.from(result.result, 'base64');
|
76
|
+
return reply.headers(headers).send(buffer);
|
77
|
+
}
|
78
|
+
|
79
|
+
// for body edit
|
80
|
+
return { name, title, html: body, route };
|
81
|
+
}
|
@@ -1,7 +1,19 @@
|
|
1
1
|
import cardPrint from './controllers/cardPrint.js';
|
2
|
+
import printTemplateAdd from './controllers/printTemplate.add.js';
|
3
|
+
import printTemplateEdit from './controllers/printTemplate.edit.js';
|
4
|
+
import printTemplateDelete from './controllers/printTemplate.delete.js';
|
2
5
|
import printTemplate from './controllers/printTemplate.js';
|
6
|
+
import printTemplatePreview from './controllers/printTemplatePreview.js';
|
7
|
+
import printTemplateList from './controllers/printTemplateList.js';
|
8
|
+
|
9
|
+
const policy = ['user'];
|
3
10
|
|
4
11
|
export default async function route(app) {
|
5
|
-
app.get(`/card-print/:table/:id`, cardPrint);
|
6
|
-
app.get('/print-template/:name/:id
|
12
|
+
app.get(`/card-print/:table/:id`, { config: { policy } }, cardPrint);
|
13
|
+
app.get('/print-template/:name/:id', { config: { policy } }, printTemplate);
|
14
|
+
app.get('/print-template/:name', { config: { policy } }, printTemplatePreview);
|
15
|
+
app.get('/print-template', { config: { policy } }, printTemplateList);
|
16
|
+
app.post('/print-template', { config: { policy } }, printTemplateAdd);
|
17
|
+
app.put('/print-template/:id', { config: { policy } }, printTemplateEdit);
|
18
|
+
app.delete('/print-template/:id', { config: { policy } }, printTemplateDelete);
|
7
19
|
}
|
@@ -3,9 +3,9 @@ const table = 'admin.properties';
|
|
3
3
|
import { setSettings } from '../../../../utils.js';
|
4
4
|
|
5
5
|
export default async function postSettingsApp({
|
6
|
-
pg,
|
6
|
+
pg, user = {}, body = {},
|
7
7
|
}) {
|
8
|
-
if (
|
8
|
+
if (!user?.user_type?.includes?.('admin')) {
|
9
9
|
return { message: 'access restricted', status: 403 };
|
10
10
|
}
|
11
11
|
const { key, val } = body;
|
@@ -11,7 +11,7 @@ import { propertiesSchema } from './schema.js';
|
|
11
11
|
|
12
12
|
export default async function route(fastify) {
|
13
13
|
fastify.get('/settings-app/:key?', { scheme: propertiesSchema }, getSettingsApp);
|
14
|
-
fastify.post('/settings-app', { config: { policy: ['
|
14
|
+
fastify.post('/settings-app', { config: { policy: ['admin'] } }, postSettingsApp);
|
15
15
|
|
16
16
|
fastify.get('/settings-user/:key?', { scheme: propertiesSchema }, getSettingsUser);
|
17
17
|
fastify.post('/settings-user', {}, postSettingsUser);
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import { handlebars, pgClients, getTemplate, metaFormat, handlebarsSync } from '@opengis/fastify-table/utils.js';
|
2
|
+
|
3
|
+
import getFilterQuery from '../utils/getFilterQuery.js';
|
4
|
+
|
5
|
+
const maxLimit = 100;
|
6
|
+
|
7
|
+
export default async function reportData({
|
8
|
+
pg = pgClients.client, params = {}, query = {}, user = {},
|
9
|
+
}) {
|
10
|
+
if (!params?.name) {
|
11
|
+
return { message: 'not enough params: name', status: 400 };
|
12
|
+
}
|
13
|
+
|
14
|
+
const loadTemplate = await getTemplate('report', params.name);
|
15
|
+
|
16
|
+
if (!loadTemplate?.sql) {
|
17
|
+
return { message: `report not found: ${params.name}`, status: 404 };
|
18
|
+
}
|
19
|
+
|
20
|
+
const { uid } = user;
|
21
|
+
|
22
|
+
const { kpi, sql, meta, filters } = loadTemplate;
|
23
|
+
const { date, columns: metaColumns } = meta || {};
|
24
|
+
|
25
|
+
const granularity = query.granularity && date && false ? `date_trunc('${query.granularity}',${date})::date::text` : null;
|
26
|
+
const groupby = [meta?.groupby, granularity].filter(el => el).join(',');
|
27
|
+
|
28
|
+
const period = query.period && date ? `${date}=${query.period}` : null;
|
29
|
+
const filter = [query.filter, period].filter(el => el).join(';');
|
30
|
+
|
31
|
+
const limit = Math.min(maxLimit, +(query.limit || 20));
|
32
|
+
const offset = query.page && query.page > 0 ? (query.page - 1) * limit : 0;
|
33
|
+
|
34
|
+
const kpiColumns = kpi ? kpi?.map(el => `${el.sql || 'count(*)'} as "${el.name}"`)?.join(',') : '';
|
35
|
+
const columnsList = metaColumns || loadTemplate.columns?.map(el => el.name)?.join(',');
|
36
|
+
|
37
|
+
const qMeta = await handlebars.compile(sql)({ user, uid });
|
38
|
+
const { fields = [] } = await pg.query(`select * from (${qMeta})q limit 0`);
|
39
|
+
|
40
|
+
const where = filter ? getFilterQuery({ pg, filter, fields, filterList: filters })?.map?.(el => el.query)?.join(' and ') : null;
|
41
|
+
|
42
|
+
const qAgg = `select ${kpiColumns || ''} from (${qMeta})q where ${where || '1=1'}`;
|
43
|
+
const q = `select ${columnsList || '*'} from (${qMeta})q where ${where || '1=1'} ${groupby ? `group by ${groupby}` : ''} limit ${limit} offset ${offset}`;
|
44
|
+
|
45
|
+
if (query.sql && user?.user_type?.includes('admin')) return `${qAgg};${q}`;
|
46
|
+
|
47
|
+
const kpiData = await pg.query(qAgg).then(el => el.rows?.[0] || {});
|
48
|
+
kpi?.forEach(el => Object.assign(el, { count: kpiData[el.name] || '0' }));
|
49
|
+
|
50
|
+
const { rows = [] } = await pg.query(q);
|
51
|
+
|
52
|
+
const cls = meta?.cls || loadTemplate.columns
|
53
|
+
?.filter(el => (el.option || el.data))
|
54
|
+
?.reduce((acc, curr) => Object.assign(acc, { [curr.name]: (curr.option || curr.data) }), {});
|
55
|
+
|
56
|
+
const titles = meta?.titles
|
57
|
+
|| loadTemplate.columns?.reduce((acc, curr) => Object.assign(acc, { [curr.name]: curr.title }), {});
|
58
|
+
|
59
|
+
await metaFormat({ rows, cls, sufix: true });
|
60
|
+
|
61
|
+
const columns = loadTemplate.columns
|
62
|
+
|| fields.map(el => ({
|
63
|
+
name: el.name,
|
64
|
+
title: titles?.[el.name] || el.name,
|
65
|
+
type: cls?.[el.name] ? 'Autocomplete' : 'Text',
|
66
|
+
format: pg.pgType?.[el.dataTypeID],
|
67
|
+
data: cls?.[el.name],
|
68
|
+
}));
|
69
|
+
|
70
|
+
return {
|
71
|
+
q: user?.user_type?.includes('admin') ? q : undefined,
|
72
|
+
kpi,
|
73
|
+
rows,
|
74
|
+
columns,
|
75
|
+
};
|
76
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { getTemplatePath, getTemplate } from '@opengis/fastify-table/utils.js';
|
2
|
+
|
3
|
+
export default async function reportList({
|
4
|
+
user = {},
|
5
|
+
}) {
|
6
|
+
const arr = getTemplatePath('report').filter(el => el[2] === 'json').map(el => el[0]);
|
7
|
+
|
8
|
+
const rows = await Promise.all(arr.map(async (el) => {
|
9
|
+
const loadTemplate = await getTemplate('report', el);
|
10
|
+
return {
|
11
|
+
name: el,
|
12
|
+
filters: loadTemplate?.filters || [],
|
13
|
+
title: loadTemplate?.title || el,
|
14
|
+
sql: user.user_type?.includes('admin') ? loadTemplate?.sql : undefined,
|
15
|
+
};
|
16
|
+
}));
|
17
|
+
return { rows };
|
18
|
+
}
|