@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.
Files changed (96) hide show
  1. package/dist/assets/ArticlesPage-BveM4q3g.js +11 -0
  2. package/dist/assets/CollectionsPage-D5td-UBm.js +1 -0
  3. package/dist/assets/ContentBlock.vue_vue_type_script_setup_true_lang-BwF6D-yB.js +30 -0
  4. package/dist/assets/CreateCollectionPage-Cu0RW5ui.js +76 -0
  5. package/dist/assets/Dashboard-faSjwmB8.js +11 -0
  6. package/dist/assets/EditCollectionPage-K5oPPzCd.js +1 -0
  7. package/dist/assets/MediaPage-BoW3aWgN.js +1 -0
  8. package/dist/assets/PermissionsPage-DGy5fha2.js +1 -0
  9. package/dist/assets/SingletonsPage-C1X2xkQE.js +1 -0
  10. package/dist/assets/UniversalTable.vue_vue_type_script_setup_true_lang-DUqfWJcy.js +6 -0
  11. package/dist/assets/calendar-hsWc4yH-.js +6 -0
  12. package/dist/assets/contentForm-DMVC4vho.js +1 -0
  13. package/dist/assets/database-BTxZQzYy.js +6 -0
  14. package/dist/assets/index-9GY17iSP.css +1 -0
  15. package/dist/assets/index-DYyZmLWO.js +2138 -0
  16. package/dist/assets/index-xsH4HHeE.js +6 -0
  17. package/dist/assets/logo-Cct5WB26.png +0 -0
  18. package/dist/assets/plus-D9etvrM2.js +6 -0
  19. package/dist/assets/save-C2B6th9J.js +11 -0
  20. package/dist/assets/search-BI-hqhq6.js +6 -0
  21. package/dist/assets/settings-DbyDiH2g.js +6 -0
  22. package/dist/assets/square-pen-61CkyXzK.js +6 -0
  23. package/dist/assets/trash-2-CJSl_r88.js +6 -0
  24. package/dist/assets/vue.-sixQ7xP-DwXf3zRn.js +1 -0
  25. package/dist/assets/x-BNquQe5y.js +6 -0
  26. package/dist/assets/x-circle-C3q70RMH.js +16 -0
  27. package/dist/images/logo.png +0 -0
  28. package/dist/index.html +30 -0
  29. package/package.json +38 -32
  30. package/server/app.js +32 -10
  31. package/server/index.js +3 -3
  32. package/server/migrations/site.sql +26 -8
  33. package/server/plugins/adminHook.js +78 -0
  34. package/server/plugins/hook.js +54 -78
  35. package/server/plugins/vite.js +13 -9
  36. package/server/routes/cms/controllers/deleteContent.js +60 -0
  37. package/server/routes/{site → cms}/controllers/deleteMedia.js +46 -46
  38. package/server/routes/{site → cms}/controllers/downloadMedia.js +48 -48
  39. package/server/routes/cms/controllers/getContent.js +96 -0
  40. package/server/routes/{site → cms}/controllers/getPermissions.js +15 -15
  41. package/server/routes/cms/controllers/insertContent.js +69 -0
  42. package/server/routes/{site → cms}/controllers/listMedia.js +72 -72
  43. package/server/routes/{site → cms}/controllers/metadataMedia.js +37 -37
  44. package/server/routes/{site → cms}/controllers/setPermissions.js +49 -49
  45. package/server/routes/cms/controllers/updateContent.js +112 -0
  46. package/server/routes/{site → cms}/controllers/uploadMedia.js +65 -65
  47. package/server/routes/cms/index.mjs +45 -0
  48. package/server/routes/contentType/controllers/cms.type.delete.js +22 -0
  49. package/server/routes/contentType/controllers/cms.type.get.js +22 -0
  50. package/server/routes/contentType/controllers/cms.type.list.js +25 -0
  51. package/server/routes/contentType/controllers/cms.type.post.js +22 -0
  52. package/server/routes/contentType/controllers/cms.type.put.js +24 -0
  53. package/server/routes/contentType/index.mjs +25 -0
  54. package/server/routes/contentType/utils/builderCache.js +58 -0
  55. package/server/routes/fileContent/data/deleteContent.js +34 -0
  56. package/server/routes/fileContent/data/deleteMedia.js +28 -0
  57. package/server/routes/fileContent/data/downloadMedia.js +41 -0
  58. package/server/routes/fileContent/data/getContent.js +32 -0
  59. package/server/routes/fileContent/data/insertContent.js +37 -0
  60. package/server/routes/fileContent/data/listMedia.js +47 -0
  61. package/server/routes/fileContent/data/metadataMedia.js +38 -0
  62. package/server/routes/fileContent/data/updateContent.js +40 -0
  63. package/server/routes/fileContent/data/uploadMedia.js +49 -0
  64. package/server/routes/fileContent/index.mjs +54 -0
  65. package/server/routes/fileContent/type/contentTypeList.js +7 -0
  66. package/server/routes/fileContent/type/createContentType.js +31 -0
  67. package/server/routes/fileContent/type/deleteContentType.js +29 -0
  68. package/server/routes/fileContent/type/getContentType.js +15 -0
  69. package/server/routes/fileContent/type/updateContentType.js +40 -0
  70. package/server/routes/fileContent/utils/astroBuilderCache.js +47 -0
  71. package/server/routes/fileContent/utils/contentDir.js +12 -0
  72. package/server/routes/fileContent/utils/contentTypeExists.js +15 -0
  73. package/server/routes/root.mjs +0 -7
  74. package/dist/cms.js +0 -6727
  75. package/dist/cms.umd.cjs +0 -19
  76. package/dist/style.css +0 -1
  77. package/server/routes/builder/controllers/cms.builder.delete.js +0 -21
  78. package/server/routes/builder/controllers/cms.builder.get.js +0 -17
  79. package/server/routes/builder/controllers/cms.builder.list.js +0 -16
  80. package/server/routes/builder/controllers/cms.builder.post.js +0 -21
  81. package/server/routes/builder/controllers/cms.builder.put.js +0 -23
  82. package/server/routes/builder/index.mjs +0 -22
  83. package/server/routes/manager/controllers/cms.manager.delete.js +0 -22
  84. package/server/routes/manager/controllers/cms.manager.get.js +0 -21
  85. package/server/routes/manager/controllers/cms.manager.list.js +0 -31
  86. package/server/routes/manager/controllers/cms.manager.post.js +0 -28
  87. package/server/routes/manager/controllers/cms.manager.put.js +0 -23
  88. package/server/routes/manager/index.mjs +0 -22
  89. package/server/routes/media/controllers/delete.js +0 -59
  90. package/server/routes/media/controllers/edit.js +0 -94
  91. package/server/routes/media/controllers/list.js +0 -74
  92. package/server/routes/media/controllers/metadata.js +0 -51
  93. package/server/routes/media/controllers/preview.js +0 -47
  94. package/server/routes/media/controllers/upload.js +0 -79
  95. package/server/routes/media/index.mjs +0 -16
  96. package/server/routes/site/index.mjs +0 -34
