@opengis/cms 0.0.44 → 0.0.46

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 (104) hide show
  1. package/README.md +98 -98
  2. package/dist/{en-OTy187va.js → en-sWmaB9uu.js} +3 -1
  3. package/dist/index.html +29 -29
  4. package/dist/index.js +4208 -4125
  5. package/dist/index.umd.cjs +70 -65
  6. package/dist/{uk-Dy2psXBp.js → uk-rH4IPxG5.js} +4 -2
  7. package/input-types.json +9 -9
  8. package/module/cms/cls/content.status.json +17 -17
  9. package/module/cms/cls/user_type.json +9 -9
  10. package/module/cms/form/admin.users.form.json +77 -77
  11. package/module/cms/select/cms.page_type.sql +1 -1
  12. package/module/cms/select/news_tag_id.sql +11 -11
  13. package/module/cms/table/admin.users.table.json +53 -53
  14. package/module/cms/table/collection.default.table.json +96 -96
  15. package/module/cms/table/single.default.table.json +116 -116
  16. package/package.json +66 -65
  17. package/plugin.js +43 -43
  18. package/server/app.js +35 -35
  19. package/server/config.js +4 -4
  20. package/server/functions/getContent.js +44 -0
  21. package/server/functions/getDraftKey.js +22 -22
  22. package/server/functions/getUser.js +26 -0
  23. package/server/index.js +22 -22
  24. package/server/migrations/fixes.sql +124 -124
  25. package/server/migrations/site.sql +595 -595
  26. package/server/plugins/adminHook.js +78 -78
  27. package/server/plugins/hook.js +59 -59
  28. package/server/plugins/vite.js +75 -75
  29. package/server/routes/category/controllers/cms.category.delete.js +21 -21
  30. package/server/routes/category/controllers/cms.category.get.js +17 -17
  31. package/server/routes/category/controllers/cms.category.list.js +16 -16
  32. package/server/routes/category/controllers/cms.category.post.js +21 -21
  33. package/server/routes/category/controllers/cms.category.put.js +23 -23
  34. package/server/routes/category/index.mjs +22 -22
  35. package/server/routes/cms/controllers/cmsStat.js +55 -55
  36. package/server/routes/cms/controllers/cmsSuggest.js +57 -57
  37. package/server/routes/cms/controllers/deleteContent.js +113 -113
  38. package/server/routes/cms/controllers/deleteMedia.js +76 -76
  39. package/server/routes/cms/controllers/downloadMedia.js +84 -49
  40. package/server/routes/cms/controllers/getContent.js +110 -110
  41. package/server/routes/cms/controllers/getContentBySlug.js +93 -93
  42. package/server/routes/cms/controllers/getPermissions.js +15 -15
  43. package/server/routes/cms/controllers/insertContent.js +217 -217
  44. package/server/routes/cms/controllers/listMedia.js +108 -94
  45. package/server/routes/cms/controllers/metadataMedia.js +39 -39
  46. package/server/routes/cms/controllers/properties.get.js +18 -53
  47. package/server/routes/cms/controllers/properties.post.js +99 -99
  48. package/server/routes/cms/controllers/searchContent.js +205 -205
  49. package/server/routes/cms/controllers/setPermissions.js +49 -49
  50. package/server/routes/cms/controllers/translate.js +89 -89
  51. package/server/routes/cms/controllers/updateContent.js +238 -238
  52. package/server/routes/cms/controllers/uploadMedia.js +79 -79
  53. package/server/routes/cms/functions/getSettings.js +46 -0
  54. package/server/routes/cms/index.mjs +112 -112
  55. package/server/routes/cms/utils/additionalData.js +35 -35
  56. package/server/routes/cms/utils/getCollection.js +82 -82
  57. package/server/routes/cms/utils/getSingle.js +187 -187
  58. package/server/routes/cms/utils/inputTypes.js +5 -5
  59. package/server/routes/cms/utils/insertContentLocalization.js +86 -86
  60. package/server/routes/cms/utils/requestTranslation.js +85 -85
  61. package/server/routes/cms/utils/updateLocalization.js +47 -47
  62. package/server/routes/cmsSpace/controllers/deleteSpace.js +25 -25
  63. package/server/routes/cmsSpace/controllers/getSpaces.js +27 -27
  64. package/server/routes/cmsSpace/controllers/insertSpace.js +21 -21
  65. package/server/routes/cmsSpace/controllers/updateSpace.js +23 -23
  66. package/server/routes/cmsSpace/index.mjs +20 -20
  67. package/server/routes/contentType/controllers/addContentType.js +162 -162
  68. package/server/routes/contentType/controllers/contentTypeList.js +54 -54
  69. package/server/routes/contentType/controllers/delContentType.js +75 -75
  70. package/server/routes/contentType/controllers/editContentType.js +61 -61
  71. package/server/routes/contentType/controllers/getContentType.js +37 -37
  72. package/server/routes/contentType/index.mjs +35 -35
  73. package/server/routes/contentType/utils/updateContents.js +28 -28
  74. package/server/routes/contentType/utils/updateCustomContentTable.js +53 -53
  75. package/server/routes/feedback/controllers/email.list.js +24 -24
  76. package/server/routes/feedback/controllers/feedback.js +48 -48
  77. package/server/routes/feedback/controllers/feedback.list.js +37 -37
  78. package/server/routes/feedback/controllers/news.subscriptions.js +44 -44
  79. package/server/routes/feedback/index.mjs +71 -71
  80. package/server/routes/logs/controllers/export.user.logs.js +77 -77
  81. package/server/routes/logs/controllers/user.logs.js +44 -44
  82. package/server/routes/logs/index.mjs +9 -9
  83. package/server/routes/menu/controllers/addMenu.js +37 -37
  84. package/server/routes/menu/controllers/delMenu.js +31 -31
  85. package/server/routes/menu/controllers/editMenu.js +41 -41
  86. package/server/routes/menu/controllers/getMenu.js +24 -42
  87. package/server/routes/menu/functions/getMenu.js +48 -0
  88. package/server/routes/menu/index.mjs +13 -13
  89. package/server/routes/migration/controllers/collectionToCustom.js +137 -137
  90. package/server/routes/migration/index.mjs +8 -8
  91. package/server/routes/root.mjs +8 -8
  92. package/server/routes/tags/controllers/add.tags.js +24 -24
  93. package/server/routes/tags/controllers/del.tags.js +19 -19
  94. package/server/routes/tags/controllers/edit.tags.js +25 -25
  95. package/server/routes/tags/controllers/get.tags.js +15 -15
  96. package/server/routes/tags/index.mjs +14 -14
  97. package/server/templates/cls/cms.category_type.json +9 -9
  98. package/server/templates/cls/cms.content_review_status.json +9 -9
  99. package/server/templates/cls/cms.content_status.json +9 -9
  100. package/server/templates/cls/cms.content_type.json +9 -9
  101. package/server/templates/cls/cms.lang.json +9 -9
  102. package/server/templates/page/login.html +126 -126
  103. package/server/templates/select/core.user_mentioned.sql +1 -1
  104. package/utils.js +6 -0
