@opengis/cms 0.0.61 → 0.0.63

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 (34) hide show
  1. package/dist/{ArticlesPage-CFjE_cw_.js → ArticlesPage-BjYzvTWM.js} +3 -3
  2. package/dist/{CollectionsBreadcrumb-BCxeRikP.js → CollectionsBreadcrumb-HePNJb-d.js} +1 -1
  3. package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-BJh-tjam.js +53 -0
  4. package/dist/{Dashboard-C1eGscNd.js → Dashboard-CXkg_pk8.js} +132 -132
  5. package/dist/{EditCollectionPage-3Q57ptN3.js → EditCollectionPage-BGZCMcQS.js} +3 -3
  6. package/dist/{MenuAddPage-D-p3gFgm.js → MenuAddPage-QTnwCoGh.js} +1 -1
  7. package/dist/{MenuBody-rN5j4YBu.js → MenuBody-Bi0ONVZf.js} +2 -2
  8. package/dist/{MenuItemPage-BoJw885D.js → MenuItemPage-B7Y9KFyb.js} +3 -3
  9. package/dist/{MenuList-DFEBS0NB.js → MenuList-BLIpeqSd.js} +53 -53
  10. package/dist/{MenuPage-BCZB_S8j.js → MenuPage-3W6jZ15H.js} +1 -1
  11. package/dist/{MenuWrapper-AZ_8s-zd.js → MenuWrapper-OrOv6sOb.js} +1 -1
  12. package/dist/{MonacoEditor-Db-3Jc3E.js → MonacoEditor-ByPT8pnv.js} +1 -1
  13. package/dist/MonacoEditor.vue_vue_type_script_setup_true_lang-C8cip9Ci.js +84 -0
  14. package/dist/{UniversalTable-CzqPG-tY.js → UniversalTable-GBd_pStq.js} +80 -80
  15. package/dist/{UniversalTablePagination-4gL47A7I.js → UniversalTablePagination-Dw2hc0nc.js} +46 -46
  16. package/dist/{contentForm-CLStrfSg.js → contentForm-NmskI6Ye.js} +146 -148
  17. package/dist/index.js +5 -5
  18. package/dist/{vs-builder-monaco-B3Jj0V31.js → vs-builder-monaco-Cw-f19gc.js} +1 -1
  19. package/dist/{vs-builder-preview-BH4VAM3a.js → vs-builder-preview-l5KhqlrF.js} +12 -13
  20. package/dist/vs-form-custom-datatable-D880w8gx.js +493 -0
  21. package/dist/{vs-form-reference-list-Dtv8fJJU.js → vs-form-reference-list-hZufPxfE.js} +696 -353
  22. package/locales/en.json +8 -0
  23. package/locales/uk.json +9 -1
  24. package/package.json +2 -2
  25. package/server/routes/cms/controllers/deleteMedia.js +76 -76
  26. package/server/routes/cms/controllers/getPermissions.js +15 -15
  27. package/server/routes/cms/controllers/metadataMedia.js +39 -39
  28. package/server/routes/cms/controllers/setPermissions.js +49 -49
  29. package/server/routes/cms/controllers/uploadMedia.js +79 -79
  30. package/server/templates/select/core.user_mentioned.sql +1 -1
  31. package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-umRzB5mY.js +0 -53
  32. package/dist/MonacoEditor.vue_vue_type_script_setup_true_lang-B1DrxmQX.js +0 -84
  33. package/dist/getField-CpwVE28P.js +0 -179
  34. package/dist/vs-form-custom-datatable-BDZo48w3.js +0 -317
package/locales/en.json CHANGED
@@ -301,6 +301,14 @@
301
301
  "documents": "Documents",
302
302
  "infoLine": "Information Line",
303
303
  "iframe": "Iframe",
