@opengis/fastify-table 1.1.24 → 1.1.26

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.
Files changed (39) hide show
  1. package/Changelog.md +4 -0
  2. package/README.md +26 -26
  3. package/config.js +10 -10
  4. package/cron/controllers/cronApi.js +22 -22
  5. package/cron/controllers/utils/cronList.js +1 -1
  6. package/cron/funcs/addCron.js +131 -131
  7. package/cron/index.js +10 -10
  8. package/crud/controllers/utils/checkXSS.js +45 -45
  9. package/crud/controllers/utils/xssInjection.js +72 -72
  10. package/crud/funcs/getToken.js +27 -27
  11. package/crud/funcs/isFileExists.js +13 -13
  12. package/crud/funcs/setToken.js +53 -53
  13. package/notification/controllers/testEmail.js +49 -49
  14. package/notification/funcs/utils/sendEmail.js +39 -39
  15. package/package.json +1 -1
  16. package/pg/funcs/getPG.js +30 -30
  17. package/redis/funcs/getRedis.js +23 -23
  18. package/server/migrations/cls.sql +39 -0
  19. package/server/migrations/log.sql +80 -80
  20. package/server/migrations/properties.sql +64 -1
  21. package/server.js +14 -14
  22. package/table/controllers/card.js +44 -44
  23. package/table/controllers/form.js +28 -28
  24. package/test/api/crud.xss.test.js +72 -72
  25. package/test/config.example +18 -18
  26. package/test/funcs/pg.test.js +34 -34
  27. package/test/funcs/redis.test.js +19 -19
  28. package/test/templates/cls/test.json +9 -9
  29. package/test/templates/form/cp_building.form.json +32 -32
  30. package/test/templates/select/account_id.json +3 -3
  31. package/test/templates/select/storage.data.json +2 -2
  32. package/test/templates/table/gis.dataset.table.json +20 -20
  33. package/user/controllers/user.cls.js +4 -4
  34. package/util/controllers/logger.file.js +90 -90
  35. package/util/controllers/next.id.js +4 -4
  36. package/util/controllers/properties.get.js +19 -19
  37. package/util/controllers/utils/checkUserAccess.js +19 -19
  38. package/util/controllers/utils/getRootDir.js +20 -20
  39. package/util/index.js +23 -23
