@opengis/cms 0.0.56 → 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.
- package/README.md +131 -131
- package/dist/AddNewItemInTree-05PSSEFi.js +76 -0
- package/dist/ArticlesPage-CFjE_cw_.js +298 -0
- package/dist/CollectionsBreadcrumb-BCxeRikP.js +4 -0
- package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-umRzB5mY.js +53 -0
- package/dist/CollectionsPage-DHfPNql6.js +124 -0
- package/dist/{CreateForm-BMOBeP4G.js → CreateForm-5FvT45vH.js} +1 -1
- package/dist/Dashboard-C1eGscNd.js +358 -0
- package/dist/EditCollectionPage-DIr1tdtn.js +187 -0
- package/dist/{EmptyData-DaZt_nAm.js → EmptyData-DxPrSXhV.js} +1 -1
- package/dist/{MenuAddPage-Bf48Z-ah.js → MenuAddPage-D-p3gFgm.js} +40 -35
- package/dist/MenuBody-rN5j4YBu.js +125 -0
- package/dist/MenuItemPage-BoJw885D.js +1027 -0
- package/dist/MenuList-DFEBS0NB.js +172 -0
- package/dist/MenuPage-BCZB_S8j.js +107 -0
- package/dist/MenuWrapper-AZ_8s-zd.js +12 -0
- package/dist/MonacoEditor-Db-3Jc3E.js +4 -0
- package/dist/{UniversalTable.vue_vue_type_script_setup_true_lang-CJGTsd1V.js → UniversalTable-CzqPG-tY.js} +12 -12
- package/dist/{UniversalTablePagination.vue_vue_type_script_setup_true_lang-GYZd_gkA.js → UniversalTablePagination-4gL47A7I.js} +1 -1
- package/dist/VsFormTags-CMjiu9sY.js +114 -0
- package/dist/VsPreview-DwETkOpb.js +63 -0
- package/dist/contentForm-CtMhQTG0.js +489 -0
- package/dist/getField-CpwVE28P.js +179 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.html +29 -29
- package/dist/index.js +72 -71
- package/dist/style.css +1 -1
- package/dist/vs-builder-edit-D-q1o8tF.js +604 -0
- package/dist/vs-builder-monaco-B3Jj0V31.js +33 -0
- package/dist/vs-builder-preview-BH4VAM3a.js +44 -0
- package/dist/vs-form-custom-datatable-BDZo48w3.js +317 -0
- package/dist/vs-form-integer-BZ855R3g.js +61 -0
- package/dist/vs-form-media-select-NY27EaG1.js +837 -0
- package/dist/vs-form-reference-list-Dtv8fJJU.js +1536 -0
- package/dist/vs-form-reletion-link-BhzNQszm.js +34 -0
- package/dist/vs-form-tiptap-DDFQjRjY.js +4 -0
- package/dist/vs-form-tiptap.vue_vue_type_script_setup_true_lang-DGgsqXwg.js +11 -0
- package/dist/vs-richtext-md-C098v_6Q.js +4 -0
- package/dist/vs-richtext-md.vue_vue_type_script_setup_true_lang-Ct8uTV-J.js +14 -0
- package/input-types.json +9 -9
- package/locales/en.json +815 -814
- package/locales/uk.json +813 -812
- package/module/cms/cls/content.status.json +17 -17
- package/module/cms/cls/user_type.json +9 -9
- package/module/cms/form/admin.users.form.json +77 -77
- package/module/cms/select/cms.page_type.sql +1 -1
- package/module/cms/select/news_tag_id.sql +11 -11
- package/module/cms/table/admin.users.table.json +53 -53
- package/module/cms/table/collection.default.table.json +96 -96
- package/module/cms/table/single.default.table.json +116 -116
- package/package.json +68 -68
- package/plugin.js +43 -43
- package/server/app.js +35 -35
- package/server/config.js +4 -4
- package/server/functions/getContent.js +45 -45
- package/server/functions/getDraftKey.js +22 -22
- package/server/functions/getSearchData.js +31 -31
- package/server/functions/getTags.js +30 -30
- package/server/functions/getUser.js +27 -27
- package/server/functions/utils/mock.reply.js +55 -55
- package/server/index.js +22 -22
- package/server/migrations/fixes.sql +129 -129
- package/server/migrations/site.sql +595 -595
- package/server/plugins/adminHook.js +78 -78
- package/server/plugins/hook.js +59 -59
- package/server/plugins/vite.js +75 -75
- package/server/routes/category/controllers/cms.category.delete.js +21 -21
- package/server/routes/category/controllers/cms.category.get.js +17 -17
- package/server/routes/category/controllers/cms.category.list.js +16 -16
- package/server/routes/category/controllers/cms.category.post.js +21 -21
- package/server/routes/category/controllers/cms.category.put.js +23 -23
- package/server/routes/category/index.mjs +22 -22
- package/server/routes/cms/controllers/cmsStat.js +55 -55
- package/server/routes/cms/controllers/cmsSuggest.js +57 -57
- package/server/routes/cms/controllers/deleteContent.js +113 -113
- package/server/routes/cms/controllers/deleteMedia.js +76 -76
- package/server/routes/cms/controllers/downloadMedia.js +84 -84
- package/server/routes/cms/controllers/getContent.js +113 -113
- package/server/routes/cms/controllers/getContentBySlug.js +93 -93
- package/server/routes/cms/controllers/insertContent.js +217 -217
- package/server/routes/cms/controllers/listMedia.js +155 -155
- package/server/routes/cms/controllers/metadataMedia.js +39 -39
- package/server/routes/cms/controllers/properties.get.js +18 -18
- package/server/routes/cms/controllers/properties.post.js +99 -99
- package/server/routes/cms/controllers/searchContent.js +214 -214
- package/server/routes/cms/controllers/translate.js +89 -89
- package/server/routes/cms/controllers/updateContent.js +266 -264
- package/server/routes/cms/controllers/uploadMedia.js +79 -79
- package/server/routes/cms/functions/getSettings.js +48 -48
- package/server/routes/cms/index.mjs +112 -112
- package/server/routes/cms/utils/additionalData.js +35 -35
- package/server/routes/cms/utils/getCollection.js +89 -89
- package/server/routes/cms/utils/getSingle.js +188 -183
- package/server/routes/cms/utils/inputTypes.js +5 -5
- package/server/routes/cms/utils/insertContentLocalization.js +104 -104
- package/server/routes/cms/utils/requestTranslation.js +85 -85
- package/server/routes/cms/utils/updateLocalization.js +47 -47
- package/server/routes/cmsSpace/controllers/deleteSpace.js +25 -25
- package/server/routes/cmsSpace/controllers/getSpaces.js +27 -27
- package/server/routes/cmsSpace/controllers/insertSpace.js +21 -21
- package/server/routes/cmsSpace/controllers/updateSpace.js +23 -23
- package/server/routes/cmsSpace/index.mjs +20 -20
- package/server/routes/contentType/controllers/addContentType.js +160 -160
- package/server/routes/contentType/controllers/contentTypeList.js +54 -54
- package/server/routes/contentType/controllers/delContentType.js +75 -75
- package/server/routes/contentType/controllers/editContentType.js +88 -81
- package/server/routes/contentType/controllers/getContentType.js +65 -57
- package/server/routes/contentType/index.mjs +35 -35
- package/server/routes/contentType/utils/updateContents.js +44 -44
- package/server/routes/contentType/utils/updateCustomContentTable.js +53 -53
- package/server/routes/feedback/controllers/email.list.js +24 -24
- package/server/routes/feedback/controllers/feedback.js +48 -48
- package/server/routes/feedback/controllers/feedback.list.js +37 -37
- package/server/routes/feedback/controllers/news.subscriptions.js +44 -44
- package/server/routes/feedback/index.mjs +71 -71
- package/server/routes/logs/controllers/export.user.logs.js +77 -77
- package/server/routes/logs/controllers/user.logs.js +44 -44
- package/server/routes/logs/index.mjs +9 -9
- package/server/routes/menu/controllers/addMenu.js +37 -37
- package/server/routes/menu/controllers/delMenu.js +31 -31
- package/server/routes/menu/controllers/editMenu.js +41 -41
- package/server/routes/menu/controllers/getMenu.js +24 -24
- package/server/routes/menu/functions/getMenu.js +50 -50
- package/server/routes/menu/index.mjs +13 -13
- package/server/routes/migration/controllers/collectionToCustom.js +137 -137
- package/server/routes/migration/index.mjs +8 -8
- package/server/routes/root.mjs +8 -8
- package/server/routes/tags/controllers/add.tags.js +24 -24
- package/server/routes/tags/controllers/del.tags.js +19 -19
- package/server/routes/tags/controllers/edit.tags.js +25 -25
- package/server/routes/tags/controllers/get.tags.js +15 -15
- package/server/routes/tags/index.mjs +14 -14
- package/server/templates/cls/cms.category_type.json +9 -9
- package/server/templates/cls/cms.content_review_status.json +9 -9
- package/server/templates/cls/cms.content_status.json +9 -9
- package/server/templates/cls/cms.content_type.json +9 -9
- package/server/templates/cls/cms.lang.json +9 -9
- package/server/templates/page/login.html +126 -126
- package/utils.d.ts +52 -52
- package/utils.js +8 -8
- package/dist/ArticlesPage-BcR1hbds.js +0 -286
- package/dist/BuilderPage-CK_osM89.js +0 -386
- package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-CnOe9ORD.js +0 -45
- package/dist/CollectionsPage-JfmrHNR_.js +0 -110
- package/dist/EditCollectionPage-Cw3GQYRe.js +0 -809
- package/dist/MenuItemPage-CXn5HC8j.js +0 -1366
- package/dist/MenuPage-tJZtK46W.js +0 -106
- package/dist/contentForm-B6gHgGkz.js +0 -586
- 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
|
-
?
|
|
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) {
|
|
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, 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
|
+
}
|