@opengis/fastify-table 1.1.20 → 1.1.21
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/Changelog.md +4 -0
- package/index.js +10 -6
- package/module/core/select/core.user_mentioned.sql +2 -0
- package/notification/controllers/readNotifications.js +30 -0
- package/notification/controllers/userNotifications.js +52 -7
- package/notification/funcs/addNotification.js +16 -5
- package/notification/hook/onWidgetSet.js +63 -0
- package/notification/index.js +20 -3
- package/package.json +1 -1
- package/policy/funcs/checkPolicy.js +31 -22
- package/policy/index.js +1 -1
- package/server/migrations/crm.sql +20 -3
- package/utils.js +10 -0
- package/widget/controllers/widget.set.js +6 -5
package/Changelog.md
CHANGED
package/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
3
4
|
|
|
4
5
|
import fp from 'fastify-plugin';
|
|
5
6
|
import config from './config.js';
|
|
@@ -18,7 +19,7 @@ import hookPlugin from './hook/index.js';
|
|
|
18
19
|
|
|
19
20
|
import pgClients from './pg/pgClients.js';
|
|
20
21
|
|
|
21
|
-
import execMigrations from './
|
|
22
|
+
import { addTemplateDir, execMigrations } from './utils.js';
|
|
22
23
|
|
|
23
24
|
async function plugin(fastify, opt) {
|
|
24
25
|
// console.log(opt);
|
|
@@ -68,10 +69,8 @@ async function plugin(fastify, opt) {
|
|
|
68
69
|
await client.query(sql, [JSON.stringify(rows).replace(/'/g, "''")]);
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
|
-
// call from another repo / project
|
|
72
|
-
fastify.execMigrations = execMigrations;
|
|
73
72
|
// execute core migrations
|
|
74
|
-
await
|
|
73
|
+
await execMigrations();
|
|
75
74
|
});
|
|
76
75
|
if (!fastify.funcs) {
|
|
77
76
|
fastify.addHook('onRequest', async (req) => {
|
|
@@ -94,6 +93,11 @@ async function plugin(fastify, opt) {
|
|
|
94
93
|
utilPlugin(fastify, opt);
|
|
95
94
|
cronPlugin(fastify, opt);
|
|
96
95
|
hookPlugin(fastify, opt);
|
|
96
|
+
|
|
97
|
+
// core templates && cls
|
|
98
|
+
const filename = fileURLToPath(import.meta.url);
|
|
99
|
+
const cwd = path.dirname(filename);
|
|
100
|
+
addTemplateDir(path.join(cwd, 'module/core'));
|
|
97
101
|
}
|
|
98
102
|
export default fp(plugin);
|
|
99
103
|
// export { rclient };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export default async function readNotifications({
|
|
2
|
+
pg, params = {}, query = {}, session = {},
|
|
3
|
+
}) {
|
|
4
|
+
const { uid } = session.passport?.user || {};
|
|
5
|
+
|
|
6
|
+
if (!uid) {
|
|
7
|
+
return { message: 'access restricted', status: 403 };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const { userId } = await pg.query('select uid as "userId" from admin.users where $1 in (uid,login) limit 1', [uid])
|
|
12
|
+
.then((res) => res.rows?.[0] || {});
|
|
13
|
+
|
|
14
|
+
if (!userId) {
|
|
15
|
+
return { message: 'access restricted: 2', status: 403 };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const q = `update crm.notifications set read=true where read is not true
|
|
19
|
+
and ${params?.id ? 'notification_id=$2' : '1=1'} and addressee_id=$1`;
|
|
20
|
+
|
|
21
|
+
if (query.sql) return q;
|
|
22
|
+
|
|
23
|
+
const { rowCount = 0 } = await pg.query(q, [userId, params?.id].filter((el) => el));
|
|
24
|
+
|
|
25
|
+
return { message: `${rowCount} unread notifications marked as read`, status: 200 };
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
return { error: err.toString(), status: 500 };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -1,16 +1,61 @@
|
|
|
1
|
+
// for example only
|
|
2
|
+
/*
|
|
3
|
+
const res = await funcs.dataInsert({
|
|
4
|
+
pg,
|
|
5
|
+
table: 'crm.notifications',
|
|
6
|
+
data: {
|
|
7
|
+
subject: 'notif title',
|
|
8
|
+
body: 'notif body',
|
|
9
|
+
link: 'http://localhost:3000/api/notification',
|
|
10
|
+
addressee_id: userId,
|
|
11
|
+
author_id: userId,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { getSelectVal } from '../../utils.js';
|
|
17
|
+
|
|
18
|
+
const maxLimit = 100;
|
|
19
|
+
|
|
1
20
|
export default async function userNotifications({
|
|
2
|
-
pg,
|
|
21
|
+
pg, query = {}, session = {},
|
|
3
22
|
}) {
|
|
4
23
|
const time = Date.now();
|
|
24
|
+
|
|
25
|
+
const { uid } = session.passport?.user || {};
|
|
26
|
+
|
|
27
|
+
if (!uid) {
|
|
28
|
+
return { message: 'access restricted', status: 403 };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const limit = Math.min(maxLimit, +(query.limit || 5));
|
|
32
|
+
const offset = query.page && query.page > 0 ? (query.page - 1) * limit : 0;
|
|
33
|
+
|
|
5
34
|
try {
|
|
6
|
-
const {
|
|
7
|
-
|
|
35
|
+
const { userId } = await pg.query('select uid as "userId" from admin.users where $1 in (uid,login) limit 1', [uid])
|
|
36
|
+
.then((res) => res.rows?.[0] || {});
|
|
37
|
+
|
|
38
|
+
if (!userId) {
|
|
39
|
+
return { message: 'access restricted: 2', status: 403 };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const q = `select notification_id as id, subject, body, cdate,
|
|
43
|
+
author_id, read, link, entity_id, (select avatar from admin.users where uid=a.author_id limit 1) as avatar from crm.notifications a where addressee_id=$1 order by cdate desc limit ${limit} offset ${offset}`;
|
|
44
|
+
|
|
45
|
+
if (query.sql) return q;
|
|
46
|
+
|
|
47
|
+
const { rows = [] } = await pg.query(q, [userId]);
|
|
48
|
+
|
|
49
|
+
const values = rows.map((el) => el.author_id)
|
|
50
|
+
?.filter((el, idx, arr) => el && arr.indexOf(el) === idx);
|
|
8
51
|
|
|
9
|
-
|
|
52
|
+
if (values?.length) {
|
|
53
|
+
const vals = await getSelectVal({ name: 'core.user_mentioned', values });
|
|
54
|
+
rows.forEach((row) => {
|
|
55
|
+
Object.assign(row, { author: vals?.[row.author_id] });
|
|
56
|
+
});
|
|
57
|
+
}
|
|
10
58
|
|
|
11
|
-
// queryCache not supports $1 params
|
|
12
|
-
const { rows } = await queryFunc(`select notification_id as id, notification_type as type,
|
|
13
|
-
notification_status as status, title, body, link, uid from crm.notification where uid='${uid}'`);
|
|
14
59
|
return { time: Date.now() - time, total: rows?.length, rows };
|
|
15
60
|
}
|
|
16
61
|
catch (err) {
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
export default async function addNotification({
|
|
2
|
-
pg, session = {},
|
|
2
|
+
pg, funcs, session = {}, subject, body, link, uid, entity,
|
|
3
3
|
}) {
|
|
4
|
-
const uid
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const { uid: author } = session.passport?.user || {};
|
|
5
|
+
const res = await funcs.dataInsert({
|
|
6
|
+
pg,
|
|
7
|
+
table: 'crm.notifications',
|
|
8
|
+
data: {
|
|
9
|
+
subject,
|
|
10
|
+
body,
|
|
11
|
+
link,
|
|
12
|
+
addressee_id: uid,
|
|
13
|
+
author_id: author,
|
|
14
|
+
entity_id: entity,
|
|
15
|
+
uid: author,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
return res?.rows?.[0] || {};
|
|
8
19
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import {
|
|
3
|
+
getSelect, addNotification, sendNotification,
|
|
4
|
+
} from '../../utils.js';
|
|
5
|
+
|
|
6
|
+
function sequence(arr, data, fn) {
|
|
7
|
+
return arr.reduce((promise, row) => promise.then(() => fn({
|
|
8
|
+
...data, ...row,
|
|
9
|
+
})), Promise.resolve());
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default async function onWidgetSet({ req, type, data = {} }) {
|
|
13
|
+
const values = data.body?.match(/\B@[a-zA-z0-9а-яА-яіїІЇєЄ]+ [a-zA-z0-9а-яА-яіїІЇєЄ]+/g)
|
|
14
|
+
?.filter((el, idx, arr) => el?.replace && arr.indexOf(el) === idx)
|
|
15
|
+
?.map((el) => el.replace(/@/g, ''));
|
|
16
|
+
|
|
17
|
+
if (type !== 'comment' || !values?.length) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
pg, funcs, path: link, session = {}, log,
|
|
23
|
+
} = req;
|
|
24
|
+
const { config = {} } = funcs;
|
|
25
|
+
|
|
26
|
+
const { sql } = await getSelect('core.user_mentioned');
|
|
27
|
+
|
|
28
|
+
const { rows = [] } = await pg.query(`with data (id,name,email) as (${sql})
|
|
29
|
+
select id, name, email from data where name = any($1)`, [values]);
|
|
30
|
+
|
|
31
|
+
if (!rows?.length) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const message = `${data.body?.substring(0, 50)}...`;
|
|
36
|
+
const subject = 'You were mentioned';
|
|
37
|
+
|
|
38
|
+
await sequence(rows, {}, async ({ id, name, email }) => {
|
|
39
|
+
const res = await addNotification({
|
|
40
|
+
pg, funcs, session, subject, entity: data.entity_id, body: message, link, uid: id,
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
const res1 = await sendNotification({
|
|
44
|
+
pg,
|
|
45
|
+
funcs,
|
|
46
|
+
log,
|
|
47
|
+
to: email,
|
|
48
|
+
// template,
|
|
49
|
+
message,
|
|
50
|
+
title: subject,
|
|
51
|
+
data,
|
|
52
|
+
nocache: 1,
|
|
53
|
+
});
|
|
54
|
+
if (config.local) console.info('comment notification', name, id, email, res1);
|
|
55
|
+
await pg.query('update crm.notifications set sent=true where notification_id=$1', [res?.notification_id]);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
console.error('comment notification send error', err.toString());
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
console.log('comment notification add', rows);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
package/notification/index.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
// api
|
|
2
|
-
import
|
|
2
|
+
import readNotifications from './controllers/readNotifications.js'; // mark as read
|
|
3
|
+
import userNotifications from './controllers/userNotifications.js'; // check all, backend pagination
|
|
3
4
|
import testEmail from './controllers/testEmail.js';
|
|
4
5
|
// funcs
|
|
5
6
|
import addNotification from './funcs/addNotification.js'; // add to db
|
|
6
|
-
import notification from './funcs/sendNotification.js'; // send
|
|
7
|
+
import notification from './funcs/sendNotification.js'; // send notification
|
|
8
|
+
|
|
9
|
+
import onWidgetSet from './hook/onWidgetSet.js'; // send notification on comment
|
|
10
|
+
import { addHook } from '../utils.js';
|
|
7
11
|
|
|
8
12
|
const tableSchema = {
|
|
13
|
+
params: {
|
|
14
|
+
id: { type: 'string' },
|
|
15
|
+
},
|
|
9
16
|
querystring: {
|
|
10
17
|
nocache: { type: 'string', pattern: '^(\\d+)$' },
|
|
11
18
|
},
|
|
@@ -17,11 +24,20 @@ async function plugin(fastify, config = {}) {
|
|
|
17
24
|
method: 'GET',
|
|
18
25
|
url: `${prefix}/notification`,
|
|
19
26
|
config: {
|
|
20
|
-
policy: ['user'],
|
|
27
|
+
policy: ['user'],
|
|
21
28
|
},
|
|
22
29
|
schema: tableSchema,
|
|
23
30
|
handler: userNotifications,
|
|
24
31
|
});
|
|
32
|
+
fastify.route({
|
|
33
|
+
method: 'GET',
|
|
34
|
+
url: `${prefix}/notification-read/:id?`,
|
|
35
|
+
config: {
|
|
36
|
+
policy: ['user'],
|
|
37
|
+
},
|
|
38
|
+
schema: tableSchema,
|
|
39
|
+
handler: readNotifications,
|
|
40
|
+
});
|
|
25
41
|
fastify.route({
|
|
26
42
|
method: 'GET',
|
|
27
43
|
url: `${prefix}/test-email`,
|
|
@@ -33,6 +49,7 @@ async function plugin(fastify, config = {}) {
|
|
|
33
49
|
|
|
34
50
|
fastify.decorate('addNotification', addNotification);
|
|
35
51
|
fastify.decorate('notification', notification);
|
|
52
|
+
addHook('onWidgetSet', onWidgetSet);
|
|
36
53
|
}
|
|
37
54
|
|
|
38
55
|
export default plugin;
|
package/package.json
CHANGED
|
@@ -24,60 +24,69 @@ export default function checkPolicy(req) {
|
|
|
24
24
|
|
|
25
25
|
/*= == 0.Check superadmin access === */
|
|
26
26
|
if (policy.includes('superadmin') && user?.user_type !== 'superadmin') {
|
|
27
|
-
log.warn({
|
|
28
|
-
|
|
27
|
+
log.warn('api/superadmin', {
|
|
28
|
+
path, params, query, body: JSON.stringify(req?.body || {}).substring(30), message: 'access restricted: 0',
|
|
29
29
|
});
|
|
30
30
|
return { message: 'access restricted: 0', status: 403 };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/*= == 1.File injection === */
|
|
34
34
|
if (JSON.stringify(params || {})?.includes('../') || JSON.stringify(query || {})?.includes('../') || path?.includes('../')) {
|
|
35
|
-
log.warn({
|
|
36
|
-
|
|
35
|
+
log.warn('injection/file', {
|
|
36
|
+
path, params, query, message: 'access restricted: 1',
|
|
37
37
|
});
|
|
38
38
|
return { message: 'access restricted: 1', status: 403 };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
/* === 1.1 File === */
|
|
42
42
|
const allowExtPublic = ['.png', '.jpg', '.svg'];
|
|
43
43
|
const ext = path.toLowerCase().substr(-4);
|
|
44
44
|
if (path.includes('files/') && allowExtPublic.includes(ext)) return null;
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
/* === 2.SQL Injection policy: no-sql === */
|
|
47
47
|
if (!policy.includes('no-sql')) {
|
|
48
48
|
// skip polyline param - data filter (geometry bounds)
|
|
49
49
|
const stopWords = block.filter((el) => path.replace(query.polyline, '').includes(el));
|
|
50
50
|
if (stopWords?.length) {
|
|
51
|
-
log.warn(
|
|
51
|
+
log.warn('injection/sql', { stopWords, message: 'access restricted: 2', path });
|
|
52
52
|
return { message: 'access restricted: 2', status: 403 };
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
/*
|
|
56
|
-
const isApi = ['/files/', '/api/format/', '/api-user/', '/logger', '/file/'].filter((el) => path.includes(el)).length;
|
|
57
|
-
if (!isApi)
|
|
58
|
-
|
|
59
|
-
/*= == 3. policy: referer === */
|
|
60
|
-
if (!hs?.referer?.includes?.(hostname) && policy.includes('referer') && !config.local && !config.debug) {
|
|
61
|
-
log.warn({ name: 'referer', message: 'access restricted: 3' });
|
|
62
|
-
return { message: 'access restricted: 3', status: 403 };
|
|
55
|
+
/* policy: skip if not API */
|
|
56
|
+
const isApi = ['/files/', '/api/format/', '/api', '/api-user/', '/logger', '/file/'].filter((el) => path.includes(el)).length;
|
|
57
|
+
if (!isApi) {
|
|
58
|
+
return null;
|
|
63
59
|
}
|
|
64
60
|
|
|
65
|
-
|
|
61
|
+
/* === policy: public === */
|
|
66
62
|
if (policy.includes('public')) {
|
|
67
63
|
return null;
|
|
68
64
|
}
|
|
69
65
|
|
|
70
|
-
|
|
71
|
-
if (!policy.includes('
|
|
72
|
-
log.warn(
|
|
66
|
+
/* === 3. policy: user === */
|
|
67
|
+
if (!user && policy.includes('user') && false) {
|
|
68
|
+
log.warn('policy/user', { message: 'access restricted: 3', path });
|
|
69
|
+
return { message: 'access restricted: 3', status: 403 };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* === 4. policy: referer === */
|
|
73
|
+
if (!hs?.referer?.includes?.(hostname) && policy.includes('referer') && !config.local && !config.debug) {
|
|
74
|
+
log.warn('policy/referer', { message: 'access restricted: 4', uid: user?.uid });
|
|
73
75
|
return { message: 'access restricted: 4', status: 403 };
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
if (sid ===
|
|
78
|
-
log.warn(
|
|
78
|
+
/* === 5. policy: site auth === */
|
|
79
|
+
if (!policy.includes('site') && sid === 1 && isUser && !config.local && !config.debug) {
|
|
80
|
+
log.warn('policy/site', { message: 'access restricted: 5', path, uid: user?.uid });
|
|
79
81
|
return { message: 'access restricted: 5', status: 403 };
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
/* === 6. base policy: block api, except login === */
|
|
85
|
+
if (sid === 35 && !isUser && isServer && !config.local && !config.debug
|
|
86
|
+
&& !path.startsWith(`${config.prefix || '/api'}/login`)) {
|
|
87
|
+
log.warn('policy/api', { message: 'access restricted: 6', path, uid: user?.uid });
|
|
88
|
+
return { message: 'access restricted: 6', status: 403 };
|
|
89
|
+
}
|
|
90
|
+
|
|
82
91
|
return null;
|
|
83
92
|
}
|
package/policy/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import checkPolicy from './funcs/checkPolicy.js';
|
|
4
4
|
|
|
5
5
|
async function plugin(fastify) {
|
|
6
|
-
fastify.addHook('
|
|
6
|
+
fastify.addHook('preParsing', async (request, reply) => {
|
|
7
7
|
const hookData = checkPolicy(request);
|
|
8
8
|
if (hookData?.status && hookData?.message) {
|
|
9
9
|
return reply.status(hookData?.status).send(hookData.message);
|
|
@@ -6,12 +6,20 @@ CREATE TABLE IF NOT EXISTS crm.notifications();
|
|
|
6
6
|
ALTER TABLE crm.notifications DROP CONSTRAINT IF EXISTS crm_notifications_pkey;
|
|
7
7
|
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS notification_id text NOT NULL DEFAULT next_id();
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
ALTER TABLE crm.notifications
|
|
11
|
-
ALTER TABLE crm.notifications
|
|
9
|
+
-- drop deprecated columns
|
|
10
|
+
ALTER TABLE crm.notifications DROP COLUMN IF EXISTS notification_user_id;
|
|
11
|
+
ALTER TABLE crm.notifications DROP COLUMN IF EXISTS notification_type;
|
|
12
|
+
ALTER TABLE crm.notifications DROP COLUMN IF EXISTS notification_status;
|
|
13
|
+
|
|
14
|
+
-- add actual columns
|
|
15
|
+
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS addressee_id text;
|
|
16
|
+
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS read boolean;
|
|
17
|
+
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS sent boolean;
|
|
12
18
|
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS subject text;
|
|
13
19
|
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS body text;
|
|
14
20
|
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS link text;
|
|
21
|
+
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS author_id text;
|
|
22
|
+
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS entity_id text;
|
|
15
23
|
|
|
16
24
|
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS uid text;
|
|
17
25
|
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS files json;
|
|
@@ -20,6 +28,15 @@ ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS editor_id text;
|
|
|
20
28
|
ALTER TABLE crm.notifications ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
|
|
21
29
|
ALTER TABLE crm.notifications ADD CONSTRAINT crm_notifications_pkey PRIMARY KEY (notification_id);
|
|
22
30
|
|
|
31
|
+
COMMENT ON COLUMN crm.notifications.addressee_id is 'ID користувача отримувача повідомлення';
|
|
32
|
+
COMMENT ON COLUMN crm.notifications.read is 'Чи було повідомлення прочитане';
|
|
33
|
+
COMMENT ON COLUMN crm.notifications.sent is 'Чи було повідомлення відправлене';
|
|
34
|
+
COMMENT ON COLUMN crm.notifications.subject is 'Тема повідомлення';
|
|
35
|
+
COMMENT ON COLUMN crm.notifications.body is 'Зміст повідомлення';
|
|
36
|
+
COMMENT ON COLUMN crm.notifications.link is 'Посилання на об''єкт';
|
|
37
|
+
COMMENT ON COLUMN crm.notifications.author_id is 'ID користувача автора повідомлення';
|
|
38
|
+
COMMENT ON COLUMN crm.notifications.entity_id is 'ID на об''єкту';
|
|
39
|
+
|
|
23
40
|
-- crm.files
|
|
24
41
|
-- DROP TABLE IF EXISTS crm.files;
|
|
25
42
|
CREATE TABLE IF NOT EXISTS crm.files();
|
package/utils.js
CHANGED
|
@@ -24,8 +24,13 @@ import gisIRColumn from './table/controllers/utils/gisIRColumn.js';
|
|
|
24
24
|
import getMeta from './pg/funcs/getMeta.js';
|
|
25
25
|
import getAccess from './crud/funcs/getAccess.js';
|
|
26
26
|
import getSelectVal from './table/funcs/metaFormat/getSelectVal.js';
|
|
27
|
+
import getSelect from './table/controllers/utils/getSelect.js';
|
|
28
|
+
import getSelectMeta from './table/controllers/utils/getSelectMeta.js';
|
|
27
29
|
import applyHook from './hook/funcs/applyHook.js';
|
|
28
30
|
import addHook from './hook/funcs/addHook.js';
|
|
31
|
+
import execMigrations from './migration/exec.migrations.js';
|
|
32
|
+
import addNotification from './notification/funcs/addNotification.js';
|
|
33
|
+
import sendNotification from './notification/funcs/sendNotification.js';
|
|
29
34
|
|
|
30
35
|
export default null;
|
|
31
36
|
export {
|
|
@@ -49,6 +54,11 @@ export {
|
|
|
49
54
|
getMeta,
|
|
50
55
|
getAccess,
|
|
51
56
|
getSelectVal,
|
|
57
|
+
getSelectMeta,
|
|
58
|
+
getSelect,
|
|
52
59
|
applyHook,
|
|
53
60
|
addHook,
|
|
61
|
+
execMigrations,
|
|
62
|
+
addNotification,
|
|
63
|
+
sendNotification,
|
|
54
64
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
getMeta, dataInsert, dataUpdate, applyHook,
|
|
5
|
+
} from '../../utils.js';
|
|
6
6
|
|
|
7
7
|
const tableList = {
|
|
8
8
|
comment: 'crm.communications',
|
|
@@ -21,8 +21,8 @@ export default async function widgetSet(req) {
|
|
|
21
21
|
} = req;
|
|
22
22
|
const { user = {} } = session.passport || {};
|
|
23
23
|
const { type, id, objectid } = params;
|
|
24
|
-
if (!['comment', 'checklist', 'file', 'gallery'].includes(type)) return {
|
|
25
|
-
if (!objectid) return {
|
|
24
|
+
if (!['comment', 'checklist', 'file', 'gallery'].includes(type)) return { message: 'param type not valid', status: 400 };
|
|
25
|
+
if (!objectid) return { message: 'id required', status: 400 };
|
|
26
26
|
|
|
27
27
|
const table = tableList[type];
|
|
28
28
|
|
|
@@ -57,6 +57,7 @@ export default async function widgetSet(req) {
|
|
|
57
57
|
|
|
58
58
|
const data = { ...body, uid: user?.uid, entity_id: objectid };
|
|
59
59
|
|
|
60
|
+
await applyHook('onWidgetSet', { req, type, data });
|
|
60
61
|
const result = id
|
|
61
62
|
? await dataUpdate({
|
|
62
63
|
table, data, id, uid: user?.uid,
|