@@ -0,0 +1,25 @@
1
+ import { config, pgClients, getMeta } from '@opengis/fastify-table/utils.js';
2
+
3
+ import builderCache from '../utils/builderCache.js';
4
+
5
+ export default async function builderList({ pg = pgClients.client, query = {} }, reply) {
6
+
7
+ const { pk } = await getMeta({ pg, table: 'site.content_types' });
8
+
9
+ if (config.cms?.provider === 'pg') {
10
+ const cache = builderCache();
11
+ return reply.status(200).send({ total: cache?.length, rows: cache });
12
+ }
13
+
14
+ if (!pk) {
15
+ return reply.status(404).send('table not found');
16
+ }
17
+
18
+ const q = `select a.content_type_id as id, a.name, a.title, a.description, a.table_name as table FROM site.content_types a
19
+ where ${query.content_type_id ? 'content_type_id=$1' : '1=1'}`
20
+
21
+ const { rows = [] } = pk ? await pg.query(q, [query.content_type_id].filter((el) => el)) : {};
22
+ rows.forEach(row => Object.assign(row, { provider: 'pg' }));
23
+
24
+ return reply.status(200).send({ total: rows.length, rows });
25
+ }
@@ -0,0 +1,22 @@
1
+ import { dataInsert, getMeta, pgClients } from "@opengis/fastify-table/utils.js";
2
+
3
+ export default async function builderPost(req, reply) {
4
+ const { pg = pgClients.client, user = {}, body = {} } = req;
5
+
6
+ const { pk } = await getMeta({ pg, table: 'site.content_types' });
7
+
8
+ if (!pk) {
9
+ return reply.status(404).send('table not found');
10
+ }
11
+
12
+ const resData = await dataInsert({
13
+ pg,
14
+ table: "site.content_types",
15
+ data: body,
16
+ uid: user?.uid,
17
+ });
18
+ // console.log(resData);
19
+ const res = resData?.rows?.[0];
20
+
21
+ return { id: res?.content_type_id, rows: [res].filter(Boolean) };
22
+ }
@@ -0,0 +1,24 @@
1
+ import { dataUpdate, getMeta, pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function builderPut(req, reply) {
4
+ const { pg = pgClients.client, body = {}, params = {} } = req;
5
+ if (!params.id) {
6
+ return reply.status(400).send('not enough params: id');
7
+ }
8
+ const { pk } = await getMeta({ pg, table: 'site.content_types' });
9
+
10
+ if (!pk) {
11
+ return reply.status(404).send('table not found');
12
+ }
13
+
14
+ const resData = await dataUpdate({
15
+ pg,
16
+ table: 'site.content_types',
17
+ data: body,
18
+ id: params.id,
19
+ });
20
+ // console.log(resData)
21
+ const res = resData;
22
+
23
+ return { id: res.content_type_id, rows: [res].filter(Boolean) };
24
+ }
@@ -0,0 +1,25 @@
1
+ import cmsTypeGet from './controllers/cms.type.get.js';
2
+ import cmsTypeList from './controllers/cms.type.list.js';
3
+ import cmsTypePut from './controllers/cms.type.put.js';
4
+ import cmsTypePost from './controllers/cms.type.post.js';
5
+ import cmsTypeDelete from './controllers/cms.type.delete.js';
6
+
7
+ const policy = ['public'];
8
+
9
+ export default async function route(app) {
10
+ // Type add
11
+ app.post('/cms-type', { config: { policy } }, cmsTypePost);
12
+
13
+ // Type edit
14
+ app.put('/cms-type/:id', { config: { policy } }, cmsTypePut);
15
+
16
+ // Type list
17
+ app.get('/cms', { config: { policy } }, cmsTypeList);
18
+ app.get('/cms-type', { config: { policy } }, cmsTypeList);
19
+
20
+ // Type get
21
+ app.get('/cms-type/:id', { config: { policy } }, cmsTypeGet);
22
+
23
+ // Type delete
24
+ app.delete('/cms-type/:id', { config: { policy } }, cmsTypeDelete);
25
+ }
@@ -0,0 +1,58 @@
1
+ import path from 'node:path';
2
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
3
+
4
+ import { subscribe } from '@parcel/watcher';
5
+
6
+ import { config, yml2json } from '@opengis/fastify-table/utils.js';
7
+
8
+ function parseYml(filepath) {
9
+ const name = path.basename(filepath, '.yml');
10
+
11
+ const yml = readFileSync(filepath, 'utf8');
12
+ const json = yml2json(yml);
13
+
14
+ return {
15
+ [name]: {
16
+ ...json,
17
+ name,
18
+ id: json?.type === 'single' ? name : undefined,
19
+ table: json?.type === 'single' ? undefined : name,
20
+ provider: json.provider || 'pg',
21
+ slug: json?.type === 'single' ? name : (json.slug || 'slug')
22
+ }
23
+ };
24
+ }
25
+
26
+ const content = config.cms?.root && existsSync(config.cms?.root)
27
+ ? readdirSync(config.cms?.root, { withFileTypes: true })
28
+ : [];
29
+
30
+ const files = content.filter(el => el.isFile() && path.extname(el.name) === '.yml');
31
+
32
+ const cache = files.reduce((acc, curr) => {
33
+ return { ...acc, ...parseYml(path.join(config.cms.root, curr.name)) };
34
+ }, {});
35
+
36
+ async function watchDirectory() {
37
+ await subscribe(config.cms.root, (err, events) => {
38
+ if (err) throw new Error(err.toString());
39
+
40
+ const { path: filepath, type } = events[0];
41
+ const name = path.basename(filepath, '.yml');
42
+ if (path.extname(filepath) === '.yml') {
43
+ if (type !== 'delete') { cache[name] = parseYml(filepath); }
44
+ if (type === 'delete') { delete cache[name]; }
45
+ }
46
+ });
47
+ }
48
+
49
+ if (config.cms?.root && config.local) {
50
+ watchDirectory();
51
+ }
52
+
53
+ export default function builderCache(name) {
54
+ if (name) {
55
+ return cache[name];
56
+ }
57
+ return Object.values(cache || {});
58
+ }
@@ -0,0 +1,34 @@
1
+ import path from 'node:path';
2
+ import { existsSync, rmSync } from 'node:fs';
3
+
4
+ import contentDir from '../utils/contentDir.js';
5
+ import contentTypeExists from '../utils/contentTypeExists.js';
6
+
7
+ export default async function deleteContent(req, reply) {
8
+ const { params = {} } = req;
9
+ const { type, id } = params;
10
+
11
+ if (!type) {
12
+ return reply.status(400).send('not enough params: type');
13
+ }
14
+
15
+ if (!id) {
16
+ return reply.status(400).send('not enough params: id');
17
+ }
18
+
19
+ if (!contentTypeExists(type)) {
20
+ return reply.status(400).send('invalid params: type');
21
+ }
22
+
23
+
24
+ const filepath = [path.join(contentDir, type, `${id}.mdoc`), path.join(contentDir, type, `${id}.md`)]
25
+ .find(el => existsSync(el));
26
+
27
+ if (!filepath) {
28
+ return reply.status(404).send('file not found');
29
+ }
30
+
31
+ rmSync(filepath, { recursive: true });
32
+
33
+ return reply.status(200).send({ id, type, filepath });
34
+ }
@@ -0,0 +1,28 @@
1
+ import path from 'node:path';
2
+ import { existsSync, rmSync } from 'node:fs';
3
+
4
+ import { config, getFolder } from "@opengis/fastify-table/utils.js";
5
+
6
+ const rootDir = getFolder(config, 'local');
7
+
8
+ export default async function deleteMedia({
9
+ params = {},
10
+ }, reply) {
11
+ if (!params['*']) {
12
+ return reply.status(400).send('not enough params');
13
+ }
14
+
15
+ if (typeof params['*'] !== 'string' || params['*'].includes('..')) {
16
+ return reply.status(403).send('invalid params');
17
+ }
18
+
19
+ const filepath = path.join(rootDir, params['*']);
20
+
21
+ if (!existsSync(filepath)) {
22
+ return reply.status(404).send('file not found');
23
+ }
24
+
25
+ rmSync(filepath, { recursive: true });
26
+
27
+ return { filepath: params['*'] };
28
+ }
@@ -0,0 +1,41 @@
1
+ import path from 'node:path';
2
+
3
+ import { config, getFolder } from "@opengis/fastify-table/utils.js";
4
+ import { downloadFile, isFileExists, getMimeType } from '@opengis/fastify-file/utils.js';
5
+
6
+ const rootDir = getFolder(config, 'local');
7
+
8
+ export default async function downloadMedia({
9
+ params = {}, query = {},
10
+ }, reply) {
11
+ if (!params['*']) {
12
+ return reply.status(400).send('not enough params: id');
13
+ }
14
+
15
+ if (typeof params['*'] !== 'string' || params['*'].includes('..')) {
16
+ return reply.status(403).send('invalid params');
17
+ }
18
+
19
+ const filepath = path.join(rootDir, params['*']);
20
+
21
+ const exists = await isFileExists(filepath);
22
+
23
+ if (!exists) {
24
+ return reply.status(404).send('file not found');
25
+ }
26
+
27
+ const buffer = await downloadFile(filepath, { buffer: true });
28
+ const mime = getMimeType(filepath);
29
+
30
+ // skip xml load for preview
31
+ if (query.preview && path.extname(filepath) !== '.xml') {
32
+ return reply.headers({ 'Content-Type': mime }).send(buffer);
33
+ }
34
+
35
+ return reply
36
+ .headers({
37
+ 'Content-Type': mime,
38
+ 'Content-Disposition': `attachment; filename=${path.basename(filepath)}`,
39
+ })
40
+ .send(buffer);
41
+ }
@@ -0,0 +1,32 @@
1
+ import { config } from '@opengis/fastify-table/utils.js';
2
+
3
+ import contentTypeExists from '../utils/contentTypeExists.js';
4
+ import { contentTypes } from '../utils/astroBuilderCache.js';
5
+
6
+ export default async function getContent(req, reply) {
7
+ const { params = {} } = req;
8
+ const { type, id } = params;
9
+
10
+ if (!type) {
11
+ return reply.status(400).send('not enough params: type');
12
+ }
13
+
14
+ const metadata = contentTypeExists(type, 'metadata.json');
15
+
16
+ if (!metadata) {
17
+ return reply.status(400).send('invalid params: type');
18
+ }
19
+
20
+ if (!id) {
21
+ const rows = Object.values(contentTypes || {});
22
+ return { metadata, rows };
23
+ }
24
+
25
+ const data = contentTypeExists(type, id);
26
+
27
+ if (!data) {
28
+ return reply.status(404).send('file not found');
29
+ }
30
+
31
+ return { metadata, data };
32
+ }
@@ -0,0 +1,37 @@
1
+ import path from 'node:path';
2
+ import { writeFileSync } from 'node:fs';
3
+
4
+ import contentDir from '../utils/contentDir.js';
5
+ import contentTypeExists from '../utils/contentTypeExists.js';
6
+
7
+ const extension = 'mdoc'; // extract from collection?
8
+
9
+ export default async function insertContent(req, reply) {
10
+ const { params = {}, body = {} } = req;
11
+ const { slug, content } = body;
12
+
13
+ if (!params.type) {
14
+ return reply.status(400).send('not enough params: type');
15
+ }
16
+
17
+ if (!slug) {
18
+ return reply.status(400).send('not enough body params: slug');
19
+ }
20
+
21
+ if (!content) {
22
+ return reply.status(400).send('not enough body params: content');
23
+ }
24
+
25
+ if (typeof content !== 'string') {
26
+ return reply.status(400).send('invalid params: content');
27
+ }
28
+
29
+ if (!contentTypeExists(params.type)) {
30
+ return reply.status(400).send('invalid params: type');
31
+ }
32
+
33
+ const filepath = path.join(contentDir, params.type, `${slug}.${extension}`);
34
+ writeFileSync(filepath, content, { recursive: true });
35
+
36
+ return reply.status(200).send({ id: slug, type: params.type, filepath });
37
+ }
@@ -0,0 +1,47 @@
1
+ import path from 'node:path';
2
+ import { existsSync, mkdirSync } from 'node:fs';
3
+ import { readdir, stat } from 'node:fs/promises';
4
+
5
+ import { config, getFolder } from '@opengis/fastify-table/utils.js';
6
+
7
+ const rootDir = getFolder(config, 'local');
8
+ mkdirSync(path.join(rootDir, 'media'), { recursive: true });
9
+
10
+ export default async function listMedia(req, reply) {
11
+ const { query = {} } = req;
12
+ const { subdir = '' } = query;
13
+
14
+ if (typeof subdir !== 'string' || subdir.includes('..')) {
15
+ return reply.status(403).send('invalid params: subdir');
16
+ }
17
+
18
+ const relpath = path.join('/media', subdir).replace(/\\/g, '/');
19
+ const dirpath = path.join(rootDir, relpath).replace(/\\/g, '/');
20
+
21
+ if (!existsSync(dirpath)) {
22
+ return { data: [], relpath, msg: 'directory not exists' };
23
+ }
24
+
25
+ const isDirectory = (await stat(dirpath)).isDirectory();
26
+
27
+ const items = isDirectory ? await readdir(dirpath, { withFileTypes: true }) : [];
28
+
29
+ const content = items.map(el => ({ type: el.isDirectory() ? 'dir' : 'file', name: el.name }));
30
+
31
+ await Promise.all(content.filter(el => el.type === 'dir').map(async (item) => {
32
+ const items = await readdir(path.join(dirpath, item.name));
33
+ Object.assign(item, { count: items.length, exists: true });
34
+ }));
35
+
36
+ content.filter(el => el.type === 'file').forEach(item => {
37
+ item.exists = true;
38
+ });
39
+
40
+ const result = { relpath, data: content };
41
+
42
+ if (config.debug) {
43
+ Object.assign(result, { rootDir });
44
+ }
45
+
46
+ return result;
47
+ }
@@ -0,0 +1,38 @@
1
+ import path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+
4
+ import { config, getFolder } from "@opengis/fastify-table/utils.js";
5
+ import { stat } from 'node:fs/promises';
6
+
7
+ import downloadMedia from './downloadMedia.js';
8
+
9
+ const rootDir = getFolder(config, 'local');
10
+
11
+ export default async function metadataMedia(req, reply) {
12
+ if (!req.query?.metadata) return downloadMedia(req, reply);
13
+
14
+ const { params = {}, } = req;
15
+
16
+ if (!params['*']) {
17
+ return reply.status(400).send('not enough params: id');
18
+ }
19
+
20
+ if (typeof params['*'] !== 'string' || params['*'].includes('..')) {
21
+ return reply.status(403).send('invalid params: subdir');
22
+ }
23
+
24
+ const filepath = path.join(rootDir, params['*']);
25
+
26
+ const stats = existsSync(filepath) ? await stat(filepath) : null;
27
+
28
+ if (!stats) {
29
+ return reply.status(404).send('file not found');
30
+ }
31
+
32
+ return reply.status(200).send({
33
+ ...stats,
34
+ url: `${req.routeOptions.url}/${params['*']}?download=1`.replace(/\*/g, '').replace(/\/\//g, ''),
35
+ filepath: `${req.routeOptions.url}/${params['*']}`.replace(/\*/g, '').replace(/\/\//g, ''),
36
+ exists: true,
37
+ });
38
+ }
@@ -0,0 +1,40 @@
1
+ import path from 'node:path';
2
+ import { existsSync, rmSync, writeFileSync } from 'node:fs';
3
+
4
+ import contentDir from '../utils/contentDir.js';
5
+ import contentTypeExists from '../utils/contentTypeExists.js';
6
+
7
+ export default async function updateContent(req, reply) {
8
+ const { params = {}, body = {} } = req;
9
+ const { type, id } = params;
10
+ const { content } = body;
11
+
12
+ if (!type) {
13
+ return reply.status(400).send('not enough params: type');
14
+ }
15
+
16
+ if (!id) {
17
+ return reply.status(400).send('not enough params: id');
18
+ }
19
+
20
+ if (!content) {
21
+ return reply.status(400).send('not enough body params: content');
22
+ }
23
+
24
+ if (!contentTypeExists(type)) {
25
+ return reply.status(400).send('invalid params: type');
26
+ }
27
+
28
+
29
+ const filepath = [path.join(contentDir, type, `${id}.mdoc`), path.join(contentDir, type, `${id}.md`)]
30
+ .find(el => existsSync(el));
31
+
32
+ if (!filepath) {
33
+ return reply.status(404).send('file not found');
34
+ }
35
+
36
+ rmSync(filepath, { recursive: true });
37
+ writeFileSync(filepath, content, { recursive: true });
38
+
39
+ return reply.status(200).send({ id, type, filepath });
40
+ }
@@ -0,0 +1,49 @@
1
+ import path from 'node:path';
2
+ import { mkdir, rename } from 'node:fs/promises';
3
+
4
+ import { config, getFolder } from "@opengis/fastify-table/utils.js";
5
+
6
+ import { getMimeType, getFileType, uploadMultiPart } from '@opengis/fastify-file/utils.js';
7
+
8
+ const rootDir = getFolder(config, 'local');
9
+
10
+ export default async function uploadMedia(req, reply) {
11
+ const { query = {} } = req;
12
+
13
+ if (query.subdir && (typeof query.subdir !== 'string' || query.subdir.includes('..'))) {
14
+ return reply.status(403).send('invalid query params: subdir');
15
+ }
16
+
17
+ // upload assets
18
+ if (req.headers['content-type']?.split?.(';')?.shift?.() === 'multipart/form-data') {
19
+ const file = await uploadMultiPart(req);
20
+ const filename = `${file.newFilename}${path.extname(file.filepath)}`;
21
+ const mime = getMimeType(file.filepath);
22
+ const filetype = getFileType(file.filepath);
23
+
24
+ const newfilepath = path.join(rootDir, 'media', query.subdir || '', filename);
25
+ await mkdir(path.dirname(newfilepath), { recursive: true });
26
+ await rename(file.filepath, newfilepath);
27
+
28
+ const relpath = path.join('/media', query.subdir || '', filename).replace(/\\/g, '/');
29
+
30
+ return reply.status(200).send({
31
+ relpath,
32
+ mime,
33
+ filename,
34
+ filetype,
35
+ type: 'file',
36
+ });
37
+ }
38
+
39
+ // create directory
40
+ const relpath = path.join('/media', query.subdir).replace(/\\/g, '/');
41
+ const dirpath = path.join(rootDir, relpath);
42
+ await mkdir(dirpath, { recursive: true });
43
+
44
+ return reply.status(200).send({
45
+ relpath,
46
+ dirname: path.basename(query.subdir),
47
+ type: 'dir',
48
+ });
49
+ }
@@ -0,0 +1,54 @@
1
+ // type
2
+ import cmsTypeGet from './type/getContentType.js';
3
+ import cmsTypeList from './type/contentTypeList.js';
4
+ import cmsTypePut from './type/updateContentType.js';
5
+ import cmsTypePost from './type/createContentType.js';
6
+ import cmsTypeDelete from './type/deleteContentType.js';
7
+
8
+ // media
9
+ import listMedia from './data/listMedia.js';
10
+ import metadata from './data/metadataMedia.js';
11
+ import uploadMedia from './data/uploadMedia.js';
12
+ import del from './data/deleteMedia.js';
13
+ import download from './data/downloadMedia.js';
14
+
15
+ // content
16
+ import getContent from './data/getContent.js';
17
+ import insertContent from './data/insertContent.js';
18
+ import updateContent from './data/updateContent.js';
19
+ import deleteContent from './data/deleteContent.js';
20
+
21
+ const schemaObj = {
22
+ type: 'object',
23
+ properties: {
24
+ params: {
25
+ type: 'string',
26
+ enum: ['preview', 'download'],
27
+ }
28
+ }
29
+ };
30
+
31
+ const params = { config: { policy: ['site', 'user'] } };
32
+
33
+ export default async function route(app) {
34
+ // content type
35
+ app.post('/cms-type', { config: { policy: ['site'] } }, cmsTypePost);
36
+ app.put('/cms-type/:id', { config: { policy: ['site'] } }, cmsTypePut);
37
+ app.get('/cms-type', { config: { policy: ['site'] } }, cmsTypeList);
38
+ app.get('/cms-type/:id', { config: { policy: ['site'] } }, cmsTypeGet);
39
+ app.delete('/cms-type/:id', { config: { policy: ['site'] } }, cmsTypeDelete);
40
+
41
+ // content media
42
+ app.post('/cms-media/upload', params, uploadMedia);
43
+ app.get('/cms-media', params, listMedia);
44
+ // app.get('/cms-media/:id/file', { ...params, schema: schemaObj }, download);
45
+ app.get('/cms-media/*', params, metadata); // preview + metadata + download via query params
46
+ app.delete('/cms-media/*', params, del);
47
+
48
+ // content data
49
+ app.get('/cms/:type/:id?', params, getContent);
50
+ app.post('/cms/:type/:id?', params, insertContent);
51
+ app.put('/cms/:type/:id', params, updateContent);
52
+ app.delete('/cms/:type/:id', params, deleteContent);
53
+ }
54
+
@@ -0,0 +1,7 @@
1
+ import { contentTypes } from '../utils/astroBuilderCache.js';
2
+
3
+ export default async function builderList({ params = {} }, reply) {
4
+ const rows = Object.values(contentTypes || {}).filter(Boolean);
5
+
6
+ return reply.status(200).send({ total: rows.length, rows });
7
+ }
@@ -0,0 +1,31 @@
1
+ import path from 'node:path';
2
+ import { mkdirSync, writeFileSync } from 'node:fs';
3
+
4
+ import { config } from '@opengis/fastify-table/utils.js';
5
+
6
+ import contentDir from '../utils/contentDir.js';
7
+ import contentTypeExists from '../utils/contentTypeExists.js';
8
+
9
+ export default async function builderPost(req, reply) {
10
+ const { body = {} } = req;
11
+
12
+ if (!body.type) { Object.assign(body, { type: 'collection' }) };
13
+
14
+ if (!body.slug) {
15
+ return reply.status(400).send('not enough body params: slug');
16
+ }
17
+
18
+ if (typeof body.slug !== 'string' || body.slug.includes('..')) {
19
+ return reply.status(409).send('invalid body params: slug');
20
+ }
21
+
22
+ if (contentTypeExists(body.slug)) {
23
+ return reply.status(400).send('type already exists');
24
+ }
25
+
26
+ // support watcher?
27
+ mkdirSync(path.join(contentDir, body.slug), { recursive: true });
28
+ writeFileSync(path.join(contentDir, body.slug, 'metadata.json'), JSON.stringify(body));
29
+
30
+ return { id: body.slug, rows: [body] };
31
+ }
@@ -0,0 +1,29 @@
1
+ import path from 'node:path';
2
+ import { rm } from 'node:fs/promises';
3
+
4
+ import { config } from '@opengis/fastify-table/utils.js';
5
+
6
+ import contentDir from '../utils/contentDir.js';
7
+ import contentTypeExists from '../utils/contentTypeExists.js';
8
+
9
+ export default async function builderDelete({
10
+ params = {},
11
+ }, reply) {
12
+ if (!params.id) {
13
+ return reply.status(400).send('not enough params: id');
14
+ }
15
+
16
+ if (config.cms?.provider !== 'file') {
17
+ return reply.status(400).send('invalid provider');
18
+ }
19
+
20
+ if (!contentTypeExists(params.id)) {
21
+ return reply.status(400).send('nothing to delete');
22
+ }
23
+
24
+ if (params.id) {
25
+ await rm(path.join(contentDir, params.id), { recursive: true });
26
+ }
27
+
28
+ return { id: params.id };
29
+ }
@@ -0,0 +1,15 @@
1
+ import { config } from '@opengis/fastify-table/utils.js';
2
+
3
+ import { contentTypes } from '../utils/astroBuilderCache.js';
4
+
5
+ export default async function builderGet({ params = {} }, reply) {
6
+ if (!params.id) {
7
+ return reply.status(400).send('not enough params: id');
8
+ }
9
+
10
+ if (config.cms?.provider !== 'file') {
11
+ return reply.status(400).send('invalid provider');
12
+ }
13
+
14
+ return reply.status(200).send({ total: contentTypes[params.id] ? 1 : 0, rows: [contentTypes[params.id]].filter(Boolean) });
15
+ }