@opengis/cms 0.0.58 → 0.0.59

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 (126) hide show
  1. package/README.md +131 -131
  2. package/dist/{ArticlesPage-CFjE_cw_.js → ArticlesPage-BjYzvTWM.js} +3 -3
  3. package/dist/{CollectionsBreadcrumb-BCxeRikP.js → CollectionsBreadcrumb-HePNJb-d.js} +1 -1
  4. package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-BJh-tjam.js +53 -0
  5. package/dist/{Dashboard-C1eGscNd.js → Dashboard-CXkg_pk8.js} +132 -132
  6. package/dist/{EditCollectionPage-DIr1tdtn.js → EditCollectionPage-CqYHpEON.js} +2 -2
  7. package/dist/{MenuAddPage-D-p3gFgm.js → MenuAddPage-QTnwCoGh.js} +1 -1
  8. package/dist/{MenuBody-rN5j4YBu.js → MenuBody-Bi0ONVZf.js} +2 -2
  9. package/dist/{MenuItemPage-BoJw885D.js → MenuItemPage-B7Y9KFyb.js} +3 -3
  10. package/dist/{MenuList-DFEBS0NB.js → MenuList-BLIpeqSd.js} +53 -53
  11. package/dist/{MenuPage-BCZB_S8j.js → MenuPage-3W6jZ15H.js} +1 -1
  12. package/dist/{MenuWrapper-AZ_8s-zd.js → MenuWrapper-OrOv6sOb.js} +1 -1
  13. package/dist/{MonacoEditor-Db-3Jc3E.js → MonacoEditor-ByPT8pnv.js} +1 -1
  14. package/dist/MonacoEditor.vue_vue_type_script_setup_true_lang-C8cip9Ci.js +84 -0
  15. package/dist/{UniversalTable-CzqPG-tY.js → UniversalTable-GBd_pStq.js} +80 -80
  16. package/dist/{UniversalTablePagination-4gL47A7I.js → UniversalTablePagination-Dw2hc0nc.js} +46 -46
  17. package/dist/{contentForm-CtMhQTG0.js → contentForm-Buku-lel.js} +1 -1
  18. package/dist/index.html +29 -29
  19. package/dist/index.js +5 -5
  20. package/dist/{vs-builder-monaco-B3Jj0V31.js → vs-builder-monaco-Cw-f19gc.js} +1 -1
  21. package/input-types.json +9 -9
  22. package/locales/en.json +815 -815
  23. package/locales/uk.json +813 -813
  24. package/module/cms/cls/content.status.json +17 -17
  25. package/module/cms/cls/user_type.json +9 -9
  26. package/module/cms/form/admin.users.form.json +77 -77
  27. package/module/cms/select/cms.page_type.sql +1 -1
  28. package/module/cms/select/news_tag_id.sql +11 -11
  29. package/module/cms/table/admin.users.table.json +53 -53
  30. package/module/cms/table/collection.default.table.json +96 -96
  31. package/module/cms/table/single.default.table.json +116 -116
  32. package/package.json +2 -1
  33. package/plugin.js +43 -43
  34. package/server/app.js +35 -35
  35. package/server/config.js +4 -4
  36. package/server/functions/getContent.js +45 -45
  37. package/server/functions/getDraftKey.js +22 -22
  38. package/server/functions/getSearchData.js +31 -31
  39. package/server/functions/getTags.js +30 -30
  40. package/server/functions/getUser.js +27 -27
  41. package/server/functions/utils/mock.reply.js +55 -55
  42. package/server/index.js +22 -22
  43. package/server/migrations/fixes.sql +129 -129
  44. package/server/migrations/site.sql +595 -595
  45. package/server/plugins/adminHook.js +78 -78
  46. package/server/plugins/hook.js +59 -59
  47. package/server/plugins/vite.js +75 -75
  48. package/server/routes/category/controllers/cms.category.delete.js +21 -21
  49. package/server/routes/category/controllers/cms.category.get.js +17 -17
  50. package/server/routes/category/controllers/cms.category.list.js +16 -16
  51. package/server/routes/category/controllers/cms.category.post.js +21 -21
  52. package/server/routes/category/controllers/cms.category.put.js +23 -23
  53. package/server/routes/category/index.mjs +22 -22
  54. package/server/routes/cms/controllers/cmsStat.js +55 -55
  55. package/server/routes/cms/controllers/cmsSuggest.js +57 -57
  56. package/server/routes/cms/controllers/deleteContent.js +113 -113
  57. package/server/routes/cms/controllers/deleteMedia.js +76 -76
  58. package/server/routes/cms/controllers/downloadMedia.js +84 -84
  59. package/server/routes/cms/controllers/getContent.js +113 -113
  60. package/server/routes/cms/controllers/getContentBySlug.js +93 -93
  61. package/server/routes/cms/controllers/getPermissions.js +15 -15
  62. package/server/routes/cms/controllers/insertContent.js +217 -217
  63. package/server/routes/cms/controllers/listMedia.js +155 -155
  64. package/server/routes/cms/controllers/metadataMedia.js +39 -39
  65. package/server/routes/cms/controllers/properties.get.js +18 -18
  66. package/server/routes/cms/controllers/properties.post.js +99 -99
  67. package/server/routes/cms/controllers/searchContent.js +214 -214
  68. package/server/routes/cms/controllers/setPermissions.js +49 -49
  69. package/server/routes/cms/controllers/translate.js +89 -89
  70. package/server/routes/cms/controllers/updateContent.js +266 -266
  71. package/server/routes/cms/controllers/uploadMedia.js +79 -79
  72. package/server/routes/cms/functions/getSettings.js +48 -48
  73. package/server/routes/cms/index.mjs +112 -112
  74. package/server/routes/cms/utils/additionalData.js +35 -35
  75. package/server/routes/cms/utils/getCollection.js +89 -89
  76. package/server/routes/cms/utils/getSingle.js +188 -188
  77. package/server/routes/cms/utils/inputTypes.js +5 -5
  78. package/server/routes/cms/utils/insertContentLocalization.js +104 -104
  79. package/server/routes/cms/utils/requestTranslation.js +85 -85
  80. package/server/routes/cms/utils/updateLocalization.js +47 -47
  81. package/server/routes/cmsSpace/controllers/deleteSpace.js +25 -25
  82. package/server/routes/cmsSpace/controllers/getSpaces.js +27 -27
  83. package/server/routes/cmsSpace/controllers/insertSpace.js +21 -21
  84. package/server/routes/cmsSpace/controllers/updateSpace.js +23 -23
  85. package/server/routes/cmsSpace/index.mjs +20 -20
  86. package/server/routes/contentType/controllers/addContentType.js +160 -160
  87. package/server/routes/contentType/controllers/contentTypeList.js +54 -54
  88. package/server/routes/contentType/controllers/delContentType.js +75 -75
  89. package/server/routes/contentType/controllers/editContentType.js +88 -88
  90. package/server/routes/contentType/controllers/getContentType.js +65 -65
  91. package/server/routes/contentType/index.mjs +35 -35
  92. package/server/routes/contentType/utils/updateContents.js +44 -44
  93. package/server/routes/contentType/utils/updateCustomContentTable.js +53 -53
  94. package/server/routes/feedback/controllers/email.list.js +24 -24
  95. package/server/routes/feedback/controllers/feedback.js +48 -48
  96. package/server/routes/feedback/controllers/feedback.list.js +37 -37
  97. package/server/routes/feedback/controllers/news.subscriptions.js +44 -44
  98. package/server/routes/feedback/index.mjs +71 -71
  99. package/server/routes/logs/controllers/export.user.logs.js +77 -77
  100. package/server/routes/logs/controllers/user.logs.js +44 -44
  101. package/server/routes/logs/index.mjs +9 -9
  102. package/server/routes/menu/controllers/addMenu.js +37 -37
  103. package/server/routes/menu/controllers/delMenu.js +31 -31
  104. package/server/routes/menu/controllers/editMenu.js +41 -41
  105. package/server/routes/menu/controllers/getMenu.js +24 -24
  106. package/server/routes/menu/functions/getMenu.js +50 -50
  107. package/server/routes/menu/index.mjs +13 -13
  108. package/server/routes/migration/controllers/collectionToCustom.js +137 -137
  109. package/server/routes/migration/index.mjs +8 -8
  110. package/server/routes/root.mjs +8 -8
  111. package/server/routes/tags/controllers/add.tags.js +24 -24
  112. package/server/routes/tags/controllers/del.tags.js +19 -19
  113. package/server/routes/tags/controllers/edit.tags.js +25 -25
  114. package/server/routes/tags/controllers/get.tags.js +15 -15
  115. package/server/routes/tags/index.mjs +14 -14
  116. package/server/templates/cls/cms.category_type.json +9 -9
  117. package/server/templates/cls/cms.content_review_status.json +9 -9
  118. package/server/templates/cls/cms.content_status.json +9 -9
  119. package/server/templates/cls/cms.content_type.json +9 -9
  120. package/server/templates/cls/cms.lang.json +9 -9
  121. package/server/templates/page/login.html +126 -126
  122. package/server/templates/select/core.user_mentioned.sql +1 -1
  123. package/utils.d.ts +52 -52
  124. package/utils.js +8 -8
  125. package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-umRzB5mY.js +0 -53
  126. package/dist/MonacoEditor.vue_vue_type_script_setup_true_lang-B1DrxmQX.js +0 -84
@@ -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
+ }
@@ -1,16 +1,16 @@
1
- import { pgClients } from '@opengis/fastify-table/utils.js';
2
-
3
- export default async function getPermissions(req, reply) {
4
- const { pg = pgClients.client, params = {}, user = {} } = req;
5
-
6
- if (!user?.uid) {
7
- return reply.status(401).send('unauthorized');
8
- }
9
-
10
- const { rows = [] } = await pg.query(
11
- `select * from site.permissions where ${params.id ? 'user_id=$1' : 'true'}`,
12
- [params.id].filter(Boolean),
13
- );
14
-
15
- return { permissions: rows };
1
+ import { pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function getPermissions(req, reply) {
4
+ const { pg = pgClients.client, params = {}, user = {} } = req;
5
+
6
+ if (!user?.uid) {
7
+ return reply.status(401).send('unauthorized');
8
+ }
9
+
10
+ const { rows = [] } = await pg.query(
11
+ `select * from site.permissions where ${params.id ? 'user_id=$1' : 'true'}`,
12
+ [params.id].filter(Boolean),
13
+ );
14
+
15
+ return { permissions: rows };
16
16
  }