@opengis/cms 0.0.35 → 0.0.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/cms",
3
- "version": "0.0.35",
3
+ "version": "0.0.37",
4
4
  "description": "cms",
5
5
  "type": "module",
6
6
  "author": "Softpro",
@@ -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,49 +1,49 @@
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
- // 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
-
10
- export default async function downloadMedia({
11
- pg = pgClients.client, params = {},
12
- }, reply) {
13
- if (!params?.id) {
14
- return reply.status(400).send('not enough params: id');
15
- }
16
-
17
- if (!pg.pk?.['site.media']) {
18
- return reply.status(404).send('table not found');
19
- }
20
-
21
- const { filename, mime, id, url: relpath } = await pg.query(
22
- 'select media_id as id, filename, mime, url from site.media where media_id = $1 and url is not null',
23
- [params.id],
24
- ).then(el => el.rows?.[0] || {});
25
-
26
- if (!id) {
27
- return reply.status(404).send('media not found: ' + params.id);
28
- }
29
-
30
- const filepath = path.join(rootDir, relpath);
31
-
32
- if (!existsSync(filepath)) {
33
- return reply.status(404).send('file not found');
34
- }
35
-
36
- const buffer = await readFile(filepath, { buffer: true });
37
-
38
- // skip xml load for preview
39
- if (params.type === 'preview' && path.extname(filename) !== '.xml') {
40
- return reply.headers({ 'Content-Type': mime }).send(buffer);
41
- }
42
-
43
- return reply
44
- .headers({
45
- 'Content-Type': mime,
46
- 'Content-Disposition': `attachment; filename=${filename || path.basename(filepath)}`,
47
- })
48
- .send(buffer);
49
- }
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
+ // 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
+
10
+ export default async function downloadMedia({
11
+ pg = pgClients.client, params = {},
12
+ }, reply) {
13
+ if (!params?.id) {
14
+ return reply.status(400).send('not enough params: id');
15
+ }
16
+
17
+ if (!pg.pk?.['site.media']) {
18
+ return reply.status(404).send('table not found');
19
+ }
20
+
21
+ const { filename, mime, id, url: relpath } = await pg.query(
22
+ 'select media_id as id, filename, mime, url from site.media where media_id = $1 and url is not null',
23
+ [params.id],
24
+ ).then(el => el.rows?.[0] || {});
25
+
26
+ if (!id) {
27
+ return reply.status(404).send('media not found: ' + params.id);
28
+ }
29
+
30
+ const filepath = path.join(rootDir, relpath);
31
+
32
+ if (!existsSync(filepath)) {
33
+ return reply.status(404).send('file not found');
34
+ }
35
+
36
+ const buffer = await readFile(filepath, { buffer: true });
37
+
38
+ // skip xml load for preview
39
+ if (params.type === 'preview' && path.extname(filename) !== '.xml') {
40
+ return reply.headers({ 'Content-Type': mime }).send(buffer);
41
+ }
42
+
43
+ return reply
44
+ .headers({
45
+ 'Content-Type': mime,
46
+ 'Content-Disposition': `attachment; filename=${filename || path.basename(filepath)}`,
47
+ })
48
+ .send(buffer);
49
+ }
@@ -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,95 +1,95 @@
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, pgClients, getMimeType } from '@opengis/fastify-table/utils.js';
6
- import { createHash } from 'node:crypto';
7
-
8
- // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
9
- const rootDir = path.resolve(getFolder(config, 'local'));
10
- const dir = '/files';
11
-
12
- mkdirSync(path.join(rootDir, dir), { recursive: true });
13
-
14
- export default async function listMedia(req, reply) {
15
- const { pg = pgClients.client, query = {} } = req;
16
- const { subdir: subdir1 = '', search } = query;
17
-
18
- if (!pg.pk?.['site.media']) {
19
- return reply.status(404).send('table not found');
20
- }
21
-
22
- if (typeof subdir1 !== 'string' || subdir1.includes('..')) {
23
- return reply.status(403).send('invalid params: subdir');
24
- }
25
-
26
- const subdir = subdir1.replace(/\/{2,}/g, '/');
27
- const relpath = path.join(dir, subdir1).replace(/\\/g, '/');
28
- const dirpath = path.join(rootDir, relpath).replace(/\\/g, '/');
29
-
30
- if (!existsSync(dirpath)) {
31
- return { data: [], relpath, msg: 'directory not exists' };
32
- }
33
-
34
- const isDirectory = (await stat(dirpath)).isDirectory();
35
-
36
- const allItems = isDirectory ? await readdir(dirpath, { withFileTypes: true, recursive: true }) : [];
37
- const items = isDirectory ? await readdir(dirpath, { withFileTypes: true }) : [];
38
-
39
- const rows = await pg.query(
40
- `select
41
- media_id as id, filename, filetype, filesize, url, description, alt,
42
- mime, preview_url, created_at, updated_at, created_by, updated_by
43
- from site.media
44
- where ${subdir ? 'subdir = $1' : (search ? '1=1' : 'subdir is null')} ${search ? `and filename ilike '%${search.replace(/'/g, "''")}%'` : ''}`,
45
- [subdir].filter(Boolean),
46
- ).then(el => el.rows || []); // ?.filter(row => items.map(el => el.name).includes(row.filename))
47
-
48
- const subdirs = items
49
- .filter(el => el.isDirectory())
50
- .map(el => ({ type: 'dir', name: el.name }))
51
- .filter(el => search ? el.name.includes(search) : true);
52
-
53
- if (subdirs.length) {
54
- await Promise.all(subdirs.map(async (item) => {
55
- const items = isDirectory ? await readdir(path.join(dirpath, item.name)) : [];
56
- Object.assign(item, { count: items.length });
57
- }));
58
- }
59
-
60
- const result = { relpath };
61
-
62
- if (config.debug) {
63
- Object.assign(result, { rootDir });
64
- }
65
-
66
- const files = (search ? allItems.filter(el => el.name.includes(search)) : items)
67
- .filter(el => el.isFile())
68
- .map(el => {
69
- const media = rows.find(row => row.filename === el.name);
70
- const filepath = media ? media.url : ('/files/' + (el.path.split('/files/')[1] ? el.path.split('/files/')[1] + '/' : '') + el.name);
71
- return media ? {
72
- type: 'file',
73
- ...media,
74
- url: `${req.routeOptions.url}/${media.id}/file`,
75
- preview: `${req.routeOptions.url}/${media.id}/preview`,
76
- filepath,
77
- metadata: `${req.routeOptions.url}/${media.id}`,
78
- } : {
79
- id: createHash('md5').update(filepath).digest('hex'),
80
- hash: true,
81
- type: 'file',
82
- filename: el.name,
83
- filepath,
84
- filetype: getMimeType(el.name)?.startsWith('image/') ? "image" : "other",
85
- filesize: 0,
86
- url: filepath,
87
- mime: getMimeType(el.name),
88
- preview: filepath,
89
- }
90
- });
91
-
92
- Object.assign(result, { data: subdirs.concat(files) });
93
- return result;
94
-
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, pgClients, getMimeType } from '@opengis/fastify-table/utils.js';
6
+ import { createHash } from 'node:crypto';
7
+
8
+ // path.resolve() converts POSIX paths from getFolder to valid Windows paths (Bun/Node fs require this on Windows)
9
+ const rootDir = path.resolve(getFolder(config, 'local'));
10
+ const dir = '/files';
11
+
12
+ mkdirSync(path.join(rootDir, dir), { recursive: true });
13
+
14
+ export default async function listMedia(req, reply) {
15
+ const { pg = pgClients.client, query = {} } = req;
16
+ const { subdir: subdir1 = '', search } = query;
17
+
18
+ if (!pg.pk?.['site.media']) {
19
+ return reply.status(404).send('table not found');
20
+ }
21
+
22
+ if (typeof subdir1 !== 'string' || subdir1.includes('..')) {
23
+ return reply.status(403).send('invalid params: subdir');
24
+ }
25
+
26
+ const subdir = subdir1.replace(/\/{2,}/g, '/');
27
+ const relpath = path.join(dir, subdir1).replace(/\\/g, '/');
28
+ const dirpath = path.join(rootDir, relpath).replace(/\\/g, '/');
29
+
30
+ if (!existsSync(dirpath)) {
31
+ return { data: [], relpath, msg: 'directory not exists' };
32
+ }
33
+
34
+ const isDirectory = (await stat(dirpath)).isDirectory();
35
+
36
+ const allItems = isDirectory ? await readdir(dirpath, { withFileTypes: true, recursive: true }) : [];
37
+ const items = isDirectory ? await readdir(dirpath, { withFileTypes: true }) : [];
38
+
39
+ const rows = await pg.query(
40
+ `select
41
+ media_id as id, filename, filetype, filesize, url, description, alt,
42
+ mime, preview_url, created_at, updated_at, created_by, updated_by
43
+ from site.media
44
+ where ${subdir ? 'subdir = $1' : (search ? '1=1' : 'subdir is null')} ${search ? `and filename ilike '%${search.replace(/'/g, "''")}%'` : ''}`,
45
+ [subdir].filter(Boolean),
46
+ ).then(el => el.rows || []); // ?.filter(row => items.map(el => el.name).includes(row.filename))
47
+
48
+ const subdirs = items
49
+ .filter(el => el.isDirectory())
50
+ .map(el => ({ type: 'dir', name: el.name }))
51
+ .filter(el => search ? el.name.includes(search) : true);
52
+
53
+ if (subdirs.length) {
54
+ await Promise.all(subdirs.map(async (item) => {
55
+ const items = isDirectory ? await readdir(path.join(dirpath, item.name)) : [];
56
+ Object.assign(item, { count: items.length });
57
+ }));
58
+ }
59
+
60
+ const result = { relpath };
61
+
62
+ if (config.debug) {
63
+ Object.assign(result, { rootDir });
64
+ }
65
+
66
+ const files = (search ? allItems.filter(el => el.name.includes(search)) : items)
67
+ .filter(el => el.isFile())
68
+ .map(el => {
69
+ const media = rows.find(row => row.filename === el.name);
70
+ const filepath = media ? media.url : ('/files/' + (el.path.split('/files/')[1] ? el.path.split('/files/')[1] + '/' : '') + el.name);
71
+ return media ? {
72
+ type: 'file',
73
+ ...media,
74
+ url: `${req.routeOptions.url}/${media.id}/file`,
75
+ preview: `${req.routeOptions.url}/${media.id}/preview`,
76
+ filepath,
77
+ metadata: `${req.routeOptions.url}/${media.id}`,
78
+ } : {
79
+ id: createHash('md5').update(filepath).digest('hex'),
80
+ hash: true,
81
+ type: 'file',
82
+ filename: el.name,
83
+ filepath,
84
+ filetype: getMimeType(el.name)?.startsWith('image/') ? "image" : "other",
85
+ filesize: 0,
86
+ url: filepath,
87
+ mime: getMimeType(el.name),
88
+ preview: filepath,
89
+ }
90
+ });
91
+
92
+ Object.assign(result, { data: subdirs.concat(files) });
93
+ return result;
94
+
95
95
  }
@@ -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
  }