@opengis/cms 0.0.57 → 0.0.58

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 (149) hide show
  1. package/README.md +131 -131
  2. package/dist/AddNewItemInTree-05PSSEFi.js +76 -0
  3. package/dist/ArticlesPage-CFjE_cw_.js +298 -0
  4. package/dist/CollectionsBreadcrumb-BCxeRikP.js +4 -0
  5. package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-umRzB5mY.js +53 -0
  6. package/dist/CollectionsPage-DHfPNql6.js +124 -0
  7. package/dist/{CreateForm-BMOBeP4G.js → CreateForm-5FvT45vH.js} +1 -1
  8. package/dist/Dashboard-C1eGscNd.js +358 -0
  9. package/dist/EditCollectionPage-DIr1tdtn.js +187 -0
  10. package/dist/{EmptyData-DaZt_nAm.js → EmptyData-DxPrSXhV.js} +1 -1
  11. package/dist/{MenuAddPage-Bf48Z-ah.js → MenuAddPage-D-p3gFgm.js} +40 -35
  12. package/dist/MenuBody-rN5j4YBu.js +125 -0
  13. package/dist/MenuItemPage-BoJw885D.js +1027 -0
  14. package/dist/MenuList-DFEBS0NB.js +172 -0
  15. package/dist/MenuPage-BCZB_S8j.js +107 -0
  16. package/dist/MenuWrapper-AZ_8s-zd.js +12 -0
  17. package/dist/MonacoEditor-Db-3Jc3E.js +4 -0
  18. package/dist/{UniversalTable.vue_vue_type_script_setup_true_lang-CJGTsd1V.js → UniversalTable-CzqPG-tY.js} +12 -12
  19. package/dist/{UniversalTablePagination.vue_vue_type_script_setup_true_lang-GYZd_gkA.js → UniversalTablePagination-4gL47A7I.js} +1 -1
  20. package/dist/VsFormTags-CMjiu9sY.js +114 -0
  21. package/dist/VsPreview-DwETkOpb.js +63 -0
  22. package/dist/contentForm-CtMhQTG0.js +489 -0
  23. package/dist/getField-CpwVE28P.js +179 -0
  24. package/dist/index.d.ts +8 -0
  25. package/dist/index.html +29 -29
  26. package/dist/index.js +72 -71
  27. package/dist/style.css +1 -1
  28. package/dist/vs-builder-edit-D-q1o8tF.js +604 -0
  29. package/dist/vs-builder-monaco-B3Jj0V31.js +33 -0
  30. package/dist/vs-builder-preview-BH4VAM3a.js +44 -0
  31. package/dist/vs-form-custom-datatable-BDZo48w3.js +317 -0
  32. package/dist/vs-form-integer-BZ855R3g.js +61 -0
  33. package/dist/vs-form-media-select-NY27EaG1.js +837 -0
  34. package/dist/vs-form-reference-list-Dtv8fJJU.js +1536 -0
  35. package/dist/vs-form-reletion-link-BhzNQszm.js +34 -0
  36. package/dist/vs-form-tiptap-DDFQjRjY.js +4 -0
  37. package/dist/vs-form-tiptap.vue_vue_type_script_setup_true_lang-DGgsqXwg.js +11 -0
  38. package/dist/vs-richtext-md-C098v_6Q.js +4 -0
  39. package/dist/vs-richtext-md.vue_vue_type_script_setup_true_lang-Ct8uTV-J.js +14 -0
  40. package/input-types.json +9 -9
  41. package/locales/en.json +815 -814
  42. package/locales/uk.json +813 -812
  43. package/module/cms/cls/content.status.json +17 -17
  44. package/module/cms/cls/user_type.json +9 -9
  45. package/module/cms/form/admin.users.form.json +77 -77
  46. package/module/cms/select/cms.page_type.sql +1 -1
  47. package/module/cms/select/news_tag_id.sql +11 -11
  48. package/module/cms/table/admin.users.table.json +53 -53
  49. package/module/cms/table/collection.default.table.json +96 -96
  50. package/module/cms/table/single.default.table.json +116 -116
  51. package/package.json +68 -68
  52. package/plugin.js +43 -43
  53. package/server/app.js +35 -35
  54. package/server/config.js +4 -4
  55. package/server/functions/getContent.js +45 -45
  56. package/server/functions/getDraftKey.js +22 -22
  57. package/server/functions/getSearchData.js +31 -31
  58. package/server/functions/getTags.js +30 -30
  59. package/server/functions/getUser.js +27 -27
  60. package/server/functions/utils/mock.reply.js +55 -55
  61. package/server/index.js +22 -22
  62. package/server/migrations/fixes.sql +129 -129
  63. package/server/migrations/site.sql +595 -595
  64. package/server/plugins/adminHook.js +78 -78
  65. package/server/plugins/hook.js +59 -59
  66. package/server/plugins/vite.js +75 -75
  67. package/server/routes/category/controllers/cms.category.delete.js +21 -21
  68. package/server/routes/category/controllers/cms.category.get.js +17 -17
  69. package/server/routes/category/controllers/cms.category.list.js +16 -16
  70. package/server/routes/category/controllers/cms.category.post.js +21 -21
  71. package/server/routes/category/controllers/cms.category.put.js +23 -23
  72. package/server/routes/category/index.mjs +22 -22
  73. package/server/routes/cms/controllers/cmsStat.js +55 -55
  74. package/server/routes/cms/controllers/cmsSuggest.js +57 -57
  75. package/server/routes/cms/controllers/deleteContent.js +113 -113
  76. package/server/routes/cms/controllers/deleteMedia.js +76 -76
  77. package/server/routes/cms/controllers/downloadMedia.js +84 -84
  78. package/server/routes/cms/controllers/getContent.js +113 -113
  79. package/server/routes/cms/controllers/getContentBySlug.js +93 -93
  80. package/server/routes/cms/controllers/insertContent.js +217 -217
  81. package/server/routes/cms/controllers/listMedia.js +155 -155
  82. package/server/routes/cms/controllers/metadataMedia.js +39 -39
  83. package/server/routes/cms/controllers/properties.get.js +18 -18
  84. package/server/routes/cms/controllers/properties.post.js +99 -99
  85. package/server/routes/cms/controllers/searchContent.js +214 -214
  86. package/server/routes/cms/controllers/translate.js +89 -89
  87. package/server/routes/cms/controllers/updateContent.js +266 -266
  88. package/server/routes/cms/controllers/uploadMedia.js +79 -79
  89. package/server/routes/cms/functions/getSettings.js +48 -48
  90. package/server/routes/cms/index.mjs +112 -112
  91. package/server/routes/cms/utils/additionalData.js +35 -35
  92. package/server/routes/cms/utils/getCollection.js +89 -89
  93. package/server/routes/cms/utils/getSingle.js +188 -188
  94. package/server/routes/cms/utils/inputTypes.js +5 -5
  95. package/server/routes/cms/utils/insertContentLocalization.js +104 -104
  96. package/server/routes/cms/utils/requestTranslation.js +85 -85
  97. package/server/routes/cms/utils/updateLocalization.js +47 -47
  98. package/server/routes/cmsSpace/controllers/deleteSpace.js +25 -25
  99. package/server/routes/cmsSpace/controllers/getSpaces.js +27 -27
  100. package/server/routes/cmsSpace/controllers/insertSpace.js +21 -21
  101. package/server/routes/cmsSpace/controllers/updateSpace.js +23 -23
  102. package/server/routes/cmsSpace/index.mjs +20 -20
  103. package/server/routes/contentType/controllers/addContentType.js +160 -160
  104. package/server/routes/contentType/controllers/contentTypeList.js +54 -54
  105. package/server/routes/contentType/controllers/delContentType.js +75 -75
  106. package/server/routes/contentType/controllers/editContentType.js +88 -88
  107. package/server/routes/contentType/controllers/getContentType.js +65 -65
  108. package/server/routes/contentType/index.mjs +35 -35
  109. package/server/routes/contentType/utils/updateContents.js +44 -44
  110. package/server/routes/contentType/utils/updateCustomContentTable.js +53 -53
  111. package/server/routes/feedback/controllers/email.list.js +24 -24
  112. package/server/routes/feedback/controllers/feedback.js +48 -48
  113. package/server/routes/feedback/controllers/feedback.list.js +37 -37
  114. package/server/routes/feedback/controllers/news.subscriptions.js +44 -44
  115. package/server/routes/feedback/index.mjs +71 -71
  116. package/server/routes/logs/controllers/export.user.logs.js +77 -77
  117. package/server/routes/logs/controllers/user.logs.js +44 -44
  118. package/server/routes/logs/index.mjs +9 -9
  119. package/server/routes/menu/controllers/addMenu.js +37 -37
  120. package/server/routes/menu/controllers/delMenu.js +31 -31
  121. package/server/routes/menu/controllers/editMenu.js +41 -41
  122. package/server/routes/menu/controllers/getMenu.js +24 -24
  123. package/server/routes/menu/functions/getMenu.js +50 -50
  124. package/server/routes/menu/index.mjs +13 -13
  125. package/server/routes/migration/controllers/collectionToCustom.js +137 -137
  126. package/server/routes/migration/index.mjs +8 -8
  127. package/server/routes/root.mjs +8 -8
  128. package/server/routes/tags/controllers/add.tags.js +24 -24
  129. package/server/routes/tags/controllers/del.tags.js +19 -19
  130. package/server/routes/tags/controllers/edit.tags.js +25 -25
  131. package/server/routes/tags/controllers/get.tags.js +15 -15
  132. package/server/routes/tags/index.mjs +14 -14
  133. package/server/templates/cls/cms.category_type.json +9 -9
  134. package/server/templates/cls/cms.content_review_status.json +9 -9
  135. package/server/templates/cls/cms.content_status.json +9 -9
  136. package/server/templates/cls/cms.content_type.json +9 -9
  137. package/server/templates/cls/cms.lang.json +9 -9
  138. package/server/templates/page/login.html +126 -126
  139. package/utils.d.ts +52 -52
  140. package/utils.js +8 -8
  141. package/dist/ArticlesPage-BcR1hbds.js +0 -286
  142. package/dist/BuilderPage-CK_osM89.js +0 -386
  143. package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-CnOe9ORD.js +0 -45
  144. package/dist/CollectionsPage-JfmrHNR_.js +0 -110
  145. package/dist/EditCollectionPage-Cw3GQYRe.js +0 -809
  146. package/dist/MenuItemPage-CXn5HC8j.js +0 -1366
  147. package/dist/MenuPage-tJZtK46W.js +0 -106
  148. package/dist/contentForm-B6gHgGkz.js +0 -586
  149. package/dist/getField-Y5WXnRR0.js +0 -2948
