@opengis/cms 0.0.13 → 0.0.15
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/dist/assets/ArticlesPage-BveM4q3g.js +11 -0
- package/dist/assets/CollectionsPage-D5td-UBm.js +1 -0
- package/dist/assets/ContentBlock.vue_vue_type_script_setup_true_lang-BwF6D-yB.js +30 -0
- package/dist/assets/CreateCollectionPage-Cu0RW5ui.js +76 -0
- package/dist/assets/Dashboard-faSjwmB8.js +11 -0
- package/dist/assets/EditCollectionPage-K5oPPzCd.js +1 -0
- package/dist/assets/MediaPage-BoW3aWgN.js +1 -0
- package/dist/assets/PermissionsPage-DGy5fha2.js +1 -0
- package/dist/assets/SingletonsPage-C1X2xkQE.js +1 -0
- package/dist/assets/UniversalTable.vue_vue_type_script_setup_true_lang-DUqfWJcy.js +6 -0
- package/dist/assets/calendar-hsWc4yH-.js +6 -0
- package/dist/assets/contentForm-DMVC4vho.js +1 -0
- package/dist/assets/database-BTxZQzYy.js +6 -0
- package/dist/assets/index-9GY17iSP.css +1 -0
- package/dist/assets/index-DYyZmLWO.js +2138 -0
- package/dist/assets/index-xsH4HHeE.js +6 -0
- package/dist/assets/logo-Cct5WB26.png +0 -0
- package/dist/assets/plus-D9etvrM2.js +6 -0
- package/dist/assets/save-C2B6th9J.js +11 -0
- package/dist/assets/search-BI-hqhq6.js +6 -0
- package/dist/assets/settings-DbyDiH2g.js +6 -0
- package/dist/assets/square-pen-61CkyXzK.js +6 -0
- package/dist/assets/trash-2-CJSl_r88.js +6 -0
- package/dist/assets/vue.-sixQ7xP-DwXf3zRn.js +1 -0
- package/dist/assets/x-BNquQe5y.js +6 -0
- package/dist/assets/x-circle-C3q70RMH.js +16 -0
- package/dist/images/logo.png +0 -0
- package/dist/index.html +30 -0
- package/package.json +38 -32
- package/server/app.js +32 -10
- package/server/index.js +3 -3
- package/server/migrations/site.sql +26 -8
- package/server/plugins/adminHook.js +78 -0
- package/server/plugins/hook.js +54 -78
- package/server/plugins/vite.js +13 -9
- package/server/routes/cms/controllers/deleteContent.js +60 -0
- package/server/routes/{site → cms}/controllers/deleteMedia.js +46 -46
- package/server/routes/{site → cms}/controllers/downloadMedia.js +48 -48
- package/server/routes/cms/controllers/getContent.js +96 -0
- package/server/routes/{site → cms}/controllers/getPermissions.js +15 -15
- package/server/routes/cms/controllers/insertContent.js +69 -0
- package/server/routes/{site → cms}/controllers/listMedia.js +72 -72
- package/server/routes/{site → cms}/controllers/metadataMedia.js +37 -37
- package/server/routes/{site → cms}/controllers/setPermissions.js +49 -49
- package/server/routes/cms/controllers/updateContent.js +112 -0
- package/server/routes/{site → cms}/controllers/uploadMedia.js +65 -65
- package/server/routes/cms/index.mjs +45 -0
- package/server/routes/contentType/controllers/cms.type.delete.js +22 -0
- package/server/routes/contentType/controllers/cms.type.get.js +22 -0
- package/server/routes/contentType/controllers/cms.type.list.js +25 -0
- package/server/routes/contentType/controllers/cms.type.post.js +22 -0
- package/server/routes/contentType/controllers/cms.type.put.js +24 -0
- package/server/routes/contentType/index.mjs +25 -0
- package/server/routes/contentType/utils/builderCache.js +58 -0
- package/server/routes/fileContent/data/deleteContent.js +34 -0
- package/server/routes/fileContent/data/deleteMedia.js +28 -0
- package/server/routes/fileContent/data/downloadMedia.js +41 -0
- package/server/routes/fileContent/data/getContent.js +32 -0
- package/server/routes/fileContent/data/insertContent.js +37 -0
- package/server/routes/fileContent/data/listMedia.js +47 -0
- package/server/routes/fileContent/data/metadataMedia.js +38 -0
- package/server/routes/fileContent/data/updateContent.js +40 -0
- package/server/routes/fileContent/data/uploadMedia.js +49 -0
- package/server/routes/fileContent/index.mjs +54 -0
- package/server/routes/fileContent/type/contentTypeList.js +7 -0
- package/server/routes/fileContent/type/createContentType.js +31 -0
- package/server/routes/fileContent/type/deleteContentType.js +29 -0
- package/server/routes/fileContent/type/getContentType.js +15 -0
- package/server/routes/fileContent/type/updateContentType.js +40 -0
- package/server/routes/fileContent/utils/astroBuilderCache.js +47 -0
- package/server/routes/fileContent/utils/contentDir.js +12 -0
- package/server/routes/fileContent/utils/contentTypeExists.js +15 -0
- package/server/routes/root.mjs +0 -7
- package/dist/cms.js +0 -6727
- package/dist/cms.umd.cjs +0 -19
- package/dist/style.css +0 -1
- package/server/routes/builder/controllers/cms.builder.delete.js +0 -21
- package/server/routes/builder/controllers/cms.builder.get.js +0 -17
- package/server/routes/builder/controllers/cms.builder.list.js +0 -16
- package/server/routes/builder/controllers/cms.builder.post.js +0 -21
- package/server/routes/builder/controllers/cms.builder.put.js +0 -23
- package/server/routes/builder/index.mjs +0 -22
- package/server/routes/manager/controllers/cms.manager.delete.js +0 -22
- package/server/routes/manager/controllers/cms.manager.get.js +0 -21
- package/server/routes/manager/controllers/cms.manager.list.js +0 -31
- package/server/routes/manager/controllers/cms.manager.post.js +0 -28
- package/server/routes/manager/controllers/cms.manager.put.js +0 -23
- package/server/routes/manager/index.mjs +0 -22
- package/server/routes/media/controllers/delete.js +0 -59
- package/server/routes/media/controllers/edit.js +0 -94
- package/server/routes/media/controllers/list.js +0 -74
- package/server/routes/media/controllers/metadata.js +0 -51
- package/server/routes/media/controllers/preview.js +0 -47
- package/server/routes/media/controllers/upload.js +0 -79
- package/server/routes/media/index.mjs +0 -16
- package/server/routes/site/index.mjs +0 -34
package/server/plugins/hook.js
CHANGED
|
@@ -1,91 +1,67 @@
|
|
|
1
1
|
import fp from 'fastify-plugin';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import config from '../config.js';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
// to export the decorators to the outer scope
|
|
3
|
+
import { config, pgClients } from '@opengis/fastify-table/utils.js';
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
fastify.decorate('config', config);
|
|
5
|
+
import builderCache from '../routes/contentType/utils/builderCache.js';
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const { user } = req.session?.passport || {};
|
|
15
|
-
req.user = user;
|
|
16
|
-
});
|
|
7
|
+
const matches = {
|
|
8
|
+
date: 'timestamp without time zone',
|
|
9
|
+
};
|
|
17
10
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
11
|
+
const defaultColumns = [
|
|
12
|
+
{
|
|
13
|
+
name: 'created_at',
|
|
14
|
+
type: 'date',
|
|
15
|
+
default: 'now()',
|
|
16
|
+
label: 'Created at',
|
|
17
|
+
required: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'created_by',
|
|
21
|
+
type: 'text',
|
|
22
|
+
label: 'Created by'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'updated_at',
|
|
26
|
+
type: 'date',
|
|
27
|
+
default: 'now()',
|
|
28
|
+
label: 'Updated at',
|
|
29
|
+
required: true,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'updated_by',
|
|
33
|
+
type: 'text',
|
|
34
|
+
label: 'Updated by'
|
|
31
35
|
}
|
|
36
|
+
];
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
reply.headers(payload.headers);
|
|
38
|
-
} */
|
|
39
|
-
if (payload.buffer) {
|
|
40
|
-
return payload.buffer;
|
|
41
|
-
}
|
|
42
|
-
if (payload.file) {
|
|
43
|
-
// const buffer = await readFile(payload.file);
|
|
44
|
-
// return reply.send(buffer);
|
|
45
|
-
const stream = fs.createReadStream(payload.file);
|
|
46
|
-
return stream;
|
|
47
|
-
// return reply.send(stream);
|
|
48
|
-
}
|
|
38
|
+
function createTableSQL(name, slug = 'slug', columns = {}) {
|
|
39
|
+
const columnList = (Array.isArray(columns)
|
|
40
|
+
? columns.filter(el => el.name)
|
|
41
|
+
: Object.entries(columns).map(el => ({ name: el[0], ...el[1] })));
|
|
49
42
|
|
|
50
|
-
|
|
51
|
-
return payload.message;
|
|
52
|
-
}
|
|
53
|
-
return payload;
|
|
54
|
-
});
|
|
43
|
+
defaultColumns.forEach(el => columnList.push(el));
|
|
55
44
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
catch (err) {
|
|
66
|
-
// throw new Error('invalid body');
|
|
67
|
-
// return { error: 'invalid body', status: 400 };
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// allow upload file
|
|
73
|
-
const kIsMultipart = Symbol.for('[FastifyMultipart.isMultipart]');
|
|
74
|
-
fastify.addContentTypeParser('multipart', (request, _, done) => {
|
|
75
|
-
request[kIsMultipart] = true;
|
|
76
|
-
done(null);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// parse Body
|
|
80
|
-
function contentParser(req, body, done) {
|
|
81
|
-
const parseBody = decodeURIComponent(body.toString()).split('&').reduce((acc, el) => {
|
|
82
|
-
const [key, val] = el.split('=');
|
|
83
|
-
return { ...acc, [key]: val };
|
|
84
|
-
}, {});
|
|
85
|
-
done(null, parseBody);
|
|
86
|
-
}
|
|
45
|
+
return `CREATE TABLE IF NOT EXISTS site.${name} ();
|
|
46
|
+
ALTER TABLE site.${name} ADD COLUMN IF NOT EXISTS ${name}_id text not null default next_id() PRIMARY KEY;
|
|
47
|
+
ALTER TABLE site.${name} ADD COLUMN IF NOT EXISTS ${slug} text;
|
|
48
|
+
${columnList.map(el => `ALTER TABLE site.${name} ADD COLUMN IF NOT EXISTS ${el.name} ${matches[el.type] || 'text'};`).join('\n')}
|
|
49
|
+
${columnList.filter(el => el.default).map(el => `ALTER TABLE site.${name} ALTER COLUMN ${el.name} SET DEFAULT ${el.default};`).join('\n')}
|
|
50
|
+
${columnList.filter(el => Object.hasOwn(el, 'required')).map(el => `ALTER TABLE site.${name} ALTER COLUMN ${el.name} ${el.required ? 'SET' : 'DROP'} NOT NULL;`).join('\n')}
|
|
51
|
+
${columnList.filter(el => el.label).map(el => `COMMENT ON COLUMN site.${name}.${el.name} IS '${el.label}';`).join('\n')}`;
|
|
52
|
+
}
|
|
87
53
|
|
|
88
|
-
|
|
54
|
+
async function plugin(app) {
|
|
55
|
+
if (config.pg && builderCache()?.length) {
|
|
56
|
+
app.addHook('onListen', async () => {
|
|
57
|
+
const files = builderCache().filter(el => el.type === 'collection' && el.provider === 'pg' && el.name);
|
|
58
|
+
const q = files.map(el => createTableSQL(el.name, el.slug, el.columns)).filter(el => el).join(';\n');
|
|
59
|
+
await pgClients.client.query(`create schema if not exists site;${q}`).catch(err => {
|
|
60
|
+
console.error('Builder deploy error:', err);
|
|
61
|
+
});
|
|
62
|
+
console.log('Builder deploy complete', files?.length);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
89
65
|
}
|
|
90
66
|
|
|
91
67
|
export default fp(plugin);
|
package/server/plugins/vite.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path, { dirname } from 'path';
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path, { dirname } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
|
|
5
|
+
import { config } from '@opengis/fastify-table/utils.js';
|
|
6
|
+
|
|
5
7
|
const dir = dirname(fileURLToPath(import.meta.url));
|
|
6
8
|
const root = `${dir}/../..`;
|
|
7
|
-
import config from '../config.js';
|
|
8
|
-
|
|
9
9
|
const isProduction = process.env.NODE_ENV === 'production' || config.production;
|
|
10
10
|
|
|
11
11
|
async function plugin(fastify) {
|
|
@@ -33,8 +33,7 @@ async function plugin(fastify) {
|
|
|
33
33
|
// this is middleware for vite's dev servert
|
|
34
34
|
fastify.addHook('onRequest', async (req, reply) => {
|
|
35
35
|
const { user } = req.session?.passport || {};
|
|
36
|
-
|
|
37
|
-
if (!user && !disableAuth) {
|
|
36
|
+
if (!user && config.pg && !config.auth?.disable) {
|
|
38
37
|
return reply.redirect('/login');
|
|
39
38
|
}
|
|
40
39
|
|
|
@@ -50,9 +49,14 @@ async function plugin(fastify) {
|
|
|
50
49
|
// From Build
|
|
51
50
|
fastify.get('*', async (req, reply) => {
|
|
52
51
|
const { user } = req.session?.passport || {};
|
|
53
|
-
const
|
|
54
|
-
if (!user
|
|
55
|
-
|
|
52
|
+
const indexPath = path.join(root, 'dist', 'index.html');
|
|
53
|
+
// if (!user && config.pg && !config.auth?.disable) {
|
|
54
|
+
// return reply.redirect('/login');
|
|
55
|
+
// }
|
|
56
|
+
if (!fs.existsSync(indexPath)) {
|
|
57
|
+
return reply.status(404).send('index.html not found');
|
|
58
|
+
}
|
|
59
|
+
const stream = fs.createReadStream(indexPath);
|
|
56
60
|
return reply.type('text/html').send(stream);
|
|
57
61
|
});
|
|
58
62
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { config, pgClients, dataDelete } from '@opengis/fastify-table/utils.js';
|
|
2
|
+
|
|
3
|
+
import builderCache from '../../contentType/utils/builderCache.js';
|
|
4
|
+
|
|
5
|
+
export default async function deleteContent(req, reply) {
|
|
6
|
+
const {
|
|
7
|
+
pg = pgClients.client,
|
|
8
|
+
params = {},
|
|
9
|
+
user = {},
|
|
10
|
+
headers = {},
|
|
11
|
+
} = req;
|
|
12
|
+
|
|
13
|
+
const { type, id } = params;
|
|
14
|
+
|
|
15
|
+
if (!type) {
|
|
16
|
+
return reply.status(400).send('not enough params: type');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!id) {
|
|
20
|
+
return reply.status(400).send('not enough params: id');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (builderCache[type]?.table && builderCache[type]?.provider === 'pg' && config.pg) {
|
|
24
|
+
const result = await dataDelete({
|
|
25
|
+
pg,
|
|
26
|
+
id,
|
|
27
|
+
table: 'site.' + builderCache[type]?.table,
|
|
28
|
+
referer: headers?.referer,
|
|
29
|
+
uid: user?.uid || 0,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const pk = pg.pk?.['site.' + builderCache[type]?.table];
|
|
33
|
+
return reply.status(200).send({ id: result?.[pk], ...result || {} });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const arr = config.pg ? await pg.query(`select array_agg(relname)::text[] from pg_class a
|
|
37
|
+
left join pg_namespace b on a.relnamespace=b.oid
|
|
38
|
+
where a.relam=2 and b.nspname='site'`).then(el => el.rows?.[0]?.array_agg || []) : [];
|
|
39
|
+
|
|
40
|
+
if (!arr.length) {
|
|
41
|
+
return reply.status(400).send('empty schema: site');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const table = arr.find(el => el === params.type);
|
|
45
|
+
|
|
46
|
+
if (!table) {
|
|
47
|
+
return reply.status(400).send('invalid params: type');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const result = await dataDelete({
|
|
51
|
+
pg,
|
|
52
|
+
id,
|
|
53
|
+
table: 'site.' + table,
|
|
54
|
+
referer: headers?.referer,
|
|
55
|
+
uid: user?.uid || 0,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const pk = pg.pk?.['site.' + table];
|
|
59
|
+
return reply.status(200).send({ id: result?.[pk], ...result || {} });
|
|
60
|
+
}
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
3
|
-
import { rm } from 'node:fs/promises';
|
|
4
|
-
|
|
5
|
-
import { config, dataDelete, getFolder, pgClients } from "@opengis/fastify-table/utils.js";
|
|
6
|
-
|
|
7
|
-
const rootDir = getFolder(config, 'local');
|
|
8
|
-
|
|
9
|
-
export default async function deleteMedia({
|
|
10
|
-
pg = pgClients.client, params = {}, user = {}, method = 'DELETE',
|
|
11
|
-
}, reply) {
|
|
12
|
-
if (!config.debug && method !== 'DELETE') {
|
|
13
|
-
return reply.status(403).send('access restricted');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (!params?.id) {
|
|
17
|
-
return reply.status(400).send('not enough params: id');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (!pg.pk?.['site.media']) {
|
|
21
|
-
return reply.status(404).send('table not found');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const { url: relpath, id } = await pg.query(
|
|
25
|
-
'select media_id as id, url from site.media where media_id = $1 and url is not null',
|
|
26
|
-
[params.id],
|
|
27
|
-
).then(el => el.rows?.[0] || {});
|
|
28
|
-
|
|
29
|
-
if (!id) {
|
|
30
|
-
return reply.status(404).send('media not found: ' + params.id);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const res = await dataDelete({
|
|
34
|
-
pg,
|
|
35
|
-
id,
|
|
36
|
-
table: 'site.media',
|
|
37
|
-
uid: user?.uid || 0,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const filepath = path.join(rootDir, relpath);
|
|
41
|
-
|
|
42
|
-
if (existsSync(filepath)) {
|
|
43
|
-
await rm(filepath, { recursive: true });
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return { id, ...res || {} };
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { rm } from 'node:fs/promises';
|
|
4
|
+
|
|
5
|
+
import { config, dataDelete, getFolder, pgClients } from "@opengis/fastify-table/utils.js";
|
|
6
|
+
|
|
7
|
+
const rootDir = getFolder(config, 'local');
|
|
8
|
+
|
|
9
|
+
export default async function deleteMedia({
|
|
10
|
+
pg = pgClients.client, params = {}, user = {}, method = 'DELETE',
|
|
11
|
+
}, reply) {
|
|
12
|
+
if (!config.debug && method !== 'DELETE') {
|
|
13
|
+
return reply.status(403).send('access restricted');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!params?.id) {
|
|
17
|
+
return reply.status(400).send('not enough params: id');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!pg.pk?.['site.media']) {
|
|
21
|
+
return reply.status(404).send('table not found');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { url: relpath, id } = await pg.query(
|
|
25
|
+
'select media_id as id, url from site.media where media_id = $1 and url is not null',
|
|
26
|
+
[params.id],
|
|
27
|
+
).then(el => el.rows?.[0] || {});
|
|
28
|
+
|
|
29
|
+
if (!id) {
|
|
30
|
+
return reply.status(404).send('media not found: ' + params.id);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const res = await dataDelete({
|
|
34
|
+
pg,
|
|
35
|
+
id,
|
|
36
|
+
table: 'site.media',
|
|
37
|
+
uid: user?.uid || 0,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const filepath = path.join(rootDir, relpath);
|
|
41
|
+
|
|
42
|
+
if (existsSync(filepath)) {
|
|
43
|
+
await rm(filepath, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { id, ...res || {} };
|
|
47
47
|
}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
3
|
-
import { readFile } from 'node:fs/promises';
|
|
4
|
-
|
|
5
|
-
import { config, getFolder, pgClients } from "@opengis/fastify-table/utils.js";
|
|
6
|
-
|
|
7
|
-
const rootDir = getFolder(config, 'local');
|
|
8
|
-
|
|
9
|
-
export default async function downloadMedia({
|
|
10
|
-
pg = pgClients.client, params = {}, query = {},
|
|
11
|
-
}, reply) {
|
|
12
|
-
if (!params?.id) {
|
|
13
|
-
return reply.status(400).send('not enough params: id');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (!pg.pk?.['site.media']) {
|
|
17
|
-
return reply.status(404).send('table not found');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const { filename, mime, id, url: relpath } = await pg.query(
|
|
21
|
-
'select media_id as id, filename, mime, url from site.media where media_id = $1 and url is not null',
|
|
22
|
-
[params.id],
|
|
23
|
-
).then(el => el.rows?.[0] || {});
|
|
24
|
-
|
|
25
|
-
if (!id) {
|
|
26
|
-
return reply.status(404).send('media not found: ' + params.id);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const filepath = path.join(rootDir, relpath);
|
|
30
|
-
|
|
31
|
-
if (!existsSync(filepath)) {
|
|
32
|
-
return reply.status(404).send('file not found');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const buffer = await readFile(filepath, { buffer: true });
|
|
36
|
-
|
|
37
|
-
// skip xml load for preview
|
|
38
|
-
if (query.preview && path.extname(filename) !== '.xml') {
|
|
39
|
-
return reply.headers({ 'Content-Type': mime }).send(buffer);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return reply
|
|
43
|
-
.headers({
|
|
44
|
-
'Content-Type': mime,
|
|
45
|
-
'Content-Disposition': `attachment; filename=${filename || path.basename(filepath)}`,
|
|
46
|
-
})
|
|
47
|
-
.send(buffer);
|
|
48
|
-
}
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
|
|
5
|
+
import { config, getFolder, pgClients } from "@opengis/fastify-table/utils.js";
|
|
6
|
+
|
|
7
|
+
const rootDir = getFolder(config, 'local');
|
|
8
|
+
|
|
9
|
+
export default async function downloadMedia({
|
|
10
|
+
pg = pgClients.client, params = {}, query = {},
|
|
11
|
+
}, reply) {
|
|
12
|
+
if (!params?.id) {
|
|
13
|
+
return reply.status(400).send('not enough params: id');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!pg.pk?.['site.media']) {
|
|
17
|
+
return reply.status(404).send('table not found');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { filename, mime, id, url: relpath } = await pg.query(
|
|
21
|
+
'select media_id as id, filename, mime, url from site.media where media_id = $1 and url is not null',
|
|
22
|
+
[params.id],
|
|
23
|
+
).then(el => el.rows?.[0] || {});
|
|
24
|
+
|
|
25
|
+
if (!id) {
|
|
26
|
+
return reply.status(404).send('media not found: ' + params.id);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const filepath = path.join(rootDir, relpath);
|
|
30
|
+
|
|
31
|
+
if (!existsSync(filepath)) {
|
|
32
|
+
return reply.status(404).send('file not found');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const buffer = await readFile(filepath, { buffer: true });
|
|
36
|
+
|
|
37
|
+
// skip xml load for preview
|
|
38
|
+
if (query.preview && path.extname(filename) !== '.xml') {
|
|
39
|
+
return reply.headers({ 'Content-Type': mime }).send(buffer);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return reply
|
|
43
|
+
.headers({
|
|
44
|
+
'Content-Type': mime,
|
|
45
|
+
'Content-Disposition': `attachment; filename=${filename || path.basename(filepath)}`,
|
|
46
|
+
})
|
|
47
|
+
.send(buffer);
|
|
48
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { config, pgClients, getData, getMeta } from '@opengis/fastify-table/utils.js';
|
|
2
|
+
|
|
3
|
+
import builderCache from '../../contentType/utils/builderCache.js';
|
|
4
|
+
|
|
5
|
+
const maxLimit = 100;
|
|
6
|
+
|
|
7
|
+
const matches = {
|
|
8
|
+
16: 'yes_no', // boolean
|
|
9
|
+
701: 'number', // double precision
|
|
10
|
+
1082: 'date', // date
|
|
11
|
+
1184: 'date', // timestamp w/ time zone
|
|
12
|
+
1114: 'date', // timestamp w/o time zone
|
|
13
|
+
1700: 'number', // numeric
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default async function getContent(req, reply) {
|
|
17
|
+
const { pg = pgClients.client, params = {}, query = {}, user = {} } = req;
|
|
18
|
+
const { type, id } = params;
|
|
19
|
+
const { filter, state, limit = 16, page, search, order, sql } = query;
|
|
20
|
+
|
|
21
|
+
if (!type) {
|
|
22
|
+
return reply.status(400).send('not enough params: type');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (type === 'single' && id) {
|
|
26
|
+
const cache = builderCache(id) || {};
|
|
27
|
+
const row = await pg.query('select json_object_agg(key, value) from site.single_type_values where type_name=$1', [id])
|
|
28
|
+
.then(el => el.rows?.[0]?.json_object_agg);
|
|
29
|
+
if (row) Object.assign(row, { id });
|
|
30
|
+
return {
|
|
31
|
+
pk: 'id',
|
|
32
|
+
title: cache?.title || id,
|
|
33
|
+
id,
|
|
34
|
+
columns: Object.entries(cache?.columns || {}).map(el => ({ name: el[0], ...el[1] })) || [],
|
|
35
|
+
rows: [row].filter(Boolean),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const builderData = builderCache(type);
|
|
40
|
+
|
|
41
|
+
if (builderData?.table && builderData?.provider === 'pg' && config.pg) {
|
|
42
|
+
const { columns = [] } = await getMeta({ pg, table: 'site.' + builderData.table });
|
|
43
|
+
const res = await getData({
|
|
44
|
+
pg,
|
|
45
|
+
id,
|
|
46
|
+
table: 'site.' + builderData.table,
|
|
47
|
+
filter,
|
|
48
|
+
state,
|
|
49
|
+
limit: Math.min(limit, maxLimit),
|
|
50
|
+
page,
|
|
51
|
+
search,
|
|
52
|
+
order,
|
|
53
|
+
sql,
|
|
54
|
+
user,
|
|
55
|
+
}, reply, 1);
|
|
56
|
+
|
|
57
|
+
if (columns?.length) {
|
|
58
|
+
columns.forEach(el => {
|
|
59
|
+
el.type = matches[el.dataTypeID] || 'text';
|
|
60
|
+
el.hidden = !Object.keys((builderCache(type))?.columns || {}).includes(el.name);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { ...res || {}, columns: columns.filter(el => !el.hidden).map(({ name, title, type }) => ({ name, title, type })) };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const arr = config.pg ? await pg.query(`select array_agg(relname)::text[] from pg_class a
|
|
68
|
+
left join pg_namespace b on a.relnamespace=b.oid
|
|
69
|
+
where a.relam=2 and b.nspname='site'`).then(el => el.rows?.[0]?.array_agg || []) : [];
|
|
70
|
+
|
|
71
|
+
if (!arr.length) {
|
|
72
|
+
return reply.status(400).send('empty schema: site');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const table = arr.find(el => el === params.type);
|
|
76
|
+
|
|
77
|
+
if (!table) {
|
|
78
|
+
return reply.status(400).send('invalid params: type');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const res = await getData({
|
|
82
|
+
pg,
|
|
83
|
+
id,
|
|
84
|
+
table: 'site.' + table,
|
|
85
|
+
filter,
|
|
86
|
+
state,
|
|
87
|
+
limit: Math.min(limit, maxLimit),
|
|
88
|
+
page,
|
|
89
|
+
search,
|
|
90
|
+
order,
|
|
91
|
+
sql,
|
|
92
|
+
user,
|
|
93
|
+
}, reply, 1);
|
|
94
|
+
|
|
95
|
+
return res;
|
|
96
|
+
}
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { config, pgClients, dataInsert } from '@opengis/fastify-table/utils.js';
|
|
2
|
+
|
|
3
|
+
import builderCache from '../../contentType/utils/builderCache.js';
|
|
4
|
+
|
|
5
|
+
export default async function insertContent(req, reply) {
|
|
6
|
+
const {
|
|
7
|
+
pg = pgClients.client,
|
|
8
|
+
params = {},
|
|
9
|
+
user = {},
|
|
10
|
+
body = {},
|
|
11
|
+
headers = {},
|
|
12
|
+
} = req;
|
|
13
|
+
|
|
14
|
+
const { type, id = body?.id } = params;
|
|
15
|
+
|
|
16
|
+
if (!type) {
|
|
17
|
+
return reply.status(400).send('not enough params: type');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (builderCache[type]?.table && builderCache[type]?.provider === 'pg' && config.pg) {
|
|
21
|
+
const { rows = [] } = await dataInsert({
|
|
22
|
+
pg,
|
|
23
|
+
id,
|
|
24
|
+
table: 'site.' + builderCache[type]?.table,
|
|
25
|
+
data: body,
|
|
26
|
+
referer: headers?.referer,
|
|
27
|
+
uid: user?.uid || 0,
|
|
28
|
+
}).catch(err => {
|
|
29
|
+
if (err.message?.includes?.('unique constraint')) {
|
|
30
|
+
return reply.status(400).send('Порушенні унікальності: ' + err.message?.match?.(/([^"]+)/g)?.[1]);
|
|
31
|
+
}
|
|
32
|
+
return reply.status(500).send(err.toString());
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const pk = pg.pk?.['site.' + builderCache[type]?.table];
|
|
36
|
+
return reply.status(200).send({ id: rows[0]?.[pk], rows });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const arr = config.pg ? await pg.query(`select array_agg(relname)::text[] from pg_class a
|
|
40
|
+
left join pg_namespace b on a.relnamespace=b.oid
|
|
41
|
+
where a.relam=2 and b.nspname='site'`).then(el => el.rows?.[0]?.array_agg || []) : [];
|
|
42
|
+
|
|
43
|
+
if (!arr.length) {
|
|
44
|
+
return reply.status(400).send('empty schema: site');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const table = arr.find(el => el === params.type);
|
|
48
|
+
|
|
49
|
+
if (!table) {
|
|
50
|
+
return reply.status(400).send('invalid params: type');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const { rows = [] } = await dataInsert({
|
|
54
|
+
pg,
|
|
55
|
+
id,
|
|
56
|
+
table: 'site.' + table,
|
|
57
|
+
data: body,
|
|
58
|
+
referer: headers?.referer,
|
|
59
|
+
uid: user?.uid || 0,
|
|
60
|
+
}).catch(err => {
|
|
61
|
+
if (err.message?.includes?.('unique constraint')) {
|
|
62
|
+
return reply.status(400).send('Порушенні унікальності: ' + err.message?.match?.(/([^"]+)/g)?.[1]);
|
|
63
|
+
}
|
|
64
|
+
return reply.status(500).send(err.toString());
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const pk = pg.pk?.['site.' + table];
|
|
68
|
+
return reply.status(200).send({ id: rows[0]?.[pk], rows });
|
|
69
|
+
}
|