@@ -1,80 +1,80 @@
1
- import path from 'node:path';
2
- import { mkdir } from 'node:fs/promises';
3
-
4
- import { uploadMultiPart, config, getFolder, dataInsert, pgClients } from "@opengis/fastify-table/utils.js";
5
-
6
- // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
7
- const rootDir = path.resolve(getFolder(config, 'local'));
8
- const dir = '/files';
9
-
10
- export default async function uploadMedia(req, reply) {
11
- const { pg = pgClients.client, user = {}, query = {} } = req;
12
-
13
- if (!pg?.pk?.['site.media']) {
14
- return reply.status(404).send('table not found');
15
- }
16
-
17
- if (query.subdir && (typeof query.subdir !== 'string' || query.subdir.includes('..'))) {
18
- return reply.status(403).send('invalid query params: subdir');
19
- }
20
-
21
- // upload assets
22
- if (req.headers['content-type']?.split?.(';')?.shift?.() === 'multipart/form-data') {
23
- const file = await uploadMultiPart(req, { subdir: query.subdir || '', originalFilename: true }).catch(err => {
24
- if (err.message === 'file with specified name already exists in directory') {
25
- err.message = 'Файл з вказаною назвою вже існує';
26
- err.statusCode = 400;
27
- }
28
- throw err;
29
- });
30
-
31
- const { originalFilename: filename, filetype, mimetype } = file;
32
- const relpath = path.join(dir, query.subdir || '', file.originalFilename).replace(/\\/g, '/');
33
-
34
- const id = await dataInsert({
35
- pg,
36
- table: 'site.media',
37
- data: {
38
- filename,
39
- filetype,
40
- subdir: query.subdir,
41
- url: relpath,
42
- mime: mimetype,
43
- filesize: file.size,
44
- },
45
- uid: user?.uid,
46
- }).then(el => el?.rows?.[0]?.media_id);
47
-
48
- return reply.status(200).send({
49
- res: 'ok',
50
- name: filename,
51
- type: 'file',
52
- mimetype,
53
- result: {
54
- file_id: id,
55
- format: file.extension,
56
- size: file.size,
57
- // entity_id: resultInsert?.entity_id,
58
- file_path: relpath,
59
- file_name: filename,
60
- dir: path.dirname(relpath).replace(/\\/g, '/'),
61
- native_file_name: filename,
62
- },
63
- });
64
- }
65
-
66
- if (!query.subdir) {
67
- return reply.status(400).send('not enough query params: subdir');
68
- }
69
-
70
- // create directory
71
- const relpath = path.join(dir, query.subdir).replace(/\\/g, '/');
72
- const dirpath = path.join(rootDir, relpath);
73
- await mkdir(dirpath, { recursive: true });
74
-
75
- return reply.status(200).send({
76
- relpath,
77
- dirname: path.basename(query.subdir),
78
- type: 'dir',
79
- });
1
+ import path from 'node:path';
2
+ import { mkdir } from 'node:fs/promises';
3
+
4
+ import { uploadMultiPart, config, getFolder, dataInsert, pgClients } from "@opengis/fastify-table/utils.js";
5
+
6
+ // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
7
+ const rootDir = path.resolve(getFolder(config, 'local'));
8
+ const dir = '/files';
9
+
10
+ export default async function uploadMedia(req, reply) {
11
+ const { pg = pgClients.client, user = {}, query = {} } = req;
12
+
13
+ if (!pg?.pk?.['site.media']) {
14
+ return reply.status(404).send('table not found');
15
+ }
16
+
17
+ if (query.subdir && (typeof query.subdir !== 'string' || query.subdir.includes('..'))) {
18
+ return reply.status(403).send('invalid query params: subdir');
19
+ }
20
+
21
+ // upload assets
22
+ if (req.headers['content-type']?.split?.(';')?.shift?.() === 'multipart/form-data') {
23
+ const file = await uploadMultiPart(req, { subdir: query.subdir || '', originalFilename: true }).catch(err => {
24
+ if (err.message === 'file with specified name already exists in directory') {
25
+ err.message = 'Файл з вказаною назвою вже існує';
26
+ err.statusCode = 400;
27
+ }
28
+ throw err;
29
+ });
30
+
31
+ const { originalFilename: filename, filetype, mimetype } = file;
32
+ const relpath = path.join(dir, query.subdir || '', file.originalFilename).replace(/\\/g, '/');
33
+
34
+ const id = await dataInsert({
35
+ pg,
36
+ table: 'site.media',
37
+ data: {
38
+ filename,
39
+ filetype,
40
+ subdir: query.subdir,
41
+ url: relpath,
42
+ mime: mimetype,
43
+ filesize: file.size,
44
+ },
45
+ uid: user?.uid,
46
+ }).then(el => el?.rows?.[0]?.media_id);
47
+
48
+ return reply.status(200).send({
49
+ res: 'ok',
50
+ name: filename,
51
+ type: 'file',
52
+ mimetype,
53
+ result: {
54
+ file_id: id,
55
+ format: file.extension,
56
+ size: file.size,
57
+ // entity_id: resultInsert?.entity_id,
58
+ file_path: relpath,
59
+ file_name: filename,
60
+ dir: path.dirname(relpath).replace(/\\/g, '/'),
61
+ native_file_name: filename,
62
+ },
63
+ });
64
+ }
65
+
66
+ if (!query.subdir) {
67
+ return reply.status(400).send('not enough query params: subdir');
68
+ }
69
+
70
+ // create directory
71
+ const relpath = path.join(dir, query.subdir).replace(/\\/g, '/');
72
+ const dirpath = path.join(rootDir, relpath);
73
+ await mkdir(dirpath, { recursive: true });
74
+
75
+ return reply.status(200).send({
76
+ relpath,
77
+ dirname: path.basename(query.subdir),
78
+ type: 'dir',
79
+ });
80
80
  }
@@ -0,0 +1,46 @@
1
+ import { config, pgClients, getRedis } from "@opengis/fastify-table/utils.js";
2
+
3
+ const rclient = getRedis();
4
+
5
+ export default async function getSettings({ entity = 'app', keys = ['cms'], uid = 0, ttl = 3600, pg = pgClients.client } = {}) {
6
+ if (!pg) {
7
+ return { error: "empty pg", code: 500 };
8
+ }
9
+
10
+ if (!pg.pk?.["admin.properties"]) {
11
+ return { error: "properties table not found", code: 404 };
12
+ }
13
+
14
+ const cacheKey = [config.pg?.database, "cms:settings", entity, keys, uid].filter(Boolean).join(":");
15
+ const cache = ttl === 0 ? null : await rclient.get(cacheKey);
16
+
17
+ // return from cache
18
+ if (cache) {
19
+ return { cache: true, ...JSON.parse(cache) };
20
+ }
21
+
22
+ const rows = await pg.query(
23
+ "select property_key as key, property_text, property_json from admin.properties where property_entity=$1",
24
+ entity === "user" ? [uid] : [entity || "app"]
25
+ ).then(el => el.rows || []);
26
+
27
+ const settings = rows
28
+ .filter((row) =>
29
+ typeof keys === "string" ? keys.includes(row.key) : true
30
+ )
31
+ .reduce(
32
+ (
33
+ acc,
34
+ { key, property_text, property_json }
35
+ ) => ({
36
+ ...acc,
37
+ [key]: property_text || property_json,
38
+ }),
39
+ {}
40
+ );
41
+
42
+ // save to cache, default = 1 hour
43
+ if (ttl) await rclient.set(cacheKey, JSON.stringify(settings), 'EX', ttl);
44
+
45
+ return settings;
46
+ }
@@ -1,112 +1,112 @@
1
- // perimissions
2
- import getPermissions from './controllers/getPermissions.js';
3
- import setPermissions from './controllers/setPermissions.js';
4
-
5
- // media
6
- import listMedia from './controllers/listMedia.js';
7
- import metadata from './controllers/metadataMedia.js';
8
- import uploadMedia from './controllers/uploadMedia.js';
9
- import del from './controllers/deleteMedia.js';
10
- import download from './controllers/downloadMedia.js';
11
-
12
- // content
13
- import getContent from './controllers/getContent.js';
14
- import getContentBySlug from './controllers/getContentBySlug.js';
15
- import insertContent from './controllers/insertContent.js';
16
- import updateContent from './controllers/updateContent.js';
17
- import deleteContent from './controllers/deleteContent.js';
18
- import { translateContent, translateCollection } from './controllers/translate.js';
19
-
20
- // suggest
21
- import cmsSuggest from './controllers/cmsSuggest.js';
22
-
23
- // statistics
24
- import cmsStat from './controllers/cmsStat.js';
25
- import searchContent from './controllers/searchContent.js';
26
-
27
- import getAppSettings from "./controllers/properties.get.js";
28
- import postAppSettings from "./controllers/properties.post.js";
29
-
30
- const suggestSchema = {
31
- querystring: {
32
- type: 'object',
33
- properties: {
34
- search: { type: 'string' },
35
- filter: { type: 'string' },
36
- limit: { type: 'number', exclusiveMinimum: 0 },
37
- },
38
- },
39
- };
40
-
41
- const propertiesSchema = {
42
- type: "object",
43
- properties: {
44
- params: {
45
- id: { type: "string", pattern: "^([\\d\\w]+)$" },
46
- key: { type: "string", pattern: "^([\\d\\w._]+)$" },
47
- },
48
- querystring: {
49
- json: { type: "string", pattern: "^([\\d\\w]+)$" },
50
- },
51
- },
52
- };
53
-
54
- const translateContentSchema = {
55
- type: "object",
56
- properties: {
57
- params: {
58
- id: {
59
- type: "string"
60
- },
61
- },
62
- querystring: {
63
- required: ['to'],
64
- additionalProperties: false,
65
- from: {
66
- type: "string"
67
- },
68
- to: {
69
- type: "string"
70
- },
71
- },
72
- },
73
- };
74
-
75
- const params = { config: { policy: 'L0' } };
76
-
77
- export default function route(app) {
78
- app.post('/cms-permissions/:id', { config: { role: 'admin' } }, setPermissions);
79
- app.get('/cms-permissions/:id?', params, getPermissions);
80
- app.get('/cms-translate', { config: { role: "admin" }, schema: translateContentSchema }, translateContent);
81
- app.get('/cms-translate-collection/:id', { config: { role: "admin" }, schema: translateContentSchema }, translateCollection);
82
-
83
- app.post('/cms-media/upload', params, uploadMedia);
84
- app.get('/cms-media', params, listMedia);
85
- app.get('/cms-media/:id/:type', params, download);
86
- app.get('/cms-media/:id', params, metadata);
87
- app.delete('/cms-media/:id?', params, del); // media file with id / subdir without
88
- app.get('/cms-media/:id/delete', params, del); // debug
89
-
90
- // Bearer token required
91
- app.get('/cms-content/:slug/:lang?', params, getContentBySlug);
92
- app.get('/cms-search', params, searchContent);
93
- app.get('/cms/:type', params, getContent);
94
- app.get('/cms/:type/:id/:lang?', params, getContent);
95
-
96
- app.post('/cms/:type/:id?', params, insertContent);
97
- app.put('/cms/:type/:id?', params, updateContent);
98
- app.delete('/cms/:type/:id', params, deleteContent);
99
-
100
- app.get('/cms-suggest/:id', { ...params, schema: suggestSchema }, cmsSuggest);
101
- app.get('/cms-stat', params, cmsStat);
102
- app.get(
103
- "/settings",
104
- { config: { policy: "L0" }, schema: propertiesSchema },
105
- getAppSettings
106
- );
107
- app.post(
108
- "/settings",
109
- { config: { policy: "L0" } },
110
- postAppSettings
111
- );
112
- }
1
+ // perimissions
2
+ import getPermissions from './controllers/getPermissions.js';
3
+ import setPermissions from './controllers/setPermissions.js';
4
+
5
+ // media
6
+ import listMedia from './controllers/listMedia.js';
7
+ import metadata from './controllers/metadataMedia.js';
8
+ import uploadMedia from './controllers/uploadMedia.js';
9
+ import del from './controllers/deleteMedia.js';
10
+ import download from './controllers/downloadMedia.js';
11
+
12
+ // content
13
+ import getContent from './controllers/getContent.js';
14
+ import getContentBySlug from './controllers/getContentBySlug.js';
15
+ import insertContent from './controllers/insertContent.js';
16
+ import updateContent from './controllers/updateContent.js';
17
+ import deleteContent from './controllers/deleteContent.js';
18
+ import { translateContent, translateCollection } from './controllers/translate.js';
19
+
20
+ // suggest
21
+ import cmsSuggest from './controllers/cmsSuggest.js';
22
+
23
+ // statistics
24
+ import cmsStat from './controllers/cmsStat.js';
25
+ import searchContent from './controllers/searchContent.js';
26
+
27
+ import getAppSettings from "./controllers/properties.get.js";
28
+ import postAppSettings from "./controllers/properties.post.js";
29
+
30
+ const suggestSchema = {
31
+ querystring: {
32
+ type: 'object',
33
+ properties: {
34
+ search: { type: 'string' },
35
+ filter: { type: 'string' },
36
+ limit: { type: 'number', exclusiveMinimum: 0 },
37
+ },
38
+ },
39
+ };
40
+
41
+ const propertiesSchema = {
42
+ type: "object",
43
+ properties: {
44
+ params: {
45
+ id: { type: "string", pattern: "^([\\d\\w]+)$" },
46
+ key: { type: "string", pattern: "^([\\d\\w._]+)$" },
47
+ },
48
+ querystring: {
49
+ json: { type: "string", pattern: "^([\\d\\w]+)$" },
50
+ },
51
+ },
52
+ };
53
+
54
+ const translateContentSchema = {
55
+ type: "object",
56
+ properties: {
57
+ params: {
58
+ id: {
59
+ type: "string"
60
+ },
61
+ },
62
+ querystring: {
63
+ required: ['to'],
64
+ additionalProperties: false,
65
+ from: {
66
+ type: "string"
67
+ },
68
+ to: {
69
+ type: "string"
70
+ },
71
+ },
72
+ },
73
+ };
74
+
75
+ const params = { config: { policy: 'L0' } };
76
+
77
+ export default function route(app) {
78
+ app.post('/cms-permissions/:id', { config: { role: 'admin' } }, setPermissions);
79
+ app.get('/cms-permissions/:id?', params, getPermissions);
80
+ app.get('/cms-translate', { config: { role: "admin" }, schema: translateContentSchema }, translateContent);
81
+ app.get('/cms-translate-collection/:id', { config: { role: "admin" }, schema: translateContentSchema }, translateCollection);
82
+
83
+ app.post('/cms-media/upload', params, uploadMedia);
84
+ app.get('/cms-media', params, listMedia);
85
+ app.get('/cms-media/:id/:type', params, download);
86
+ app.get('/cms-media/:id', params, metadata);
87
+ app.delete('/cms-media/:id?', params, del); // media file with id / subdir without
88
+ app.get('/cms-media/:id/delete', params, del); // debug
89
+
90
+ // Bearer token required
91
+ app.get('/cms-content/:slug/:lang?', params, getContentBySlug);
92
+ app.get('/cms-search', params, searchContent);
93
+ app.get('/cms/:type', params, getContent);
94
+ app.get('/cms/:type/:id/:lang?', params, getContent);
95
+
96
+ app.post('/cms/:type/:id?', params, insertContent);
97
+ app.put('/cms/:type/:id?', params, updateContent);
98
+ app.delete('/cms/:type/:id', params, deleteContent);
99
+
100
+ app.get('/cms-suggest/:id', { ...params, schema: suggestSchema }, cmsSuggest);
101
+ app.get('/cms-stat', params, cmsStat);
102
+ app.get(
103
+ "/settings",
104
+ { config: { policy: "L0" }, schema: propertiesSchema },
105
+ getAppSettings
106
+ );
107
+ app.post(
108
+ "/settings",
109
+ { config: { policy: "L0" } },
110
+ postAppSettings
111
+ );
112
+ }
@@ -1,36 +1,36 @@
1
- export default async function additionalData(pg, rows, locale, fields = '*') {
2
- const { rows: localizations = [] } = await pg.query(`select json_object_agg(field_key, field_value), object_id from site.localization
3
- where object_id=any($1) and ${locale && locale !== 'uk' ? 'REVERSE(split_part(REVERSE(field_key), \':\', 1)) = $2' : '1=1'} group by object_id`, [rows.map(el => el.id), locale && locale !== 'uk' ? locale : null].filter(Boolean));
4
-
5
- const { rows: tagsList = [] } = await pg.query(`
6
- SELECT
7
- td.data_id,
8
- json_agg(
9
- json_build_object('id', t.tag_id, 'text', t.value, 'color', t.color, 'slug', t.slug, 'locale', t.locale)
10
- ) AS tag
11
- FROM site.tag_data td
12
- JOIN site.tags t ON t.tag_id = td.tag_id
13
- where data_id=any($1)
14
- GROUP BY td.data_id;
15
- `, [rows.map(el => el.id)]);
16
-
17
- rows.forEach(row => {
18
- if (locale && locale !== 'uk' && row.meta && Object.keys(row.meta || {}).find(key => key.split(':').pop() === locale)) {
19
- row.meta = Object.fromEntries(Object.keys(row.meta || {}).filter(key => key.split(':').pop() === locale && row.meta?.[key]).map(key => [key.split(':').shift(), row.meta[key]]));
20
- }
21
-
22
- const localization = localizations.find(el => el.object_id === row.id);
23
- const localizationKeys = Object.keys(localization?.json_object_agg || {}).filter(key => row[key.split(':').shift()]);
24
- const localizationObj1 = Object.entries(localization?.json_object_agg || {}).filter(([key]) => rows.length > 1 ? true : localizationKeys.includes(key)).reduce((acc, curr) => ({ ...acc, [curr[0]]: typeof curr[1] === 'string' && curr[1].startsWith('[') && curr[1].endsWith(']') ? JSON.parse(curr[1]) : curr[1] }), {});
25
- const localizationObj = fields?.length ? Object.fromEntries(Object.entries(localizationObj1).filter(([key]) => fields.split(',').includes(key.split(':').shift()))) : localizationObj1;
26
- if (locale && locale !== 'uk') {
27
- Object.assign(row, Object.keys(localizationObj).reduce((acc, curr) => ({ ...acc, [curr.replace(`:${locale}`, '')]: localizationObj[curr] }), {}));
28
- } else if (!locale) {
29
- Object.assign(row, localizationObj);
30
- }
31
-
32
- const { tag = [] } = tagsList.find(el => el?.data_id === row?.id) || {};
33
- const localizedTags = locale ? tag.map(el => ({ ...el, text: el.locale?.[locale] || el.text })) : tag;
34
- Object.assign(row, { tag_list: localizedTags });
35
- });
1
+ export default async function additionalData(pg, rows, locale, fields = '*') {
2
+ const { rows: localizations = [] } = await pg.query(`select json_object_agg(field_key, field_value), object_id from site.localization
3
+ where object_id=any($1) and ${locale && locale !== 'uk' ? 'REVERSE(split_part(REVERSE(field_key), \':\', 1)) = $2' : '1=1'} group by object_id`, [rows.map(el => el.id), locale && locale !== 'uk' ? locale : null].filter(Boolean));
4
+
5
+ const { rows: tagsList = [] } = await pg.query(`
6
+ SELECT
7
+ td.data_id,
8
+ json_agg(
9
+ json_build_object('id', t.tag_id, 'text', t.value, 'color', t.color, 'slug', t.slug, 'locale', t.locale)
10
+ ) AS tag
11
+ FROM site.tag_data td
12
+ JOIN site.tags t ON t.tag_id = td.tag_id
13
+ where data_id=any($1)
14
+ GROUP BY td.data_id;
15
+ `, [rows.map(el => el.id)]);
16
+
17
+ rows.forEach(row => {
18
+ if (locale && locale !== 'uk' && row.meta && Object.keys(row.meta || {}).find(key => key.split(':').pop() === locale)) {
19
+ row.meta = Object.fromEntries(Object.keys(row.meta || {}).filter(key => key.split(':').pop() === locale && row.meta?.[key]).map(key => [key.split(':').shift(), row.meta[key]]));
20
+ }
21
+
22
+ const localization = localizations.find(el => el.object_id === row.id);
23
+ const localizationKeys = Object.keys(localization?.json_object_agg || {}).filter(key => row[key.split(':').shift()]);
24
+ const localizationObj1 = Object.entries(localization?.json_object_agg || {}).filter(([key]) => rows.length > 1 ? true : localizationKeys.includes(key)).reduce((acc, curr) => ({ ...acc, [curr[0]]: typeof curr[1] === 'string' && curr[1].startsWith('[') && curr[1].endsWith(']') ? JSON.parse(curr[1]) : curr[1] }), {});
25
+ const localizationObj = fields?.length ? Object.fromEntries(Object.entries(localizationObj1).filter(([key]) => fields.split(',').includes(key.split(':').shift()))) : localizationObj1;
26
+ if (locale && locale !== 'uk') {
27
+ Object.assign(row, Object.keys(localizationObj).reduce((acc, curr) => ({ ...acc, [curr.replace(`:${locale}`, '')]: localizationObj[curr] }), {}));
28
+ } else if (!locale) {
29
+ Object.assign(row, localizationObj);
30
+ }
31
+
32
+ const { tag = [] } = tagsList.find(el => el?.data_id === row?.id) || {};
33
+ const localizedTags = locale ? tag.map(el => ({ ...el, text: el.locale?.[locale] || el.text })) : tag;
34
+ Object.assign(row, { tag_list: localizedTags });
35
+ });
36
36
  }