@@ -1,84 +1,84 @@
1
- import path from 'node:path';
2
- import { existsSync } from 'node:fs';
3
- import { readFile } from 'node:fs/promises';
4
-
5
- import sharp from "sharp";
6
- import { imageSize } from "image-size";
7
-
8
- import { config, getFolder, pgClients, grpc, getMimeType } from "@opengis/fastify-table/utils.js";
9
-
10
- const { resizeImage } = grpc();
11
-
12
- // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
13
- const rootDir = path.resolve(getFolder(config, 'local'));
14
-
15
- const previewWidth = 200;
16
- const resizeQuality = 75;
17
-
18
- export default async function downloadMedia({
19
- pg = pgClients.client, params = {}, query,
20
- }, reply) {
21
- if (!params?.id) {
22
- return reply.status(400).send({ error: 'not enough params: id', code: 400 });
23
- }
24
-
25
- if (!pg.pk?.['site.media']) {
26
- return reply.status(404).send({ error: 'table not found', code: 404 });
27
- }
28
-
29
- const media = await pg.query(
30
- `select media_id as id, filename, mime, url from site.media where media_id = $1 and url is not null`,
31
- [params.id],
32
- ).then(el => el.rows?.[0] || {});
33
-
34
- if (!media.id) {
35
- // return reply.status(404).send('media not found: ' + params.id);
36
- const decodedPath = Buffer.from(params.id, 'base64url').toString('utf-8');
37
- Object.assign(media, { url: decodedPath, mime: getMimeType(decodedPath) });
38
- }
39
-
40
- if (!media.url || media.url.includes('..')) {
41
- return reply.status(403).send({ error: "wrong params", code: 403 });
42
- }
43
-
44
- const filepath = path.join(rootDir, media.url);
45
-
46
- if (!existsSync(filepath)) {
47
- return reply.status(404).send({ error: 'file not found', code: 404 });
48
- }
49
-
50
- const buffer = await readFile(filepath, { buffer: true });
51
- const headers = { 'Content-Type': media.mime, 'Cache-Control': query.nocache ? 'no-store, no-cache, must-revalidate' : 'public, max-age=2592000' };
52
-
53
- // without nocache - cache resized images for 30d
54
- if (params.type === 'preview' && media.mime && media.mime.startsWith('image/') && !media.mime.endsWith('/svg+xml')) {
55
- const { width, height } = imageSize(buffer) || {};
56
- const ratio = width / height;
57
- const resizeWidth = Math.min(previewWidth, width);
58
- const resizeHeight = resizeWidth / ratio;
59
-
60
- const { result } = await resizeImage({
61
- base64: buffer.toString("base64"),
62
- width: resizeWidth,
63
- height: resizeHeight,
64
- quality: resizeQuality,
65
- });
66
- if (media.mime && media.mime.endsWith('/webp')) {
67
- const webp = await sharp(Buffer.from(result, "base64")).webp({ quality: resizeQuality }).toBuffer({ resolveWithObject: false });
68
- return reply.headers(headers).send(webp);
69
- }
70
- return reply.headers(headers).send(Buffer.from(result, "base64"));
71
- }
72
-
73
- // skip xml load for preview
74
- if (params.type === 'preview' && ['.xml'].includes(path.extname(filepath))) {
75
- return reply
76
- .headers({
77
- 'Content-Type': media.mime,
78
- 'Content-Disposition': `attachment; filename=${path.basename(filepath)}`,
79
- })
80
- .send(buffer);
81
- }
82
-
83
- return reply.headers(headers).send(buffer);
84
- }
1
+ import path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { readFile } from 'node:fs/promises';
4
+
5
+ import sharp from "sharp";
6
+ import { imageSize } from "image-size";
7
+
8
+ import { config, getFolder, pgClients, grpc, getMimeType } from "@opengis/fastify-table/utils.js";
9
+
10
+ const { resizeImage } = grpc();
11
+
12
+ // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
13
+ const rootDir = path.resolve(getFolder(config, 'local'));
14
+
15
+ const previewWidth = 200;
16
+ const resizeQuality = 75;
17
+
18
+ export default async function downloadMedia({
19
+ pg = pgClients.client, params = {}, query,
20
+ }, reply) {
21
+ if (!params?.id) {
22
+ return reply.status(400).send({ error: 'not enough params: id', code: 400 });
23
+ }
24
+
25
+ if (!pg.pk?.['site.media']) {
26
+ return reply.status(404).send({ error: 'table not found', code: 404 });
27
+ }
28
+
29
+ const media = await pg.query(
30
+ `select media_id as id, filename, mime, url from site.media where media_id = $1 and url is not null`,
31
+ [params.id],
32
+ ).then(el => el.rows?.[0] || {});
33
+
34
+ if (!media.id) {
35
+ // return reply.status(404).send('media not found: ' + params.id);
36
+ const decodedPath = Buffer.from(params.id, 'base64url').toString('utf-8');
37
+ Object.assign(media, { url: decodedPath, mime: getMimeType(decodedPath) });
38
+ }
39
+
40
+ if (!media.url || media.url.includes('..')) {
41
+ return reply.status(403).send({ error: "wrong params", code: 403 });
42
+ }
43
+
44
+ const filepath = path.join(rootDir, media.url);
45
+
46
+ if (!existsSync(filepath)) {
47
+ return reply.status(404).send({ error: 'file not found', code: 404 });
48
+ }
49
+
50
+ const buffer = await readFile(filepath, { buffer: true });
51
+ const headers = { 'Content-Type': media.mime, 'Cache-Control': query.nocache ? 'no-store, no-cache, must-revalidate' : 'public, max-age=2592000' };
52
+
53
+ // without nocache - cache resized images for 30d
54
+ if (params.type === 'preview' && media.mime && media.mime.startsWith('image/') && !media.mime.endsWith('/svg+xml')) {
55
+ const { width, height } = imageSize(buffer) || {};
56
+ const ratio = width / height;
57
+ const resizeWidth = Math.min(previewWidth, width);
58
+ const resizeHeight = resizeWidth / ratio;
59
+
60
+ const { result } = await resizeImage({
61
+ base64: buffer.toString("base64"),
62
+ width: resizeWidth,
63
+ height: resizeHeight,
64
+ quality: resizeQuality,
65
+ });
66
+ if (media.mime && media.mime.endsWith('/webp')) {
67
+ const webp = await sharp(Buffer.from(result, "base64")).webp({ quality: resizeQuality }).toBuffer({ resolveWithObject: false });
68
+ return reply.headers(headers).send(webp);
69
+ }
70
+ return reply.headers(headers).send(Buffer.from(result, "base64"));
71
+ }
72
+
73
+ // skip xml load for preview
74
+ if (params.type === 'preview' && ['.xml'].includes(path.extname(filepath))) {
75
+ return reply
76
+ .headers({
77
+ 'Content-Type': media.mime,
78
+ 'Content-Disposition': `attachment; filename=${path.basename(filepath)}`,
79
+ })
80
+ .send(buffer);
81
+ }
82
+
83
+ return reply.headers(headers).send(buffer);
84
+ }
@@ -1,114 +1,114 @@
1
- import { config, pgClients, addHook, getTemplate } from '@opengis/fastify-table/utils.js';
2
-
3
- import getCollection from '../utils/getCollection.js';
4
- import getSingle from '../utils/getSingle.js';
5
- import getDraftKey from '../../../functions/getDraftKey.js';
6
-
7
- const maxLimit = 100;
8
-
9
- addHook('preFilter', async ({ pg = pgClients.client, table }) => {
10
- if (config.trace) console.log('preFilter', table);
11
- if (!pg || !table) return null;
12
-
13
- if (!table.startsWith('data.') && ['site.contents', 'single.default.table'].includes(table)) {
14
- const { filters = [] } = await getTemplate('table', 'single.default.table') || {};
15
- return { table: 'site.contents', filters };
16
- }
17
-
18
- // default filters, same as pages collection
19
- const { filters = [] } = await getTemplate('table', 'collection.default.table') || {};
20
-
21
- if (['site.contents', 'collection.default.table'].includes(table)) {
22
- return { table: 'site.contents', filters };
23
- }
24
-
25
- if (table.startsWith('data.') && pg?.pk?.[table.replace(/"/g, '')]) {
26
- const { ctid, columns } = await pg.query(
27
- `select content_type_id as ctid, columns from site.content_types where 'data.' || table_name = $1`,
28
- [table.replace(/"/g, '')]
29
- ).then(el => el.rows?.[0] || {});
30
- if (!ctid) return null;
31
-
32
- return { table, filters: filters.concat((columns || []).filter(el => !el.hidden && el.filter && el.name && filters.map(item => item.name).includes(el.name))) };
33
- }
34
- });
35
-
36
- const defaultFields = ['title', 'slug', 'status', 'created_at', 'updated_at', 'main_image', 'published_at'];
37
-
38
- export default async function getContent(req, reply, opt) {
39
- const called = !!opt;
40
- const { showDrafts: showDrafts1 } = opt || {};
41
- const { pg = pgClients.client, params = {}, query = {}, headers = {} } = req;
42
- const user = req.user || {};
43
- const { type, id } = params;
44
- const { filter, state, limit = 16, page, sql, contextQuery, locale, tags, fields } = query;
45
- const search = filter ? filter?.split?.('search=')?.[1]?.split?.('|')?.[0] : undefined;
46
- const [order, ord] = query.order?.split?.('-') || [];
47
- const desc = query.desc || ord === 'desc';
48
-
49
- const { draftKey } = await getDraftKey();
50
- const showDrafts = query.draftKey && query.draftKey === draftKey;
51
- const statusQuery = !!req.user?.uid || showDrafts || showDrafts1 ? '1=1' : `status='published'`;
52
-
53
- // headers.authorization = 'Bearer tokenExample'
54
- const isValidToken = await pg.query(
55
- `select count(*) = 1 as isvalid from site.tokens where token_value=$1 and token_status = 'Active'`,
56
- [headers.authorization?.split(' ')?.pop?.() || '']
57
- ).then(el => el.rows?.[0]?.isvalid);
58
-
59
- if (!config.local && !isValidToken && config.mode !== 'cms' && !called) {
60
- return reply.status(403).send('access restricted: token');
61
- }
62
-
63
- if (!type) {
64
- return reply.status(400).send('not enough params: type');
65
- }
66
-
67
- const { contentId, contentTypeId, meta } = params.type === 'pages'
68
- ? await pg.query(`select content_id as "contentId", content_type_id as "contentTypeId", meta from site.contents where content_id=$1`, [params.type]).then(el => el.rows?.[0] || {})
69
- : await pg.query(`select content_id as "contentId", content_type_id as "contentTypeId", meta from site.contents where $1 in (slug, content_id, content_type_id)`, [params.type]).then(el => el.rows?.[0] || {});
70
-
71
- if (!contentId) {
72
- return reply.status(404).send('content not found');
73
- }
74
-
75
- const { table, contentType, preview, columns: columns1 } = await pg.query(
76
- 'select table_name as table, type as "contentType", preview_path as preview, columns from site.content_types where content_type_id=$1',
77
- [contentTypeId],
78
- ).then(el => el.rows?.[0] || {});
79
-
80
- if (!contentType && params.type !== 'pages') {
81
- return reply.status(404).send('content type not found');
82
- }
83
-
84
- const loadTable = contentType === 'collection' && table
85
- ? await getTemplate('table', 'collection.default.table')
86
- : await getTemplate('table', 'single.default.table');
87
- const { columns: defaultColumns = [], filters: defaultFilters = [] } = loadTable || {};
88
-
89
- defaultColumns.forEach(col => Object.assign(col, { default: true }));
90
-
91
- const columns = (columns1 || []).filter(col => !defaultColumns.map(el => el.name).includes(col.name));
92
-
93
- const result = contentType === 'collection' && table
94
- ? await getCollection({
95
- table, id, limit, maxLimit, tags, order, search, filter, state, page, desc,
96
- sql, locale, contextQuery, statusQuery, columns, preview, user, defaultColumns,
97
- fields, defaultFields, defaultFilters, called,
98
- }, reply, pg)
99
- : await getSingle({
100
- contentId, contentTypeId, id, limit, maxLimit, tags, search, filter, locale, statusQuery, user, sql, page, defaultColumns, fields, defaultFields, defaultFilters,
101
- }, pg);
102
-
103
- if (!result) {
104
- return reply.status(404).send('empty content data');
105
- }
106
-
107
- if (result?.message) {
108
- return reply.status(result.status || 500).send(result.message);
109
- }
110
-
111
- Object.assign(result, { meta });
112
-
113
- return reply.status(200).send(result);
1
+ import { config, pgClients, addHook, getTemplate } from '@opengis/fastify-table/utils.js';
2
+
3
+ import getCollection from '../utils/getCollection.js';
4
+ import getSingle from '../utils/getSingle.js';
5
+ import getDraftKey from '../../../functions/getDraftKey.js';
6
+
7
+ const maxLimit = 100;
8
+
9
+ addHook('preFilter', async ({ pg = pgClients.client, table }) => {
10
+ if (config.trace) console.log('preFilter', table);
11
+ if (!pg || !table) return null;
12
+
13
+ if (!table.startsWith('data.') && ['site.contents', 'single.default.table'].includes(table)) {
14
+ const { filters = [] } = await getTemplate('table', 'single.default.table') || {};
15
+ return { table: 'site.contents', filters };
16
+ }
17
+
18
+ // default filters, same as pages collection
19
+ const { filters = [] } = await getTemplate('table', 'collection.default.table') || {};
20
+
21
+ if (['site.contents', 'collection.default.table'].includes(table)) {
22
+ return { table: 'site.contents', filters };
23
+ }
24
+
25
+ if (table.startsWith('data.') && pg?.pk?.[table.replace(/"/g, '')]) {
26
+ const { ctid, columns } = await pg.query(
27
+ `select content_type_id as ctid, columns from site.content_types where 'data.' || table_name = $1`,
28
+ [table.replace(/"/g, '')]
29
+ ).then(el => el.rows?.[0] || {});
30
+ if (!ctid) return null;
31
+
32
+ return { table, filters: filters.concat((columns || []).filter(el => !el.hidden && el.filter && el.name && filters.map(item => item.name).includes(el.name))) };
33
+ }
34
+ });
35
+
36
+ const defaultFields = ['title', 'slug', 'status', 'created_at', 'updated_at', 'main_image', 'published_at'];
37
+
38
+ export default async function getContent(req, reply, opt) {
39
+ const called = !!opt;
40
+ const { showDrafts: showDrafts1 } = opt || {};
41
+ const { pg = pgClients.client, params = {}, query = {}, headers = {} } = req;
42
+ const user = req.user || {};
43
+ const { type, id } = params;
44
+ const { filter, state, limit = 16, page, sql, contextQuery, locale, tags, fields } = query;
45
+ const search = filter ? filter?.split?.('search=')?.[1]?.split?.('|')?.[0] : undefined;
46
+ const [order, ord] = query.order?.split?.('-') || [];
47
+ const desc = query.desc || ord === 'desc';
48
+
49
+ const { draftKey } = await getDraftKey();
50
+ const showDrafts = query.draftKey && query.draftKey === draftKey;
51
+ const statusQuery = !!req.user?.uid || showDrafts || showDrafts1 ? '1=1' : `status='published'`;
52
+
53
+ // headers.authorization = 'Bearer tokenExample'
54
+ const isValidToken = await pg.query(
55
+ `select count(*) = 1 as isvalid from site.tokens where token_value=$1 and token_status = 'Active'`,
56
+ [headers.authorization?.split(' ')?.pop?.() || '']
57
+ ).then(el => el.rows?.[0]?.isvalid);
58
+
59
+ if (!config.local && !isValidToken && config.mode !== 'cms' && !called) {
60
+ return reply.status(403).send('access restricted: token');
61
+ }
62
+
63
+ if (!type) {
64
+ return reply.status(400).send('not enough params: type');
65
+ }
66
+
67
+ const { contentId, contentTypeId, meta } = params.type === 'pages'
68
+ ? await pg.query(`select content_id as "contentId", content_type_id as "contentTypeId", meta from site.contents where content_id=$1`, [params.type]).then(el => el.rows?.[0] || {})
69
+ : await pg.query(`select content_id as "contentId", content_type_id as "contentTypeId", meta from site.contents where $1 in (slug, content_id, content_type_id)`, [params.type]).then(el => el.rows?.[0] || {});
70
+
71
+ if (!contentId) {
72
+ return reply.status(404).send('content not found');
73
+ }
74
+
75
+ const { table, contentType, preview, columns: columns1 } = await pg.query(
76
+ 'select table_name as table, type as "contentType", preview_path as preview, columns from site.content_types where content_type_id=$1',
77
+ [contentTypeId],
78
+ ).then(el => el.rows?.[0] || {});
79
+
80
+ if (!contentType && params.type !== 'pages') {
81
+ return reply.status(404).send('content type not found');
82
+ }
83
+
84
+ const loadTable = contentType === 'collection' && table
85
+ ? await getTemplate('table', 'collection.default.table')
86
+ : await getTemplate('table', 'single.default.table');
87
+ const { columns: defaultColumns = [], filters: defaultFilters = [] } = loadTable || {};
88
+
89
+ defaultColumns.forEach(col => Object.assign(col, { default: true }));
90
+
91
+ const columns = (columns1 || []).filter(col => !defaultColumns.map(el => el.name).includes(col.name));
92
+
93
+ const result = contentType === 'collection' && table
94
+ ? await getCollection({
95
+ table, id, limit, maxLimit, tags, order, search, filter, state, page, desc,
96
+ sql, locale, contextQuery, statusQuery, columns, preview, user, defaultColumns,
97
+ fields, defaultFields, defaultFilters, called,
98
+ }, reply, pg)
99
+ : await getSingle({
100
+ contentId, contentTypeId, id, limit, maxLimit, tags, search, filter, locale, statusQuery, user, sql, page, defaultColumns, fields, defaultFields, defaultFilters,
101
+ }, pg);
102
+
103
+ if (!result) {
104
+ return reply.status(404).send('empty content data');
105
+ }
106
+
107
+ if (result?.message) {
108
+ return reply.status(result.status || 500).send(result.message);
109
+ }
110
+
111
+ Object.assign(result, { meta });
112
+
113
+ return reply.status(200).send(result);
114
114
  }
@@ -1,93 +1,93 @@
1
- import { config, pgClients } from '@opengis/fastify-table/utils.js';
2
-
3
- import inputTypes from '../utils/inputTypes.js';
4
-
5
- export default async function getContentBySlug({ pg = pgClients.client, params = {}, headers = {} }, reply) {
6
- if (!params.slug) {
7
- return reply.status(400).send('not enough params: slug');
8
- }
9
-
10
- if (!pg?.pk?.['site.content_types']) {
11
- return reply.status(404).send('table not found');
12
- }
13
-
14
- // headers.authorization = 'Bearer tokenExample'
15
- const isValidToken = await pg.query(
16
- `select count(*) = 1 as isvalid from site.tokens where token_value=$1 and token_status = 'Active'`,
17
- [headers.authorization?.split(' ')?.pop?.() || '']
18
- ).then(el => el.rows?.[0]?.isvalid);
19
-
20
- if (!config.local && !isValidToken && config.mode !== 'cms') {
21
- return reply.status(403).send('access restricted: token');
22
- }
23
-
24
- const { cid } = await pg.query(
25
- `select content_id as cid from site.contents where $1 in (content_id,slug) limit 1`,
26
- [params.slug],
27
- ).then(el => el.rows?.[0] || {});
28
-
29
- if (cid) {
30
- const { rows: data = [] } = await pg.query(
31
- `select field_key, field_type, field_value, field_value_object, content_id from site.content_data where content_id=$1`, [cid]
32
- );
33
-
34
- const meta = await pg.query(
35
- 'select title, status, revision, locale, slug, content_type_id, (select columns from site.content_types where content_type_id=a.content_type_id limit 1) as columns from site.contents a where content_id=$1 limit 1',
36
- [cid],
37
- ).then(el => el.rows?.[0] || {});
38
-
39
- const rows = meta.content_type_id ? [{
40
- ...meta,
41
- columns: undefined,
42
- ...Object.fromEntries(
43
- data.map(k =>
44
- [k.field_key, inputTypes[k.field_type] === 'json' || k.field_key === 'meta'
45
- ? k.field_value_object
46
- : k.field_value]
47
- ) || []
48
- )
49
- }] : [];
50
-
51
- const meta1 = meta.content_type_id ? await pg.query(
52
- 'select type from site.content_types where content_type_id=$1',
53
- [meta.content_type_id],
54
- ).then(el => el.rows?.[0] || {}) : {};
55
-
56
- return reply.status(200).send({
57
- ...meta,
58
- ...meta1,
59
- rows,
60
- });
61
- }
62
-
63
- const { rows: contentTypes = [] } = await pg.query(`select content_type_id as ctid, table_name as table, type from site.content_types where table_name is not null and type='collection' `);
64
-
65
- if (!contentTypes.length || contentTypes.find(row => row.table && pg.pk[row.table])) {
66
- return reply.status(404).send('content not found');
67
- }
68
-
69
- const tlist = await pg.query(`select array_agg((select nspname from pg_namespace where oid=relnamespace)||'.'||relname) tlist
70
- from pg_class where relkind in ('r','v')`).then((d) => d.rows[0].tlist);
71
-
72
- const q = contentTypes.filter(row => row.table && tlist.includes(`data.${row.table}`)).map(row => `select id as dataid, '${row.ctid}' as ctid from data."${row.table}" where slug='${params.slug.replace(/'/g, "''")}'`).join(' union all ');
73
-
74
- const { dataid, ctid } = await pg.query(q).then(el => el.rows?.[0] || {});
75
-
76
- if (!dataid) {
77
- return reply.status(404).send('content not found: 2');
78
- }
79
-
80
- if (!ctid) {
81
- return reply.status(404).send('content not found: 3');
82
- }
83
-
84
- const meta = await pg.query('select content_type_id, type, table_name as table, title, status, name as slug, columns from site.content_types where content_type_id=$1', [ctid]).then(el => el.rows?.[0] || {});
85
-
86
- if (!meta?.table) {
87
- return reply.status(404).send('content not found: 4');
88
- }
89
-
90
- const { rows = [] } = await pg.query(`select * from data.${meta.table} where id=$1`, [dataid]);
91
-
92
- return reply.status(200).send({ ...meta, custom: true, rows });
93
- }
1
+ import { config, pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ import inputTypes from '../utils/inputTypes.js';
4
+
5
+ export default async function getContentBySlug({ pg = pgClients.client, params = {}, headers = {} }, reply) {
6
+ if (!params.slug) {
7
+ return reply.status(400).send('not enough params: slug');
8
+ }
9
+
10
+ if (!pg?.pk?.['site.content_types']) {
11
+ return reply.status(404).send('table not found');
12
+ }
13
+
14
+ // headers.authorization = 'Bearer tokenExample'
15
+ const isValidToken = await pg.query(
16
+ `select count(*) = 1 as isvalid from site.tokens where token_value=$1 and token_status = 'Active'`,
17
+ [headers.authorization?.split(' ')?.pop?.() || '']
18
+ ).then(el => el.rows?.[0]?.isvalid);
19
+
20
+ if (!config.local && !isValidToken && config.mode !== 'cms') {
21
+ return reply.status(403).send('access restricted: token');
22
+ }
23
+
24
+ const { cid } = await pg.query(
25
+ `select content_id as cid from site.contents where $1 in (content_id,slug) limit 1`,
26
+ [params.slug],
27
+ ).then(el => el.rows?.[0] || {});
28
+
29
+ if (cid) {
30
+ const { rows: data = [] } = await pg.query(
31
+ `select field_key, field_type, field_value, field_value_object, content_id from site.content_data where content_id=$1`, [cid]
32
+ );
33
+
34
+ const meta = await pg.query(
35
+ 'select title, status, revision, locale, slug, content_type_id, (select columns from site.content_types where content_type_id=a.content_type_id limit 1) as columns from site.contents a where content_id=$1 limit 1',
36
+ [cid],
37
+ ).then(el => el.rows?.[0] || {});
38
+
39
+ const rows = meta.content_type_id ? [{
40
+ ...meta,
41
+ columns: undefined,
42
+ ...Object.fromEntries(
43
+ data.map(k =>
44
+ [k.field_key, inputTypes[k.field_type] === 'json' || k.field_key === 'meta'
45
+ ? k.field_value_object
46
+ : k.field_value]
47
+ ) || []
48
+ )
49
+ }] : [];
50
+
51
+ const meta1 = meta.content_type_id ? await pg.query(
52
+ 'select type from site.content_types where content_type_id=$1',
53
+ [meta.content_type_id],
54
+ ).then(el => el.rows?.[0] || {}) : {};
55
+
56
+ return reply.status(200).send({
57
+ ...meta,
58
+ ...meta1,
59
+ rows,
60
+ });
61
+ }
62
+
63
+ const { rows: contentTypes = [] } = await pg.query(`select content_type_id as ctid, table_name as table, type from site.content_types where table_name is not null and type='collection' `);
64
+
65
+ if (!contentTypes.length || contentTypes.find(row => row.table && pg.pk[row.table])) {
66
+ return reply.status(404).send('content not found');
67
+ }
68
+
69
+ const tlist = await pg.query(`select array_agg((select nspname from pg_namespace where oid=relnamespace)||'.'||relname) tlist
70
+ from pg_class where relkind in ('r','v')`).then((d) => d.rows[0].tlist);
71
+
72
+ const q = contentTypes.filter(row => row.table && tlist.includes(`data.${row.table}`)).map(row => `select id as dataid, '${row.ctid}' as ctid from data."${row.table}" where slug='${params.slug.replace(/'/g, "''")}'`).join(' union all ');
73
+
74
+ const { dataid, ctid } = await pg.query(q).then(el => el.rows?.[0] || {});
75
+
76
+ if (!dataid) {
77
+ return reply.status(404).send('content not found: 2');
78
+ }
79
+
80
+ if (!ctid) {
81
+ return reply.status(404).send('content not found: 3');
82
+ }
83
+
84
+ const meta = await pg.query('select content_type_id, type, table_name as table, title, status, name as slug, columns from site.content_types where content_type_id=$1', [ctid]).then(el => el.rows?.[0] || {});
85
+
86
+ if (!meta?.table) {
87
+ return reply.status(404).send('content not found: 4');
88
+ }
89
+
90
+ const { rows = [] } = await pg.query(`select * from data.${meta.table} where id=$1`, [dataid]);
91
+
92
+ return reply.status(200).send({ ...meta, custom: true, rows });
93
+ }