@opengis/admin 0.3.19 → 0.3.21
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/README.md +97 -97
- package/config.js +4 -4
- package/dist/{add-page-BZ4-a4Ds.js → add-page-DsqxZzEq.js} +1 -1
- package/dist/{admin-interface-Cel1oSUl.js → admin-interface-C8SGlC1N.js} +2 -2
- package/dist/{admin-view-DI3sCqf2.js → admin-view-DtdqTUPa.js} +2 -2
- package/dist/admin.js +1 -1
- package/dist/admin.umd.cjs +75 -75
- package/dist/assets/logo.svg +41 -41
- package/dist/{card-view-D8BG77Bz.js → card-view-juhvChuX.js} +1 -1
- package/dist/{edit-page-BEGG0O_f.js → edit-page-DB1hlRzp.js} +1 -1
- package/dist/{import-file-D-rfELS1.js → import-file-BSdaeh6s.js} +17380 -17196
- package/dist/{profile-page-BZczsbQL.js → profile-page-B-RhB5JP.js} +1 -1
- package/dist/style.css +1 -1
- package/module/settings/card/admin.accounts.table/index.yml +7 -7
- package/module/settings/card/admin.accounts.table/rules.hbs +18 -18
- package/module/settings/card/admin.accounts.table/users.hbs +13 -13
- package/module/settings/card/admin.routes.table/groups.hbs +11 -11
- package/module/settings/card/admin.routes.table/users.hbs +16 -16
- package/module/settings/cls/core.actions.json +17 -17
- package/module/settings/cls/core.scope.json +13 -13
- package/module/settings/cls/properties.site_status.json +13 -13
- package/module/settings/cls/properties.widget_status.json +13 -13
- package/module/settings/cls/users.user_type.json +13 -13
- package/module/settings/cls/yes_no.json +11 -11
- package/module/settings/form/admin.accounts.form.json +13 -13
- package/module/settings/form/admin.properties.form.json +15 -15
- package/module/settings/form/admin.roles.form.json +21 -21
- package/module/settings/form/admin.user_properties.form.json +15 -15
- package/module/settings/form/admin.user_roles_card.form.json +13 -13
- package/module/settings/interface/admin.properties.json +4 -4
- package/module/settings/interface/admin.roles.json +4 -4
- package/module/settings/interface/admin.routes.json +4 -4
- package/module/settings/interface/admin.users.json +4 -4
- package/module/settings/select/core.routes.sql +1 -1
- package/module/settings/select/core.user_mentioned.sql +1 -1
- package/module/settings/select/core.user_uid.sql +1 -1
- package/module/settings/table/admin.properties.table.json +39 -39
- package/module/settings/table/admin.user_properties.table.json +34 -34
- package/package.json +5 -4
- package/server/helpers/core/badge.js +16 -16
- package/server/helpers/core/buttonHelper.js +21 -21
- package/server/helpers/core/coalesce.js +7 -7
- package/server/helpers/core/select.js +48 -48
- package/server/helpers/core/token.js +18 -18
- package/server/helpers/list/buttonHelper.js +21 -21
- package/server/helpers/list/utils/button.js +5 -5
- package/server/helpers/temp/contentList.js +58 -58
- package/server/helpers/temp/ifCond.js +101 -101
- package/server/helpers/utils/button.js +5 -5
- package/server/helpers/utils/mdToHTML.js +17 -17
- package/server/plugins/access/index.mjs +6 -6
- package/server/plugins/adminHook.js +81 -81
- package/server/plugins/cron.js +10 -10
- package/server/plugins/docs.js +28 -28
- package/server/plugins/hook.js +19 -8
- package/server/routes/access/controllers/access.group.js +29 -29
- package/server/routes/access/controllers/access.group.post.js +54 -54
- package/server/routes/access/index.mjs +11 -11
- package/server/routes/access/schema.mjs +67 -67
- package/server/routes/calendar/controllers/calendar.data.js +125 -125
- package/server/routes/calendar/index.mjs +7 -7
- package/server/routes/calendar/schema.js +21 -21
- package/server/routes/data/controllers/cardTabData.js +49 -49
- package/server/routes/data/controllers/funcs/getFilterSQL/index.js +92 -92
- package/server/routes/data/controllers/funcs/getFilterSQL/util/formatValue.js +170 -170
- package/server/routes/data/controllers/funcs/getFilterSQL/util/getCustomQuery.js +13 -13
- package/server/routes/data/controllers/funcs/getFilterSQL/util/getFilterQuery.js +64 -64
- package/server/routes/data/controllers/funcs/getFilterSQL/util/getOptimizedQuery.js +12 -12
- package/server/routes/data/controllers/funcs/getFilterSQL/util/getTableSql.js +34 -34
- package/server/routes/data/controllers/tableData.js +31 -31
- package/server/routes/data/controllers/tableDataId.js +27 -27
- package/server/routes/data/controllers/tokenInfo.js +9 -9
- package/server/routes/data/controllers/utils/assignTokens.js +30 -30
- package/server/routes/data/controllers/utils/conditions.js +20 -20
- package/server/routes/data/controllers/utils/getColumns.js +8 -8
- package/server/routes/data/index.mjs +20 -20
- package/server/routes/data/schema.js +54 -54
- package/server/routes/notifications/controllers/readNotifications.js +27 -27
- package/server/routes/notifications/controllers/testEmail.js +35 -35
- package/server/routes/notifications/controllers/userNotifications.js +53 -53
- package/server/routes/notifications/funcs/addNotification.js +21 -21
- package/server/routes/notifications/funcs/sendNotification.js +92 -92
- package/server/routes/notifications/hook/onWidgetSet.js +56 -56
- package/server/routes/notifications/index.mjs +26 -26
- package/server/routes/notifications/schema.js +16 -16
- package/server/routes/print/controllers/cardPrint.js +135 -135
- package/server/routes/properties/schema.js +10 -10
- package/server/routes/report/controllers/data.js +76 -76
- package/server/routes/report/controllers/list.js +18 -18
- package/server/routes/report/index.mjs +7 -7
- package/server/routes/report/utils/formatValue.js +179 -179
- package/server/routes/root.mjs +3 -3
- package/server/routes/templates/controllers/getTemplate.js +44 -44
- package/server/routes/templates/schema.js +8 -8
- package/server/routes/user/controllers/user.cls.id.js +14 -14
- package/server/routes/user/controllers/user.cls.js +72 -72
- package/server/routes/user/controllers/user.info.js +17 -17
- package/server/routes/user/schema.js +14 -14
- package/server/routes/util/controllers/code.generator.js +89 -89
- package/server/routes/widget/controllers/widget.del.js +63 -47
- package/server/routes/widget/controllers/widget.set.js +84 -84
- package/server/routes/widget/hook/onWidgetSet.js +12 -12
- package/server/routes/widget/index.mjs +16 -16
- package/server/routes/widget/schema.js +12 -12
- package/server/templates/cls/itree.recrzone_category.json +73 -73
- package/server/templates/cls/test.json +9 -9
- package/server/templates/form/admin.user_cls.data.form.json +49 -49
- package/server/templates/form/admin.user_group_rel.form.json +21 -21
- package/server/templates/form/form-user-pass.json +10 -10
- package/server/templates/form/form-user_group.json +39 -39
- package/server/templates/form/form-users.json +156 -156
- package/server/templates/form/user_group_access.form.json +22 -22
- package/server/templates/select/account_id.json +2 -2
- package/server/templates/table/gis.dataset.table.json +43 -43
- package/server/templates/table/management.user_group.table.json +112 -112
- package/server/templates/table/management.users.table.json +126 -126
@@ -1,55 +1,55 @@
|
|
1
|
-
import { pgClients } from '@opengis/fastify-table/utils.js';
|
2
|
-
|
3
|
-
import { getAdminAccess } from '../../../../utils.js';
|
4
|
-
import accessGroup from './access.group.js';
|
5
|
-
|
6
|
-
export default async function accessGroupPost({
|
7
|
-
pg = pgClients.client, params = {}, session = {}, body = {},
|
8
|
-
}) {
|
9
|
-
const { id } = params;
|
10
|
-
const { user } = session?.passport || {};
|
11
|
-
if (!user) return { status: 403, message: 'access restricted' }
|
12
|
-
|
13
|
-
// restrict access - admin only
|
14
|
-
const check = await getAdminAccess({ id, user });
|
15
|
-
if (check) return check;
|
16
|
-
|
17
|
-
const { users = [], routes = [] } = body;
|
18
|
-
|
19
|
-
if (!routes?.length) {
|
20
|
-
// return { message: 'not enough params: users / routes', status: 400 };
|
21
|
-
await pg.query(`delete from admin.role_access where role_id=$1`, [id]);
|
22
|
-
|
23
|
-
if (!users?.length) {
|
24
|
-
return { message: { id, routes }, status: 200 };
|
25
|
-
}
|
26
|
-
}
|
27
|
-
|
28
|
-
if (routes?.length) {
|
29
|
-
const { routesDB = [] } = await pg.query('select array_agg(route_id) as "routesDB" from admin.routes where enabled')
|
30
|
-
.then((res1) => res1.rows?.[0] || {});
|
31
|
-
await pg.query(`delete from admin.role_access where role_id=$1;`, [id]);
|
32
|
-
|
33
|
-
|
34
|
-
const q = `insert into admin.role_access(role_id,route_id,actions) values ($1,$2,$3)`;
|
35
|
-
await Promise.all(routes.filter(el => routesDB.includes(el.path) && el.actions).map(el => pg.query(q, [id, el.path, el.actions])))
|
36
|
-
|
37
|
-
const { rows } = await pg.query(`select a.route_id as path, b.actions as actions from admin.routes a
|
38
|
-
left join admin.role_access b on a.route_id=b.route_id
|
39
|
-
where b.role_id=$1`, [id]);
|
40
|
-
|
41
|
-
if (!users?.length) {
|
42
|
-
return { message: { id, routes: rows }, status: 200 };
|
43
|
-
}
|
44
|
-
}
|
45
|
-
|
46
|
-
const q = `delete from admin.user_roles where role_id='${id.replace(/'/g, "''")}';
|
47
|
-
insert into admin.user_roles(role_id,user_uid,access_granted)
|
48
|
-
values ${users.filter((el) => el?.id).map((el) => `('${id.replace(/'/g, "''")}','${el.id.replace(/'/g, "''")}','${uid?.replace(/'/g, "''")}')`)}`;
|
49
|
-
|
50
|
-
await pg.query(q);
|
51
|
-
|
52
|
-
const res = await accessGroup({ pg, params, session });
|
53
|
-
|
54
|
-
return res;
|
1
|
+
import { pgClients } from '@opengis/fastify-table/utils.js';
|
2
|
+
|
3
|
+
import { getAdminAccess } from '../../../../utils.js';
|
4
|
+
import accessGroup from './access.group.js';
|
5
|
+
|
6
|
+
export default async function accessGroupPost({
|
7
|
+
pg = pgClients.client, params = {}, session = {}, body = {},
|
8
|
+
}) {
|
9
|
+
const { id } = params;
|
10
|
+
const { user } = session?.passport || {};
|
11
|
+
if (!user) return { status: 403, message: 'access restricted' }
|
12
|
+
|
13
|
+
// restrict access - admin only
|
14
|
+
const check = await getAdminAccess({ id, user });
|
15
|
+
if (check) return check;
|
16
|
+
|
17
|
+
const { users = [], routes = [] } = body;
|
18
|
+
|
19
|
+
if (!routes?.length) {
|
20
|
+
// return { message: 'not enough params: users / routes', status: 400 };
|
21
|
+
await pg.query(`delete from admin.role_access where role_id=$1`, [id]);
|
22
|
+
|
23
|
+
if (!users?.length) {
|
24
|
+
return { message: { id, routes }, status: 200 };
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
if (routes?.length) {
|
29
|
+
const { routesDB = [] } = await pg.query('select array_agg(route_id) as "routesDB" from admin.routes where enabled')
|
30
|
+
.then((res1) => res1.rows?.[0] || {});
|
31
|
+
await pg.query(`delete from admin.role_access where role_id=$1;`, [id]);
|
32
|
+
|
33
|
+
|
34
|
+
const q = `insert into admin.role_access(role_id,route_id,actions) values ($1,$2,$3)`;
|
35
|
+
await Promise.all(routes.filter(el => routesDB.includes(el.path) && el.actions).map(el => pg.query(q, [id, el.path, el.actions])))
|
36
|
+
|
37
|
+
const { rows } = await pg.query(`select a.route_id as path, b.actions as actions from admin.routes a
|
38
|
+
left join admin.role_access b on a.route_id=b.route_id
|
39
|
+
where b.role_id=$1`, [id]);
|
40
|
+
|
41
|
+
if (!users?.length) {
|
42
|
+
return { message: { id, routes: rows }, status: 200 };
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
const q = `delete from admin.user_roles where role_id='${id.replace(/'/g, "''")}';
|
47
|
+
insert into admin.user_roles(role_id,user_uid,access_granted)
|
48
|
+
values ${users.filter((el) => el?.id).map((el) => `('${id.replace(/'/g, "''")}','${el.id.replace(/'/g, "''")}','${uid?.replace(/'/g, "''")}')`)}`;
|
49
|
+
|
50
|
+
await pg.query(q);
|
51
|
+
|
52
|
+
const res = await accessGroup({ pg, params, session });
|
53
|
+
|
54
|
+
return res;
|
55
55
|
}
|
@@ -1,11 +1,11 @@
|
|
1
|
-
import accessGroup from "./controllers/access.group.js";
|
2
|
-
import accessGroupPost from "./controllers/access.group.post.js";
|
3
|
-
import accessInterface from "./controllers/access.interface.js";
|
4
|
-
|
5
|
-
import { accessGroupPostSchema, accessGroupSchema, accessInterfaceSchema } from "./schema.mjs";
|
6
|
-
|
7
|
-
export default async function route(fastify) {
|
8
|
-
fastify.get('/access-group/:id', { schema: accessGroupSchema }, accessGroup);
|
9
|
-
fastify.post('/access-group/:id', { schema: accessGroupPostSchema }, accessGroupPost);
|
10
|
-
fastify.get('/access-interface/:name', { schema: accessInterfaceSchema }, accessInterface);
|
11
|
-
}
|
1
|
+
import accessGroup from "./controllers/access.group.js";
|
2
|
+
import accessGroupPost from "./controllers/access.group.post.js";
|
3
|
+
import accessInterface from "./controllers/access.interface.js";
|
4
|
+
|
5
|
+
import { accessGroupPostSchema, accessGroupSchema, accessInterfaceSchema } from "./schema.mjs";
|
6
|
+
|
7
|
+
export default async function route(fastify) {
|
8
|
+
fastify.get('/access-group/:id', { schema: accessGroupSchema }, accessGroup);
|
9
|
+
fastify.post('/access-group/:id', { schema: accessGroupPostSchema }, accessGroupPost);
|
10
|
+
fastify.get('/access-interface/:name', { schema: accessInterfaceSchema }, accessInterface);
|
11
|
+
}
|
@@ -1,68 +1,68 @@
|
|
1
|
-
export default null;
|
2
|
-
export { accessGroupSchema, accessGroupPostSchema, accessInterfaceSchema }
|
3
|
-
|
4
|
-
const accessGroupSchema = {
|
5
|
-
params: {
|
6
|
-
type: 'object',
|
7
|
-
properties: {
|
8
|
-
id: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
|
9
|
-
},
|
10
|
-
required: ['id'],
|
11
|
-
},
|
12
|
-
};
|
13
|
-
|
14
|
-
const accessInterfaceSchema = {
|
15
|
-
params: {
|
16
|
-
type: 'object',
|
17
|
-
properties: {
|
18
|
-
name: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
|
19
|
-
},
|
20
|
-
required: ['name'],
|
21
|
-
},
|
22
|
-
};
|
23
|
-
|
24
|
-
const accessGroupPostSchema = {
|
25
|
-
params: {
|
26
|
-
type: 'object',
|
27
|
-
properties: {
|
28
|
-
id: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
|
29
|
-
},
|
30
|
-
required: ['id'],
|
31
|
-
},
|
32
|
-
body: {
|
33
|
-
type: 'object',
|
34
|
-
properties: {
|
35
|
-
users: {
|
36
|
-
type: 'array',
|
37
|
-
items: {
|
38
|
-
type: 'object',
|
39
|
-
properties: {
|
40
|
-
id: {
|
41
|
-
type: 'string',
|
42
|
-
pattern: '^([\\d\\w._-]+)$',
|
43
|
-
},
|
44
|
-
},
|
45
|
-
},
|
46
|
-
},
|
47
|
-
routes: {
|
48
|
-
type: 'array',
|
49
|
-
/*items: {
|
50
|
-
type: 'object',
|
51
|
-
properties: {
|
52
|
-
path: {
|
53
|
-
type: 'string',
|
54
|
-
pattern: '^([\\d\\w._-]+)$',
|
55
|
-
},
|
56
|
-
actions: {
|
57
|
-
type: 'array',
|
58
|
-
items: {
|
59
|
-
type: 'string',
|
60
|
-
enum: ['get', 'add', 'edit', 'del'],
|
61
|
-
},
|
62
|
-
},
|
63
|
-
},
|
64
|
-
},*/
|
65
|
-
},
|
66
|
-
},
|
67
|
-
},
|
1
|
+
export default null;
|
2
|
+
export { accessGroupSchema, accessGroupPostSchema, accessInterfaceSchema }
|
3
|
+
|
4
|
+
const accessGroupSchema = {
|
5
|
+
params: {
|
6
|
+
type: 'object',
|
7
|
+
properties: {
|
8
|
+
id: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
|
9
|
+
},
|
10
|
+
required: ['id'],
|
11
|
+
},
|
12
|
+
};
|
13
|
+
|
14
|
+
const accessInterfaceSchema = {
|
15
|
+
params: {
|
16
|
+
type: 'object',
|
17
|
+
properties: {
|
18
|
+
name: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
|
19
|
+
},
|
20
|
+
required: ['name'],
|
21
|
+
},
|
22
|
+
};
|
23
|
+
|
24
|
+
const accessGroupPostSchema = {
|
25
|
+
params: {
|
26
|
+
type: 'object',
|
27
|
+
properties: {
|
28
|
+
id: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
|
29
|
+
},
|
30
|
+
required: ['id'],
|
31
|
+
},
|
32
|
+
body: {
|
33
|
+
type: 'object',
|
34
|
+
properties: {
|
35
|
+
users: {
|
36
|
+
type: 'array',
|
37
|
+
items: {
|
38
|
+
type: 'object',
|
39
|
+
properties: {
|
40
|
+
id: {
|
41
|
+
type: 'string',
|
42
|
+
pattern: '^([\\d\\w._-]+)$',
|
43
|
+
},
|
44
|
+
},
|
45
|
+
},
|
46
|
+
},
|
47
|
+
routes: {
|
48
|
+
type: 'array',
|
49
|
+
/*items: {
|
50
|
+
type: 'object',
|
51
|
+
properties: {
|
52
|
+
path: {
|
53
|
+
type: 'string',
|
54
|
+
pattern: '^([\\d\\w._-]+)$',
|
55
|
+
},
|
56
|
+
actions: {
|
57
|
+
type: 'array',
|
58
|
+
items: {
|
59
|
+
type: 'string',
|
60
|
+
enum: ['get', 'add', 'edit', 'del'],
|
61
|
+
},
|
62
|
+
},
|
63
|
+
},
|
64
|
+
},*/
|
65
|
+
},
|
66
|
+
},
|
67
|
+
},
|
68
68
|
};
|
@@ -1,126 +1,126 @@
|
|
1
|
-
import { getMeta, getTemplate, pgClients, handlebarsSync } from '@opengis/fastify-table/utils.js';
|
2
|
-
|
3
|
-
const isValidDate = (dateStr) => (new Date(dateStr)).toString() !== 'Invalid Date';
|
4
|
-
|
5
|
-
export default async function calendarData({
|
6
|
-
pg = pgClients.client, params = {}, query = {}, session = {},
|
7
|
-
}) {
|
8
|
-
const { name } = params;
|
9
|
-
const { granularity = 'month' } = query;
|
10
|
-
const { user = {} } = session.passport || {};
|
11
|
-
|
12
|
-
if (!name) {
|
13
|
-
return { message: 'not enough params: name', status: 400 };
|
14
|
-
}
|
15
|
-
|
16
|
-
if (query.granularity && !['day', 'month', 'week', 'year'].includes(granularity)) {
|
17
|
-
return { message: 'invalid query params: granularity', status: 400 };
|
18
|
-
}
|
19
|
-
|
20
|
-
const body = await getTemplate('calendar', name);
|
21
|
-
|
22
|
-
if (!body) {
|
23
|
-
return { message: `calendar not found: ${name}`, status: 404 };
|
24
|
-
}
|
25
|
-
|
26
|
-
const {
|
27
|
-
title,
|
28
|
-
table,
|
29
|
-
meta = {},
|
30
|
-
} = body || {};
|
31
|
-
|
32
|
-
if (!table) {
|
33
|
-
return { message: 'not enough calendar params: table', status: 404 };
|
34
|
-
}
|
35
|
-
if (!pg.pk?.[table]) {
|
36
|
-
return { message: `table pkey not found: ${table}`, status: 404 };
|
37
|
-
}
|
38
|
-
|
39
|
-
const filterList = (body?.filter || [])
|
40
|
-
.concat(body?.filterInline || [])
|
41
|
-
.concat(body?.filterCustom || [])
|
42
|
-
.concat(body?.filterState || [])
|
43
|
-
.concat(body?.filterList || [])
|
44
|
-
.concat(body?.filters || [])
|
45
|
-
.concat(body?.filter_list || []);
|
46
|
-
|
47
|
-
const { columns = [] } = await getMeta({ pg, table });
|
48
|
-
const columnList = columns?.map((el) => el?.name);
|
49
|
-
|
50
|
-
const dateColumn = columns?.find((el) => el?.name === (meta?.date || meta?.start))?.name
|
51
|
-
|| columns?.find((el) => el?.name === 'cdate')?.name;
|
52
|
-
|
53
|
-
if (query.date && !dateColumn) {
|
54
|
-
return { message: 'invalid template params: date column not found', status: 400 };
|
55
|
-
}
|
56
|
-
|
57
|
-
if (query.date && !isValidDate(query.date)) {
|
58
|
-
return { message: 'Invalid date: out of range or incorrect format', status: 400 };
|
59
|
-
}
|
60
|
-
|
61
|
-
const filterWhere = filterList?.length && query.filter?.length
|
62
|
-
? filterList
|
63
|
-
.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true))
|
64
|
-
.map((el) => {
|
65
|
-
const val = query.filter.split(',').find((e) => e?.split('=')?.shift()?.includes(el.column || el.name))?.split('=')?.pop();
|
66
|
-
if (val) return el.column && val ? `(${[`${el.column}::text='${val.replace(/'/g, "''")}'::text`, el.query].filter((el) => el).join(' and ')})` : el.query;
|
67
|
-
})
|
68
|
-
.filter((el) => el)
|
69
|
-
.join(' and ')
|
70
|
-
: undefined;
|
71
|
-
|
72
|
-
const queryWhere = handlebarsSync.compile(body.query || '1=1')({ user, uid: user?.uid });
|
73
|
-
const filterDate = query.date ? `date_trunc('${granularity}', "${dateColumn}"::date)='${query.date}'::date` : undefined;
|
74
|
-
|
75
|
-
const where = [queryWhere, filterDate, filterWhere].filter((el) => el).join(' and ');
|
76
|
-
|
77
|
-
const filtersByColumn = filterList.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true) && el?.column);
|
78
|
-
|
79
|
-
const filters = [];
|
80
|
-
if (filtersByColumn?.length) {
|
81
|
-
await Promise.all(filtersByColumn.map(async (el) => {
|
82
|
-
const { rows: filterData = [] } = await pg.query(`select ${el.column.replace(/'/g, "''")} as id, count(*) from ${table}
|
83
|
-
where ${el.query || '1=1'} and ${filterWhere || '1=1'} group by ${el.column.replace(/'/g, "''")}`);
|
84
|
-
if (!filterData?.length) return;
|
85
|
-
|
86
|
-
const clsData = await getTemplate(['cls', 'select'], el.cls);
|
87
|
-
|
88
|
-
if (!el.cls || !clsData?.length) {
|
89
|
-
filterData.forEach((item) => filters.push(item));
|
90
|
-
return;
|
91
|
-
}
|
92
|
-
|
93
|
-
filterData.forEach((filter) => {
|
94
|
-
const cls = clsData.find((item) => item.id === filter.id.toString());
|
95
|
-
Object.assign(filter, { title: cls?.text, color: cls?.color });
|
96
|
-
filters.push(filter);
|
97
|
-
});
|
98
|
-
}));
|
99
|
-
}
|
100
|
-
|
101
|
-
const filtersByPreparedQuery = filterList.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true) && !el?.column && el?.query);
|
102
|
-
|
103
|
-
if (filtersByPreparedQuery?.length) {
|
104
|
-
await Promise.all(filtersByPreparedQuery.map(async (el) => {
|
105
|
-
const { rows = [] } = await pg.query(`select ${el.query} as id, count(*) from ${table}
|
106
|
-
where ${filterWhere || '1=1'} group by ${el.query}`);
|
107
|
-
rows.forEach((item) => filters.push(item));
|
108
|
-
}));
|
109
|
-
}
|
110
|
-
|
111
|
-
const metaColumns = Object.keys(meta)
|
112
|
-
.filter((el) => ['date', 'start', 'end', 'title', 'status'].includes(el) && columnList.includes(meta[el]))
|
113
|
-
.map((el) => `"${meta[el]}" as ${el}`);
|
114
|
-
|
115
|
-
if (!metaColumns?.length) {
|
116
|
-
return { message: `calendar param meta is invalid: invalid/empty keys`, status: 404 };
|
117
|
-
}
|
118
|
-
|
119
|
-
const q = `select ${metaColumns.join(',')} from ${table} where ${where}`;
|
120
|
-
if (query?.sql && user?.user_type?.includes('admin')) return q;
|
121
|
-
|
122
|
-
const { rows = [] } = await pg.query(q);
|
123
|
-
|
124
|
-
return { title, filters, granularity, sql: user?.user_type?.includes('admin') ? q : undefined, rows };
|
125
|
-
|
1
|
+
import { getMeta, getTemplate, pgClients, handlebarsSync } from '@opengis/fastify-table/utils.js';
|
2
|
+
|
3
|
+
const isValidDate = (dateStr) => (new Date(dateStr)).toString() !== 'Invalid Date';
|
4
|
+
|
5
|
+
export default async function calendarData({
|
6
|
+
pg = pgClients.client, params = {}, query = {}, session = {},
|
7
|
+
}) {
|
8
|
+
const { name } = params;
|
9
|
+
const { granularity = 'month' } = query;
|
10
|
+
const { user = {} } = session.passport || {};
|
11
|
+
|
12
|
+
if (!name) {
|
13
|
+
return { message: 'not enough params: name', status: 400 };
|
14
|
+
}
|
15
|
+
|
16
|
+
if (query.granularity && !['day', 'month', 'week', 'year'].includes(granularity)) {
|
17
|
+
return { message: 'invalid query params: granularity', status: 400 };
|
18
|
+
}
|
19
|
+
|
20
|
+
const body = await getTemplate('calendar', name);
|
21
|
+
|
22
|
+
if (!body) {
|
23
|
+
return { message: `calendar not found: ${name}`, status: 404 };
|
24
|
+
}
|
25
|
+
|
26
|
+
const {
|
27
|
+
title,
|
28
|
+
table,
|
29
|
+
meta = {},
|
30
|
+
} = body || {};
|
31
|
+
|
32
|
+
if (!table) {
|
33
|
+
return { message: 'not enough calendar params: table', status: 404 };
|
34
|
+
}
|
35
|
+
if (!pg.pk?.[table]) {
|
36
|
+
return { message: `table pkey not found: ${table}`, status: 404 };
|
37
|
+
}
|
38
|
+
|
39
|
+
const filterList = (body?.filter || [])
|
40
|
+
.concat(body?.filterInline || [])
|
41
|
+
.concat(body?.filterCustom || [])
|
42
|
+
.concat(body?.filterState || [])
|
43
|
+
.concat(body?.filterList || [])
|
44
|
+
.concat(body?.filters || [])
|
45
|
+
.concat(body?.filter_list || []);
|
46
|
+
|
47
|
+
const { columns = [] } = await getMeta({ pg, table });
|
48
|
+
const columnList = columns?.map((el) => el?.name);
|
49
|
+
|
50
|
+
const dateColumn = columns?.find((el) => el?.name === (meta?.date || meta?.start))?.name
|
51
|
+
|| columns?.find((el) => el?.name === 'cdate')?.name;
|
52
|
+
|
53
|
+
if (query.date && !dateColumn) {
|
54
|
+
return { message: 'invalid template params: date column not found', status: 400 };
|
55
|
+
}
|
56
|
+
|
57
|
+
if (query.date && !isValidDate(query.date)) {
|
58
|
+
return { message: 'Invalid date: out of range or incorrect format', status: 400 };
|
59
|
+
}
|
60
|
+
|
61
|
+
const filterWhere = filterList?.length && query.filter?.length
|
62
|
+
? filterList
|
63
|
+
.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true))
|
64
|
+
.map((el) => {
|
65
|
+
const val = query.filter.split(',').find((e) => e?.split('=')?.shift()?.includes(el.column || el.name))?.split('=')?.pop();
|
66
|
+
if (val) return el.column && val ? `(${[`${el.column}::text='${val.replace(/'/g, "''")}'::text`, el.query].filter((el) => el).join(' and ')})` : el.query;
|
67
|
+
})
|
68
|
+
.filter((el) => el)
|
69
|
+
.join(' and ')
|
70
|
+
: undefined;
|
71
|
+
|
72
|
+
const queryWhere = handlebarsSync.compile(body.query || '1=1')({ user, uid: user?.uid });
|
73
|
+
const filterDate = query.date ? `date_trunc('${granularity}', "${dateColumn}"::date)='${query.date}'::date` : undefined;
|
74
|
+
|
75
|
+
const where = [queryWhere, filterDate, filterWhere].filter((el) => el).join(' and ');
|
76
|
+
|
77
|
+
const filtersByColumn = filterList.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true) && el?.column);
|
78
|
+
|
79
|
+
const filters = [];
|
80
|
+
if (filtersByColumn?.length) {
|
81
|
+
await Promise.all(filtersByColumn.map(async (el) => {
|
82
|
+
const { rows: filterData = [] } = await pg.query(`select ${el.column.replace(/'/g, "''")} as id, count(*) from ${table}
|
83
|
+
where ${el.query || '1=1'} and ${filterWhere || '1=1'} group by ${el.column.replace(/'/g, "''")}`);
|
84
|
+
if (!filterData?.length) return;
|
85
|
+
|
86
|
+
const clsData = await getTemplate(['cls', 'select'], el.cls);
|
87
|
+
|
88
|
+
if (!el.cls || !clsData?.length) {
|
89
|
+
filterData.forEach((item) => filters.push(item));
|
90
|
+
return;
|
91
|
+
}
|
92
|
+
|
93
|
+
filterData.forEach((filter) => {
|
94
|
+
const cls = clsData.find((item) => item.id === filter.id.toString());
|
95
|
+
Object.assign(filter, { title: cls?.text, color: cls?.color });
|
96
|
+
filters.push(filter);
|
97
|
+
});
|
98
|
+
}));
|
99
|
+
}
|
100
|
+
|
101
|
+
const filtersByPreparedQuery = filterList.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true) && !el?.column && el?.query);
|
102
|
+
|
103
|
+
if (filtersByPreparedQuery?.length) {
|
104
|
+
await Promise.all(filtersByPreparedQuery.map(async (el) => {
|
105
|
+
const { rows = [] } = await pg.query(`select ${el.query} as id, count(*) from ${table}
|
106
|
+
where ${filterWhere || '1=1'} group by ${el.query}`);
|
107
|
+
rows.forEach((item) => filters.push(item));
|
108
|
+
}));
|
109
|
+
}
|
110
|
+
|
111
|
+
const metaColumns = Object.keys(meta)
|
112
|
+
.filter((el) => ['date', 'start', 'end', 'title', 'status'].includes(el) && columnList.includes(meta[el]))
|
113
|
+
.map((el) => `"${meta[el]}" as ${el}`);
|
114
|
+
|
115
|
+
if (!metaColumns?.length) {
|
116
|
+
return { message: `calendar param meta is invalid: invalid/empty keys`, status: 404 };
|
117
|
+
}
|
118
|
+
|
119
|
+
const q = `select ${metaColumns.join(',')} from ${table} where ${where}`;
|
120
|
+
if (query?.sql && user?.user_type?.includes('admin')) return q;
|
121
|
+
|
122
|
+
const { rows = [] } = await pg.query(q);
|
123
|
+
|
124
|
+
return { title, filters, granularity, sql: user?.user_type?.includes('admin') ? q : undefined, rows };
|
125
|
+
|
126
126
|
}
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import calendarData from "./controllers/calendar.data.js";
|
2
|
-
|
3
|
-
import { calendarDataSchema } from './schema.js';
|
4
|
-
|
5
|
-
export default async function route(fastify) {
|
6
|
-
fastify.get('/calendar/:name', { schema: calendarDataSchema }, calendarData);
|
7
|
-
}
|
1
|
+
import calendarData from "./controllers/calendar.data.js";
|
2
|
+
|
3
|
+
import { calendarDataSchema } from './schema.js';
|
4
|
+
|
5
|
+
export default async function route(fastify) {
|
6
|
+
fastify.get('/calendar/:name', { schema: calendarDataSchema }, calendarData);
|
7
|
+
}
|
@@ -1,22 +1,22 @@
|
|
1
|
-
const calendarDataSchema = {
|
2
|
-
params: {
|
3
|
-
name: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
|
4
|
-
},
|
5
|
-
querystring: {
|
6
|
-
date: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
|
7
|
-
sql: { type: 'string', pattern: '^(\\d)$' },
|
8
|
-
},
|
9
|
-
|
10
|
-
// query: {
|
11
|
-
// type: 'object',
|
12
|
-
// properties: {
|
13
|
-
// date: {
|
14
|
-
// type: 'string',
|
15
|
-
// format: 'date',
|
16
|
-
// },
|
17
|
-
// },
|
18
|
-
// },
|
19
|
-
};
|
20
|
-
|
21
|
-
export { calendarDataSchema }
|
1
|
+
const calendarDataSchema = {
|
2
|
+
params: {
|
3
|
+
name: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
|
4
|
+
},
|
5
|
+
querystring: {
|
6
|
+
date: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
|
7
|
+
sql: { type: 'string', pattern: '^(\\d)$' },
|
8
|
+
},
|
9
|
+
|
10
|
+
// query: {
|
11
|
+
// type: 'object',
|
12
|
+
// properties: {
|
13
|
+
// date: {
|
14
|
+
// type: 'string',
|
15
|
+
// format: 'date',
|
16
|
+
// },
|
17
|
+
// },
|
18
|
+
// },
|
19
|
+
};
|
20
|
+
|
21
|
+
export { calendarDataSchema }
|
22
22
|
export default null;
|