@opengis/fastify-table 1.1.50 → 1.1.51

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 (211) hide show
  1. package/.eslintrc.cjs +42 -42
  2. package/.gitlab-ci.yml +18 -18
  3. package/Changelog.md +352 -351
  4. package/README.md +26 -26
  5. package/config.js +10 -10
  6. package/cron/controllers/cronApi.js +22 -22
  7. package/cron/controllers/utils/cronList.js +1 -1
  8. package/cron/funcs/addCron.js +132 -132
  9. package/cron/index.js +10 -10
  10. package/crud/controllers/deleteCrud.js +36 -36
  11. package/crud/controllers/insert.js +71 -71
  12. package/crud/controllers/update.js +76 -76
  13. package/crud/controllers/utils/xssInjection.js +72 -72
  14. package/crud/funcs/dataDelete.js +19 -19
  15. package/crud/funcs/dataInsert.js +30 -30
  16. package/crud/funcs/dataUpdate.js +48 -48
  17. package/crud/funcs/getAccess.js +46 -44
  18. package/crud/funcs/getOpt.js +10 -10
  19. package/crud/funcs/getToken.js +27 -27
  20. package/crud/funcs/isFileExists.js +13 -13
  21. package/crud/funcs/setOpt.js +16 -16
  22. package/crud/funcs/setToken.js +53 -53
  23. package/crud/funcs/utils/getFolder.js +9 -9
  24. package/crud/funcs/utils/logChanges.js +62 -62
  25. package/crud/index.js +36 -36
  26. package/docs/.vitepress/abbr.mjs +26 -26
  27. package/docs/.vitepress/config.mjs +127 -127
  28. package/docs/.vitepress/navigation.mjs +82 -82
  29. package/docs/.vitepress/theme/Layout.vue +17 -17
  30. package/docs/.vitepress/theme/components/NavigationLinks.vue +102 -102
  31. package/docs/.vitepress/theme/components/Panzoom.vue +169 -169
  32. package/docs/.vitepress/theme/index.mjs +15 -15
  33. package/docs/.vitepress/theme/style.scss +163 -163
  34. package/docs/abbr.json +4 -4
  35. package/docs/api/cron/cronApi.md +56 -56
  36. package/docs/api/crud/deleteCrud.md +58 -58
  37. package/docs/api/crud/insert.md +82 -82
  38. package/docs/api/crud/update.md +85 -85
  39. package/docs/api/index.md +47 -47
  40. package/docs/api/notification/testEmail.md +91 -91
  41. package/docs/api/table/card.md +73 -73
  42. package/docs/api/table/data.md +134 -134
  43. package/docs/api/table/export.md +60 -60
  44. package/docs/api/table/filter.md +104 -104
  45. package/docs/api/table/form.md +126 -126
  46. package/docs/api/table/search.md +123 -123
  47. package/docs/api/table/suggest.md +156 -156
  48. package/docs/api/table/table.md +107 -107
  49. package/docs/api/user/user.cls.id.md +77 -77
  50. package/docs/api/user/user.cls.md +49 -49
  51. package/docs/api/user/user.cls.post.md +62 -62
  52. package/docs/api/user/user.info.md +37 -37
  53. package/docs/api/utils/logger.file.md +60 -60
  54. package/docs/api/utils/next.id.md +34 -34
  55. package/docs/api/utils/properties.add.md +127 -127
  56. package/docs/api/utils/properties.get.md +73 -73
  57. package/docs/api/utils/status.monitor.md +36 -36
  58. package/docs/api/widget/widget.del.md +76 -76
  59. package/docs/api/widget/widget.get.md +233 -233
  60. package/docs/api/widget/widget.set.md +88 -88
  61. package/docs/db/admin.md +947 -947
  62. package/docs/db/crm.md +564 -564
  63. package/docs/db/index.md +9 -9
  64. package/docs/db/log.md +204 -204
  65. package/docs/hook/card/afterCard.md +20 -20
  66. package/docs/hook/card/preCard.md +25 -25
  67. package/docs/hook/data/afterData.md +26 -26
  68. package/docs/hook/data/preData.md +26 -26
  69. package/docs/hook/deleteCrud/afterDelete.md +21 -21
  70. package/docs/hook/deleteCrud/preDelete.md +26 -26
  71. package/docs/hook/form/afterForm.md +19 -19
  72. package/docs/hook/form/preForm.md +26 -26
  73. package/docs/hook/getTemplate/afterTemplate.md +24 -24
  74. package/docs/hook/getTemplate/preTemplate.md +29 -29
  75. package/docs/hook/index.md +45 -45
  76. package/docs/hook/insert/afterInsert.md +41 -41
  77. package/docs/hook/insert/preInsert.md +25 -25
  78. package/docs/hook/table/afterTable.md +20 -20
  79. package/docs/hook/table/preTable.md +25 -25
  80. package/docs/hook/update/afterUpdate.md +41 -41
  81. package/docs/hook/update/preUpdate.md +25 -25
  82. package/docs/index.md +42 -42
  83. package/docs/public/fastify-dark.svg +3 -3
  84. package/docs/public/logo-short-dark.svg +11 -11
  85. package/docs/public/logo-short.svg +10 -10
  86. package/docs/public/logo.svg +19 -19
  87. package/docs/readme/index.md +121 -121
  88. package/docs/templates/card.md +83 -83
  89. package/docs/templates/cls.md +29 -29
  90. package/docs/templates/filters.md +91 -91
  91. package/docs/templates/forms.md +139 -139
  92. package/docs/templates/index.md +28 -28
  93. package/docs/templates/select.md +90 -90
  94. package/docs/templates/table.md +162 -162
  95. package/docs/utils/cron/addCron.md +29 -29
  96. package/docs/utils/crud/dataInsert.md +51 -51
  97. package/docs/utils/crud/dataUpdate.md +52 -52
  98. package/docs/utils/crud/getOpt.md +33 -33
  99. package/docs/utils/crud/isFileExists.md +37 -37
  100. package/docs/utils/crud/setOpt.md +37 -37
  101. package/docs/utils/hook/addHook.md +74 -74
  102. package/docs/utils/hook/applyHook.md +64 -64
  103. package/docs/utils/index.md +47 -47
  104. package/docs/utils/notification/addNotification.md +28 -28
  105. package/docs/utils/notification/notification.md +41 -41
  106. package/docs/utils/pg/autoIndex.md +22 -22
  107. package/docs/utils/pg/getMeta.md +58 -58
  108. package/docs/utils/pg/getPG.md +34 -34
  109. package/docs/utils/pg/init.md +29 -29
  110. package/docs/utils/pg/pg.md +70 -70
  111. package/docs/utils/redis/getRedis.md +36 -36
  112. package/docs/utils/redis/rclient.md +74 -74
  113. package/docs/utils/table/getForm.md +68 -68
  114. package/docs/utils/table/getMeta.md +55 -55
  115. package/docs/utils/table/getSelect.md +38 -38
  116. package/docs/utils/table/getSelectMeta.md +46 -46
  117. package/docs/utils/table/getTable.md +77 -77
  118. package/helper.js +30 -30
  119. package/index.js +105 -105
  120. package/migration/exec.migrations.js +79 -79
  121. package/module/core/select/core.user_mentioned.sql +1 -1
  122. package/module/test/cls/itree.composition.json +25 -25
  123. package/module/test/table/test.rest_zone.table.json +265 -265
  124. package/notification/controllers/readNotifications.js +30 -30
  125. package/notification/controllers/testEmail.js +50 -50
  126. package/notification/controllers/userNotifications.js +64 -64
  127. package/notification/funcs/addNotification.js +21 -21
  128. package/notification/funcs/sendNotification.js +112 -112
  129. package/package.json +41 -41
  130. package/pg/funcs/getMeta.js +29 -29
  131. package/pg/funcs/getPG.js +30 -30
  132. package/pg/pgClients.js +20 -20
  133. package/policy/funcs/checkPolicy.js +92 -92
  134. package/policy/funcs/sqlInjection.js +33 -33
  135. package/policy/index.js +14 -14
  136. package/redis/client.js +8 -8
  137. package/redis/funcs/getRedis.js +23 -23
  138. package/redis/funcs/redisClients.js +2 -2
  139. package/redis/index.js +19 -19
  140. package/server/migrations/0.sql +78 -78
  141. package/server/migrations/cls.sql +39 -39
  142. package/server/migrations/log.sql +80 -80
  143. package/server/migrations/properties.sql +144 -144
  144. package/server/migrations/roles.sql +172 -166
  145. package/server/migrations/users.sql +168 -168
  146. package/server.js +26 -26
  147. package/table/controllers/data.js +150 -150
  148. package/table/controllers/filter.js +50 -50
  149. package/table/controllers/form.js +42 -42
  150. package/table/controllers/search.js +80 -80
  151. package/table/controllers/suggest.js +79 -79
  152. package/table/controllers/table.js +83 -83
  153. package/table/controllers/utils/addTemplateDir.js +8 -8
  154. package/table/controllers/utils/getSelect.js +19 -19
  155. package/table/controllers/utils/getSelectMeta.js +66 -66
  156. package/table/controllers/utils/getTemplate_old.js +28 -28
  157. package/table/controllers/utils/getTemplates.js +18 -18
  158. package/table/controllers/utils/gisIRColumn.js +67 -67
  159. package/table/controllers/utils/loadTemplate.js +1 -1
  160. package/table/controllers/utils/loadTemplatePath.js +1 -1
  161. package/table/controllers/utils/userTemplateDir.js +1 -1
  162. package/table/funcs/getFilterSQL/index.js +94 -94
  163. package/table/funcs/getFilterSQL/util/formatValue.js +170 -170
  164. package/table/funcs/getFilterSQL/util/getCustomQuery.js +13 -13
  165. package/table/funcs/getFilterSQL/util/getFilterQuery.js +66 -66
  166. package/table/funcs/getFilterSQL/util/getOptimizedQuery.js +12 -12
  167. package/table/funcs/getFilterSQL/util/getTableSql.js +34 -34
  168. package/table/funcs/metaFormat/getSelectVal.js +21 -21
  169. package/table/funcs/metaFormat/index.js +28 -28
  170. package/table/index.js +37 -37
  171. package/table/schema.js +54 -54
  172. package/test/api/applyHook.test.js +95 -95
  173. package/test/api/crud.test.js +89 -89
  174. package/test/api/crud.xss.test.js +80 -80
  175. package/test/api/suggest.test.js +66 -66
  176. package/test/api/table.test.js +134 -134
  177. package/test/api/user.test.js +85 -85
  178. package/test/api/widget.test.js +117 -117
  179. package/test/config.example +18 -18
  180. package/test/funcs/pg.test.js +34 -34
  181. package/test/funcs/redis.test.js +19 -19
  182. package/test/helper/formatDate.test.js +62 -62
  183. package/test/templates/cls/test.json +9 -9
  184. package/test/templates/form/cp_building.form.json +32 -32
  185. package/test/templates/select/account_id.json +3 -3
  186. package/test/templates/select/storage.data.json +2 -2
  187. package/test/templates/select/test.storage.data.json +3 -3
  188. package/test/templates/select/test.suggest.ato_new.json +3 -3
  189. package/test/templates/select/test.suggest.ato_new.sql +25 -25
  190. package/test/templates/select/test.suggest.data.json +4 -4
  191. package/test/templates/select/test.suggest.parent.sql +1 -1
  192. package/test/templates/table/gis.dataset.table.json +20 -20
  193. package/user/controllers/user.cls.id.js +14 -14
  194. package/user/controllers/user.cls.js +75 -75
  195. package/user/controllers/user.cls.post.js +55 -55
  196. package/user/controllers/user.info.js +21 -21
  197. package/util/controllers/logger.file.js +91 -91
  198. package/util/controllers/next.id.js +4 -4
  199. package/util/controllers/properties.add.js +60 -60
  200. package/util/controllers/properties.get.js +19 -19
  201. package/util/controllers/status.monitor.js +8 -8
  202. package/util/controllers/utils/checkUserAccess.js +17 -17
  203. package/util/controllers/utils/getRootDir.js +25 -25
  204. package/util/index.js +23 -23
  205. package/utils.js +104 -104
  206. package/widget/controllers/utils/historyFormat.js +76 -76
  207. package/widget/controllers/utils/obj2db.js +13 -13
  208. package/widget/controllers/widget.del.js +44 -44
  209. package/widget/controllers/widget.get.js +102 -102
  210. package/widget/controllers/widget.set.js +79 -79
  211. package/widget/index.js +40 -40
