@opengis/fastify-table 1.0.61 → 1.0.62
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/notification/controllers/testEmail.js +49 -0
- package/notification/controllers/utils/pin-m-ty-media-record-outline+303070.png +0 -0
- package/notification/funcs/sendNotification.js +104 -0
- package/notification/funcs/utils/sendEmail.js +39 -0
- package/notification/index.js +31 -19
- package/package.json +3 -2
- package/table/controllers/utils/getTemplates.js +18 -18
package/Changelog.md
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const fileName = fileURLToPath(import.meta.url);
|
|
6
|
+
const dirName = path.dirname(fileName);
|
|
7
|
+
|
|
8
|
+
import notification from '../funcs/sendNotification.js';
|
|
9
|
+
|
|
10
|
+
export default async function testNotification({
|
|
11
|
+
pg, funcs = {}, log, query = {}, session = {},
|
|
12
|
+
}) {
|
|
13
|
+
const { local } = funcs.config || {};
|
|
14
|
+
if (!session?.passport?.user?.user_type?.includes('admin') && !local) {
|
|
15
|
+
return { message: 'Forbidden', status: 403 };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const date = new Date().toISOString().split('T')[0];
|
|
19
|
+
if (!query.to) {
|
|
20
|
+
return { message: 'param to is required', status: 400 };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const {
|
|
25
|
+
to, template, table, id, nocache,
|
|
26
|
+
} = query;
|
|
27
|
+
const file = [path.join(dirName, '../../', 'changelog.md'), path.join(dirName, 'utils', 'pin-m-ty-media-record-outline+303070.png')].filter((el) => existsSync(el));
|
|
28
|
+
const data = await notification({
|
|
29
|
+
pg,
|
|
30
|
+
funcs,
|
|
31
|
+
log,
|
|
32
|
+
to,
|
|
33
|
+
template,
|
|
34
|
+
title: `Test Softpro ${date}`,
|
|
35
|
+
table,
|
|
36
|
+
nocache,
|
|
37
|
+
file,
|
|
38
|
+
id,
|
|
39
|
+
message: `Test mail Softpro ${date} Lorem Ipsum Lorem Ipsum`,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
message: data || 'ok',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
return { error: err.toString(), status: 500 };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
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 sendEmail from './utils/sendEmail.js';
|
|
7
|
+
|
|
8
|
+
async function generateNotificationContent({
|
|
9
|
+
pg, funcs, table, template, id, message,
|
|
10
|
+
}) {
|
|
11
|
+
if (table && template && id) {
|
|
12
|
+
const data = await pg.one(`select * from ${table} where ${pg.pk[table]}=$1`, [id]);
|
|
13
|
+
// console.log(data);
|
|
14
|
+
const body = await funcs.getTemplate('pt', template);
|
|
15
|
+
// console.log(body);
|
|
16
|
+
const html = funcs.handlebars.compile(body || 'template not found')(data);
|
|
17
|
+
// console.log(html);
|
|
18
|
+
return html;
|
|
19
|
+
}
|
|
20
|
+
return message;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default async function notification({
|
|
24
|
+
pg,
|
|
25
|
+
funcs,
|
|
26
|
+
log,
|
|
27
|
+
provider = ['email'],
|
|
28
|
+
from,
|
|
29
|
+
to,
|
|
30
|
+
template,
|
|
31
|
+
table,
|
|
32
|
+
message,
|
|
33
|
+
title,
|
|
34
|
+
file,
|
|
35
|
+
id,
|
|
36
|
+
nocache,
|
|
37
|
+
}) {
|
|
38
|
+
const rclient = funcs.getRedis();
|
|
39
|
+
|
|
40
|
+
if (pg?.readonly) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const keyTo = `${pg.options?.database}:mail:${provider[0]}:${to || ''}${id || ''}${table || ''}${title || ''}`;
|
|
45
|
+
const uniqueTo = await rclient.setnx(keyTo, 1);
|
|
46
|
+
log.info('notification/sent', { keyTo, send: uniqueTo, nocache });
|
|
47
|
+
|
|
48
|
+
if (!uniqueTo && !nocache) {
|
|
49
|
+
return `already sent: ${keyTo}`;
|
|
50
|
+
}
|
|
51
|
+
await rclient.expire(keyTo, 1000 * 600);
|
|
52
|
+
|
|
53
|
+
if (!to.length) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!(Array.isArray(provider) && provider.length)) {
|
|
58
|
+
return 'notification provider - must be array and not empty';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const html = await generateNotificationContent({
|
|
63
|
+
pg, funcs, table, template, id, message,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// return html;
|
|
67
|
+
|
|
68
|
+
if (provider.includes('email')) {
|
|
69
|
+
const files = Array.isArray(file) ? file : [file];
|
|
70
|
+
const attachments = files?.length ? await Promise.all(files?.filter((el) => el).map(async (el) => {
|
|
71
|
+
const content = await funcs.downloadFile(el, { funcs, buffer: true }); // ?
|
|
72
|
+
return {
|
|
73
|
+
filename: el.split('/').pop(),
|
|
74
|
+
encoding: 'base64',
|
|
75
|
+
content,
|
|
76
|
+
};
|
|
77
|
+
})) : [];
|
|
78
|
+
|
|
79
|
+
const toEmail = Array.isArray(to) ? to.map((emails) => emails.match(emailReg)?.join(',')) : to;
|
|
80
|
+
|
|
81
|
+
const result = await sendEmail({
|
|
82
|
+
funcs,
|
|
83
|
+
attachments,
|
|
84
|
+
html,
|
|
85
|
+
subject: title,
|
|
86
|
+
from,
|
|
87
|
+
to: toEmail,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
log.info('notification', {
|
|
91
|
+
provider, title, to: toEmail, from, result, len: message?.length, files,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
log.info('notification/error', {
|
|
100
|
+
provider, from, to, err: err.toString(),
|
|
101
|
+
});
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Надсилає поваідомлення на пошту
|
|
5
|
+
*
|
|
6
|
+
* @type function
|
|
7
|
+
* @alias sendEmail
|
|
8
|
+
* @summary Функція здійснює розсилку по email
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export default async function sendEmail({
|
|
12
|
+
funcs, to, from, subject, html, attachments,
|
|
13
|
+
}) {
|
|
14
|
+
const { config = {} } = funcs;
|
|
15
|
+
|
|
16
|
+
if (!to?.length) {
|
|
17
|
+
throw new Error('empty to list');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { mailSetting = {} } = config;
|
|
21
|
+
|
|
22
|
+
/*= == check service and setting === */
|
|
23
|
+
if (!mailSetting.service) {
|
|
24
|
+
throw new Error('service is not defined in config');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Object.assign(mailSetting, { rejectUnauthorized: false });
|
|
28
|
+
|
|
29
|
+
if (mailSetting.port === 465) {
|
|
30
|
+
Object.assign(mailSetting, { secure: true });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const transport = nodemailer.createTransport(mailSetting);
|
|
34
|
+
|
|
35
|
+
const result = await transport.sendMail({
|
|
36
|
+
from: from || mailSetting.from, to, subject, html, attachments,
|
|
37
|
+
});
|
|
38
|
+
return result;
|
|
39
|
+
}
|
package/notification/index.js
CHANGED
|
@@ -1,19 +1,31 @@
|
|
|
1
|
-
// api
|
|
2
|
-
import userNotifications from './controllers/userNotifications.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
// api
|
|
2
|
+
import userNotifications from './controllers/userNotifications.js';
|
|
3
|
+
import testEmail from './controllers/testEmail.js';
|
|
4
|
+
// funcs
|
|
5
|
+
import addNotification from './funcs/addNotification.js'; // add to db
|
|
6
|
+
import notification from './funcs/sendNotification.js'; // send
|
|
7
|
+
|
|
8
|
+
async function plugin(fastify, config = {}) {
|
|
9
|
+
const prefix = config.prefix || '/api';
|
|
10
|
+
fastify.route({
|
|
11
|
+
method: 'GET',
|
|
12
|
+
url: `${prefix}/notification`,
|
|
13
|
+
config: {
|
|
14
|
+
policy: ['user'], // implement user auth check policy??
|
|
15
|
+
},
|
|
16
|
+
handler: userNotifications,
|
|
17
|
+
});
|
|
18
|
+
fastify.route({
|
|
19
|
+
method: 'GET',
|
|
20
|
+
url: `${prefix}/test-email`,
|
|
21
|
+
config: {
|
|
22
|
+
policy: ['user'],
|
|
23
|
+
},
|
|
24
|
+
handler: testEmail,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
fastify.decorate('addNotification', addNotification);
|
|
28
|
+
fastify.decorate('notification', notification);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default plugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengis/fastify-table",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.62",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "core-plugins",
|
|
6
6
|
"main": "index.js",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"fastify": "^4.26.1",
|
|
14
14
|
"fastify-plugin": "^4.0.0",
|
|
15
15
|
"ioredis": "^5.3.2",
|
|
16
|
-
"pg": "^8.11.3"
|
|
16
|
+
"pg": "^8.11.3",
|
|
17
|
+
"nodemailer": "^6.5.0"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
19
20
|
"eslint": "^8.49.0",
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import config from '../../../config.js';
|
|
4
|
-
|
|
5
|
-
const loadTemplate = {};
|
|
6
|
-
|
|
7
|
-
export default async function getTemplateDir(type) {
|
|
8
|
-
if (!type) return null;
|
|
9
|
-
|
|
10
|
-
const cwd = process.cwd();
|
|
11
|
-
const typeDir = path.join(cwd, (config.templateDir || 'server/templates'), type);
|
|
12
|
-
|
|
13
|
-
if (!loadTemplate[type]) {
|
|
14
|
-
const typeList = fs.existsSync(typeDir) ? fs.readdirSync(typeDir) : [];
|
|
15
|
-
loadTemplate[type] = typeList;
|
|
16
|
-
}
|
|
17
|
-
return loadTemplate[type];
|
|
18
|
-
}
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import config from '../../../config.js';
|
|
4
|
+
|
|
5
|
+
const loadTemplate = {};
|
|
6
|
+
|
|
7
|
+
export default async function getTemplateDir(type) {
|
|
8
|
+
if (!type) return null;
|
|
9
|
+
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const typeDir = path.join(cwd, (config.templateDir || 'server/templates'), type);
|
|
12
|
+
|
|
13
|
+
if (!loadTemplate[type]) {
|
|
14
|
+
const typeList = fs.existsSync(typeDir) ? fs.readdirSync(typeDir) : [];
|
|
15
|
+
loadTemplate[type] = typeList;
|
|
16
|
+
}
|
|
17
|
+
return loadTemplate[type];
|
|
18
|
+
}
|