@opengis/fastify-table 1.0.95 → 1.0.97
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 +8 -0
- package/migration/exec.migrations.js +79 -76
- package/notification/funcs/sendNotification.js +111 -111
- package/package.json +1 -1
- package/server/migrations/properties.sql +31 -0
- package/server/migrations/roles.sql +164 -0
- package/server/migrations/users.sql +89 -0
- package/table/funcs/getFilterSQL/util/getCustomQuery.js +1 -1
package/Changelog.md
CHANGED
|
@@ -1,76 +1,79 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
const time = Date.now();
|
|
5
|
-
|
|
6
|
-
import getPG from '../pg/funcs/getPG.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
await pg
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const time = Date.now();
|
|
5
|
+
|
|
6
|
+
import getPG from '../pg/funcs/getPG.js';
|
|
7
|
+
|
|
8
|
+
function getCallerDir() {
|
|
9
|
+
const originalFunc = Error.prepareStackTrace;
|
|
10
|
+
|
|
11
|
+
let callerfile;
|
|
12
|
+
try {
|
|
13
|
+
const err = new Error();
|
|
14
|
+
// let currentfile;
|
|
15
|
+
|
|
16
|
+
Error.prepareStackTrace = function (err, stack) { return stack; };
|
|
17
|
+
|
|
18
|
+
const currentfile = err.stack.shift().getFileName();
|
|
19
|
+
|
|
20
|
+
while (err.stack.length) {
|
|
21
|
+
callerfile = err.stack.shift().getFileName();
|
|
22
|
+
|
|
23
|
+
if (currentfile !== callerfile) break;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (err) { }
|
|
27
|
+
|
|
28
|
+
Error.prepareStackTrace = originalFunc;
|
|
29
|
+
|
|
30
|
+
return path.dirname(callerfile);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function sequence(files, data, fn) {
|
|
34
|
+
return files.reduce((promise, filename) => promise.then(() => fn({
|
|
35
|
+
...data, filename,
|
|
36
|
+
})), Promise.resolve());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function execSql({
|
|
40
|
+
pg, dir, filename,
|
|
41
|
+
}) {
|
|
42
|
+
const start = Date.now();
|
|
43
|
+
const filepath = path.join(dir, filename);
|
|
44
|
+
const sql = fs.readFileSync(filepath, 'utf-8');
|
|
45
|
+
try {
|
|
46
|
+
console.log(filename, 'start', Date.now() - start);
|
|
47
|
+
await pg.query(sql);
|
|
48
|
+
console.log(filename, 'finish', Date.now() - start);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
console.log(filepath, 'error', err.toString(), Date.now() - start);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default async function execMigrations(opt) {
|
|
56
|
+
try {
|
|
57
|
+
const pg = opt?.pg || getPG({ name: 'client' });
|
|
58
|
+
const rootDir = getCallerDir();
|
|
59
|
+
const dir = path.join(rootDir.replace(/\\/g, '/').replace(/^file:\/\/\//, ''), rootDir.endsWith('plugins') ? '../..' : '', 'server/migrations');
|
|
60
|
+
|
|
61
|
+
console.log('migrations start', dir, Date.now() - time);
|
|
62
|
+
const exists = fs.existsSync(dir);
|
|
63
|
+
if (exists) {
|
|
64
|
+
// get directory sql file list
|
|
65
|
+
const content = fs.readdirSync(dir, { withFileTypes: true })
|
|
66
|
+
?.filter((el) => el.isFile() && path.extname(el.name) === '.sql')
|
|
67
|
+
?.map((el) => el.name) || [];
|
|
68
|
+
|
|
69
|
+
// execute sql files
|
|
70
|
+
if (content?.length) {
|
|
71
|
+
await sequence(content, { pg, dir }, execSql);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
console.log('migrations finish', dir, exists, Date.now() - time);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
console.error('migrations error', err.toString(), Date.now() - time);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
// eslint-disable-next-line max-len, no-control-regex
|
|
2
|
-
const emailReg = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/g;
|
|
3
|
-
|
|
4
|
-
// eslint-disable-next-line import/no-relative-packages
|
|
5
|
-
// import downloadFile from '../../../fastify-file/file/funcs/downloadFile.js'; // test only
|
|
6
|
-
import pgClients from '../../pg/pgClients.js';
|
|
7
|
-
import sendEmail from './utils/sendEmail.js';
|
|
8
|
-
import getTemplate from '../../table/controllers/utils/getTemplate.js';
|
|
9
|
-
import getRedis from '../../redis/funcs/getRedis.js';
|
|
10
|
-
|
|
11
|
-
async function generateNotificationContent({
|
|
12
|
-
pg, funcs, table, template, id, message, data: data1,
|
|
13
|
-
}) {
|
|
14
|
-
if (template) {
|
|
15
|
-
const data = table && id ? await pg.one(`select * from ${table} where ${pg.pk[table]}=$1`, [id]) : data1;
|
|
16
|
-
// console.log(data);
|
|
17
|
-
const body = await getTemplate('pt', template);
|
|
18
|
-
// console.log(body);
|
|
19
|
-
const html = funcs.handlebars.compile(body || 'template not found')(data || {});
|
|
20
|
-
// console.log(html);
|
|
21
|
-
return html;
|
|
22
|
-
}
|
|
23
|
-
return message;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export default async function notification({
|
|
27
|
-
pg: pg1,
|
|
28
|
-
funcs,
|
|
29
|
-
log,
|
|
30
|
-
provider = ['email'],
|
|
31
|
-
from,
|
|
32
|
-
to,
|
|
33
|
-
template,
|
|
34
|
-
table,
|
|
35
|
-
message,
|
|
36
|
-
title,
|
|
37
|
-
file,
|
|
38
|
-
data,
|
|
39
|
-
id,
|
|
40
|
-
nocache,
|
|
41
|
-
}) {
|
|
42
|
-
const rclient = getRedis();
|
|
43
|
-
const pg = pg1 || pgClients.client;
|
|
44
|
-
|
|
45
|
-
if (!pg) throw new Error('need pg');
|
|
46
|
-
|
|
47
|
-
if (pg?.readonly) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const keyTo = `${pg.options?.database}:mail:${provider[0]}:${to || ''}${id || ''}${table || ''}${title || ''}`;
|
|
52
|
-
const uniqueTo = await rclient.setnx(keyTo, 1);
|
|
53
|
-
log.info('notification/sent', { keyTo, send: uniqueTo, nocache });
|
|
54
|
-
|
|
55
|
-
if (!uniqueTo && !nocache) {
|
|
56
|
-
return `already sent: ${keyTo}`;
|
|
57
|
-
}
|
|
58
|
-
await rclient.expire(keyTo, 1000 * 600);
|
|
59
|
-
|
|
60
|
-
if (!to.length) {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!(Array.isArray(provider) && provider.length)) {
|
|
65
|
-
return 'notification provider - must be array and not empty';
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const html = await generateNotificationContent({
|
|
70
|
-
pg, funcs, table, template, id, message, data,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// return html;
|
|
74
|
-
|
|
75
|
-
if (provider.includes('email')) {
|
|
76
|
-
const files = Array.isArray(file) ? file : [file];
|
|
77
|
-
const attachments = files?.length ? await Promise.all(files?.filter((el) => el).map(async (el) => {
|
|
78
|
-
const content = await funcs.downloadFile(el, { funcs, buffer: true }); // ?
|
|
79
|
-
return {
|
|
80
|
-
filename: el.split('/').pop(),
|
|
81
|
-
encoding: 'base64',
|
|
82
|
-
content,
|
|
83
|
-
};
|
|
84
|
-
})) : [];
|
|
85
|
-
|
|
86
|
-
const toEmail = Array.isArray(to) ? to.map((emails) => emails.match(emailReg)?.join(',')) : to;
|
|
87
|
-
|
|
88
|
-
const result = await sendEmail({
|
|
89
|
-
funcs,
|
|
90
|
-
attachments,
|
|
91
|
-
html,
|
|
92
|
-
subject: title,
|
|
93
|
-
from,
|
|
94
|
-
to: toEmail,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
log.info('notification', {
|
|
98
|
-
provider, title, to: toEmail, from, result, len: message?.length, files,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
return result;
|
|
102
|
-
}
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
catch (err) {
|
|
106
|
-
log.info('notification/error', {
|
|
107
|
-
provider, from, to, err: err.toString(),
|
|
108
|
-
});
|
|
109
|
-
throw err;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
1
|
+
// eslint-disable-next-line max-len, no-control-regex
|
|
2
|
+
const emailReg = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/g;
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line import/no-relative-packages
|
|
5
|
+
// import downloadFile from '../../../fastify-file/file/funcs/downloadFile.js'; // test only
|
|
6
|
+
import pgClients from '../../pg/pgClients.js';
|
|
7
|
+
import sendEmail from './utils/sendEmail.js';
|
|
8
|
+
import getTemplate from '../../table/controllers/utils/getTemplate.js';
|
|
9
|
+
import getRedis from '../../redis/funcs/getRedis.js';
|
|
10
|
+
|
|
11
|
+
async function generateNotificationContent({
|
|
12
|
+
pg, funcs, table, template, id, message, data: data1,
|
|
13
|
+
}) {
|
|
14
|
+
if (template) {
|
|
15
|
+
const data = table && id ? await pg.one(`select * from ${table} where ${pg.pk[table]}=$1`, [id]) : data1;
|
|
16
|
+
// console.log(data);
|
|
17
|
+
const body = await getTemplate('pt', template);
|
|
18
|
+
// console.log(body);
|
|
19
|
+
const html = funcs.handlebars.compile(body || template || 'template not found')(data || {});
|
|
20
|
+
// console.log(html);
|
|
21
|
+
return html;
|
|
22
|
+
}
|
|
23
|
+
return message;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default async function notification({
|
|
27
|
+
pg: pg1,
|
|
28
|
+
funcs,
|
|
29
|
+
log,
|
|
30
|
+
provider = ['email'],
|
|
31
|
+
from,
|
|
32
|
+
to,
|
|
33
|
+
template,
|
|
34
|
+
table,
|
|
35
|
+
message,
|
|
36
|
+
title,
|
|
37
|
+
file,
|
|
38
|
+
data,
|
|
39
|
+
id,
|
|
40
|
+
nocache,
|
|
41
|
+
}) {
|
|
42
|
+
const rclient = getRedis();
|
|
43
|
+
const pg = pg1 || pgClients.client;
|
|
44
|
+
|
|
45
|
+
if (!pg) throw new Error('need pg');
|
|
46
|
+
|
|
47
|
+
if (pg?.readonly) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const keyTo = `${pg.options?.database}:mail:${provider[0]}:${to || ''}${id || ''}${table || ''}${title || ''}`;
|
|
52
|
+
const uniqueTo = await rclient.setnx(keyTo, 1);
|
|
53
|
+
log.info('notification/sent', { keyTo, send: uniqueTo, nocache });
|
|
54
|
+
|
|
55
|
+
if (!uniqueTo && !nocache) {
|
|
56
|
+
return `already sent: ${keyTo}`;
|
|
57
|
+
}
|
|
58
|
+
await rclient.expire(keyTo, 1000 * 600);
|
|
59
|
+
|
|
60
|
+
if (!to.length) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!(Array.isArray(provider) && provider.length)) {
|
|
65
|
+
return 'notification provider - must be array and not empty';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const html = await generateNotificationContent({
|
|
70
|
+
pg, funcs, table, template, id, message, data,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// return html;
|
|
74
|
+
|
|
75
|
+
if (provider.includes('email')) {
|
|
76
|
+
const files = Array.isArray(file) ? file : [file];
|
|
77
|
+
const attachments = files?.length ? await Promise.all(files?.filter((el) => el).map(async (el) => {
|
|
78
|
+
const content = await funcs.downloadFile(el, { funcs, buffer: true }); // ?
|
|
79
|
+
return {
|
|
80
|
+
filename: el.split('/').pop(),
|
|
81
|
+
encoding: 'base64',
|
|
82
|
+
content,
|
|
83
|
+
};
|
|
84
|
+
})) : [];
|
|
85
|
+
|
|
86
|
+
const toEmail = Array.isArray(to) ? to.map((emails) => emails.match(emailReg)?.join(',')) : to;
|
|
87
|
+
|
|
88
|
+
const result = await sendEmail({
|
|
89
|
+
funcs,
|
|
90
|
+
attachments,
|
|
91
|
+
html,
|
|
92
|
+
subject: title,
|
|
93
|
+
from,
|
|
94
|
+
to: toEmail,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
log.info('notification', {
|
|
98
|
+
provider, title, to: toEmail, from, result, len: message?.length, files,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
log.info('notification/error', {
|
|
107
|
+
provider, from, to, err: err.toString(),
|
|
108
|
+
});
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
create schema if not exists admin;
|
|
2
|
+
|
|
3
|
+
-- DROP TABLE admin.properties;
|
|
4
|
+
CREATE TABLE IF NOT EXISTS admin.properties();
|
|
5
|
+
ALTER TABLE admin.properties DROP CONSTRAINT IF EXISTS admin_properties_property_id_pkey;
|
|
6
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS property_id text NOT NULL DEFAULT next_id();
|
|
7
|
+
|
|
8
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS property_entity text;
|
|
9
|
+
COMMENT ON COLUMN admin.properties.property_entity IS 'Сутність';
|
|
10
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS property_key text;
|
|
11
|
+
COMMENT ON COLUMN admin.properties.property_key IS 'Ключ';
|
|
12
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS property_text text;
|
|
13
|
+
COMMENT ON COLUMN admin.properties.property_text IS 'Текстове значення налаштування';
|
|
14
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS property_int integer;
|
|
15
|
+
COMMENT ON COLUMN admin.properties.property_int IS 'Цілочислове значения';
|
|
16
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS property_json json;
|
|
17
|
+
COMMENT ON COLUMN admin.properties.property_json IS 'Значення налаштування';
|
|
18
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS level text;
|
|
19
|
+
COMMENT ON COLUMN admin.properties.level IS 'Рівень (user/system)';
|
|
20
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS object_id text;
|
|
21
|
+
COMMENT ON COLUMN admin.properties.object_id IS 'ID Об''єкту';
|
|
22
|
+
|
|
23
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS uid text NOT NULL DEFAULT '1'::text;
|
|
24
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS editor_id text;
|
|
25
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
|
|
26
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT now();
|
|
27
|
+
ALTER TABLE admin.properties ADD COLUMN IF NOT EXISTS files json;
|
|
28
|
+
|
|
29
|
+
ALTER TABLE admin.properties ADD CONSTRAINT admin_properties_property_id_pkey PRIMARY KEY(property_id);
|
|
30
|
+
|
|
31
|
+
COMMENT ON TABLE admin.properties IS 'Налаштування';
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
create schema if not exists admin;
|
|
2
|
+
|
|
3
|
+
-- Role (Group)
|
|
4
|
+
-- drop table if exists admin.roles;
|
|
5
|
+
CREATE TABLE if not exists admin.roles();
|
|
6
|
+
alter table admin.roles DROP CONSTRAINT if exists admin_roles_id_pkey cascade;
|
|
7
|
+
|
|
8
|
+
ALTER TABLE admin.roles ADD COLUMN if not exists role_id text NOT NULL DEFAULT next_id();
|
|
9
|
+
ALTER TABLE admin.roles ADD COLUMN if not exists name text;
|
|
10
|
+
ALTER TABLE admin.roles ADD COLUMN if not exists enabled boolean;
|
|
11
|
+
ALTER TABLE admin.roles ADD COLUMN if not exists info text;
|
|
12
|
+
ALTER TABLE admin.roles ADD COLUMN if not exists cdate timestamp without time zone DEFAULT date_trunc('seconds'::text, now());
|
|
13
|
+
ALTER TABLE admin.roles ADD COLUMN if not exists uid text;
|
|
14
|
+
ALTER TABLE admin.roles ADD COLUMN if not exists editor_id text;
|
|
15
|
+
ALTER TABLE admin.roles ADD COLUMN if not exists editor_date timestamp without time zone;
|
|
16
|
+
|
|
17
|
+
ALTER TABLE admin.roles ADD CONSTRAINT admin_roles_id_pkey PRIMARY KEY(role_id);
|
|
18
|
+
|
|
19
|
+
COMMENT ON TABLE admin.roles IS 'Групи';
|
|
20
|
+
COMMENT ON COLUMN admin.roles.role_id IS 'ID';
|
|
21
|
+
COMMENT ON COLUMN admin.roles.name IS 'Назва групи';
|
|
22
|
+
COMMENT ON COLUMN admin.roles.enabled IS 'On / Off';
|
|
23
|
+
COMMENT ON COLUMN admin.roles.info IS 'Опис групи';
|
|
24
|
+
COMMENT ON COLUMN admin.roles.cdate IS 'Дата створення';
|
|
25
|
+
COMMENT ON COLUMN admin.roles.uid IS 'Хто створив';
|
|
26
|
+
COMMENT ON COLUMN admin.roles.editor_id IS 'Останній редагувач';
|
|
27
|
+
COMMENT ON COLUMN admin.roles.editor_date IS 'Дата останнього редагування';
|
|
28
|
+
|
|
29
|
+
-- Routes (Interfaces)
|
|
30
|
+
-- DROP TABLE if exists admin.routes;
|
|
31
|
+
CREATE TABLE if not exists admin.routes();
|
|
32
|
+
alter table admin.routes DROP CONSTRAINT if exists admin_route_id_pkey cascade;
|
|
33
|
+
alter table admin.routes DROP constraint if exists admin_route_menu_id_fkey cascade;
|
|
34
|
+
|
|
35
|
+
alter table admin.routes add column if not exists route_id text NOT NULL default next_id();
|
|
36
|
+
alter table admin.routes add column if not exists alias text NOT NULL;
|
|
37
|
+
alter table admin.routes add column if not exists table_name text NOT NULL;
|
|
38
|
+
alter table admin.routes add column if not exists title text;
|
|
39
|
+
alter table admin.routes add column if not exists public boolean;
|
|
40
|
+
alter table admin.routes add column if not exists menu_id text;
|
|
41
|
+
alter table admin.routes add column if not exists uid text;
|
|
42
|
+
alter table admin.routes add column if not exists editor_id text;
|
|
43
|
+
alter table admin.routes add column if not exists editor_date timestamp without time zone;
|
|
44
|
+
alter table admin.routes add column if not exists cdate timestamp without time zone DEFAULT now();
|
|
45
|
+
alter table admin.routes add column if not exists enabled boolean NOT NULL DEFAULT true;
|
|
46
|
+
alter table admin.routes add CONSTRAINT admin_route_id_pkey PRIMARY KEY (route_id);
|
|
47
|
+
|
|
48
|
+
COMMENT ON TABLE admin.routes IS 'Список інтерфейсів';
|
|
49
|
+
COMMENT ON COLUMN admin.routes.route_id IS 'Ідентифікатор інтерфейса';
|
|
50
|
+
COMMENT ON COLUMN admin.routes.alias IS 'Назва файлу інтерфейса';
|
|
51
|
+
COMMENT ON COLUMN admin.routes.table_name IS 'Таблиця в БД';
|
|
52
|
+
COMMENT ON COLUMN admin.routes.title IS 'Назва інтерфейсу українською';
|
|
53
|
+
COMMENT ON COLUMN admin.routes.public IS 'Ознака чи для всіх показується';
|
|
54
|
+
COMMENT ON COLUMN admin.routes.enabled IS 'On / Off';
|
|
55
|
+
COMMENT ON COLUMN admin.routes.menu_id IS 'Пункт меню (для collapse)';
|
|
56
|
+
|
|
57
|
+
-- User <> Role (Group)
|
|
58
|
+
-- DROP TABLE if exists admin.user_roles;
|
|
59
|
+
CREATE TABLE if not exists admin.user_roles();
|
|
60
|
+
ALTER TABLE admin.user_roles DROP CONSTRAINT IF EXISTS admin_user_roles_role_id_pkey;
|
|
61
|
+
ALTER TABLE admin.user_roles DROP CONSTRAINT IF EXISTS admin_user_roles_role_id_fkey;
|
|
62
|
+
ALTER TABLE admin.user_roles DROP CONSTRAINT IF EXISTS admin_user_roles_user_uid_fkey;
|
|
63
|
+
ALTER TABLE admin.user_roles DROP CONSTRAINT IF EXISTS admin_user_roles_user_uid_role_id_key;
|
|
64
|
+
|
|
65
|
+
ALTER TABLE admin.user_roles ADD COLUMN IF NOT EXISTS ugr_id text NOT NULL DEFAULT next_id();
|
|
66
|
+
ALTER TABLE admin.user_roles ADD COLUMN IF NOT EXISTS user_uid text NOT NULL;
|
|
67
|
+
ALTER TABLE admin.user_roles ADD COLUMN IF NOT EXISTS role_id text NOT NULL;
|
|
68
|
+
ALTER TABLE admin.user_roles ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT date_trunc('seconds'::text, now());
|
|
69
|
+
ALTER TABLE admin.user_roles ADD COLUMN IF NOT EXISTS uid text;
|
|
70
|
+
ALTER TABLE admin.user_roles ADD COLUMN IF NOT EXISTS editor_id text;
|
|
71
|
+
ALTER TABLE admin.user_roles ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
|
|
72
|
+
ALTER TABLE admin.user_roles ADD COLUMN IF NOT EXISTS expiration date;
|
|
73
|
+
ALTER TABLE admin.user_roles ADD COLUMN IF NOT EXISTS access_granted text;
|
|
74
|
+
ALTER TABLE admin.user_roles ADD COLUMN IF NOT EXISTS access_granted_time timestamp without time zone;
|
|
75
|
+
|
|
76
|
+
ALTER TABLE admin.user_roles ADD CONSTRAINT admin_user_roles_role_id_pkey PRIMARY KEY (ugr_id);
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
COMMENT ON TABLE admin.user_roles IS 'Відношення користувачів до груп';
|
|
80
|
+
COMMENT ON COLUMN admin.user_roles.user_uid IS 'ID користувача';
|
|
81
|
+
COMMENT ON COLUMN admin.user_roles.role_id IS 'ID групи';
|
|
82
|
+
COMMENT ON COLUMN admin.user_roles.uid IS 'Користувач, який створив запис в БД';
|
|
83
|
+
COMMENT ON COLUMN admin.user_roles.expiration IS 'закінчення терміну дії доступу до групи';
|
|
84
|
+
COMMENT ON COLUMN admin.user_roles.access_granted IS 'Ідентифікатор користувача який надав доступ';
|
|
85
|
+
COMMENT ON COLUMN admin.user_roles.access_granted_time IS 'Час коли надали доступ';
|
|
86
|
+
|
|
87
|
+
CREATE INDEX IF NOT EXISTS admin_user_roles_access_user_uid_idx ON admin.user_roles USING btree (user_uid COLLATE pg_catalog."default");
|
|
88
|
+
CREATE INDEX IF NOT EXISTS admin_user_roles_cdate_btree_idx ON admin.user_roles USING btree (cdate);
|
|
89
|
+
CREATE INDEX IF NOT EXISTS admin_user_roles_editor_date_btree_idx ON admin.user_roles USING btree (editor_date);
|
|
90
|
+
CREATE INDEX IF NOT EXISTS admin_user_roles_user_uid_gin_idx ON admin.user_roles USING gin (user_uid COLLATE pg_catalog."default" gin_trgm_ops);
|
|
91
|
+
CREATE INDEX IF NOT EXISTS admin_user_roles_role_id_idx ON admin.user_roles USING btree (role_id COLLATE pg_catalog."default");
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
-- Route (Interface) <> User / Role (Group)
|
|
95
|
+
-- drop table if exists admin.access;
|
|
96
|
+
CREATE TABLE if not exists admin.access();
|
|
97
|
+
alter table admin.access DROP CONSTRAINT if exists admin_access_id_pkey;
|
|
98
|
+
alter table admin.access DROP CONSTRAINT if exists admin_access_role_id_fkey;
|
|
99
|
+
alter table admin.access DROP CONSTRAINT if exists admin_access_route_id_uid_unique;
|
|
100
|
+
alter table admin.access DROP CONSTRAINT if exists admin_access_route_id_fkey;
|
|
101
|
+
|
|
102
|
+
alter table admin.access add column if not exists access_id text NOT NULL DEFAULT next_id();
|
|
103
|
+
alter table admin.access add column if not exists route_id text NOT NULL;
|
|
104
|
+
alter table admin.access add column if not exists role_id text;
|
|
105
|
+
alter table admin.access add column if not exists user_uid text;
|
|
106
|
+
alter table admin.access add column if not exists scope text;
|
|
107
|
+
alter table admin.access add column if not exists actions text[];
|
|
108
|
+
ALTER TABLE admin.access ADD COLUMN IF NOT EXISTS access_granted text;
|
|
109
|
+
ALTER TABLE admin.access ADD COLUMN IF NOT EXISTS access_granted_time timestamp without time zone;
|
|
110
|
+
alter table admin.access add column if not exists cdate timestamp without time zone NOT NULL DEFAULT date_trunc('seconds'::text, now());
|
|
111
|
+
alter table admin.access add column if not exists uid text;
|
|
112
|
+
alter table admin.access add column if not exists editor_id text;
|
|
113
|
+
alter table admin.access add column if not exists editor_date timestamp without time zone;
|
|
114
|
+
|
|
115
|
+
alter table admin.access
|
|
116
|
+
add CONSTRAINT admin_access_id_pkey PRIMARY KEY (access_id);
|
|
117
|
+
|
|
118
|
+
COMMENT ON TABLE admin.access IS 'Налаштування прав. Відношення груп / окремих користувачів до шаблонів';
|
|
119
|
+
COMMENT ON COLUMN admin.access.route_id IS 'ID шаблона';
|
|
120
|
+
COMMENT ON COLUMN admin.access.role_id IS 'ID групи';
|
|
121
|
+
COMMENT ON COLUMN admin.access.user_uid IS 'ID користувача';
|
|
122
|
+
COMMENT ON COLUMN admin.access.scope IS 'Обмеження виведення (власні, відповідальний, всі)';
|
|
123
|
+
COMMENT ON COLUMN admin.access.actions IS 'Доступні дії';
|
|
124
|
+
COMMENT ON COLUMN admin.access.access_granted IS 'Ідентифікатор користувача який надав доступ';
|
|
125
|
+
COMMENT ON COLUMN admin.access.access_granted_time IS 'Час коли надали доступ';
|
|
126
|
+
|
|
127
|
+
CREATE INDEX if not exists admin_access_route_id_idx ON admin.access USING btree (route_id COLLATE pg_catalog."default");
|
|
128
|
+
CREATE INDEX if not exists admin_access_role_id_idx ON admin.access USING btree (role_id COLLATE pg_catalog."default");
|
|
129
|
+
|
|
130
|
+
-- Admin Menu
|
|
131
|
+
-- DROP TABLE if exists admin.menu;
|
|
132
|
+
CREATE TABLE if not exists admin.menu();
|
|
133
|
+
alter table admin.menu DROP CONSTRAINT if exists admin_menu_id_pkey cascade;
|
|
134
|
+
alter table admin.menu DROP CONSTRAINT if exists admin_menu_name_unique;
|
|
135
|
+
|
|
136
|
+
alter table admin.menu add column if not exists menu_id text NOT NULL default next_id();
|
|
137
|
+
alter table admin.menu add column if not exists name text;
|
|
138
|
+
alter table admin.menu add column if not exists ord numeric;
|
|
139
|
+
alter table admin.menu add column if not exists enabled boolean NOT NULL DEFAULT true;
|
|
140
|
+
alter table admin.menu add column if not exists uid text;
|
|
141
|
+
alter table admin.menu add column if not exists editor_id text;
|
|
142
|
+
alter table admin.menu add column if not exists editor_date timestamp without time zone;
|
|
143
|
+
alter table admin.menu add column if not exists cdate timestamp without time zone DEFAULT now();
|
|
144
|
+
alter table admin.menu add CONSTRAINT admin_menu_id_pkey PRIMARY KEY (menu_id);
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
COMMENT ON TABLE admin.menu IS 'Пункти меню';
|
|
148
|
+
COMMENT ON COLUMN admin.menu.menu_id IS 'Ідентифікатор пункту меню';
|
|
149
|
+
COMMENT ON COLUMN admin.menu.name IS 'Назва пункту меню';
|
|
150
|
+
COMMENT ON COLUMN admin.menu.ord IS 'Порядковий номер';
|
|
151
|
+
COMMENT ON COLUMN admin.menu.enabled IS 'On / Off';
|
|
152
|
+
|
|
153
|
+
alter table admin.access
|
|
154
|
+
add CONSTRAINT admin_access_role_id_fkey FOREIGN KEY (role_id) REFERENCES admin.roles (role_id);
|
|
155
|
+
alter table admin.routes add constraint admin_route_menu_id_fkey FOREIGN KEY (menu_id) REFERENCES admin.menu(menu_id);
|
|
156
|
+
|
|
157
|
+
alter table admin.access
|
|
158
|
+
add CONSTRAINT admin_access_route_id_uid_unique UNIQUE (route_id, user_uid, role_id);
|
|
159
|
+
alter table admin.access
|
|
160
|
+
add constraint admin_access_route_id_fkey FOREIGN KEY (route_id) REFERENCES admin.routes(route_id);
|
|
161
|
+
alter table admin.menu add CONSTRAINT admin_menu_name_unique UNIQUE (name);
|
|
162
|
+
ALTER TABLE admin.user_roles ADD CONSTRAINT admin_user_roles_user_role_id_fkey FOREIGN KEY (role_id) REFERENCES admin.roles (role_id);
|
|
163
|
+
ALTER TABLE admin.user_roles ADD CONSTRAINT admin_user_roles_user_uid_fkey FOREIGN KEY (user_uid) REFERENCES admin.users (uid);
|
|
164
|
+
ALTER TABLE admin.user_roles ADD CONSTRAINT admin_user_roles_user_uid_role_id_key UNIQUE (user_uid, role_id);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
create schema if not exists admin;
|
|
2
|
+
|
|
3
|
+
-- DROP TABLE is exists admin.users;
|
|
4
|
+
CREATE TABLE if not exists admin.users();
|
|
5
|
+
ALTER TABLE admin.users add column if not exists uid text NOT NULL DEFAULT next_id();
|
|
6
|
+
ALTER TABLE admin.users DROP CONSTRAINT if exists admin_user_uid_pkey cascade;
|
|
7
|
+
ALTER TABLE admin.users DROP CONSTRAINT if exists user_pk cascade;
|
|
8
|
+
|
|
9
|
+
ALTER TABLE admin.users add column if not exists login text;
|
|
10
|
+
ALTER TABLE admin.users add column if not exists password text NOT NULL DEFAULT ''::text;
|
|
11
|
+
ALTER TABLE admin.users add column if not exists user_name text;
|
|
12
|
+
ALTER TABLE admin.users add column if not exists sur_name text;
|
|
13
|
+
ALTER TABLE admin.users add column if not exists father_name text;
|
|
14
|
+
ALTER TABLE admin.users add column if not exists email text;
|
|
15
|
+
ALTER TABLE admin.users add column if not exists phone text;
|
|
16
|
+
ALTER TABLE admin.users add column if not exists avatar text;
|
|
17
|
+
ALTER TABLE admin.users add column if not exists enabled boolean;
|
|
18
|
+
ALTER TABLE admin.users add column if not exists user_personal_code text;
|
|
19
|
+
ALTER TABLE admin.users add column if not exists last_activity_date timestamp without time zone;
|
|
20
|
+
ALTER TABLE admin.users add column if not exists user_type text DEFAULT 'regular'::text;
|
|
21
|
+
ALTER TABLE admin.users add column if not exists salt text;
|
|
22
|
+
ALTER TABLE admin.users add column if not exists cdate timestamp without time zone DEFAULT date_trunc('seconds'::text, now());
|
|
23
|
+
ALTER TABLE admin.users add column if not exists editor_id text;
|
|
24
|
+
ALTER TABLE admin.users add column if not exists editor_date timestamp without time zone;
|
|
25
|
+
|
|
26
|
+
ALTER TABLE admin.users add CONSTRAINT admin_user_uid_pkey PRIMARY KEY (uid);
|
|
27
|
+
|
|
28
|
+
COMMENT ON TABLE admin.users IS 'Користувачі';
|
|
29
|
+
|
|
30
|
+
COMMENT ON COLUMN admin.users.uid IS 'ID користувача';
|
|
31
|
+
COMMENT ON COLUMN admin.users.login IS 'Логін користувача';
|
|
32
|
+
COMMENT ON COLUMN admin.users.password IS 'Пароль користувача';
|
|
33
|
+
COMMENT ON COLUMN admin.users.user_name IS 'Ім''я користувача';
|
|
34
|
+
COMMENT ON COLUMN admin.users.sur_name IS 'Прізвище користувача';
|
|
35
|
+
COMMENT ON COLUMN admin.users.father_name IS 'По-батькові користувача';
|
|
36
|
+
COMMENT ON COLUMN admin.users.email IS 'Ел. пошта користувача';
|
|
37
|
+
COMMENT ON COLUMN admin.users.phone IS 'Номер телефону користувача';
|
|
38
|
+
COMMENT ON COLUMN admin.users.avatar IS 'Аватар';
|
|
39
|
+
COMMENT ON COLUMN admin.users.enabled IS 'On / Off';
|
|
40
|
+
COMMENT ON COLUMN admin.users.last_activity_date IS 'Дата останньої активності';
|
|
41
|
+
COMMENT ON COLUMN admin.users.user_type IS 'Тип користувача';
|
|
42
|
+
COMMENT ON COLUMN admin.users.salt IS 'Сіль';
|
|
43
|
+
|
|
44
|
+
CREATE EXTENSION if not exists pgcrypto SCHEMA public VERSION "1.3";
|
|
45
|
+
CREATE OR REPLACE FUNCTION admin.crypt(text, text) RETURNS text AS '$libdir/pgcrypto', 'pg_crypt' LANGUAGE c IMMUTABLE STRICT COST 1;
|
|
46
|
+
|
|
47
|
+
-- DROP FUNCTION admin.insert_update_user_before();
|
|
48
|
+
CREATE OR REPLACE FUNCTION admin.insert_update_user_before()
|
|
49
|
+
RETURNS trigger AS
|
|
50
|
+
|
|
51
|
+
$BODY$
|
|
52
|
+
DECLARE
|
|
53
|
+
|
|
54
|
+
iterations int;
|
|
55
|
+
hash character varying;
|
|
56
|
+
|
|
57
|
+
BEGIN
|
|
58
|
+
|
|
59
|
+
if(TG_OP='INSERT' or (TG_OP='UPDATE' and new.password<>old.password)) then
|
|
60
|
+
if(char_length(new.password) <> 0 and char_length(new.password) < 8) then
|
|
61
|
+
--raise exception 'password must be longer than 8 characters';
|
|
62
|
+
end if;
|
|
63
|
+
new.salt=md5(now()::text);
|
|
64
|
+
--raise exception '%','change pass';
|
|
65
|
+
if(new.salt ='') then
|
|
66
|
+
new.salt=gen_salt('md5');
|
|
67
|
+
end if;
|
|
68
|
+
iterations = 10;
|
|
69
|
+
hash='';
|
|
70
|
+
|
|
71
|
+
loop
|
|
72
|
+
if iterations=0 then
|
|
73
|
+
exit;
|
|
74
|
+
end if;
|
|
75
|
+
hash = md5(new.password||hash||new.salt);
|
|
76
|
+
iterations=iterations-1;
|
|
77
|
+
end loop;
|
|
78
|
+
new.password=admin.crypt(hash,new.salt);
|
|
79
|
+
|
|
80
|
+
end if;
|
|
81
|
+
RETURN new;
|
|
82
|
+
END
|
|
83
|
+
$BODY$
|
|
84
|
+
|
|
85
|
+
LANGUAGE plpgsql VOLATILE COST 100;
|
|
86
|
+
|
|
87
|
+
DROP TRIGGER if exists insert_update_user_before on admin.users;
|
|
88
|
+
CREATE TRIGGER insert_update_user_before BEFORE INSERT OR UPDATE ON admin.users FOR EACH ROW
|
|
89
|
+
EXECUTE PROCEDURE admin.insert_update_user_before();
|
|
@@ -4,7 +4,7 @@ async function getCustomQuery({
|
|
|
4
4
|
if (!customFilter) return null;
|
|
5
5
|
const customFilterList = customFilter?.split(',')?.map((el) => el?.split('_').pop());
|
|
6
6
|
const { property_json: customFilterSQL } = await pg.one(`select json_agg(json_build_object('id',property_id,'name',property_key,'query',property_text)
|
|
7
|
-
) as property_json from
|
|
7
|
+
) as property_json from admin.properties where property_key is not null and property_entity='customQuery' and object_id=$1`, [table]);
|
|
8
8
|
const data = customFilterSQL?.length ? customFilterSQL.filter((el) => customFilterList.includes(el.id)) || [] : [];
|
|
9
9
|
const customQuery = data?.map((el) => el.query).join(' and ');
|
|
10
10
|
return `${customQuery}`;
|