@opengis/cms 0.0.2 → 0.0.3

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 (144) hide show
  1. package/.gitlab-ci.yml +36 -0
  2. package/config.example +21 -0
  3. package/docs/.vitepress/abbr.mjs +26 -0
  4. package/docs/.vitepress/config.mjs +119 -0
  5. package/docs/.vitepress/navigation.mjs +82 -0
  6. package/docs/.vitepress/theme/Layout.vue +17 -0
  7. package/docs/.vitepress/theme/components/NavigationLinks.vue +102 -0
  8. package/docs/.vitepress/theme/components/Panzoom.vue +169 -0
  9. package/docs/.vitepress/theme/index.mjs +15 -0
  10. package/docs/.vitepress/theme/style.scss +136 -0
  11. package/docs/abbr.json +4 -0
  12. package/docs/api/builder/cms.builder.delete.md +65 -0
  13. package/docs/api/builder/cms.builder.get.md +70 -0
  14. package/docs/api/builder/cms.builder.list.md +98 -0
  15. package/docs/api/builder/cms.builder.post.md +72 -0
  16. package/docs/api/builder/cms.builder.put.md +88 -0
  17. package/docs/api/category/cms.category.delete.md +60 -0
  18. package/docs/api/category/cms.category.get.md +61 -0
  19. package/docs/api/category/cms.category.list.md +77 -0
  20. package/docs/api/category/cms.category.post.md +62 -0
  21. package/docs/api/category/cms.category.put.md +78 -0
  22. package/docs/api/index.md +50 -0
  23. package/docs/api/manager/cms.manager.delete.md +64 -0
  24. package/docs/api/manager/cms.manager.get.md +72 -0
  25. package/docs/api/manager/cms.manager.list.md +96 -0
  26. package/docs/api/manager/cms.manager.post.md +70 -0
  27. package/docs/api/manager/cms.manager.put.md +86 -0
  28. package/docs/api/media/del.md +64 -0
  29. package/docs/api/media/edit.md +92 -0
  30. package/docs/api/media/list.md +70 -0
  31. package/docs/api/media/metadata.md +57 -0
  32. package/docs/api/media/preview.md +33 -0
  33. package/docs/api/media/upload.md +84 -0
  34. package/docs/db/erd.md +173 -0
  35. package/docs/db/index.md +7 -0
  36. package/docs/index.md +39 -0
  37. package/docs/public/logo-dark.svg +24 -0
  38. package/docs/public/logo-light.svg +24 -0
  39. package/docs/public/logo-short.svg +15 -0
  40. package/docs/public/logo.svg +19 -0
  41. package/docs/readme/index.md +6 -0
  42. package/docs/src/vs-button.vue +157 -0
  43. package/docs/vue/basic/button.md +144 -0
  44. package/docs/vue/index.md +9 -0
  45. package/index.html +14 -0
  46. package/package.json +2 -5
  47. package/server/app.js +25 -0
  48. package/server/config.js +5 -0
  49. package/server/index.js +23 -0
  50. package/server/migrations/media.sql +30 -0
  51. package/server/plugins/hook.js +91 -0
  52. package/server/plugins/vite.js +80 -0
  53. package/server/routes/builder/controllers/cms.builder.delete.js +21 -0
  54. package/server/routes/builder/controllers/cms.builder.get.js +17 -0
  55. package/server/routes/builder/controllers/cms.builder.list.js +16 -0
  56. package/server/routes/builder/controllers/cms.builder.post.js +21 -0
  57. package/server/routes/builder/controllers/cms.builder.put.js +23 -0
  58. package/server/routes/builder/index.mjs +22 -0
  59. package/server/routes/category/controllers/cms.category.delete.js +21 -0
  60. package/server/routes/category/controllers/cms.category.get.js +17 -0
  61. package/server/routes/category/controllers/cms.category.list.js +16 -0
  62. package/server/routes/category/controllers/cms.category.post.js +21 -0
  63. package/server/routes/category/controllers/cms.category.put.js +23 -0
  64. package/server/routes/category/index.mjs +22 -0
  65. package/server/routes/manager/controllers/cms.manager.delete.js +22 -0
  66. package/server/routes/manager/controllers/cms.manager.get.js +21 -0
  67. package/server/routes/manager/controllers/cms.manager.list.js +31 -0
  68. package/server/routes/manager/controllers/cms.manager.post.js +28 -0
  69. package/server/routes/manager/controllers/cms.manager.put.js +23 -0
  70. package/server/routes/manager/index.mjs +22 -0
  71. package/server/routes/media/controllers/delete.js +59 -0
  72. package/server/routes/media/controllers/edit.js +94 -0
  73. package/server/routes/media/controllers/list.js +74 -0
  74. package/server/routes/media/controllers/metadata.js +51 -0
  75. package/server/routes/media/controllers/preview.js +47 -0
  76. package/server/routes/media/controllers/upload.js +79 -0
  77. package/server/routes/media/index.mjs +16 -0
  78. package/server/routes/root.mjs +15 -0
  79. package/server/templates/cls/cms.category_type.json +10 -0
  80. package/server/templates/cls/cms.content_review_status.json +10 -0
  81. package/server/templates/cls/cms.content_status.json +10 -0
  82. package/server/templates/cls/cms.content_type.json +10 -0
  83. package/server/templates/cls/cms.lang.json +10 -0
  84. package/server/templates/page/login.html +59 -0
  85. package/server/templates/select/cms.category_id.sql +1 -0
  86. package/server/templates/select/cms.type_id.sql +1 -0
  87. package/src/App.vue +4 -0
  88. package/src/assets/tailwind/tailwind.js +62 -0
  89. package/src/assets/vue.svg +1 -0
  90. package/src/components/builder/vs-builder-content.vue +163 -0
  91. package/src/components/builder/vs-builder-menu.vue +142 -0
  92. package/src/components/formats/index.js +8 -0
  93. package/src/components/formats/vs-manager-table-date.vue +29 -0
  94. package/src/components/formats/vs-manager-table-switch.vue +16 -0
  95. package/src/components/icons/icon-actions.vue +24 -0
  96. package/src/components/icons/icon-arrow-left.vue +19 -0
  97. package/src/components/icons/icon-check.vue +23 -0
  98. package/src/components/icons/icon-chewron-right.vue +16 -0
  99. package/src/components/icons/icon-close.vue +22 -0
  100. package/src/components/icons/icon-edit.vue +22 -0
  101. package/src/components/icons/icon-folder.vue +18 -0
  102. package/src/components/icons/icon-folder2.vue +17 -0
  103. package/src/components/icons/icon-home.vue +16 -0
  104. package/src/components/icons/icon-image.vue +18 -0
  105. package/src/components/icons/icon-logo.vue +22 -0
  106. package/src/components/icons/icon-media.vue +22 -0
  107. package/src/components/icons/icon-point.vue +11 -0
  108. package/src/components/icons/icon-search.vue +22 -0
  109. package/src/components/icons/icon-table.vue +22 -0
  110. package/src/components/icons/icon-users.vue +18 -0
  111. package/src/components/icons/icon.plus.vue +18 -0
  112. package/src/components/manager/children/vs-manager-collection-content.vue +55 -0
  113. package/src/components/manager/children/vs-manager-collection-item-content.vue +116 -0
  114. package/src/components/manager/children/vs-manager-single-content.vue +112 -0
  115. package/src/components/manager/manager-table/vs-manager-colection-table-add.vue +84 -0
  116. package/src/components/manager/manager-table/vs-manager-collection-table.vue +59 -0
  117. package/src/components/manager/vs-manager-menu.vue +73 -0
  118. package/src/components/media/Breadcrumb.vue +73 -0
  119. package/src/components/shared-components/vs-not-data.vue +213 -0
  120. package/src/components/vs-main-menu.vue +53 -0
  121. package/src/helpers/debounce.js +10 -0
  122. package/src/helpers/translite.js +19 -0
  123. package/src/main.js +30 -0
  124. package/src/misc/import-file.js +32 -0
  125. package/src/pages/vs-builder.vue +22 -0
  126. package/src/pages/vs-layout.vue +17 -0
  127. package/src/pages/vs-manager.vue +30 -0
  128. package/src/pages/vs-media.vue +398 -0
  129. package/src/router/router.js +9 -0
  130. package/src/router/routes.config.js +40 -0
  131. package/src/style.css +0 -0
  132. package/src/templates/form-columns.js +70 -0
  133. package/src/templates/form-template.js +22 -0
  134. package/test/config.js +17 -0
  135. package/test/files/eye.svg +4 -0
  136. package/test/helper.js +30 -0
  137. package/test/routes/builder.test.js +99 -0
  138. package/test/routes/category.test.js +97 -0
  139. package/test/routes/manager.test.js +103 -0
  140. package/test/routes/media.test.js +252 -0
  141. package/vite.config.js +37 -0
  142. package/editor/dist/cms.js +0 -5900
  143. package/editor/dist/cms.umd.cjs +0 -19
  144. /package/{editor/dist → public}/vite.svg +0 -0
