@opengis/admin 0.4.27 → 0.4.29
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/dist/{add-page-sde7JqiX.js → add-page-VsKeXogR.js} +4 -4
- package/dist/{admin-interface-Dhqli6Vu.js → admin-interface-CRxPKyYw.js} +20 -23
- package/dist/{admin-view-BoHHzowt.js → admin-view-ulbtCtj1.js} +17 -22
- package/dist/admin.js +1 -1
- package/dist/admin.umd.cjs +59 -59
- package/dist/assets/logo.svg +41 -41
- package/dist/{card-view-Cf1C7sZW.js → card-view-DibuO4qO.js} +3 -3
- package/dist/{edit-page-Cpp4ig3I.js → edit-page-DTvgV6IT.js} +8 -8
- package/dist/{import-file-A4o0SJpP.js → import-file-BCkV-vfK.js} +11193 -11248
- package/dist/{profile-page-C2xnBGUM.js → profile-page-pFO4XMgi.js} +2 -2
- 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/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 +82 -82
- 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/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/cron.js +10 -10
- package/server/plugins/docs.js +28 -28
- package/server/routes/calendar/controllers/calendar.data.js +124 -124
- package/server/routes/calendar/index.mjs +7 -7
- package/server/routes/notifications/controllers/readNotifications.js +18 -18
- package/server/routes/notifications/controllers/testEmail.js +35 -35
- package/server/routes/notifications/controllers/userNotifications.js +53 -53
- package/server/routes/notifications/hook/onWidgetSet.js +56 -56
- package/server/routes/notifications/index.mjs +26 -26
- package/server/routes/root.mjs +3 -3
- 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/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
- package/server/utils/addNotification.js +21 -21
- package/server/utils/sendNotification.js +89 -89
@@ -1,101 +1,101 @@
|
|
1
|
-
/**
|
2
|
-
* Перетинає два масиви
|
3
|
-
*
|
4
|
-
* @example
|
5
|
-
* // returns [1, 4, 5, 6]
|
6
|
-
* intersect([1,2,3,4,5,6],[1,4,5,6,7,8,9,11])
|
7
|
-
* @param {Array} a
|
8
|
-
* @param {Array} b
|
9
|
-
* @returns {Array} Returns new intersect array
|
10
|
-
*/
|
11
|
-
function intersect(a, b) {
|
12
|
-
let aN = a; let bN = b;
|
13
|
-
if (b.length > a.length) {
|
14
|
-
[aN, bN] = [bN, aN];
|
15
|
-
}
|
16
|
-
return aN.filter((e) => bN.includes(e));
|
17
|
-
}
|
18
|
-
|
19
|
-
/**
|
20
|
-
* Створення шаблона або його частини внаслідок перевірки значення із веб-запиту та заздалегідь прописаного значення.
|
21
|
-
* Дозволяє змінювати наповнення сторінки через ряд перевірок. Є можливість внесення додаткової умови - що робити, коли умова не виконується.
|
22
|
-
*
|
23
|
-
* @summary Перевірка двох значень та виконання коду при виконанні умови, а також у всіх інших випадках.
|
24
|
-
* @priority 5
|
25
|
-
* @alias ifCond
|
26
|
-
* @type helper
|
27
|
-
* @tag condition
|
28
|
-
* @example
|
29
|
-
* {{#ifCond @root.req.domain 'in' 'help.softpro.ua,123'}} {{select user.uid data="get_full_uid"}} {{^}} Умова не виконана {{/ifCond}}
|
30
|
-
* @example
|
31
|
-
* {{#ifCond "1234567890" 'in' @root.user.group_list}} 1=1 {{^}} uid='{{uid}}' {{/ifCond}}
|
32
|
-
* @example
|
33
|
-
* {{#ifCond 'debug' 'in' @root.setting.core.setting}}Умова виконана{{^}}Не виконана умова{{/ifCond}}
|
34
|
-
* @param {Array} args Параметри для значень і умов
|
35
|
-
* @param {Array} args[0]] Перше значення
|
36
|
-
* @param {Array} args[1]] Оператор
|
37
|
-
* @param {Array} args[2]] Друге значення
|
38
|
-
* @returns {String} Returns HTML
|
39
|
-
*/
|
40
|
-
export default function ifCond(v1, operator, v2, options) {
|
41
|
-
const __obj = this;
|
42
|
-
|
43
|
-
switch (operator) {
|
44
|
-
case '==':
|
45
|
-
return (v1 == v2) ? options.fn(__obj) : options.inverse(__obj);
|
46
|
-
case '!=':
|
47
|
-
return (v1 != v2) ? options.fn(__obj) : options.inverse(__obj);
|
48
|
-
case '===':
|
49
|
-
return (v1 === v2) ? options.fn(__obj) : options.inverse(__obj);
|
50
|
-
case '!==':
|
51
|
-
return (v1 !== v2) ? options.fn(__obj) : options.inverse(__obj);
|
52
|
-
case '&&':
|
53
|
-
return (v1 && v2) ? options.fn(__obj) : options.inverse(__obj);
|
54
|
-
case '||':
|
55
|
-
return (v1 || v2) ? options.fn(__obj) : options.inverse(__obj);
|
56
|
-
case '<':
|
57
|
-
return (v1 < v2) ? options.fn(__obj) : options.inverse(__obj);
|
58
|
-
case '<=':
|
59
|
-
return (v1 <= v2) ? options.fn(__obj) : options.inverse(__obj);
|
60
|
-
case '>':
|
61
|
-
return (v1 > v2) ? options.fn(__obj) : options.inverse(__obj);
|
62
|
-
case '>=':
|
63
|
-
return (v1 >= v2) ? options.fn(__obj) : options.inverse(__obj);
|
64
|
-
case '&':
|
65
|
-
return intersect(v1, v2).length !== 0
|
66
|
-
? options.fn(__obj)
|
67
|
-
: options.inverse(__obj);
|
68
|
-
case '!~':
|
69
|
-
return (v1 || '').indexOf(v2) === -1
|
70
|
-
? options.fn(__obj)
|
71
|
-
: options.inverse(__obj);
|
72
|
-
case '~':
|
73
|
-
return (v1 || '').indexOf(v2) !== -1
|
74
|
-
? options.fn(__obj)
|
75
|
-
: options.inverse(__obj);
|
76
|
-
case 'period':
|
77
|
-
return (new Date(v1) < new Date() && new Date(v2) > new Date())
|
78
|
-
? options.fn(__obj)
|
79
|
-
: options.inverse(__obj);
|
80
|
-
case 'in': {
|
81
|
-
if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
|
82
|
-
|
83
|
-
if (Array.isArray(v1)) {
|
84
|
-
return v1.some((value) => v2.includes(value.toString()))
|
85
|
-
? options.fn(__obj)
|
86
|
-
: options.inverse(__obj);
|
87
|
-
}
|
88
|
-
return v2.includes(v1?.toString())
|
89
|
-
? options.fn(__obj)
|
90
|
-
: options.inverse(__obj);
|
91
|
-
}
|
92
|
-
case 'not in': {
|
93
|
-
if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
|
94
|
-
return !v2.includes(v1?.toString())
|
95
|
-
? options.fn(__obj)
|
96
|
-
: options.inverse(__obj);
|
97
|
-
}
|
98
|
-
default:
|
99
|
-
return options.inverse(__obj);
|
100
|
-
}
|
101
|
-
}
|
1
|
+
/**
|
2
|
+
* Перетинає два масиви
|
3
|
+
*
|
4
|
+
* @example
|
5
|
+
* // returns [1, 4, 5, 6]
|
6
|
+
* intersect([1,2,3,4,5,6],[1,4,5,6,7,8,9,11])
|
7
|
+
* @param {Array} a
|
8
|
+
* @param {Array} b
|
9
|
+
* @returns {Array} Returns new intersect array
|
10
|
+
*/
|
11
|
+
function intersect(a, b) {
|
12
|
+
let aN = a; let bN = b;
|
13
|
+
if (b.length > a.length) {
|
14
|
+
[aN, bN] = [bN, aN];
|
15
|
+
}
|
16
|
+
return aN.filter((e) => bN.includes(e));
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Створення шаблона або його частини внаслідок перевірки значення із веб-запиту та заздалегідь прописаного значення.
|
21
|
+
* Дозволяє змінювати наповнення сторінки через ряд перевірок. Є можливість внесення додаткової умови - що робити, коли умова не виконується.
|
22
|
+
*
|
23
|
+
* @summary Перевірка двох значень та виконання коду при виконанні умови, а також у всіх інших випадках.
|
24
|
+
* @priority 5
|
25
|
+
* @alias ifCond
|
26
|
+
* @type helper
|
27
|
+
* @tag condition
|
28
|
+
* @example
|
29
|
+
* {{#ifCond @root.req.domain 'in' 'help.softpro.ua,123'}} {{select user.uid data="get_full_uid"}} {{^}} Умова не виконана {{/ifCond}}
|
30
|
+
* @example
|
31
|
+
* {{#ifCond "1234567890" 'in' @root.user.group_list}} 1=1 {{^}} uid='{{uid}}' {{/ifCond}}
|
32
|
+
* @example
|
33
|
+
* {{#ifCond 'debug' 'in' @root.setting.core.setting}}Умова виконана{{^}}Не виконана умова{{/ifCond}}
|
34
|
+
* @param {Array} args Параметри для значень і умов
|
35
|
+
* @param {Array} args[0]] Перше значення
|
36
|
+
* @param {Array} args[1]] Оператор
|
37
|
+
* @param {Array} args[2]] Друге значення
|
38
|
+
* @returns {String} Returns HTML
|
39
|
+
*/
|
40
|
+
export default function ifCond(v1, operator, v2, options) {
|
41
|
+
const __obj = this;
|
42
|
+
|
43
|
+
switch (operator) {
|
44
|
+
case '==':
|
45
|
+
return (v1 == v2) ? options.fn(__obj) : options.inverse(__obj);
|
46
|
+
case '!=':
|
47
|
+
return (v1 != v2) ? options.fn(__obj) : options.inverse(__obj);
|
48
|
+
case '===':
|
49
|
+
return (v1 === v2) ? options.fn(__obj) : options.inverse(__obj);
|
50
|
+
case '!==':
|
51
|
+
return (v1 !== v2) ? options.fn(__obj) : options.inverse(__obj);
|
52
|
+
case '&&':
|
53
|
+
return (v1 && v2) ? options.fn(__obj) : options.inverse(__obj);
|
54
|
+
case '||':
|
55
|
+
return (v1 || v2) ? options.fn(__obj) : options.inverse(__obj);
|
56
|
+
case '<':
|
57
|
+
return (v1 < v2) ? options.fn(__obj) : options.inverse(__obj);
|
58
|
+
case '<=':
|
59
|
+
return (v1 <= v2) ? options.fn(__obj) : options.inverse(__obj);
|
60
|
+
case '>':
|
61
|
+
return (v1 > v2) ? options.fn(__obj) : options.inverse(__obj);
|
62
|
+
case '>=':
|
63
|
+
return (v1 >= v2) ? options.fn(__obj) : options.inverse(__obj);
|
64
|
+
case '&':
|
65
|
+
return intersect(v1, v2).length !== 0
|
66
|
+
? options.fn(__obj)
|
67
|
+
: options.inverse(__obj);
|
68
|
+
case '!~':
|
69
|
+
return (v1 || '').indexOf(v2) === -1
|
70
|
+
? options.fn(__obj)
|
71
|
+
: options.inverse(__obj);
|
72
|
+
case '~':
|
73
|
+
return (v1 || '').indexOf(v2) !== -1
|
74
|
+
? options.fn(__obj)
|
75
|
+
: options.inverse(__obj);
|
76
|
+
case 'period':
|
77
|
+
return (new Date(v1) < new Date() && new Date(v2) > new Date())
|
78
|
+
? options.fn(__obj)
|
79
|
+
: options.inverse(__obj);
|
80
|
+
case 'in': {
|
81
|
+
if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
|
82
|
+
|
83
|
+
if (Array.isArray(v1)) {
|
84
|
+
return v1.some((value) => v2.includes(value.toString()))
|
85
|
+
? options.fn(__obj)
|
86
|
+
: options.inverse(__obj);
|
87
|
+
}
|
88
|
+
return v2.includes(v1?.toString())
|
89
|
+
? options.fn(__obj)
|
90
|
+
: options.inverse(__obj);
|
91
|
+
}
|
92
|
+
case 'not in': {
|
93
|
+
if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
|
94
|
+
return !v2.includes(v1?.toString())
|
95
|
+
? options.fn(__obj)
|
96
|
+
: options.inverse(__obj);
|
97
|
+
}
|
98
|
+
default:
|
99
|
+
return options.inverse(__obj);
|
100
|
+
}
|
101
|
+
}
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
export default function button(token, title) {
|
4
|
-
return `<button onclick="window.v3plugin.$form({ token: '${token}' })"
|
5
|
-
class="inline-flex items-center px-2 py-1 text-sm font-medium text-white duration-300 bg-blue-600 border border-transparent rounded-lg gap-x-2 hover:bg-blue-700 hover:text-white">${title || 'Редагувати'}</button>`;
|
1
|
+
|
2
|
+
|
3
|
+
export default function button(token, title) {
|
4
|
+
return `<button onclick="window.v3plugin.$form({ token: '${token}' })"
|
5
|
+
class="inline-flex items-center px-2 py-1 text-sm font-medium text-white duration-300 bg-blue-600 border border-transparent rounded-lg gap-x-2 hover:bg-blue-700 hover:text-white">${title || 'Редагувати'}</button>`;
|
6
6
|
}
|
@@ -1,17 +1,17 @@
|
|
1
|
-
import md from 'markdown-it';
|
2
|
-
|
3
|
-
const md1 = md({ html: true });
|
4
|
-
|
5
|
-
/**
|
6
|
-
* Перетворення з файла readme.md до формату HTML.
|
7
|
-
* Потрабно вставити в хелпер шлях до файла або текст readme.md і за допомогою бібліотеки markdown-it перетвориться в HTML.
|
8
|
-
|
9
|
-
* @returns {String} Returns HTML
|
10
|
-
*/
|
11
|
-
export default function mdToHTML(data, options) {
|
12
|
-
// auto detect HTML or MD
|
13
|
-
// const result = md().render(data);
|
14
|
-
if (!data) return 'empty data';
|
15
|
-
const result = md1.render(data);
|
16
|
-
return result;
|
17
|
-
};
|
1
|
+
import md from 'markdown-it';
|
2
|
+
|
3
|
+
const md1 = md({ html: true });
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Перетворення з файла readme.md до формату HTML.
|
7
|
+
* Потрабно вставити в хелпер шлях до файла або текст readme.md і за допомогою бібліотеки markdown-it перетвориться в HTML.
|
8
|
+
|
9
|
+
* @returns {String} Returns HTML
|
10
|
+
*/
|
11
|
+
export default function mdToHTML(data, options) {
|
12
|
+
// auto detect HTML or MD
|
13
|
+
// const result = md().render(data);
|
14
|
+
if (!data) return 'empty data';
|
15
|
+
const result = md1.render(data);
|
16
|
+
return result;
|
17
|
+
};
|
package/server/plugins/cron.js
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
//import { addCron } from '@opengis/fastify-table/utils.js';
|
2
|
-
|
3
|
-
async function deleteOldNotifications({ pg }) {
|
4
|
-
const { rowCount } = pg?.pk?.['crm.notifications'] ? await pg.query('delete from crm.notifications where CURRENT_DATE - \'14 days\'::interval > cdate') : {};
|
5
|
-
return { rowCount };
|
6
|
-
}
|
7
|
-
|
8
|
-
export default async function cron(fastify) {
|
9
|
-
// addCron(deleteOldNotifications, 60 * 60 * 24, fastify);
|
10
|
-
// addCron(deleteOldNotifications, 60 * 1, fastify); // debug
|
1
|
+
//import { addCron } from '@opengis/fastify-table/utils.js';
|
2
|
+
|
3
|
+
async function deleteOldNotifications({ pg }) {
|
4
|
+
const { rowCount } = pg?.pk?.['crm.notifications'] ? await pg.query('delete from crm.notifications where CURRENT_DATE - \'14 days\'::interval > cdate') : {};
|
5
|
+
return { rowCount };
|
6
|
+
}
|
7
|
+
|
8
|
+
export default async function cron(fastify) {
|
9
|
+
// addCron(deleteOldNotifications, 60 * 60 * 24, fastify);
|
10
|
+
// addCron(deleteOldNotifications, 60 * 1, fastify); // debug
|
11
11
|
}
|
package/server/plugins/docs.js
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
'use strict'
|
2
|
-
|
3
|
-
import path, { dirname } from 'path';
|
4
|
-
import { fileURLToPath } from 'url';
|
5
|
-
import fs from 'fs';
|
6
|
-
|
7
|
-
const dir = dirname(fileURLToPath(import.meta.url));
|
8
|
-
const root = `${dir}/../../`;
|
9
|
-
|
10
|
-
|
11
|
-
async function plugin(fastify, opts) {
|
12
|
-
fastify.get('/docs*', async (req, reply) => {
|
13
|
-
if (!fs.existsSync(path.join(root, 'docs/.vitepress/dist/'))) {
|
14
|
-
return reply.status(404).send('docs not exists');
|
15
|
-
}
|
16
|
-
const { params } = req;
|
17
|
-
const url = params['*']
|
18
|
-
const filePath = url && url[url.length - 1] !== '/' ? path.join(root, 'docs/.vitepress/dist/', url) : path.join(root, 'docs/.vitepress/dist/', url, 'index.html')
|
19
|
-
const ext = path.extname(filePath);
|
20
|
-
const mime = {
|
21
|
-
'.js': 'text/javascript', '.css': 'text/css', '.woff2': 'application/font-woff', '.png': 'image/png', '.svg': 'image/svg+xml', '.jpg': 'image/jpg'
|
22
|
-
}[ext];
|
23
|
-
const stream = fs.createReadStream(filePath);
|
24
|
-
return mime ? reply.type(mime).send(stream) : stream;
|
25
|
-
|
26
|
-
})
|
27
|
-
}
|
28
|
-
export default plugin;
|
1
|
+
'use strict'
|
2
|
+
|
3
|
+
import path, { dirname } from 'path';
|
4
|
+
import { fileURLToPath } from 'url';
|
5
|
+
import fs from 'fs';
|
6
|
+
|
7
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
8
|
+
const root = `${dir}/../../`;
|
9
|
+
|
10
|
+
|
11
|
+
async function plugin(fastify, opts) {
|
12
|
+
fastify.get('/docs*', async (req, reply) => {
|
13
|
+
if (!fs.existsSync(path.join(root, 'docs/.vitepress/dist/'))) {
|
14
|
+
return reply.status(404).send('docs not exists');
|
15
|
+
}
|
16
|
+
const { params } = req;
|
17
|
+
const url = params['*']
|
18
|
+
const filePath = url && url[url.length - 1] !== '/' ? path.join(root, 'docs/.vitepress/dist/', url) : path.join(root, 'docs/.vitepress/dist/', url, 'index.html')
|
19
|
+
const ext = path.extname(filePath);
|
20
|
+
const mime = {
|
21
|
+
'.js': 'text/javascript', '.css': 'text/css', '.woff2': 'application/font-woff', '.png': 'image/png', '.svg': 'image/svg+xml', '.jpg': 'image/jpg'
|
22
|
+
}[ext];
|
23
|
+
const stream = fs.createReadStream(filePath);
|
24
|
+
return mime ? reply.type(mime).send(stream) : stream;
|
25
|
+
|
26
|
+
})
|
27
|
+
}
|
28
|
+
export default plugin;
|
@@ -1,125 +1,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 = {}, user = {},
|
7
|
-
}) {
|
8
|
-
const { name } = params;
|
9
|
-
const { granularity = 'month' } = query;
|
10
|
-
|
11
|
-
if (!name) {
|
12
|
-
return { message: 'not enough params: name', status: 400 };
|
13
|
-
}
|
14
|
-
|
15
|
-
if (query.granularity && !['day', 'month', 'week', 'year'].includes(granularity)) {
|
16
|
-
return { message: 'invalid query params: granularity', status: 400 };
|
17
|
-
}
|
18
|
-
|
19
|
-
const body = await getTemplate('calendar', name);
|
20
|
-
|
21
|
-
if (!body) {
|
22
|
-
return { message: `calendar not found: ${name}`, status: 404 };
|
23
|
-
}
|
24
|
-
|
25
|
-
const {
|
26
|
-
title,
|
27
|
-
table,
|
28
|
-
meta = {},
|
29
|
-
} = body || {};
|
30
|
-
|
31
|
-
if (!table) {
|
32
|
-
return { message: 'not enough calendar params: table', status: 404 };
|
33
|
-
}
|
34
|
-
if (!pg.pk?.[table]) {
|
35
|
-
return { message: `table pkey not found: ${table}`, status: 404 };
|
36
|
-
}
|
37
|
-
|
38
|
-
const filterList = (body?.filter || [])
|
39
|
-
.concat(body?.filterInline || [])
|
40
|
-
.concat(body?.filterCustom || [])
|
41
|
-
.concat(body?.filterState || [])
|
42
|
-
.concat(body?.filterList || [])
|
43
|
-
.concat(body?.filters || [])
|
44
|
-
.concat(body?.filter_list || []);
|
45
|
-
|
46
|
-
const { columns = [] } = await getMeta({ pg, table });
|
47
|
-
const columnList = columns?.map((el) => el?.name);
|
48
|
-
|
49
|
-
const dateColumn = columns?.find((el) => el?.name === (meta?.date || meta?.start))?.name
|
50
|
-
|| columns?.find((el) => el?.name === 'cdate')?.name;
|
51
|
-
|
52
|
-
if (query.date && !dateColumn) {
|
53
|
-
return { message: 'invalid template params: date column not found', status: 400 };
|
54
|
-
}
|
55
|
-
|
56
|
-
if (query.date && !isValidDate(query.date)) {
|
57
|
-
return { message: 'Invalid date: out of range or incorrect format', status: 400 };
|
58
|
-
}
|
59
|
-
|
60
|
-
const filterWhere = filterList?.length && query.filter?.length
|
61
|
-
? filterList
|
62
|
-
.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true))
|
63
|
-
.map((el) => {
|
64
|
-
const val = query.filter.split(',').find((e) => e?.split('=')?.shift()?.includes(el.column || el.name))?.split('=')?.pop();
|
65
|
-
if (val) return el.column && val ? `(${[`${el.column}::text='${val.replace(/'/g, "''")}'::text`, el.query].filter((el) => el).join(' and ')})` : el.query;
|
66
|
-
})
|
67
|
-
.filter((el) => el)
|
68
|
-
.join(' and ')
|
69
|
-
: undefined;
|
70
|
-
|
71
|
-
const queryWhere = handlebarsSync.compile(body.query || '1=1')({ user, uid: user?.uid });
|
72
|
-
const filterDate = query.date ? `date_trunc('${granularity}', "${dateColumn}"::date)='${query.date}'::date` : undefined;
|
73
|
-
|
74
|
-
const where = [queryWhere, filterDate, filterWhere].filter((el) => el).join(' and ');
|
75
|
-
|
76
|
-
const filtersByColumn = filterList.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true) && el?.column);
|
77
|
-
|
78
|
-
const filters = [];
|
79
|
-
if (filtersByColumn?.length) {
|
80
|
-
await Promise.all(filtersByColumn.map(async (el) => {
|
81
|
-
const { rows: filterData = [] } = await pg.query(`select ${el.column.replace(/'/g, "''")} as id, count(*) from ${table}
|
82
|
-
where ${el.query || '1=1'} and ${filterWhere || '1=1'} group by ${el.column.replace(/'/g, "''")}`);
|
83
|
-
if (!filterData?.length) return;
|
84
|
-
|
85
|
-
const clsData = await getTemplate(['cls', 'select'], el.cls);
|
86
|
-
|
87
|
-
if (!el.cls || !clsData?.length) {
|
88
|
-
filterData.forEach((item) => filters.push(item));
|
89
|
-
return;
|
90
|
-
}
|
91
|
-
|
92
|
-
filterData.forEach((filter) => {
|
93
|
-
const cls = clsData.find((item) => item.id === filter.id.toString());
|
94
|
-
Object.assign(filter, { title: cls?.text, color: cls?.color });
|
95
|
-
filters.push(filter);
|
96
|
-
});
|
97
|
-
}));
|
98
|
-
}
|
99
|
-
|
100
|
-
const filtersByPreparedQuery = filterList.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true) && !el?.column && el?.query);
|
101
|
-
|
102
|
-
if (filtersByPreparedQuery?.length) {
|
103
|
-
await Promise.all(filtersByPreparedQuery.map(async (el) => {
|
104
|
-
const { rows = [] } = await pg.query(`select ${el.query} as id, count(*) from ${table}
|
105
|
-
where ${filterWhere || '1=1'} group by ${el.query}`);
|
106
|
-
rows.forEach((item) => filters.push(item));
|
107
|
-
}));
|
108
|
-
}
|
109
|
-
|
110
|
-
const metaColumns = Object.keys(meta)
|
111
|
-
.filter((el) => ['date', 'start', 'end', 'title', 'status'].includes(el) && columnList.includes(meta[el]))
|
112
|
-
.map((el) => `"${meta[el]}" as ${el}`);
|
113
|
-
|
114
|
-
if (!metaColumns?.length) {
|
115
|
-
return { message: `calendar param meta is invalid: invalid/empty keys`, status: 404 };
|
116
|
-
}
|
117
|
-
|
118
|
-
const q = `select ${metaColumns.join(',')} from ${table} where ${where}`;
|
119
|
-
if (query?.sql && user?.user_type?.includes('admin')) return q;
|
120
|
-
|
121
|
-
const { rows = [] } = await pg.query(q);
|
122
|
-
|
123
|
-
return { title, filters, granularity, sql: user?.user_type?.includes('admin') ? q : undefined, rows };
|
124
|
-
|
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 = {}, user = {},
|
7
|
+
}) {
|
8
|
+
const { name } = params;
|
9
|
+
const { granularity = 'month' } = query;
|
10
|
+
|
11
|
+
if (!name) {
|
12
|
+
return { message: 'not enough params: name', status: 400 };
|
13
|
+
}
|
14
|
+
|
15
|
+
if (query.granularity && !['day', 'month', 'week', 'year'].includes(granularity)) {
|
16
|
+
return { message: 'invalid query params: granularity', status: 400 };
|
17
|
+
}
|
18
|
+
|
19
|
+
const body = await getTemplate('calendar', name);
|
20
|
+
|
21
|
+
if (!body) {
|
22
|
+
return { message: `calendar not found: ${name}`, status: 404 };
|
23
|
+
}
|
24
|
+
|
25
|
+
const {
|
26
|
+
title,
|
27
|
+
table,
|
28
|
+
meta = {},
|
29
|
+
} = body || {};
|
30
|
+
|
31
|
+
if (!table) {
|
32
|
+
return { message: 'not enough calendar params: table', status: 404 };
|
33
|
+
}
|
34
|
+
if (!pg.pk?.[table]) {
|
35
|
+
return { message: `table pkey not found: ${table}`, status: 404 };
|
36
|
+
}
|
37
|
+
|
38
|
+
const filterList = (body?.filter || [])
|
39
|
+
.concat(body?.filterInline || [])
|
40
|
+
.concat(body?.filterCustom || [])
|
41
|
+
.concat(body?.filterState || [])
|
42
|
+
.concat(body?.filterList || [])
|
43
|
+
.concat(body?.filters || [])
|
44
|
+
.concat(body?.filter_list || []);
|
45
|
+
|
46
|
+
const { columns = [] } = await getMeta({ pg, table });
|
47
|
+
const columnList = columns?.map((el) => el?.name);
|
48
|
+
|
49
|
+
const dateColumn = columns?.find((el) => el?.name === (meta?.date || meta?.start))?.name
|
50
|
+
|| columns?.find((el) => el?.name === 'cdate')?.name;
|
51
|
+
|
52
|
+
if (query.date && !dateColumn) {
|
53
|
+
return { message: 'invalid template params: date column not found', status: 400 };
|
54
|
+
}
|
55
|
+
|
56
|
+
if (query.date && !isValidDate(query.date)) {
|
57
|
+
return { message: 'Invalid date: out of range or incorrect format', status: 400 };
|
58
|
+
}
|
59
|
+
|
60
|
+
const filterWhere = filterList?.length && query.filter?.length
|
61
|
+
? filterList
|
62
|
+
.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true))
|
63
|
+
.map((el) => {
|
64
|
+
const val = query.filter.split(',').find((e) => e?.split('=')?.shift()?.includes(el.column || el.name))?.split('=')?.pop();
|
65
|
+
if (val) return el.column && val ? `(${[`${el.column}::text='${val.replace(/'/g, "''")}'::text`, el.query].filter((el) => el).join(' and ')})` : el.query;
|
66
|
+
})
|
67
|
+
.filter((el) => el)
|
68
|
+
.join(' and ')
|
69
|
+
: undefined;
|
70
|
+
|
71
|
+
const queryWhere = handlebarsSync.compile(body.query || '1=1')({ user, uid: user?.uid });
|
72
|
+
const filterDate = query.date ? `date_trunc('${granularity}', "${dateColumn}"::date)='${query.date}'::date` : undefined;
|
73
|
+
|
74
|
+
const where = [queryWhere, filterDate, filterWhere].filter((el) => el).join(' and ');
|
75
|
+
|
76
|
+
const filtersByColumn = filterList.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true) && el?.column);
|
77
|
+
|
78
|
+
const filters = [];
|
79
|
+
if (filtersByColumn?.length) {
|
80
|
+
await Promise.all(filtersByColumn.map(async (el) => {
|
81
|
+
const { rows: filterData = [] } = await pg.query(`select ${el.column.replace(/'/g, "''")} as id, count(*) from ${table}
|
82
|
+
where ${el.query || '1=1'} and ${filterWhere || '1=1'} group by ${el.column.replace(/'/g, "''")}`);
|
83
|
+
if (!filterData?.length) return;
|
84
|
+
|
85
|
+
const clsData = await getTemplate(['cls', 'select'], el.cls);
|
86
|
+
|
87
|
+
if (!el.cls || !clsData?.length) {
|
88
|
+
filterData.forEach((item) => filters.push(item));
|
89
|
+
return;
|
90
|
+
}
|
91
|
+
|
92
|
+
filterData.forEach((filter) => {
|
93
|
+
const cls = clsData.find((item) => item.id === filter.id.toString());
|
94
|
+
Object.assign(filter, { title: cls?.text, color: cls?.color });
|
95
|
+
filters.push(filter);
|
96
|
+
});
|
97
|
+
}));
|
98
|
+
}
|
99
|
+
|
100
|
+
const filtersByPreparedQuery = filterList.filter((el) => (Object.hasOwn(el, 'enabled') ? el?.enabled : true) && !el?.column && el?.query);
|
101
|
+
|
102
|
+
if (filtersByPreparedQuery?.length) {
|
103
|
+
await Promise.all(filtersByPreparedQuery.map(async (el) => {
|
104
|
+
const { rows = [] } = await pg.query(`select ${el.query} as id, count(*) from ${table}
|
105
|
+
where ${filterWhere || '1=1'} group by ${el.query}`);
|
106
|
+
rows.forEach((item) => filters.push(item));
|
107
|
+
}));
|
108
|
+
}
|
109
|
+
|
110
|
+
const metaColumns = Object.keys(meta)
|
111
|
+
.filter((el) => ['date', 'start', 'end', 'title', 'status'].includes(el) && columnList.includes(meta[el]))
|
112
|
+
.map((el) => `"${meta[el]}" as ${el}`);
|
113
|
+
|
114
|
+
if (!metaColumns?.length) {
|
115
|
+
return { message: `calendar param meta is invalid: invalid/empty keys`, status: 404 };
|
116
|
+
}
|
117
|
+
|
118
|
+
const q = `select ${metaColumns.join(',')} from ${table} where ${where}`;
|
119
|
+
if (query?.sql && user?.user_type?.includes('admin')) return q;
|
120
|
+
|
121
|
+
const { rows = [] } = await pg.query(q);
|
122
|
+
|
123
|
+
return { title, filters, granularity, sql: user?.user_type?.includes('admin') ? q : undefined, rows };
|
124
|
+
|
125
125
|
}
|
@@ -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,18 +1,18 @@
|
|
1
|
-
export default async function readNotifications({
|
2
|
-
pg, params = {}, query = {}, user = {},
|
3
|
-
}, reply) {
|
4
|
-
const { uid } = user || {};
|
5
|
-
|
6
|
-
if (!uid) {
|
7
|
-
return reply.status(401).send('access restricted: user ');
|
8
|
-
}
|
9
|
-
|
10
|
-
const q = `update crm.notifications set read=true where read is not true
|
11
|
-
and ${params?.id ? 'notification_id=$2' : '1=1'} and addressee_id=$1`;
|
12
|
-
|
13
|
-
if (query.sql) return q;
|
14
|
-
|
15
|
-
const { rowCount = 0 } = await pg.query(q, [uid, params?.id].filter((el) => el));
|
16
|
-
|
17
|
-
return reply.status(200).send(`${rowCount} unread notifications marked as read`);
|
18
|
-
}
|
1
|
+
export default async function readNotifications({
|
2
|
+
pg, params = {}, query = {}, user = {},
|
3
|
+
}, reply) {
|
4
|
+
const { uid } = user || {};
|
5
|
+
|
6
|
+
if (!uid) {
|
7
|
+
return reply.status(401).send('access restricted: user ');
|
8
|
+
}
|
9
|
+
|
10
|
+
const q = `update crm.notifications set read=true where read is not true
|
11
|
+
and ${params?.id ? 'notification_id=$2' : '1=1'} and addressee_id=$1`;
|
12
|
+
|
13
|
+
if (query.sql) return q;
|
14
|
+
|
15
|
+
const { rowCount = 0 } = await pg.query(q, [uid, params?.id].filter((el) => el));
|
16
|
+
|
17
|
+
return reply.status(200).send(`${rowCount} unread notifications marked as read`);
|
18
|
+
}
|