@@ -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,16 +1,16 @@
1
- import { createHash } from 'crypto';
2
- import getRedis from '../../redis/funcs/getRedis.js';
3
-
4
- function md5(string) {
5
- return createHash('md5').update(string).digest('hex');
6
- }
7
-
8
- export default async function setOpt(params) {
9
- const token = Buffer.from(md5(typeof params === 'object' ? JSON.stringify(params) : params), 'hex').toString('base64').replace(/[+-=]+/g, '');
10
- // const token = md5(params);
11
- const key = `opt:${token}`;
12
-
13
- const rclient = getRedis({ db: 0, funcs: params.funcs });
14
- await rclient.set(key, JSON.stringify(params), 'EX', 60 * 60);
15
- return token;
16
- }
1
+ import { createHash } from 'crypto';
2
+ import getRedis from '../../redis/funcs/getRedis.js';
3
+
4
+ function md5(string) {
5
+ return createHash('md5').update(string).digest('hex');
6
+ }
7
+
8
+ export default async function setOpt(params) {
9
+ const token = Buffer.from(md5(typeof params === 'object' ? JSON.stringify(params) : params), 'hex').toString('base64').replace(/[+-=]+/g, '');
10
+ // const token = md5(params);
11
+ const key = `opt:${token}`;
12
+
13
+ const rclient = getRedis({ db: 0, funcs: params.funcs });
14
+ await rclient.set(key, JSON.stringify(params), 'EX', 60 * 60);
15
+ return token;
16
+ }
@@ -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, 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
- // console.log(codes);
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, 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
+ // console.log(codes);
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,9 +1,9 @@
1
- import path from 'node:path';
2
- import config from '../../../config.js';
3
-
4
- export default function getFolder(req, type = 'server') {
5
- if (!['server', 'local'].includes(type)) throw new Error('params type is invalid');
6
- const types = { local: req.root || config.root, server: req.mapServerRoot || config.mapServerRoot };
7
- const filepath = path.posix.join(types[type] || `/data/local/${req.pg?.options?.database || ''}`, req.folder || config.folder || '');
8
- return filepath;
9
- }
1
+ import path from 'node:path';
2
+ import config from '../../../config.js';
3
+
4
+ export default function getFolder(req, type = 'server') {
5
+ if (!['server', 'local'].includes(type)) throw new Error('params type is invalid');
6
+ const types = { local: req.root || config.root, server: req.mapServerRoot || config.mapServerRoot };
7
+ const filepath = path.posix.join(types[type] || `/data/local/${req.pg?.options?.database || ''}`, req.folder || config.folder || '');
8
+ return filepath;
9
+ }
@@ -1,62 +1,62 @@
1
- import getMeta from '../../../pg/funcs/getMeta.js';
2
-
3
- export default async function logChanges({
4
- pg, table, id, data, uid = 1, type,
5
- }) {
6
- if (!id) {
7
- console.error('param id is required');
8
- return null;
9
- }
10
- if (!table || !pg.pk?.[table]) {
11
- console.error('table not found');
12
- return null;
13
- }
14
- if (!pg.pk?.['log.table_changes'] || !pg.pk?.['log.table_changes_data']) {
15
- console.error('log table not found');
16
- return null;
17
- }
18
- if (!type) {
19
- console.error('invalid type');
20
- return null;
21
- }
22
-
23
- try {
24
- const { change_id: changeId } = await pg.query(`insert into log.table_changes(change_date,change_type,change_user_id,entity_type,entity_id)
25
- values(CURRENT_DATE, $1, $2, $3, $4) returning change_id`, [type, uid, table, id]).then((res) => res.rows?.[0] || {});
26
-
27
- const { fields = [] } = await pg.query(`select * from ${table} limit 0`);
28
- const columnList = fields.map((el) => el?.name);
29
- const q = `select ${Object.keys(data || {}).filter((el) => columnList.includes(el)).join(',') || '*'} from ${table} where ${pg.pk?.[table]}=$1`;
30
- // console.log(q, type, id);
31
-
32
- const old = type !== 'INSERT' ? await pg.query(q, [id]).then((res) => res.rows?.[0] || {}) : {};
33
-
34
- const { columns = [] } = await getMeta({ table: 'log.table_changes_data' });
35
- const names = columns.map((el) => el.name);
36
-
37
- const res = await Promise.all(Object.keys(data || {}).map(async (el) => {
38
- const filterData = Object.entries({
39
- change_id: changeId, entity_key: el, value_old: old[el], value_new: data[el], uid,
40
- })
41
- .filter((el) => el[1] && names.includes(el[0]));
42
-
43
- const insertQuery = `insert into log.table_changes_data (${filterData?.map((key) => `"${key[0]}"`).join(',')})
44
- values (${filterData?.map((key, i) => `$${i + 1}`).join(',')}) returning *`;
45
-
46
- const { rows = [] } = await pg.query(insertQuery, [...filterData.map((el) => (el[1] && typeof el[1] === 'object' && (!Array.isArray(el[1]) || typeof el[1]?.[0] === 'object') ? JSON.stringify(el[1]) : el[1]))]) || {};
47
- return rows[0];
48
- }));
49
-
50
- const newData = type === 'DELETE' ? {} : (Array.isArray(res) ? res : [res]).reduce((acc, curr) => Object.assign(acc, { [curr.entity_key]: curr.value_new }), {});
51
- // console.log('logChanges OK', type);
52
- return {
53
- change_id: changeId, entity_type: table, entity_id: id, uid, change_type: type, old, new: newData,
54
- };
55
- }
56
- catch (err) {
57
- console.error('logChanges error', type, table, id, data, err.toString());
58
- return {
59
- error: err.toString(), entity_type: table, entity_id: id, uid, change_type: type,
60
- };
61
- }
62
- }
1
+ import getMeta from '../../../pg/funcs/getMeta.js';
2
+
3
+ export default async function logChanges({
4
+ pg, table, id, data, uid = 1, type,
5
+ }) {
6
+ if (!id) {
7
+ console.error('param id is required');
8
+ return null;
9
+ }
10
+ if (!table || !pg.pk?.[table]) {
11
+ console.error('table not found');
12
+ return null;
13
+ }
14
+ if (!pg.pk?.['log.table_changes'] || !pg.pk?.['log.table_changes_data']) {
15
+ console.error('log table not found');
16
+ return null;
17
+ }
18
+ if (!type) {
19
+ console.error('invalid type');
20
+ return null;
21
+ }
22
+
23
+ try {
24
+ const { change_id: changeId } = await pg.query(`insert into log.table_changes(change_date,change_type,change_user_id,entity_type,entity_id)
25
+ values(CURRENT_DATE, $1, $2, $3, $4) returning change_id`, [type, uid, table, id]).then((res) => res.rows?.[0] || {});
26
+
27
+ const { fields = [] } = await pg.query(`select * from ${table} limit 0`);
28
+ const columnList = fields.map((el) => el?.name);
29
+ const q = `select ${Object.keys(data || {}).filter((el) => columnList.includes(el)).join(',') || '*'} from ${table} where ${pg.pk?.[table]}=$1`;
30
+ // console.log(q, type, id);
31
+
32
+ const old = type !== 'INSERT' ? await pg.query(q, [id]).then((res) => res.rows?.[0] || {}) : {};
33
+
34
+ const { columns = [] } = await getMeta({ table: 'log.table_changes_data' });
35
+ const names = columns.map((el) => el.name);
36
+
37
+ const res = await Promise.all(Object.keys(data || {}).map(async (el) => {
38
+ const filterData = Object.entries({
39
+ change_id: changeId, entity_key: el, value_old: old[el], value_new: data[el], uid,
40
+ })
41
+ .filter((el) => el[1] && names.includes(el[0]));
42
+
43
+ const insertQuery = `insert into log.table_changes_data (${filterData?.map((key) => `"${key[0]}"`).join(',')})
44
+ values (${filterData?.map((key, i) => `$${i + 1}`).join(',')}) returning *`;
45
+
46
+ const { rows = [] } = await pg.query(insertQuery, [...filterData.map((el) => (el[1] && typeof el[1] === 'object' && (!Array.isArray(el[1]) || typeof el[1]?.[0] === 'object') ? JSON.stringify(el[1]) : el[1]))]) || {};
47
+ return rows[0];
48
+ }));
49
+
50
+ const newData = type === 'DELETE' ? {} : (Array.isArray(res) ? res : [res]).reduce((acc, curr) => Object.assign(acc, { [curr.entity_key]: curr.value_new }), {});
51
+ // console.log('logChanges OK', type);
52
+ return {
53
+ change_id: changeId, entity_type: table, entity_id: id, uid, change_type: type, old, new: newData,
54
+ };
55
+ }
56
+ catch (err) {
57
+ console.error('logChanges error', type, table, id, data, err.toString());
58
+ return {
59
+ error: err.toString(), entity_type: table, entity_id: id, uid, change_type: type,
60
+ };
61
+ }
62
+ }
package/crud/index.js CHANGED
@@ -1,36 +1,36 @@
1
- import getOpt from './funcs/getOpt.js';
2
- import setOpt from './funcs/setOpt.js';
3
- import isFileExists from './funcs/isFileExists.js';
4
- import dataUpdate from './funcs/dataUpdate.js';
5
- import dataInsert from './funcs/dataInsert.js';
6
-
7
- import update from './controllers/update.js';
8
- import insert from './controllers/insert.js';
9
- import deleteCrud from './controllers/deleteCrud.js';
10
- import getAccessFunc from './funcs/getAccess.js';
11
-
12
- const tableSchema = {
13
- params: {
14
- id: { type: 'string', pattern: '^([\\d\\w]+)$' },
15
- table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
16
- },
17
- };
18
-
19
- async function plugin(fastify, config = {}) {
20
- const prefix = config.prefix || '/api';
21
- // funcs
22
- fastify.decorate('setOpt', setOpt);
23
- fastify.decorate('getOpt', getOpt);
24
- fastify.decorate('dataUpdate', dataUpdate);
25
- fastify.decorate('dataInsert', dataInsert);
26
- fastify.decorate('getAccess', getAccessFunc);
27
-
28
- fastify.decorate('isFileExists', isFileExists);
29
-
30
- // api
31
- fastify.put(`${prefix}/table/:table/:id?`, { schema: tableSchema }, update);
32
- fastify.delete(`${prefix}/table/:table/:id?`, { schema: tableSchema }, deleteCrud);
33
- fastify.post(`${prefix}/table/:table?`, { schema: tableSchema }, insert);
34
- }
35
-
36
- export default plugin;
1
+ import getOpt from './funcs/getOpt.js';
2
+ import setOpt from './funcs/setOpt.js';
3
+ import isFileExists from './funcs/isFileExists.js';
4
+ import dataUpdate from './funcs/dataUpdate.js';
5
+ import dataInsert from './funcs/dataInsert.js';
6
+
7
+ import update from './controllers/update.js';
8
+ import insert from './controllers/insert.js';
9
+ import deleteCrud from './controllers/deleteCrud.js';
10
+ import getAccessFunc from './funcs/getAccess.js';
11
+
12
+ const tableSchema = {
13
+ params: {
14
+ id: { type: 'string', pattern: '^([\\d\\w]+)$' },
15
+ table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
16
+ },
17
+ };
18
+
19
+ async function plugin(fastify, config = {}) {
20
+ const prefix = config.prefix || '/api';
21
+ // funcs
22
+ fastify.decorate('setOpt', setOpt);
23
+ fastify.decorate('getOpt', getOpt);
24
+ fastify.decorate('dataUpdate', dataUpdate);
25
+ fastify.decorate('dataInsert', dataInsert);
26
+ fastify.decorate('getAccess', getAccessFunc);
27
+
28
+ fastify.decorate('isFileExists', isFileExists);
29
+
30
+ // api
31
+ fastify.put(`${prefix}/table/:table/:id?`, { schema: tableSchema }, update);
32
+ fastify.delete(`${prefix}/table/:table/:id?`, { schema: tableSchema }, deleteCrud);
33
+ fastify.post(`${prefix}/table/:table?`, { schema: tableSchema }, insert);
34
+ }
35
+
36
+ export default plugin;
@@ -1,26 +1,26 @@
1
- import abbr from '../abbr.json';
2
-
3
- function handleAbbr(md) {
4
- if (!abbr || typeof abbr !== 'object') {
5
- return false;
6
- }
7
-
8
- const defaultRender = md.renderer.render.bind(md.renderer);
9
-
10
- md.renderer.render = function(tokens, options, env) {
11
- let result = defaultRender(tokens, options, env);
12
-
13
- for (const key in abbr) {
14
- const pattern = new RegExp(`${key}(?!\s*<\/abbr>)`, 'gm');
15
- const replacement = `<abbr title="${abbr[key]}">${key}</abbr>`;
16
-
17
- result = result.replace(pattern, replacement);
18
- }
19
-
20
- return result;
21
- };
22
-
23
- return true;
24
- }
25
-
26
- export default handleAbbr;
1
+ import abbr from '../abbr.json';
2
+
3
+ function handleAbbr(md) {
4
+ if (!abbr || typeof abbr !== 'object') {
5
+ return false;
6
+ }
7
+
8
+ const defaultRender = md.renderer.render.bind(md.renderer);
9
+
10
+ md.renderer.render = function(tokens, options, env) {
11
+ let result = defaultRender(tokens, options, env);
12
+
13
+ for (const key in abbr) {
14
+ const pattern = new RegExp(`${key}(?!\s*<\/abbr>)`, 'gm');
15
+ const replacement = `<abbr title="${abbr[key]}">${key}</abbr>`;
16
+
17
+ result = result.replace(pattern, replacement);
18
+ }
19
+
20
+ return result;
21
+ };
22
+
23
+ return true;
24
+ }
25
+
26
+ export default handleAbbr;