@@ -0,0 +1,99 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert';
3
+
4
+ import { config, pgClients } from '@opengis/fastify-table/utils.js';
5
+ import build from '../helper.js';
6
+
7
+ test('cms builder api', async (t) => {
8
+ const app = await build(t);
9
+
10
+ const pg = pgClients.client;
11
+
12
+ const prefix = config.prefix || '/api';
13
+
14
+ const testContentTypeId = 'testContentType';
15
+
16
+ app.addHook('onRequest', async (req) => {
17
+ req.session = { passport: { user: { user_type: 'admin', uid: '1' } } };
18
+ req.user = req.session.passport.user;
19
+ req.uid = req.user.uid;
20
+ req.unittest = 1;
21
+ });
22
+
23
+ await t.test('POST /cms-builder', async () => {
24
+ const res = await app.inject({
25
+ method: 'POST',
26
+ url: `${prefix}/cms-builder`,
27
+ body: {
28
+ content_type_id: testContentTypeId,
29
+ name: 'Created value',
30
+ template: 'news'
31
+ },
32
+ });
33
+ assert.equal(res.statusCode, 200);
34
+ const rep = res.json();
35
+ // console.log(rep);
36
+ });
37
+
38
+ await t.test('PUT /cms-builder', async () => {
39
+ // await pg.query('insert into site.content_types (content_type_id, uid) values ($1, $2)', [testContentTypeId, testUid]);
40
+ const res = await app.inject({
41
+ method: 'PUT',
42
+ url: `${prefix}/cms-builder/${testContentTypeId}`,
43
+ body: {
44
+ name: 'Edited value'
45
+ },
46
+ });
47
+ assert.equal(res.statusCode, 200);
48
+ const rep = res.json();
49
+ // console.log(rep);
50
+ });
51
+
52
+ await t.test('GET /cms-builder/:id', async () => {
53
+ // await pg.query('insert into site.contents (content_id, uid) values ($1, $2)', [testContentId, testUid]);
54
+ const res = await app.inject({
55
+ method: 'GET',
56
+ url: `${prefix}/cms-builder/${testContentTypeId}`,
57
+ });
58
+ assert.equal(res.statusCode, 200);
59
+ const rep = res.json();
60
+ // console.log(rep);
61
+ });
62
+
63
+ await t.test('GET /cms-builder', async () => {
64
+ // await pg.query('insert into site.contents (content_id, uid) values ($1, $2)', [testContentId, testUid]);
65
+ const res = await app.inject({
66
+ method: 'GET',
67
+ url: `${prefix}/cms-builder`,
68
+ });
69
+ assert.equal(res.statusCode, 200);
70
+ const rep = res.json();
71
+ // console.log(rep);
72
+ });
73
+
74
+ await t.test('GET /cms-builder?id', async () => {
75
+ // await pg.query('insert into site.contents (content_id, uid) values ($1, $2)', [testContentId, testUid]);
76
+ const res = await app.inject({
77
+ method: 'GET',
78
+ url: `${prefix}/cms-builder?content_type_id=${testContentTypeId}`,
79
+ });
80
+ assert.equal(res.statusCode, 200);
81
+ const rep = res.json();
82
+ // console.log(rep);
83
+ });
84
+
85
+ await t.test('DELETE /cms-builder/:id', async () => {
86
+ const res = await app.inject({
87
+ method: 'DELETE',
88
+ url: `${prefix}/cms-builder/${testContentTypeId}`,
89
+ });
90
+ assert.equal(res.statusCode, 200);
91
+ const rep = res.json();
92
+ // console.log(rep);
93
+ });
94
+
95
+ await t.test('clean up', async () => {
96
+ const { rowCount: contentTypesCount } = await pg.query('delete from site.content_types where content_type_id=$1', [testContentTypeId]);
97
+ console.log('clean up', contentTypesCount);
98
+ });
99
+ });
@@ -0,0 +1,97 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert';
3
+
4
+ import { config, pgClients } from '@opengis/fastify-table/utils.js';
5
+ import build from '../helper.js';
6
+
7
+ test('cms category api', async (t) => {
8
+ const app = await build(t);
9
+
10
+ const pg = pgClients.client;
11
+
12
+ const prefix = config.prefix || '/api';
13
+
14
+ const testCategoryId = 'testCategory';
15
+
16
+ app.addHook('onRequest', async (req) => {
17
+ req.session = { passport: { user: { user_type: 'admin', uid: '1' } } };
18
+ req.user = req.session.passport.user;
19
+ req.uid = req.user.uid;
20
+ req.unittest = 1;
21
+ });
22
+
23
+ await t.test('POST /cms-category', async () => {
24
+ const res = await app.inject({
25
+ method: 'POST',
26
+ url: `${prefix}/cms-category`,
27
+ body: {
28
+ category_id: testCategoryId,
29
+ title: 'Created value'
30
+ },
31
+ });
32
+ assert.equal(res.statusCode, 200);
33
+ const rep = res.json();
34
+ // console.log(rep);
35
+ });
36
+
37
+ await t.test('PUT /cms-category', async () => {
38
+ // await pg.query('insert into site.content_types (category_id, uid) values ($1, $2)', [testCategoryId, testUid]);
39
+ const res = await app.inject({
40
+ method: 'PUT',
41
+ url: `${prefix}/cms-category/${testCategoryId}`,
42
+ body: {
43
+ title: 'Edited value'
44
+ },
45
+ });
46
+ assert.equal(res.statusCode, 200);
47
+ const rep = res.json();
48
+ // console.log(rep);
49
+ });
50
+
51
+ await t.test('GET /cms-category/:id', async () => {
52
+ // await pg.query('insert into site.contents (content_id, uid) values ($1, $2)', [testContentId, testUid]);
53
+ const res = await app.inject({
54
+ method: 'GET',
55
+ url: `${prefix}/cms-category/${testCategoryId}`,
56
+ });
57
+ assert.equal(res.statusCode, 200);
58
+ const rep = res.json();
59
+ // console.log(rep);
60
+ });
61
+
62
+ await t.test('GET /cms-category', async () => {
63
+ // await pg.query('insert into site.contents (content_id, uid) values ($1, $2)', [testContentId, testUid]);
64
+ const res = await app.inject({
65
+ method: 'GET',
66
+ url: `${prefix}/cms-category`,
67
+ });
68
+ assert.equal(res.statusCode, 200);
69
+ const rep = res.json();
70
+ // console.log(rep);
71
+ });
72
+
73
+ await t.test('GET /cms-category?id', async () => {
74
+ // await pg.query('insert into site.contents (content_id, uid) values ($1, $2)', [testContentId, testUid]);
75
+ const res = await app.inject({
76
+ method: 'GET',
77
+ url: `${prefix}/cms-category?category_id=${testCategoryId}`,
78
+ });
79
+ assert.equal(res.statusCode, 200);
80
+ const rep = res.json();
81
+ // console.log(rep);
82
+ });
83
+
84
+ await t.test('DELETE /cms-category/:id', async () => {
85
+ const res = await app.inject({
86
+ method: 'DELETE',
87
+ url: `${prefix}/cms-category/${testCategoryId}`,
88
+ });
89
+ assert.equal(res.statusCode, 200);
90
+ const rep = res.json();
91
+ // console.log(rep);
92
+ });
93
+ await t.test('clean up', async () => {
94
+ const { rowCount: categoryCount } = await pg.query('delete from site.categories where category_id=$1', [testCategoryId]);
95
+ console.log('clean up', categoryCount);
96
+ });
97
+ });
@@ -0,0 +1,103 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert';
3
+
4
+ import { config, pgClients, getMeta } from '@opengis/fastify-table/utils.js';
5
+ import build from '../helper.js';
6
+
7
+ test('cms manager api', async (t) => {
8
+ const app = await build(t);
9
+
10
+ const pg = pgClients.client;
11
+
12
+ const prefix = config.prefix || '/api';
13
+
14
+ const testContentId = 'testContent';
15
+ const testTypeId = 'testContent';
16
+ const testCategoryId = 'testContent';
17
+ const testTemplate = 'testContent';
18
+ const testUid = '1';
19
+
20
+ app.addHook('onRequest', async (req) => {
21
+ req.session = { passport: { user: { user_type: 'admin', uid: '1' } } };
22
+ req.user = req.session.passport.user;
23
+ req.unittest = 1;
24
+ });
25
+
26
+ await t.test('POST /cms-manager/:template', async () => {
27
+ const res = await app.inject({
28
+ method: 'POST',
29
+ url: `${prefix}/cms-manager/news`,
30
+ body: {
31
+ content_id: testContentId,
32
+ title: 'Created value',
33
+ },
34
+ });
35
+ assert.equal(res.statusCode, 200);
36
+ const rep = res.json();
37
+ // console.log(rep);
38
+ });
39
+
40
+ await t.test('GET /cms-manager/:template', async () => {
41
+ const res = await app.inject({
42
+ method: 'GET',
43
+ url: `${prefix}/cms-manager/news`,
44
+ });
45
+ assert.equal(res.statusCode, 200);
46
+ const rep = res.json();
47
+
48
+ const contentColumns = await getMeta({ table: 'site.contents' }).then(el => el.columns?.filter?.(el => !['cdate', 'editor_date', 'uid', 'editor_date'].includes(el.name))?.length || 0);
49
+ const customColumns = await pg.query(`select jsonb_array_length(columns::jsonb) from site.content_types where template=$1`, ['news']).then(el => el.rows?.[0]?.jsonb_array_length || 0);
50
+ assert.equal(rep.columns?.length, contentColumns + customColumns, res.body);
51
+ });
52
+
53
+ await t.test('PUT /cms-manager', async () => {
54
+ const res = await app.inject({
55
+ method: 'PUT',
56
+ url: `${prefix}/cms-manager/${testContentId}`,
57
+ body: {
58
+ title: 'Edited value'
59
+ },
60
+ });
61
+ assert.equal(res.statusCode, 200);
62
+ const rep = res.json();
63
+ // console.log(rep);
64
+ });
65
+
66
+ await t.test('GET /cms-manager/:type', async () => {
67
+ const res = await app.inject({
68
+ method: 'GET',
69
+ url: `${prefix}/cms-manager/${testTemplate}`,
70
+ });
71
+ assert.equal(res.statusCode, 200);
72
+ const rep = res.json();
73
+ // console.log(rep);
74
+ });
75
+
76
+ await t.test('GET /cms-manager/:type/:id', async () => {
77
+ const res = await app.inject({
78
+ method: 'GET',
79
+ url: `${prefix}/cms-manager/${testTemplate}/${testContentId}`,
80
+ });
81
+ assert.equal(res.statusCode, 200);
82
+ const rep = res.json();
83
+ // console.log(rep);
84
+ });
85
+
86
+
87
+ await t.test('DELETE /cms-manager/:id', async () => {
88
+ const res = await app.inject({
89
+ method: 'DELETE',
90
+ url: `${prefix}/cms-manager/${testContentId}`,
91
+ });
92
+ assert.equal(res.statusCode, 200);
93
+ const rep = res.json();
94
+ // console.log(rep);
95
+ });
96
+
97
+ await t.test('clean up', async () => {
98
+ const { rowCount: contentCount } = await pg.query('delete from site.contents where content_id=$1', [testContentId]);
99
+ // const { rowCount: typeCount } = await pg.query('delete from site.content_types where content_type_id=$1', [testTypeId]);
100
+ // const { rowCount: categoryCount } = await pg.query('delete from site.categories where category_id=$1', [testCategoryId]);
101
+ console.log('clean up', contentCount);
102
+ });
103
+ });
@@ -0,0 +1,252 @@
1
+ import path from 'node:path';
2
+ import { test } from 'node:test';
3
+ import assert from 'node:assert';
4
+ import { readFile, rm } from 'node:fs/promises';
5
+
6
+ import { config, pgClients, getFolder } from '@opengis/fastify-table/utils.js';
7
+
8
+ import build from '../helper.js';
9
+ import { existsSync } from 'node:fs';
10
+
11
+ function arrayBufferToBlob(buffer, contentType) {
12
+ const uInt8Array = new Uint8Array(buffer);
13
+ return new Blob([uInt8Array], (contentType ? { type: contentType } : {}));
14
+ }
15
+
16
+ const { prefix = '/api' } = config;
17
+
18
+ const uid = '1';
19
+
20
+ const tokens = {};
21
+ const ids = [];
22
+ const rootDir = getFolder(config, 'local');
23
+ const dir = 'testdirname';
24
+ const subdirname = 'subdirname';
25
+
26
+ test('cms manager api', async (t) => {
27
+ const app = await build(t);
28
+
29
+ app.addHook('onRequest', async (req) => {
30
+ req.session = { passport: { user: { user_type: 'admin', uid: '1' } } };
31
+ req.user = req.session.passport.user;
32
+ req.unittest = 1;
33
+ });
34
+
35
+ await t.test('GET /media (list)', async () => {
36
+ const res = await app.inject({
37
+ method: 'GET',
38
+ url: `${prefix}/cms-media`,
39
+ });
40
+ assert.equal(res.statusCode, 200);
41
+ const rep = res.json();
42
+ assert.ok(rep.token);
43
+ Object.assign(tokens, { root: rep.token });
44
+ assert.ok(rep.data);
45
+ });
46
+
47
+ await t.test('POST /cms-media/:token (add directory)', async () => {
48
+ // create directory
49
+ const res = await app.inject({
50
+ method: 'POST',
51
+ url: `${prefix}/cms-media/${tokens.root}`,
52
+ body: {
53
+ name: dir,
54
+ },
55
+ });
56
+ assert.equal(res.statusCode, 200);
57
+ const rep = res.json();
58
+ assert.equal(rep.name, dir);
59
+ assert.ok(rep.token);
60
+ Object.assign(tokens, { dir: rep.token });
61
+
62
+ // create subdirectory
63
+ const res1 = await app.inject({
64
+ method: 'POST',
65
+ url: `${prefix}/cms-media/${tokens.dir}`,
66
+ body: {
67
+ name: subdirname,
68
+ },
69
+ });
70
+ assert.equal(res1.statusCode, 200);
71
+ const rep1 = res1.json();
72
+ assert.ok(rep1.token);
73
+ assert.equal(rep1.name, subdirname);
74
+ assert.equal(rep1.dir, path.join('/media', dir, subdirname).replace(/\\/g, '/'));
75
+ });
76
+
77
+ await t.test('POST /cms-media/:token (upload file)', async () => {
78
+ const formdata = new FormData();
79
+ const filepath = path.join(process.cwd(), 'test/files/eye.svg');
80
+ const buffer = await readFile(filepath, { buffer: true });
81
+ const data = arrayBufferToBlob(buffer);
82
+ formdata.append('file', data, path.basename(filepath));
83
+
84
+ const res = await app.inject({
85
+ method: 'POST',
86
+ url: `${prefix}/cms-media/${tokens.dir}`,
87
+ body: formdata,
88
+ });
89
+ assert.equal(res.statusCode, 200);
90
+ const rep = res.json();
91
+ assert.ok(rep.token);
92
+ assert.ok(rep.id, 'insert error (crm.media table exists?)');
93
+ ids.push(rep.id);
94
+ Object.assign(tokens, { file: rep.token });
95
+ });
96
+
97
+ await t.test('GET /cms-media/:token (preview file)', async () => {
98
+ const res = await app.inject({
99
+ method: 'GET',
100
+ url: `${prefix}/cms-media/${tokens.file}`,
101
+ });
102
+ assert.equal(res.statusCode, 200);
103
+ assert.equal(res.headers['content-type'], "image/svg+xml");
104
+ });
105
+
106
+ await t.test('GET /cms-media/:token (download file)', async () => {
107
+ const res = await app.inject({
108
+ method: 'GET',
109
+ url: `${prefix}/cms-media/${tokens.file}`,
110
+ query: { download: 1 },
111
+ });
112
+ assert.equal(res.statusCode, 200);
113
+ assert.ok(res.headers['content-disposition']?.startsWith('attachment'), 'no attachment on download');
114
+ });
115
+
116
+ await t.test('GET /cms-media-metadata/:token (file metadata by token)', async () => {
117
+ const res = await app.inject({
118
+ method: 'GET',
119
+ url: `${prefix}/cms-media-metadata/${tokens.file}`,
120
+ });
121
+ assert.equal(res.statusCode, 200);
122
+ const rep = res.json();
123
+ assert.ok(rep.id);
124
+ assert.ok(rep.name);
125
+ assert.ok(rep.size);
126
+ assert.ok(rep.extension);
127
+ assert.ok(rep.ctime);
128
+ assert.ok(rep.mtime);
129
+ });
130
+
131
+ await t.test('GET /cms-media-metadata/:id (file metadata by id)', async () => {
132
+ const res = await app.inject({
133
+ method: 'GET',
134
+ url: `${prefix}/cms-media-metadata/${ids[0]}`,
135
+ });
136
+ assert.equal(res.statusCode, 200);
137
+ const rep = res.json();
138
+ assert.ok(rep.id);
139
+ assert.ok(rep.name);
140
+ assert.ok(rep.size);
141
+ assert.ok(rep.extension);
142
+ assert.ok(rep.ctime);
143
+ assert.ok(rep.mtime);
144
+ });
145
+
146
+ await t.test('PUT /cms-media/:token (edit file metadata by token)', async () => {
147
+ const res = await app.inject({
148
+ method: 'PUT',
149
+ url: `${prefix}/cms-media/${tokens.file}`,
150
+ body: {
151
+ name: '123.svg',
152
+ caption: 'Icon',
153
+ altname: 'image alt name',
154
+ },
155
+ });
156
+ assert.equal(res.statusCode, 200);
157
+ const rep = res.json();
158
+ assert.ok(rep);
159
+ assert.ok(rep.name, '123.svg');
160
+ assert.ok(rep.caption, 'Icon');
161
+ assert.ok(rep.altname, 'image alt name');
162
+ });
163
+
164
+ await t.test('PUT /cms-media/:id (edit file metadata by id)', async () => {
165
+ const res = await app.inject({
166
+ method: 'PUT',
167
+ url: `${prefix}/cms-media/${ids[0]}`,
168
+ body: {
169
+ name: '1234.svg',
170
+ },
171
+ });
172
+ assert.equal(res.statusCode, 200);
173
+ const rep = res.json();
174
+ assert.ok(rep.name, '1234.svg');
175
+ });
176
+
177
+ // rename directory
178
+ await t.test('PUT /cms-media/:id (rename directory)', async () => {
179
+ const res = await app.inject({
180
+ method: 'PUT',
181
+ url: `${prefix}/cms-media/${tokens.dir}`,
182
+ body: {
183
+ name: `${dir}1`,
184
+ },
185
+ });
186
+ assert.equal(res.statusCode, 200);
187
+ const rep = res.json();
188
+ assert.equal(rep.name, `${dir}1`);
189
+ assert.equal(rep.relpath, path.join('/media', `${dir}1`).replace(/\\/g, '/'));
190
+ });
191
+
192
+ // delete file
193
+ await t.test('DELETE /cms-media/:token (delete file by token)', async () => {
194
+ const res1 = await app.inject({
195
+ method: 'GET',
196
+ url: `${prefix}/cms-media`,
197
+ query: { dir: `${dir}1` },
198
+ });
199
+ assert.equal(res1.statusCode, 200);
200
+ const rep1 = res1.json();
201
+ assert.ok(rep1.data);
202
+ const newtokens = rep1.data.reduce((acc, curr) => Object.assign(acc, { [curr.type]: curr.token }), {});
203
+ Object.assign(tokens, { new: newtokens });
204
+ assert.ok(Object.keys(tokens.new).length, 'empty directory');
205
+
206
+ const res = await app.inject({
207
+ method: 'DELETE',
208
+ url: `${prefix}/cms-media/${newtokens.file}`,
209
+ });
210
+ assert.equal(res.statusCode, 200);
211
+ const rep = res.json();
212
+ assert.ok(rep.id);
213
+ assert.equal(rep.name, '1234.svg');
214
+ });
215
+
216
+ // delete directory
217
+ await t.test('DELETE /cms-media/:token (delete directory by token)', async () => {
218
+ const dirpath = path.join(rootDir, 'media', `${dir}1`, subdirname);
219
+ const existsBefore = existsSync(dirpath);
220
+ const res = await app.inject({
221
+ method: 'DELETE',
222
+ url: `${prefix}/cms-media/${tokens.new.dir}`,
223
+ });
224
+ assert.equal(res.statusCode, 200);
225
+ const existsAfter = existsSync(dirpath);
226
+ assert.ok(existsBefore && !existsAfter);
227
+ });
228
+ // restrict delete root directory
229
+ await t.test('DELETE /cms-media/:token (restrict delete root directory)', async () => {
230
+ const res = await app.inject({
231
+ method: 'DELETE',
232
+ url: `${prefix}/cms-media/${tokens.root}`,
233
+ });
234
+ assert.equal(res.statusCode, 403);
235
+ });
236
+
237
+ await t.test('clean up', async () => {
238
+ const { rowCount = 0 } = pgClients.client?.pk?.['crm.media'] ? await pgClients.client.query(`delete from crm.media where uid=$1`, [uid]) : {};
239
+ console.log('clean up: media', rowCount);
240
+ const dirpath = path.join(rootDir, 'media', dir);
241
+ const len = [dirpath, `${dirpath}1`].filter(el => existsSync(el)).length;
242
+ if (len > 0) {
243
+ await Promise.all([dirpath, `${dirpath}1`].filter(el => existsSync(el)).map(async (el) => {
244
+ console.log('clean up: test directory', el);
245
+ await rm(el, { recursive: true });
246
+ }));
247
+ } else {
248
+ console.log('clean up: test directory', 0);
249
+ }
250
+ });
251
+
252
+ });
package/vite.config.js ADDED
@@ -0,0 +1,37 @@
1
+ import { defineConfig } from 'vite'
2
+ import { fileURLToPath, URL } from 'node:url';
3
+ import vue from '@vitejs/plugin-vue'
4
+ import { resolve } from 'path';
5
+
6
+
7
+ // https://vite.dev/config/
8
+
9
+ export default defineConfig({
10
+ plugins: [vue()],
11
+ base: '/',
12
+ build: {
13
+ lib: {
14
+ entry: resolve(__dirname, './src/misc/import-file.js'),
15
+ name: 'cms',
16
+ fileName: 'cms',
17
+ },
18
+ rollupOptions: {
19
+ // make sure to externalize deps that shouldn't be bundled
20
+ // into your library
21
+ external: ['vue'],
22
+ output: {
23
+ // Provide global variables to use in the UMD build
24
+ // for externalized deps
25
+ globals: {
26
+ vue: 'Vue',
27
+ },
28
+ },
29
+ },
30
+ },
31
+ resolve: {
32
+ alias: {
33
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
34
+ 'vue': 'vue/dist/vue.esm-bundler',
35
+ },
36
+ },
37
+ })