304
+ "detailedBanner": "Detailed Banner",
305
+ "cardsReverse": "Cards Reverse",
306
+ "table": "Data Table",
307
+ "fileDownload": "File Download",
308
+ "bodyContent": "Body Content",
309
+ "columnList": "List of elements in two columns",
310
+ "list": "List of elements",
311
+ "linkList": "List of links",
304
312
  "manageContentCollections": "Manage content collections",
305
313
  "searchCollections": "Search collections...",
306
314
  "collections": "Collections",
package/locales/uk.json CHANGED
@@ -301,6 +301,14 @@
301
301
  "documents": "Документи",
302
302
  "infoLine": "Інформаційна лінія",
303
303
  "iframe": "Iframe",
304
+ "detailedBanner": "Детальний банер",
305
+ "cardsReverse": "Картки зворотнього порядку",
306
+ "table": "Таблиця даних",
307
+ "fileDownload": "Завантажити файл",
308
+ "bodyContent": "Текстовий блок",
309
+ "columnList": "Список елементів у двох колонках",
310
+ "list": "Список елементів",
311
+ "linkList": "Список посилань",
304
312
  "manageContentCollections": "Керування колекціями контенту",
305
313
  "searchCollections": "Пошук колекцій...",
306
314
  "collections": "Структура колекцій",
@@ -327,7 +335,7 @@
327
335
  "actions": "Дії",
328
336
  "noColumnsFound": "Немає колонок",
329
337
  "editCollection": "Редагувати структуру",
330
- "editPage": "Редагувати сторінку",
338
+ "editPage": "Редагувати сторінку1111111",
331
339
  "editContentForYourSite": "Редагувати контент для вашого сайту",
332
340
  "deleteObject": "Ви впевнені, що хочете видалити цей об'єкт?",
333
341
  "editField": "Редагувати поле",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/cms",
3
- "version": "0.0.61",
3
+ "version": "0.0.63",
4
4
  "description": "cms",
5
5
  "type": "module",
6
6
  "author": "Softpro",
@@ -44,7 +44,7 @@
44
44
  "@fastify/compress": "^8.1.0",
45
45
  "@opengis/core": "^0.0.30",
46
46
  "@opengis/fastify-table": "^2.0.147",
47
- "@opengis/filter": "^0.1.31",
47
+ "@opengis/filter": "^0.1.33",
48
48
  "@opengis/form": "^0.0.121",
49
49
  "@tailwindcss/typography": "0.5.10",
50
50
  "@tsconfig/node22": "^22.0.2",
