@opengis/fastify-table 1.1.37 → 1.1.39
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 +1 -1
- package/README.md +26 -26
- package/config.js +10 -10
- package/cron/controllers/cronApi.js +22 -22
- package/cron/controllers/utils/cronList.js +1 -1
- package/cron/funcs/addCron.js +131 -131
- package/cron/index.js +10 -10
- package/crud/controllers/utils/checkXSS.js +45 -45
- package/crud/controllers/utils/xssInjection.js +72 -72
- package/crud/funcs/getToken.js +27 -27
- package/crud/funcs/isFileExists.js +13 -13
- package/crud/funcs/setToken.js +53 -53
- package/helper.js +4 -2
- package/module/test/cls/itree.composition.json +26 -0
- package/module/test/table/test.rest_zone.table.json +265 -0
- package/notification/controllers/testEmail.js +49 -49
- package/notification/funcs/utils/sendEmail.js +39 -39
- package/package.json +1 -1
- package/pg/funcs/getPG.js +30 -30
- package/redis/funcs/getRedis.js +23 -23
- package/server/migrations/log.sql +80 -80
- package/server/migrations/properties.sql +1 -1
- package/table/controllers/card.js +44 -44
- package/table/controllers/data.js +6 -5
- package/table/controllers/form.js +28 -28
- package/table/controllers/table.js +4 -4
- package/table/funcs/getFilterSQL/index.js +25 -13
- package/table/funcs/getFilterSQL/util/formatValue.js +44 -19
- package/table/funcs/getFilterSQL/util/getFilterQuery.js +14 -21
- package/test/api/crud.xss.test.js +72 -72
- package/test/api/table.test.js +46 -8
- package/test/config.example +18 -18
- package/test/funcs/pg.test.js +34 -34
- package/test/funcs/redis.test.js +19 -19
- package/test/templates/cls/test.json +9 -9
- package/test/templates/form/cp_building.form.json +32 -32
- package/test/templates/select/account_id.json +3 -3
- package/test/templates/select/storage.data.json +2 -2
- package/test/templates/table/gis.dataset.table.json +20 -20
- package/util/controllers/next.id.js +4 -4
- package/util/controllers/properties.get.js +19 -19
- package/util/index.js +23 -23
- package/utils.js +1 -0
package/crud/funcs/getToken.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import getRedis from '../../redis/funcs/getRedis.js';
|
|
2
|
-
import config from '../../config.js';
|
|
3
|
-
|
|
4
|
-
function sprintf(str, ...args) {
|
|
5
|
-
return str.replace(/%s/g, () => args.shift());
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const keys = {
|
|
9
|
-
r: '%s:token:view:%s',
|
|
10
|
-
a: '%s:token:add:%s',
|
|
11
|
-
w: '%s:token:edit:%s',
|
|
12
|
-
e: '%s:token:exec:%s',
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
async function getToken({
|
|
16
|
-
uid, token, mode = 'r', json,
|
|
17
|
-
}) {
|
|
18
|
-
if (mode === 'r') return token;
|
|
19
|
-
|
|
20
|
-
const rclient = getRedis({ db: 0 });
|
|
21
|
-
|
|
22
|
-
const key = sprintf(keys[mode], config?.pg?.database, uid?.toString());
|
|
23
|
-
const id = await rclient.hget(key, token);
|
|
24
|
-
return json && id?.[0] === '{' ? JSON.parse(id) : id;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default getToken;
|
|
1
|
+
import getRedis from '../../redis/funcs/getRedis.js';
|
|
2
|
+
import config from '../../config.js';
|
|
3
|
+
|
|
4
|
+
function sprintf(str, ...args) {
|
|
5
|
+
return str.replace(/%s/g, () => args.shift());
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const keys = {
|
|
9
|
+
r: '%s:token:view:%s',
|
|
10
|
+
a: '%s:token:add:%s',
|
|
11
|
+
w: '%s:token:edit:%s',
|
|
12
|
+
e: '%s:token:exec:%s',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
async function getToken({
|
|
16
|
+
uid, token, mode = 'r', json,
|
|
17
|
+
}) {
|
|
18
|
+
if (mode === 'r') return token;
|
|
19
|
+
|
|
20
|
+
const rclient = getRedis({ db: 0 });
|
|
21
|
+
|
|
22
|
+
const key = sprintf(keys[mode], config?.pg?.database, uid?.toString());
|
|
23
|
+
const id = await rclient.hget(key, token);
|
|
24
|
+
return json && id?.[0] === '{' ? JSON.parse(id) : id;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default getToken;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { access } from 'fs/promises';
|
|
2
|
-
|
|
3
|
-
const isFileExists = async (filepath) => {
|
|
4
|
-
try {
|
|
5
|
-
await access(filepath);
|
|
6
|
-
return true;
|
|
7
|
-
}
|
|
8
|
-
catch (err) {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export default isFileExists;
|
|
1
|
+
import { access } from 'fs/promises';
|
|
2
|
+
|
|
3
|
+
const isFileExists = async (filepath) => {
|
|
4
|
+
try {
|
|
5
|
+
await access(filepath);
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
catch (err) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default isFileExists;
|
package/crud/funcs/setToken.js
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'crypto';
|
|
2
|
-
|
|
3
|
-
import config from '../../config.js';
|
|
4
|
-
import getRedis from '../../redis/funcs/getRedis.js';
|
|
5
|
-
|
|
6
|
-
const generateCodes = (ids, userToken) => {
|
|
7
|
-
const token = userToken || randomUUID();
|
|
8
|
-
const notNullIds = ids.filter((el) => el);
|
|
9
|
-
const obj = {};
|
|
10
|
-
const codes = notNullIds.reduce((acc, id) => {
|
|
11
|
-
const newToken = createHash('sha1').update(token + id).digest('base64url').replace(/-/g, '');
|
|
12
|
-
acc[newToken] = id; obj[id] = newToken;
|
|
13
|
-
return acc;
|
|
14
|
-
}, {});
|
|
15
|
-
return { codes, obj };
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
function setToken({
|
|
19
|
-
ids: idsOrigin, mode = 'r', uid, referer, array,
|
|
20
|
-
}) {
|
|
21
|
-
const rclient2 = getRedis({ db: 0 });
|
|
22
|
-
// const rclient5 = getRedis({ db: 0, funcs });
|
|
23
|
-
|
|
24
|
-
if (!uid) return { user: 'empty' };
|
|
25
|
-
if (!Object.keys(idsOrigin).length) return { ids: 'empty' };
|
|
26
|
-
|
|
27
|
-
const ids = idsOrigin.map((el) => (typeof el === 'object' ? JSON.stringify(el) : el));
|
|
28
|
-
// update/delete
|
|
29
|
-
|
|
30
|
-
if (mode === 'r') return null;
|
|
31
|
-
|
|
32
|
-
// TODO generate salt
|
|
33
|
-
const { codes, obj } = generateCodes(ids, uid);
|
|
34
|
-
|
|
35
|
-
if (!Object.keys(codes).length) return { ids: 'empty' };
|
|
36
|
-
|
|
37
|
-
rclient2.hmset(`${config.pg.database}:token:${{
|
|
38
|
-
e: 'exec', r: 'view', w: 'edit', a: 'add',
|
|
39
|
-
}[mode]}:${uid}`, codes);
|
|
40
|
-
|
|
41
|
-
// log token for debug. add extra data - uid, mode, date
|
|
42
|
-
/* const dt = new Date().toISOString();
|
|
43
|
-
const codesLog = Object.keys(codes).reduce((acc, key) => {
|
|
44
|
-
acc[key] = `{"referer": "${referer}" ,"uid":"${uid}","mode":"${mode}","date":"${dt}",${codes[key].substr(1)}`;
|
|
45
|
-
return acc;
|
|
46
|
-
}, {});
|
|
47
|
-
rclient5.hmset(`${config.pg.database}:token:edit`, codesLog); // 'EX', 64800 */
|
|
48
|
-
|
|
49
|
-
// TODO дополнительно писать в hset token -> uid
|
|
50
|
-
return array ? Object.values(obj) : obj;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export default setToken;
|
|
1
|
+
import { createHash, randomUUID } from 'crypto';
|
|
2
|
+
|
|
3
|
+
import config from '../../config.js';
|
|
4
|
+
import getRedis from '../../redis/funcs/getRedis.js';
|
|
5
|
+
|
|
6
|
+
const generateCodes = (ids, userToken) => {
|
|
7
|
+
const token = userToken || randomUUID();
|
|
8
|
+
const notNullIds = ids.filter((el) => el);
|
|
9
|
+
const obj = {};
|
|
10
|
+
const codes = notNullIds.reduce((acc, id) => {
|
|
11
|
+
const newToken = createHash('sha1').update(token + id).digest('base64url').replace(/-/g, '');
|
|
12
|
+
acc[newToken] = id; obj[id] = newToken;
|
|
13
|
+
return acc;
|
|
14
|
+
}, {});
|
|
15
|
+
return { codes, obj };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function setToken({
|
|
19
|
+
ids: idsOrigin, mode = 'r', uid, referer, array,
|
|
20
|
+
}) {
|
|
21
|
+
const rclient2 = getRedis({ db: 0 });
|
|
22
|
+
// const rclient5 = getRedis({ db: 0, funcs });
|
|
23
|
+
|
|
24
|
+
if (!uid) return { user: 'empty' };
|
|
25
|
+
if (!Object.keys(idsOrigin).length) return { ids: 'empty' };
|
|
26
|
+
|
|
27
|
+
const ids = idsOrigin.map((el) => (typeof el === 'object' ? JSON.stringify(el) : el));
|
|
28
|
+
// update/delete
|
|
29
|
+
|
|
30
|
+
if (mode === 'r') return null;
|
|
31
|
+
|
|
32
|
+
// TODO generate salt
|
|
33
|
+
const { codes, obj } = generateCodes(ids, uid);
|
|
34
|
+
|
|
35
|
+
if (!Object.keys(codes).length) return { ids: 'empty' };
|
|
36
|
+
|
|
37
|
+
rclient2.hmset(`${config.pg.database}:token:${{
|
|
38
|
+
e: 'exec', r: 'view', w: 'edit', a: 'add',
|
|
39
|
+
}[mode]}:${uid}`, codes);
|
|
40
|
+
|
|
41
|
+
// log token for debug. add extra data - uid, mode, date
|
|
42
|
+
/* const dt = new Date().toISOString();
|
|
43
|
+
const codesLog = Object.keys(codes).reduce((acc, key) => {
|
|
44
|
+
acc[key] = `{"referer": "${referer}" ,"uid":"${uid}","mode":"${mode}","date":"${dt}",${codes[key].substr(1)}`;
|
|
45
|
+
return acc;
|
|
46
|
+
}, {});
|
|
47
|
+
rclient5.hmset(`${config.pg.database}:token:edit`, codesLog); // 'EX', 64800 */
|
|
48
|
+
|
|
49
|
+
// TODO дополнительно писать в hset token -> uid
|
|
50
|
+
return array ? Object.values(obj) : obj;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default setToken;
|
package/helper.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
// between our tests.
|
|
1
|
+
import path from 'node:path';
|
|
3
2
|
import Fastify from 'fastify';
|
|
4
3
|
import config from './test/config.js';
|
|
5
4
|
import appService from './index.js';
|
|
6
5
|
|
|
7
6
|
import rclient from './redis/client.js';
|
|
8
7
|
import pgClients from './pg/pgClients.js';
|
|
8
|
+
import { addTemplateDir } from './utils.js';
|
|
9
9
|
|
|
10
10
|
// automatically build and tear down our instance
|
|
11
11
|
async function build(t) {
|
|
@@ -14,6 +14,8 @@ async function build(t) {
|
|
|
14
14
|
process.env.NODE_ENV = 'production';
|
|
15
15
|
const app = Fastify({ logger: false });
|
|
16
16
|
app.register(appService, config);
|
|
17
|
+
const cwd = process.cwd();
|
|
18
|
+
addTemplateDir(path.join(cwd, 'module/test'));
|
|
17
19
|
// close the app after we are done
|
|
18
20
|
t.after(() => {
|
|
19
21
|
// console.log('close app');
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "2",
|
|
4
|
+
"text": "Хвойні",
|
|
5
|
+
"en": "Conifers",
|
|
6
|
+
"color": "#006400"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"id": "3",
|
|
10
|
+
"text": "Чагарники",
|
|
11
|
+
"en": "Shrubs",
|
|
12
|
+
"color": "#6B8E23"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "4",
|
|
16
|
+
"text": "Плодові",
|
|
17
|
+
"en": "Fruit",
|
|
18
|
+
"color": "#808000"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "1",
|
|
22
|
+
"text": "Листяні",
|
|
23
|
+
"en": "Deciduous",
|
|
24
|
+
"color": "#228B22"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
{
|
|
2
|
+
"key": "rz_id",
|
|
3
|
+
"table": "itree.rest_zones",
|
|
4
|
+
"query": "verif",
|
|
5
|
+
"sql": [
|
|
6
|
+
{
|
|
7
|
+
"sql": "select json_agg(file_path) as img_list from admin.doc_file where object_id=t.rz_id and file_status=1",
|
|
8
|
+
"name": "image_sql"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"form": "rest_zone.form",
|
|
12
|
+
"meta": {
|
|
13
|
+
"bbox": "geom",
|
|
14
|
+
"search": "name",
|
|
15
|
+
"title": "name"
|
|
16
|
+
},
|
|
17
|
+
"columns": [
|
|
18
|
+
{
|
|
19
|
+
"ua": "Назва об'єкта",
|
|
20
|
+
"title": "Назва об'єкта",
|
|
21
|
+
"name": "name",
|
|
22
|
+
"meta": "title",
|
|
23
|
+
"html": "<a href=\"/rest_zone/mode=card/view={{rz_id}}\">{{name}}</a>",
|
|
24
|
+
"format": "html"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"en": "Green zone category",
|
|
28
|
+
"ua": "Категорія зеленої зони",
|
|
29
|
+
"meta": "category",
|
|
30
|
+
"name": "category",
|
|
31
|
+
"data": "itree.recrzone_category",
|
|
32
|
+
"format": "select"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"ru": "Вид насаджень",
|
|
36
|
+
"ua": "Вид насаджень",
|
|
37
|
+
"name": "plant_type",
|
|
38
|
+
"meta": "type",
|
|
39
|
+
"data": "itree.plant_type",
|
|
40
|
+
"format": "select"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "uid",
|
|
44
|
+
"ua": "Хто додав",
|
|
45
|
+
"width": "70",
|
|
46
|
+
"data": "contact_id",
|
|
47
|
+
"format": "select"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"ru": "Балансоутримувач об'єкта",
|
|
51
|
+
"ua": "Балансоутримувач об'єкта",
|
|
52
|
+
"hidden": true,
|
|
53
|
+
"meta": "other",
|
|
54
|
+
"name": "balancer",
|
|
55
|
+
"format": "text"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"ua": "Відповідальний за утримання",
|
|
59
|
+
"name": "maintain_respons_gp",
|
|
60
|
+
"meta": "other",
|
|
61
|
+
"hidden": true,
|
|
62
|
+
"format": "text"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"ua": "Площа, га",
|
|
66
|
+
"name": "area",
|
|
67
|
+
"meta": "other",
|
|
68
|
+
"format": "text"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"ru": "Форма власності",
|
|
72
|
+
"ua": "Форма власності",
|
|
73
|
+
"name": "ownership",
|
|
74
|
+
"meta": "other",
|
|
75
|
+
"data": "itree.ownership",
|
|
76
|
+
"format": "select"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"ru": "Категорія земель",
|
|
80
|
+
"ua": "Категорія земель",
|
|
81
|
+
"name": "landcategory",
|
|
82
|
+
"meta": "other",
|
|
83
|
+
"hidden": true,
|
|
84
|
+
"data": "itree.landcategory",
|
|
85
|
+
"format": "select"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"ru": "Цільве призначення",
|
|
89
|
+
"ua": "Цільве призначення",
|
|
90
|
+
"name": "purpose",
|
|
91
|
+
"meta": "other",
|
|
92
|
+
"hidden": true,
|
|
93
|
+
"data": "itree.purpose_type",
|
|
94
|
+
"format": "select"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"ru": "Обмеження щодо використання",
|
|
98
|
+
"ua": "Обмеження щодо використання",
|
|
99
|
+
"name": "restriction",
|
|
100
|
+
"hidden": true,
|
|
101
|
+
"meta": "other",
|
|
102
|
+
"data": "itree.restriction",
|
|
103
|
+
"format": "select"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"ru": "Площа, зайнята зеленими насадженнями, кв.м",
|
|
107
|
+
"ua": "Площа, зайнята зеленими насадженнями, кв.м",
|
|
108
|
+
"name": "plants_square",
|
|
109
|
+
"meta": "other",
|
|
110
|
+
"hidden": true,
|
|
111
|
+
"format": "text"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"ru": "Загальний стан насаджень",
|
|
115
|
+
"ua": "Загальний стан насаджень",
|
|
116
|
+
"name": "plants_cond",
|
|
117
|
+
"meta": "other",
|
|
118
|
+
"data": "itree.plants_cond",
|
|
119
|
+
"format": "badge"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"en": "Photo",
|
|
123
|
+
"ru": "Фото",
|
|
124
|
+
"ua": "Фото",
|
|
125
|
+
"html": "{{#each img_list}}<a class='btn btn-xs btn-default' href=\"{{this}}\" download><i class='fa fa-file-text'></i></a><a target=\"_blank\" class='btn btn-xs btn-default' href=\"{{this}}\"><i class='fa fa-eye'></i></a><br>{{/each}}",
|
|
126
|
+
"name": "image",
|
|
127
|
+
"format": "html"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"ru": "Видовий склад рослин",
|
|
131
|
+
"ua": "Видовий склад рослин",
|
|
132
|
+
"name": "composition",
|
|
133
|
+
"hidden": true,
|
|
134
|
+
"meta": "other",
|
|
135
|
+
"data": "itree.composition",
|
|
136
|
+
"format": "badge"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"ru": "Кількість особливо цінних порід та чагарників, шт",
|
|
140
|
+
"ua": "Кількість особливо цінних порід та чагарників, шт",
|
|
141
|
+
"name": "espec_valuable",
|
|
142
|
+
"hidden": true,
|
|
143
|
+
"meta": "other",
|
|
144
|
+
"format": "text"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"ru": "Повнота насаджень (кількість дерев на 1 гектар), шт/га",
|
|
148
|
+
"ua": "Повнота насаджень (кількість дерев на 1 гектар), шт/га",
|
|
149
|
+
"name": "planting_density",
|
|
150
|
+
"hidden": true,
|
|
151
|
+
"meta": "other",
|
|
152
|
+
"format": "text"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"ru": "Площа, зайнята деревами, кв.м",
|
|
156
|
+
"ua": "Площа, зайнята деревами, кв.м",
|
|
157
|
+
"name": "tree_square",
|
|
158
|
+
"hidden": true,
|
|
159
|
+
"meta": "other",
|
|
160
|
+
"format": "text"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"ru": "Балансова вартість",
|
|
164
|
+
"ua": "Балансова вартість",
|
|
165
|
+
"name": "book_value",
|
|
166
|
+
"meta": "other",
|
|
167
|
+
"hidden": true,
|
|
168
|
+
"format": "text"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"ru": "Кадастровий номер",
|
|
172
|
+
"ua": "Кадастровий номер",
|
|
173
|
+
"meta": "other",
|
|
174
|
+
"name": "cadnum",
|
|
175
|
+
"hidden": true,
|
|
176
|
+
"format": "text"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"ru": "Нормативна грошова оцінка",
|
|
180
|
+
"ua": "Нормативна грошова оцінка",
|
|
181
|
+
"name": "monetary_valuation",
|
|
182
|
+
"meta": "other",
|
|
183
|
+
"hidden": true,
|
|
184
|
+
"format": "text"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"ua": "Верифіковано",
|
|
188
|
+
"html": " {{#ifCond verif '==' true}} \r\n <span class=\"label\" style=\"color:#fff;background:green\"> Так</span> \r\n {{else}} \r\n <span class=\"label\" style=\"color:#fff;background:red\"> Ні</span>\r\n {{/ifCond}}",
|
|
189
|
+
"name": "verif",
|
|
190
|
+
"format": "check"
|
|
191
|
+
}
|
|
192
|
+
],
|
|
193
|
+
"title": "Зони відпочинку",
|
|
194
|
+
"filterList": [
|
|
195
|
+
{
|
|
196
|
+
"ua": "Назва",
|
|
197
|
+
"name": "name",
|
|
198
|
+
"type": "Text"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"ua": "Категорія зеленої зони",
|
|
202
|
+
"data": "itree.recrzone_category",
|
|
203
|
+
"name": "category",
|
|
204
|
+
"sort": "asc",
|
|
205
|
+
"type": "Check"
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"ua": "Вид насаджень",
|
|
209
|
+
"data": "itree.plant_type",
|
|
210
|
+
"name": "plant_type",
|
|
211
|
+
"sort": "asc",
|
|
212
|
+
"type": "Check"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"ua": "Загальний стан насаджень",
|
|
216
|
+
"data": "itree.plants_cond",
|
|
217
|
+
"name": "plants_cond",
|
|
218
|
+
"sort": "asc",
|
|
219
|
+
"type": "Check"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"ua": "Видовий склад рослин",
|
|
223
|
+
"data": "itree.composition",
|
|
224
|
+
"name": "composition",
|
|
225
|
+
"sort": "asc",
|
|
226
|
+
"type": "Check"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"ua": "Форма власності",
|
|
230
|
+
"data": "itree.ownership",
|
|
231
|
+
"name": "ownership",
|
|
232
|
+
"type": "Check"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"ua": "Категорія земель",
|
|
236
|
+
"data": "itree.landcategory",
|
|
237
|
+
"name": "landcategory",
|
|
238
|
+
"type": "Check"
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"ua": "Цільове призначення",
|
|
242
|
+
"data": "itree.purpose",
|
|
243
|
+
"name": "purpose",
|
|
244
|
+
"type": "Check"
|
|
245
|
+
}
|
|
246
|
+
],
|
|
247
|
+
"filterCustom": [
|
|
248
|
+
{
|
|
249
|
+
"name": "actual",
|
|
250
|
+
"label": "Поточний рік",
|
|
251
|
+
"sql": "cdate > '2020-01-01'::date"
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
"filterState": [
|
|
255
|
+
{
|
|
256
|
+
"name": "actual",
|
|
257
|
+
"label": "Поточний рік 2",
|
|
258
|
+
"sql": "cdate > '2022-09-12'::date"
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
"actions": [
|
|
262
|
+
"edit",
|
|
263
|
+
"del"
|
|
264
|
+
]
|
|
265
|
+
}
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { existsSync } from 'fs';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
|
|
5
|
-
const fileName = fileURLToPath(import.meta.url);
|
|
6
|
-
const dirName = path.dirname(fileName);
|
|
7
|
-
|
|
8
|
-
import notification from '../funcs/sendNotification.js';
|
|
9
|
-
|
|
10
|
-
export default async function testNotification({
|
|
11
|
-
pg, funcs = {}, log, query = {}, session = {},
|
|
12
|
-
}) {
|
|
13
|
-
const { local } = funcs.config || {};
|
|
14
|
-
if (!session?.passport?.user?.user_type?.includes('admin') && !local) {
|
|
15
|
-
return { message: 'Forbidden', status: 403 };
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const date = new Date().toISOString().split('T')[0];
|
|
19
|
-
if (!query.to) {
|
|
20
|
-
return { message: 'param to is required', status: 400 };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
const {
|
|
25
|
-
to, template, table, id, nocache,
|
|
26
|
-
} = query;
|
|
27
|
-
const file = [path.join(dirName, '../../', 'changelog.md'), path.join(dirName, 'utils', 'pin-m-ty-media-record-outline+303070.png')].filter((el) => existsSync(el));
|
|
28
|
-
const data = await notification({
|
|
29
|
-
pg,
|
|
30
|
-
funcs,
|
|
31
|
-
log,
|
|
32
|
-
to,
|
|
33
|
-
template,
|
|
34
|
-
title: `Test Softpro ${date}`,
|
|
35
|
-
table,
|
|
36
|
-
nocache,
|
|
37
|
-
file,
|
|
38
|
-
id,
|
|
39
|
-
message: `Test mail Softpro ${date} Lorem Ipsum Lorem Ipsum`,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
message: data || 'ok',
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
catch (err) {
|
|
47
|
-
return { error: err.toString(), status: 500 };
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const fileName = fileURLToPath(import.meta.url);
|
|
6
|
+
const dirName = path.dirname(fileName);
|
|
7
|
+
|
|
8
|
+
import notification from '../funcs/sendNotification.js';
|
|
9
|
+
|
|
10
|
+
export default async function testNotification({
|
|
11
|
+
pg, funcs = {}, log, query = {}, session = {},
|
|
12
|
+
}) {
|
|
13
|
+
const { local } = funcs.config || {};
|
|
14
|
+
if (!session?.passport?.user?.user_type?.includes('admin') && !local) {
|
|
15
|
+
return { message: 'Forbidden', status: 403 };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const date = new Date().toISOString().split('T')[0];
|
|
19
|
+
if (!query.to) {
|
|
20
|
+
return { message: 'param to is required', status: 400 };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const {
|
|
25
|
+
to, template, table, id, nocache,
|
|
26
|
+
} = query;
|
|
27
|
+
const file = [path.join(dirName, '../../', 'changelog.md'), path.join(dirName, 'utils', 'pin-m-ty-media-record-outline+303070.png')].filter((el) => existsSync(el));
|
|
28
|
+
const data = await notification({
|
|
29
|
+
pg,
|
|
30
|
+
funcs,
|
|
31
|
+
log,
|
|
32
|
+
to,
|
|
33
|
+
template,
|
|
34
|
+
title: `Test Softpro ${date}`,
|
|
35
|
+
table,
|
|
36
|
+
nocache,
|
|
37
|
+
file,
|
|
38
|
+
id,
|
|
39
|
+
message: `Test mail Softpro ${date} Lorem Ipsum Lorem Ipsum`,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
message: data || 'ok',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
return { error: err.toString(), status: 500 };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import nodemailer from 'nodemailer';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Надсилає поваідомлення на пошту
|
|
5
|
-
*
|
|
6
|
-
* @type function
|
|
7
|
-
* @alias sendEmail
|
|
8
|
-
* @summary Функція здійснює розсилку по email
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export default async function sendEmail({
|
|
12
|
-
funcs, to, from, subject, html, attachments,
|
|
13
|
-
}) {
|
|
14
|
-
const { config = {} } = funcs;
|
|
15
|
-
|
|
16
|
-
if (!to?.length) {
|
|
17
|
-
throw new Error('empty to list');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const { mailSetting = {} } = config;
|
|
21
|
-
|
|
22
|
-
/*= == check service and setting === */
|
|
23
|
-
if (!mailSetting.service) {
|
|
24
|
-
throw new Error('service is not defined in config');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
Object.assign(mailSetting, { rejectUnauthorized: false });
|
|
28
|
-
|
|
29
|
-
if (mailSetting.port === 465) {
|
|
30
|
-
Object.assign(mailSetting, { secure: true });
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const transport = nodemailer.createTransport(mailSetting);
|
|
34
|
-
|
|
35
|
-
const result = await transport.sendMail({
|
|
36
|
-
from: from || mailSetting.from, to, subject, html, attachments,
|
|
37
|
-
});
|
|
38
|
-
return result;
|
|
39
|
-
}
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Надсилає поваідомлення на пошту
|
|
5
|
+
*
|
|
6
|
+
* @type function
|
|
7
|
+
* @alias sendEmail
|
|
8
|
+
* @summary Функція здійснює розсилку по email
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export default async function sendEmail({
|
|
12
|
+
funcs, to, from, subject, html, attachments,
|
|
13
|
+
}) {
|
|
14
|
+
const { config = {} } = funcs;
|
|
15
|
+
|
|
16
|
+
if (!to?.length) {
|
|
17
|
+
throw new Error('empty to list');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { mailSetting = {} } = config;
|
|
21
|
+
|
|
22
|
+
/*= == check service and setting === */
|
|
23
|
+
if (!mailSetting.service) {
|
|
24
|
+
throw new Error('service is not defined in config');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Object.assign(mailSetting, { rejectUnauthorized: false });
|
|
28
|
+
|
|
29
|
+
if (mailSetting.port === 465) {
|
|
30
|
+
Object.assign(mailSetting, { secure: true });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const transport = nodemailer.createTransport(mailSetting);
|
|
34
|
+
|
|
35
|
+
const result = await transport.sendMail({
|
|
36
|
+
from: from || mailSetting.from, to, subject, html, attachments,
|
|
37
|
+
});
|
|
38
|
+
return result;
|
|
39
|
+
}
|