@@ -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;
@@ -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;
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "1.1.24",
3
+ "version": "1.1.26",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "main": "index.js",
package/pg/funcs/getPG.js CHANGED
@@ -1,30 +1,30 @@
1
- import pg from 'pg';
2
- import config from '../../config.js';
3
- import pgClients from '../pgClients.js';
4
- import init from './init.js';
5
-
6
- function getPG(param) {
7
- const {
8
- user, password, host, port, db, database, name: origin, funcs,
9
- } = param || {};
10
- if (funcs?.config) Object.assign(config, { ...funcs.config }); // unit test
11
- const name = origin || db || database || param || 'client';
12
- if (pgClients[name]) return pgClients[name];
13
-
14
- const dbConfig = {
15
- user: user || config.pg?.user,
16
- password: password || config.pg?.password,
17
- host: host || config.pg?.host,
18
- port: port || config.pg?.port,
19
- database: db || database || config.pg?.db || config.pg?.database,
20
- };
21
-
22
- pgClients[name] = new pg.Pool(dbConfig);
23
- pgClients[name].init = async () => {
24
- await init(pgClients[name]);
25
- };
26
- init(pgClients[name]);
27
- return pgClients[name];
28
- }
29
-
30
- export default getPG;
1
+ import pg from 'pg';
2
+ import config from '../../config.js';
3
+ import pgClients from '../pgClients.js';
4
+ import init from './init.js';
5
+
6
+ function getPG(param) {
7
+ const {
8
+ user, password, host, port, db, database, name: origin, funcs,
9
+ } = param || {};
10
+ if (funcs?.config) Object.assign(config, { ...funcs.config }); // unit test
11
+ const name = origin || db || database || param || 'client';
12
+ if (pgClients[name]) return pgClients[name];
13
+
14
+ const dbConfig = {
15
+ user: user || config.pg?.user,
16
+ password: password || config.pg?.password,
17
+ host: host || config.pg?.host,
18
+ port: port || config.pg?.port,
19
+ database: db || database || config.pg?.db || config.pg?.database,
20
+ };
21
+
22
+ pgClients[name] = new pg.Pool(dbConfig);
23
+ pgClients[name].init = async () => {
24
+ await init(pgClients[name]);
25
+ };
26
+ init(pgClients[name]);
27
+ return pgClients[name];
28
+ }
29
+
30
+ export default getPG;
@@ -1,23 +1,23 @@
1
- import Redis from 'ioredis';
2
- import config from '../../config.js';
3
- import redisClients from './redisClients.js';
4
-
5
- function getRedis({ db } = { db: 0 }) {
6
- if (!config.redis) return null;
7
- if (redisClients[db]) return redisClients[db];
8
-
9
- const redisConfig = {
10
- db,
11
- keyPrefix: `${config.db}:`,
12
- host: config.redis?.host || '127.0.0.1',
13
- port: config.redis?.port || 6379, // Redis port
14
- family: 4, // 4 (IPv4) or 6 (IPv6)
15
- closeClient: true,
16
- };
17
-
18
- redisClients[db] = new Redis(redisConfig);
19
-
20
- return redisClients[db];
21
- }
22
-
23
- export default getRedis;
1
+ import Redis from 'ioredis';
2
+ import config from '../../config.js';
3
+ import redisClients from './redisClients.js';
4
+
5
+ function getRedis({ db } = { db: 0 }) {
6
+ if (!config.redis) return null;
7
+ if (redisClients[db]) return redisClients[db];
8
+
9
+ const redisConfig = {
10
+ db,
11
+ keyPrefix: `${config.db}:`,
12
+ host: config.redis?.host || '127.0.0.1',
13
+ port: config.redis?.port || 6379, // Redis port
14
+ family: 4, // 4 (IPv4) or 6 (IPv6)
15
+ closeClient: true,
16
+ };
17
+
18
+ redisClients[db] = new Redis(redisConfig);
19
+
20
+ return redisClients[db];
21
+ }
22
+
23
+ export default getRedis;
@@ -0,0 +1,39 @@
1
+ create schema if not exists admin;
2
+
3
+ CREATE TABLE if not exists admin.cls();
4
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS clsid text;
5
+ ALTER TABLE admin.cls DROP CONSTRAINT IF EXISTS admin_cls_pkey;
6
+ ALTER TABLE admin.cls DROP CONSTRAINT IF EXISTS admin_cls_unique;
7
+
8
+ ALTER TABLE admin.cls ALTER COLUMN clsid SET NOT NULL;
9
+ ALTER TABLE admin.cls ALTER COLUMN clsid SET DEFAULT next_id();
10
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS name text;
11
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS data text;
12
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS type text;
13
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS code text;
14
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS parent text;
15
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS ua text;
16
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS en text;
17
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS module text;
18
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS files json;
19
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS cdate timestamp without time zone;
20
+ ALTER TABLE admin.cls ALTER COLUMN cdate SET DEFAULT (now())::timestamp without time zone;
21
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS editor_id text;
22
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
23
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS uid text;
24
+ ALTER TABLE admin.cls ALTER COLUMN uid SET NOT NULL;
25
+ ALTER TABLE admin.cls ALTER COLUMN uid SET DEFAULT '1'::text;
26
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS icon text;
27
+ ALTER TABLE admin.cls ADD COLUMN IF NOT EXISTS color text;
28
+
29
+ ALTER TABLE admin.cls ADD CONSTRAINT admin_cls_pkey PRIMARY KEY (clsid);
30
+ ALTER TABLE admin.cls ADD CONSTRAINT admin_cls_unique UNIQUE (code, parent);
31
+
32
+ COMMENT ON COLUMN admin.cls.name IS 'Назва';
33
+ COMMENT ON COLUMN admin.cls.type IS 'Тип';
34
+ COMMENT ON COLUMN admin.cls.data IS 'SQL';
35
+ COMMENT ON COLUMN admin.cls.code IS 'Код';
36
+ COMMENT ON COLUMN admin.cls.parent IS 'Назва батьківського об''єкту';
37
+ COMMENT ON COLUMN admin.cls.module IS 'Модуль';
38
+ COMMENT ON COLUMN admin.cls.icon IS 'Іконка';
39
+ COMMENT ON COLUMN admin.cls.color IS 'Колір';