@@ -1,77 +1,77 @@
1
- import path from 'node:path';
2
- import { existsSync } from 'node:fs';
3
- import { rm, stat, readdir } from 'node:fs/promises';
4
-
5
- import { config, dataDelete, getFolder, pgClients } from "@opengis/fastify-table/utils.js";
6
-
7
- // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
8
- const rootDir = path.resolve(getFolder(config, 'local'));
9
- const dir = '/files';
10
-
11
- export default async function deleteMedia({
12
- pg = pgClients.client, params = {}, query = {}, user = {}, method = 'DELETE',
13
- }, reply) {
14
- if (!config.debug && method !== 'DELETE') {
15
- return reply.status(403).send('access restricted');
16
- }
17
-
18
- if (!params.id && query.subdir) {
19
- const dirpath = path.join(rootDir, dir, query.subdir || '');
20
- const exists = existsSync(dirpath);
21
- const stats = await stat(dirpath);
22
-
23
- if (!stats.isDirectory()) {
24
- return reply.status(400).send('not a directory');
25
- }
26
-
27
- if (!exists) {
28
- return reply.status(404).send('subdir not found: ' + query.subdir);
29
- }
30
-
31
- const content = await readdir(dirpath, { recursive: true });
32
-
33
- if (query.subdir.startsWith('uploads') && !query.subdir.split('/')[1]) {
34
- return reply.status(403).send('access restricted: uploads directory');
35
- }
36
-
37
- // only admins are allowed to delete non-empty directories
38
- if (content?.length && !user?.user_type?.includes?.('admin')) {
39
- return reply.status(400).send('directory is not empty');
40
- }
41
-
42
- await rm(dirpath, { recursive: true });
43
- return reply.status(200).send('subdirectory successfully deleted');
44
- }
45
-
46
- if (!params?.id) {
47
- return reply.status(400).send('not enough params: id');
48
- }
49
-
50
- if (!pg.pk?.['site.media']) {
51
- return reply.status(404).send('table not found');
52
- }
53
-
54
- const { url: relpath, id } = await pg.query(
55
- 'select media_id as id, url from site.media where media_id = $1 and url is not null',
56
- [params.id],
57
- ).then(el => el.rows?.[0] || {});
58
-
59
- if (!id) {
60
- return reply.status(404).send('media not found: ' + params.id);
61
- }
62
-
63
- const res = await dataDelete({
64
- pg,
65
- id,
66
- table: 'site.media',
67
- uid: user?.uid || 0,
68
- });
69
-
70
- const filepath = path.join(rootDir, relpath);
71
-
72
- if (existsSync(filepath)) {
73
- await rm(filepath, { recursive: true });
74
- }
75
-
76
- return { id, ...res || {} };
1
+ import path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { rm, stat, readdir } from 'node:fs/promises';
4
+
5
+ import { config, dataDelete, getFolder, pgClients } from "@opengis/fastify-table/utils.js";
6
+
7
+ // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
8
+ const rootDir = path.resolve(getFolder(config, 'local'));
9
+ const dir = '/files';
10
+
11
+ export default async function deleteMedia({
12
+ pg = pgClients.client, params = {}, query = {}, user = {}, method = 'DELETE',
13
+ }, reply) {
14
+ if (!config.debug && method !== 'DELETE') {
15
+ return reply.status(403).send('access restricted');
16
+ }
17
+
18
+ if (!params.id && query.subdir) {
19
+ const dirpath = path.join(rootDir, dir, query.subdir || '');
20
+ const exists = existsSync(dirpath);
21
+ const stats = await stat(dirpath);
22
+
23
+ if (!stats.isDirectory()) {
24
+ return reply.status(400).send('not a directory');
25
+ }
26
+
27
+ if (!exists) {
28
+ return reply.status(404).send('subdir not found: ' + query.subdir);
29
+ }
30
+
31
+ const content = await readdir(dirpath, { recursive: true });
32
+
33
+ if (query.subdir.startsWith('uploads') && !query.subdir.split('/')[1]) {
34
+ return reply.status(403).send('access restricted: uploads directory');
35
+ }
36
+
37
+ // only admins are allowed to delete non-empty directories
38
+ if (content?.length && !user?.user_type?.includes?.('admin')) {
39
+ return reply.status(400).send('directory is not empty');
40
+ }
41
+
42
+ await rm(dirpath, { recursive: true });
43
+ return reply.status(200).send('subdirectory successfully deleted');
44
+ }
45
+
46
+ if (!params?.id) {
47
+ return reply.status(400).send('not enough params: id');
48
+ }
49
+
50
+ if (!pg.pk?.['site.media']) {
51
+ return reply.status(404).send('table not found');
52
+ }
53
+
54
+ const { url: relpath, id } = await pg.query(
55
+ 'select media_id as id, url from site.media where media_id = $1 and url is not null',
56
+ [params.id],
57
+ ).then(el => el.rows?.[0] || {});
58
+
59
+ if (!id) {
60
+ return reply.status(404).send('media not found: ' + params.id);
61
+ }
62
+
63
+ const res = await dataDelete({
64
+ pg,
65
+ id,
66
+ table: 'site.media',
67
+ uid: user?.uid || 0,
68
+ });
69
+
70
+ const filepath = path.join(rootDir, relpath);
71
+
72
+ if (existsSync(filepath)) {
73
+ await rm(filepath, { recursive: true });
74
+ }
75
+
76
+ return { id, ...res || {} };
77
77
  }
@@ -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
  }
@@ -1,39 +1,39 @@
1
- import path from 'node:path';
2
- import { existsSync } from 'node:fs';
3
-
4
- import { config, getFolder, pgClients } from "@opengis/fastify-table/utils.js";
5
-
6
- // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
7
- const rootDir = path.resolve(getFolder(config, 'local'));
8
-
9
- export default async function metadataMedia({
10
- routeOptions = {}, pg = pgClients.client, params = {},
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 data = await pg.query(
21
- 'select * from site.media where media_id = $1 and url is not null',
22
- [params.id],
23
- ).then(el => el.rows?.[0]);
24
-
25
- if (!data) {
26
- return reply.status(404).send('media not found: ' + params.id);
27
- }
28
-
29
- const filepath = path.join(rootDir, data.url);
30
-
31
- Object.assign(data, {
32
- url: `${routeOptions.url.replace(':id', params.id)}/file`,
33
- preview: `${routeOptions.url.replace(':id', params.id)}/preview`,
34
- filepath: data.url,
35
- exists: existsSync(filepath),
36
- });
37
-
38
- return reply.status(200).send(data);
39
- }
1
+ import path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+
4
+ import { config, getFolder, pgClients } from "@opengis/fastify-table/utils.js";
5
+
6
+ // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
7
+ const rootDir = path.resolve(getFolder(config, 'local'));
8
+
9
+ export default async function metadataMedia({
10
+ routeOptions = {}, pg = pgClients.client, params = {},
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 data = await pg.query(
21
+ 'select * from site.media where media_id = $1 and url is not null',
22
+ [params.id],
23
+ ).then(el => el.rows?.[0]);
24
+
25
+ if (!data) {
26
+ return reply.status(404).send('media not found: ' + params.id);
27
+ }
28
+
29
+ const filepath = path.join(rootDir, data.url);
30
+
31
+ Object.assign(data, {
32
+ url: `${routeOptions.url.replace(':id', params.id)}/file`,
33
+ preview: `${routeOptions.url.replace(':id', params.id)}/preview`,
34
+ filepath: data.url,
35
+ exists: existsSync(filepath),
36
+ });
37
+
38
+ return reply.status(200).send(data);
39
+ }
@@ -1,50 +1,50 @@
1
- import { logger, pgClients, dataInsert } from '@opengis/fastify-table/utils.js';
2
-
3
- export default async function setPermissions(req, reply) {
4
- const { pg = pgClients.client, params = {}, user = {}, body = {} } = req;
5
-
6
- if (!user?.uid) {
7
- return reply.status(401).send('unauthorized');
8
- }
9
-
10
- if (!params.id) {
11
- return reply.status(400).send('not enough params: id');
12
- }
13
-
14
- const client = await pg.connect()
15
- const result = {};
16
- try {
17
- await client.query('BEGIN');
18
-
19
- const { rowCount = 0 } = await client.query(
20
- `delete from site.permissions where user_id=$1`,
21
- [params.id].filter(Boolean),
22
- );
23
-
24
- Object.assign(result, { deleted: rowCount });
25
-
26
- if (Array.isArray(body.permissions) && body.permissions?.length) {
27
- body.permissions.forEach((el) => {
28
- Object.assign(el, { user_id: el.user_id || params.id });
29
- });
30
-
31
- await Promise.all(body.permissions.map(async (el) => dataInsert({
32
- pg: client,
33
- table: 'site.permissions',
34
- data: el,
35
- uid: user.uid,
36
- })));
37
-
38
- Object.assign(result, { inserted: body.permissions.length });
39
- }
40
-
41
- await client.query('COMMIT');
42
- return reply.status(200).send(result);
43
- } catch (err) {
44
- await client.query('ROLLBACK');
45
- logger.file('cms/permissions', { error: err.toString(), stack: err.stack });
46
- return reply.status(500).send('set permissions error');
47
- } finally {
48
- client.release();
49
- }
1
+ import { logger, pgClients, dataInsert } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function setPermissions(req, reply) {
4
+ const { pg = pgClients.client, params = {}, user = {}, body = {} } = req;
5
+
6
+ if (!user?.uid) {
7
+ return reply.status(401).send('unauthorized');
8
+ }
9
+
10
+ if (!params.id) {
11
+ return reply.status(400).send('not enough params: id');
12
+ }
13
+
14
+ const client = await pg.connect()
15
+ const result = {};
16
+ try {
17
+ await client.query('BEGIN');
18
+
19
+ const { rowCount = 0 } = await client.query(
20
+ `delete from site.permissions where user_id=$1`,
21
+ [params.id].filter(Boolean),
22
+ );
23
+
24
+ Object.assign(result, { deleted: rowCount });
25
+
26
+ if (Array.isArray(body.permissions) && body.permissions?.length) {
27
+ body.permissions.forEach((el) => {
28
+ Object.assign(el, { user_id: el.user_id || params.id });
29
+ });
30
+
31
+ await Promise.all(body.permissions.map(async (el) => dataInsert({
32
+ pg: client,
33
+ table: 'site.permissions',
34
+ data: el,
35
+ uid: user.uid,
36
+ })));
37
+
38
+ Object.assign(result, { inserted: body.permissions.length });
39
+ }
40
+
41
+ await client.query('COMMIT');
42
+ return reply.status(200).send(result);
43
+ } catch (err) {
44
+ await client.query('ROLLBACK');
45
+ logger.file('cms/permissions', { error: err.toString(), stack: err.stack });
46
+ return reply.status(500).send('set permissions error');
47
+ } finally {
48
+ client.release();
49
+ }
50
50
  }
@@ -1,80 +1,80 @@
1
- import path from 'node:path';
2
- import { mkdir } from 'node:fs/promises';
3
-
4
- import { uploadMultiPart, config, getFolder, dataInsert, pgClients } from "@opengis/fastify-table/utils.js";
5
-
6
- // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
7
- const rootDir = path.resolve(getFolder(config, 'local'));
8
- const dir = '/files';
9
-
10
- export default async function uploadMedia(req, reply) {
11
- const { pg = pgClients.client, user = {}, query = {} } = req;
12
-
13
- if (!pg?.pk?.['site.media']) {
14
- return reply.status(404).send('table not found');
15
- }
16
-
17
- if (query.subdir && (typeof query.subdir !== 'string' || query.subdir.includes('..'))) {
18
- return reply.status(403).send('invalid query params: subdir');
19
- }
20
-
21
- // upload assets
22
- if (req.headers['content-type']?.split?.(';')?.shift?.() === 'multipart/form-data') {
23
- const file = await uploadMultiPart(req, { subdir: query.subdir || '', originalFilename: true }).catch(err => {
24
- if (err.message === 'file with specified name already exists in directory') {
25
- err.message = 'Файл з вказаною назвою вже існує';
26
- err.statusCode = 400;
27
- }
28
- throw err;
29
- });
30
-
31
- const { originalFilename: filename, filetype, mimetype } = file;
32
- const relpath = path.join(dir, query.subdir || '', file.originalFilename).replace(/\\/g, '/');
33
-
34
- const id = await dataInsert({
35
- pg,
36
- table: 'site.media',
37
- data: {
38
- filename,
39
- filetype,
40
- subdir: query.subdir,
41
- url: relpath,
42
- mime: mimetype,
43
- filesize: file.size,
44
- },
45
- uid: user?.uid,
46
- }).then(el => el?.rows?.[0]?.media_id);
47
-
48
- return reply.status(200).send({
49
- res: 'ok',
50
- name: filename,
51
- type: 'file',
52
- mimetype,
53
- result: {
54
- file_id: id,
55
- format: file.extension,
56
- size: file.size,
57
- // entity_id: resultInsert?.entity_id,
58
- file_path: relpath,
59
- file_name: filename,
60
- dir: path.dirname(relpath).replace(/\\/g, '/'),
61
- native_file_name: filename,
62
- },
63
- });
64
- }
65
-
66
- if (!query.subdir) {
67
- return reply.status(400).send('not enough query params: subdir');
68
- }
69
-
70
- // create directory
71
- const relpath = path.join(dir, query.subdir).replace(/\\/g, '/');
72
- const dirpath = path.join(rootDir, relpath);
73
- await mkdir(dirpath, { recursive: true });
74
-
75
- return reply.status(200).send({
76
- relpath,
77
- dirname: path.basename(query.subdir),
78
- type: 'dir',
79
- });
1
+ import path from 'node:path';
2
+ import { mkdir } from 'node:fs/promises';
3
+
4
+ import { uploadMultiPart, config, getFolder, dataInsert, pgClients } from "@opengis/fastify-table/utils.js";
5
+
6
+ // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
7
+ const rootDir = path.resolve(getFolder(config, 'local'));
8
+ const dir = '/files';
9
+
10
+ export default async function uploadMedia(req, reply) {
11
+ const { pg = pgClients.client, user = {}, query = {} } = req;
12
+
13
+ if (!pg?.pk?.['site.media']) {
14
+ return reply.status(404).send('table not found');
15
+ }
16
+
17
+ if (query.subdir && (typeof query.subdir !== 'string' || query.subdir.includes('..'))) {
18
+ return reply.status(403).send('invalid query params: subdir');
19
+ }
20
+
21
+ // upload assets
22
+ if (req.headers['content-type']?.split?.(';')?.shift?.() === 'multipart/form-data') {
23
+ const file = await uploadMultiPart(req, { subdir: query.subdir || '', originalFilename: true }).catch(err => {
24
+ if (err.message === 'file with specified name already exists in directory') {
25
+ err.message = 'Файл з вказаною назвою вже існує';
26
+ err.statusCode = 400;
27
+ }
28
+ throw err;
29
+ });
30
+
31
+ const { originalFilename: filename, filetype, mimetype } = file;
32
+ const relpath = path.join(dir, query.subdir || '', file.originalFilename).replace(/\\/g, '/');
33
+
34
+ const id = await dataInsert({
35
+ pg,
36
+ table: 'site.media',
37
+ data: {
38
+ filename,
39
+ filetype,
40
+ subdir: query.subdir,
41
+ url: relpath,
42
+ mime: mimetype,
43
+ filesize: file.size,
44
+ },
45
+ uid: user?.uid,
46
+ }).then(el => el?.rows?.[0]?.media_id);
47
+
48
+ return reply.status(200).send({
49
+ res: 'ok',
50
+ name: filename,
51
+ type: 'file',
52
+ mimetype,
53
+ result: {
54
+ file_id: id,
55
+ format: file.extension,
56
+ size: file.size,
57
+ // entity_id: resultInsert?.entity_id,
58
+ file_path: relpath,
59
+ file_name: filename,
60
+ dir: path.dirname(relpath).replace(/\\/g, '/'),
61
+ native_file_name: filename,
62
+ },
63
+ });
64
+ }
65
+
66
+ if (!query.subdir) {
67
+ return reply.status(400).send('not enough query params: subdir');
68
+ }
69
+
70
+ // create directory
71
+ const relpath = path.join(dir, query.subdir).replace(/\\/g, '/');
72
+ const dirpath = path.join(rootDir, relpath);
73
+ await mkdir(dirpath, { recursive: true });
74
+
75
+ return reply.status(200).send({
76
+ relpath,
77
+ dirname: path.basename(query.subdir),
78
+ type: 'dir',
79
+ });
80
80
  }
@@ -1,2 +1,2 @@
1
- select uid, coalesce(sur_name,'')||coalesce(' '||user_name,'') as text, email from admin.users
1
+ select uid, coalesce(sur_name,'')||coalesce(' '||user_name,'') as text, email from admin.users
2
2
  where enabled order by coalesce(sur_name,'')||coalesce(' '||user_name,'')
@@ -1,53 +0,0 @@
1
- import { defineComponent as b, openBlock as e, createElementBlock as t, createElementVNode as s, createVNode as c, unref as n, toDisplayString as u, Fragment as r, createBlock as x, createCommentVNode as k, renderList as h } from "vue";
2
- import { Layers as g, ChevronRight as i } from "lucide-vue-next";
3
- const p = { class: "flex items-center space-x-1 mb-4 overflow-hidden" }, w = { class: "truncate block max-w-[12rem]" }, f = ["onClick"], v = { class: "truncate block max-w-[20rem]" }, y = {
4
- key: 1,
5
- class: "flex gap-x-1 items-center px-2 py-1 rounded text-sm font-medium text-slate-700 dark:text-slate-300 min-w-0 overflow-hidden whitespace-nowrap text-ellipsis"
6
- }, C = { class: "truncate block max-w-[20rem]" }, V = /* @__PURE__ */ b({
7
- __name: "CollectionsBreadcrumb",
8
- props: {
9
- items: {},
10
- loading: { type: Boolean }
11
- },
12
- emits: ["navigate"],
13
- setup(a) {
14
- return (d, l) => (e(), t("div", p, [
15
- s("button", {
16
- class: "flex gap-x-1 items-center px-2 py-1 rounded text-sm font-medium transition-all duration-200 text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20 hover:bg-blue-100 dark:hover:bg-blue-900/30 min-w-0 overflow-hidden whitespace-nowrap text-ellipsis",
17
- onClick: l[0] || (l[0] = (o) => d.$emit("navigate", "collections"))
18
- }, [
19
- c(n(g), { class: "w-3 h-3 flex-shrink-0" }),
20
- s("span", w, u(d.$t("cms.navigation.collections")), 1)
21
- ]),
22
- a.loading ? (e(), t(r, { key: 0 }, [
23
- c(n(i), { class: "w-3 h-3 text-slate-400 dark:text-slate-500 flex-shrink-0" }),
24
- l[1] || (l[1] = s("div", { class: "h-7 w-24 rounded bg-slate-200 dark:bg-slate-700 animate-pulse" }, null, -1)),
25
- c(n(i), { class: "w-3 h-3 text-slate-400 dark:text-slate-500 flex-shrink-0" }),
26
- l[2] || (l[2] = s("div", { class: "h-7 w-20 rounded bg-slate-200 dark:bg-slate-700 animate-pulse" }, null, -1))
27
- ], 64)) : (e(), t(r, { key: 1 }, [
28
- a.items.length > 0 ? (e(), x(n(i), {
29
- key: 0,
30
- class: "w-3 h-3 text-slate-400 dark:text-slate-500 flex-shrink-0"
31
- })) : k("", !0),
32
- (e(!0), t(r, null, h(a.items, (o, m) => (e(), t(r, { key: m }, [
33
- m < a.items.length - 1 ? (e(), t("button", {
34
- key: 0,
35
- class: "flex gap-x-1 items-center px-2 py-1 rounded text-sm font-medium transition-all duration-200 text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-900/20 hover:bg-blue-100 dark:hover:bg-blue-900/30 min-w-0 overflow-hidden whitespace-nowrap text-ellipsis",
36
- onClick: (B) => d.$emit("navigate", o.route)
37
- }, [
38
- s("span", v, u(o.label), 1)
39
- ], 8, f)) : (e(), t("span", y, [
40
- s("span", C, u(o.label), 1)
41
- ])),
42
- m < a.items.length - 1 ? (e(), x(n(i), {
43
- key: 2,
44
- class: "w-3 h-3 text-slate-400 dark:text-slate-500 flex-shrink-0"
45
- })) : k("", !0)
46
- ], 64))), 128))
47
- ], 64))
48
- ]));
49
- }
50
- });
51
- export {
52
- V as _
53
- };