@adaptivestone/framework 5.0.0-beta.8 → 5.0.0-beta.9
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 +10 -1
- package/controllers/Auth.js +1 -4
- package/folderConfig.js +0 -1
- package/models/User.js +23 -4
- package/package.json +10 -8
- package/server.d.ts +5 -4
- package/server.js +4 -2
- package/services/cache/Cache.d.ts +1 -1
- package/services/cache/Cache.js +2 -2
- package/services/http/HttpServer.js +0 -6
- package/services/http/middleware/RateLimiter.js +2 -2
- package/services/messaging/email/templates/.gitkeep +0 -0
- package/tests/setup.js +2 -3
- package/tests/setupVitest.js +2 -3
- package/types/TFoldersConfig.d.ts +7 -4
- package/config/mail.js +0 -29
- package/services/messaging/email/index.js +0 -217
- package/services/messaging/email/templates/emptyTemplate/html.pug +0 -9
- package/services/messaging/email/templates/emptyTemplate/subject.pug +0 -1
- package/services/messaging/email/templates/emptyTemplate/text.pug +0 -1
- package/services/messaging/index.js +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
### 5.0.0-beta.9
|
|
2
|
+
|
|
3
|
+
[BREAKING] move email module to separate package @adaptivestone/framework-module-email. Please use it if you want to send emails
|
|
4
|
+
[NEW] app now contains 'frameworkFolder' folder the framework located. Mostly for modules usage
|
|
5
|
+
[BREAKING] remove VIEWS folders at all. Should not afffect any user as this was not used internally
|
|
6
|
+
[UPDATE] update typing
|
|
7
|
+
[UPDATE] change redis -> @redis/client as we are using only client from pakage
|
|
8
|
+
[BREAKING] removed noidemailer-sendmail-transport. Not needed anymore and not recommended to use as well
|
|
9
|
+
|
|
1
10
|
### 5.0.0-beta.8
|
|
2
11
|
|
|
3
12
|
[UPDATE] update deps
|
|
4
|
-
[NEW] Lock model for
|
|
13
|
+
[NEW] Lock model for working locks via mongoDB
|
|
5
14
|
|
|
6
15
|
### 5.0.0-beta.7
|
|
7
16
|
|
package/controllers/Auth.js
CHANGED
|
@@ -107,12 +107,9 @@ class Auth extends AbstractController {
|
|
|
107
107
|
|
|
108
108
|
const { isAuthWithVefificationFlow } = this.app.getConfig('auth');
|
|
109
109
|
if (isAuthWithVefificationFlow) {
|
|
110
|
-
|
|
110
|
+
await user.sendVerificationEmail(req.i18n).catch((e) => {
|
|
111
111
|
this.logger.error(e);
|
|
112
112
|
});
|
|
113
|
-
if (!answer) {
|
|
114
|
-
return res.status(500).json();
|
|
115
|
-
}
|
|
116
113
|
}
|
|
117
114
|
return res.status(201).json();
|
|
118
115
|
}
|
package/folderConfig.js
CHANGED
|
@@ -5,7 +5,6 @@ export default {
|
|
|
5
5
|
config: path.resolve('./config'),
|
|
6
6
|
models: path.resolve('./models'),
|
|
7
7
|
controllers: path.resolve('./controllers'),
|
|
8
|
-
views: path.resolve('./views'),
|
|
9
8
|
locales: path.resolve('./locales'),
|
|
10
9
|
emails: path.resolve('./services/messaging/email/templates'),
|
|
11
10
|
commands: path.resolve('./commands'),
|
package/models/User.js
CHANGED
|
@@ -14,6 +14,7 @@ class User extends AbstractModel {
|
|
|
14
14
|
initHooks() {
|
|
15
15
|
this.mongooseSchema.pre('save', async function userPreSaveHook() {
|
|
16
16
|
if (this.isModified('password')) {
|
|
17
|
+
// @ts-ignore
|
|
17
18
|
this.password = await this.constructor.hashPassword(this.password);
|
|
18
19
|
}
|
|
19
20
|
});
|
|
@@ -172,9 +173,18 @@ class User extends AbstractModel {
|
|
|
172
173
|
async sendPasswordRecoveryEmail(i18n) {
|
|
173
174
|
const passwordRecoveryToken =
|
|
174
175
|
await User.generateUserPasswordRecoveryToken(this);
|
|
176
|
+
let Mailer;
|
|
175
177
|
// speed optimisation
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
try {
|
|
179
|
+
// @ts-ignore
|
|
180
|
+
// eslint-disable-next-line import-x/no-unresolved
|
|
181
|
+
Mailer = (await import('@adaptivestone/framework-module-email')).default;
|
|
182
|
+
} catch {
|
|
183
|
+
const error =
|
|
184
|
+
'Mailer not found. Please install @adaptivestone/framework-module-email in order to use it';
|
|
185
|
+
this.getSuper().logger.error(error);
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
178
188
|
|
|
179
189
|
const mail = new Mailer(
|
|
180
190
|
this.getSuper().app,
|
|
@@ -243,8 +253,17 @@ class User extends AbstractModel {
|
|
|
243
253
|
async sendVerificationEmail(i18n) {
|
|
244
254
|
const verificationToken = await User.generateUserVerificationToken(this);
|
|
245
255
|
// speed optimisation
|
|
246
|
-
|
|
247
|
-
|
|
256
|
+
let Mailer;
|
|
257
|
+
try {
|
|
258
|
+
// @ts-ignore
|
|
259
|
+
// eslint-disable-next-line import-x/no-unresolved
|
|
260
|
+
Mailer = (await import('@adaptivestone/framework-module-email')).default;
|
|
261
|
+
} catch {
|
|
262
|
+
const error =
|
|
263
|
+
'Mailer not found. Please install @adaptivestone/framework-module-email in order to use it';
|
|
264
|
+
this.getSuper().logger.error(error);
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
248
267
|
const mail = new Mailer(
|
|
249
268
|
this.getSuper().app,
|
|
250
269
|
'verification',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptivestone/framework",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.9",
|
|
4
4
|
"description": "Adaptive stone node js framework",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -30,20 +30,14 @@
|
|
|
30
30
|
"author": "Andrey Logunov",
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"@redis/client": "^1.6.0",
|
|
33
34
|
"deepmerge": "^4.2.2",
|
|
34
35
|
"express": "^5.0.1",
|
|
35
36
|
"formidable": "^3.5.1",
|
|
36
|
-
"html-to-text": "^9.0.3",
|
|
37
37
|
"i18next": "^24.0.0",
|
|
38
38
|
"i18next-fs-backend": "^2.0.0",
|
|
39
|
-
"juice": "^11.0.0",
|
|
40
39
|
"mongoose": "^8.0.0",
|
|
41
|
-
"nodemailer": "^6.6.3",
|
|
42
|
-
"nodemailer-sendmail-transport": "^1.0.2",
|
|
43
|
-
"nodemailer-stub-transport": "^1.1.0",
|
|
44
|
-
"pug": "^3.0.2",
|
|
45
40
|
"rate-limiter-flexible": "^5.0.0",
|
|
46
|
-
"redis": "^4.3.1",
|
|
47
41
|
"winston": "^3.3.3",
|
|
48
42
|
"winston-transport-sentry-node": "^3.0.0",
|
|
49
43
|
"yup": "^1.0.0"
|
|
@@ -64,6 +58,14 @@
|
|
|
64
58
|
"prettier": "^3.0.0",
|
|
65
59
|
"vitest": "^3.0.0"
|
|
66
60
|
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@adaptivestone/framework-module-email": "^1.0.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"@adaptivestone/framework-module-email": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
67
69
|
"lint-staged": {
|
|
68
70
|
"**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
|
|
69
71
|
"prettier --write"
|
package/server.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import TFolderConfig from './types/TFoldersConfig';
|
|
2
|
-
import { ExpandDeep } from './types/Expand';
|
|
1
|
+
import TFolderConfig from './types/TFoldersConfig.d.ts';
|
|
2
|
+
import { ExpandDeep } from './types/Expand.d.ts';
|
|
3
3
|
|
|
4
4
|
import EventEmitter from 'events';
|
|
5
5
|
|
|
6
6
|
import { Model as MongooseModel, Schema } from 'mongoose';
|
|
7
7
|
|
|
8
|
-
import BaseCli from './modules/BaseCli';
|
|
9
|
-
import Cache from './services/cache/Cache';
|
|
8
|
+
import BaseCli from './modules/BaseCli.js';
|
|
9
|
+
import Cache from './services/cache/Cache.d.ts';
|
|
10
10
|
import winston from 'winston';
|
|
11
11
|
|
|
12
12
|
import HttpServer from './services/http/HttpServer.js';
|
|
@@ -29,6 +29,7 @@ declare class Server {
|
|
|
29
29
|
get logger(): winston.Logger;
|
|
30
30
|
httpServer: HttpServer | null;
|
|
31
31
|
controllerManager: ControllerManager | null;
|
|
32
|
+
frameworkFolder: string;
|
|
32
33
|
};
|
|
33
34
|
cacheService: Cache;
|
|
34
35
|
|
package/server.js
CHANGED
|
@@ -36,9 +36,10 @@ class Server {
|
|
|
36
36
|
* @param {String} config.folders.config path to folder with config files
|
|
37
37
|
* @param {String} config.folders.models path to folder with moidels files
|
|
38
38
|
* @param {String} config.folders.controllers path to folder with controllers files
|
|
39
|
-
* @param {String} config.folders.views path to folder with view files
|
|
40
39
|
* @param {String} config.folders.locales path to folder with locales files
|
|
41
|
-
* @param {String} config.folders.emails path to folder with emails files
|
|
40
|
+
* @param {String} [config.folders.emails] path to folder with emails files
|
|
41
|
+
* @param {String} config.folders.commands path to folder with commands files
|
|
42
|
+
* @param {String} config.folders.migrations path to folder with migrations files
|
|
42
43
|
*/
|
|
43
44
|
constructor(config) {
|
|
44
45
|
this.config = config;
|
|
@@ -58,6 +59,7 @@ class Server {
|
|
|
58
59
|
},
|
|
59
60
|
httpServer: null,
|
|
60
61
|
controllerManager: null,
|
|
62
|
+
frameworkFolder: new URL('.', import.meta.url).pathname,
|
|
61
63
|
};
|
|
62
64
|
|
|
63
65
|
this.cache = {
|
package/services/cache/Cache.js
CHANGED
|
@@ -11,9 +11,9 @@ class Cache extends Base {
|
|
|
11
11
|
// at least memory and redis drivers should be presented
|
|
12
12
|
// memory drives should works on master process level
|
|
13
13
|
// we should support multiple cashe same time
|
|
14
|
-
const
|
|
14
|
+
const { createClient } = await import('@redis/client');
|
|
15
15
|
const conf = this.app.getConfig('redis');
|
|
16
|
-
this.redisClient =
|
|
16
|
+
this.redisClient = createClient({
|
|
17
17
|
url: conf.url,
|
|
18
18
|
});
|
|
19
19
|
|
|
@@ -18,12 +18,6 @@ class HttpServer extends Base {
|
|
|
18
18
|
super(app);
|
|
19
19
|
this.express = express();
|
|
20
20
|
this.express.disable('x-powered-by');
|
|
21
|
-
// const dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
|
22
|
-
// this.express.set('views', [
|
|
23
|
-
// this.app.foldersConfig.views,
|
|
24
|
-
// path.join(dirname, '../../views'),
|
|
25
|
-
// ]);
|
|
26
|
-
// this.express.set('view engine', 'pug');
|
|
27
21
|
|
|
28
22
|
this.express.use(new RequestLoggerMiddleware(this.app).getMiddleware());
|
|
29
23
|
this.express.use(new PrepareAppInfoMiddleware(this.app).getMiddleware());
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
RateLimiterMongo,
|
|
5
5
|
} from 'rate-limiter-flexible';
|
|
6
6
|
import merge from 'deepmerge';
|
|
7
|
-
import
|
|
7
|
+
import { createClient } from '@redis/client';
|
|
8
8
|
import mongoose from 'mongoose';
|
|
9
9
|
import AbstractMiddleware from './AbstractMiddleware.js';
|
|
10
10
|
|
|
@@ -46,7 +46,7 @@ class RateLimiter extends AbstractMiddleware {
|
|
|
46
46
|
|
|
47
47
|
initRedisLimiter() {
|
|
48
48
|
const redisConfig = this.app.getConfig('redis');
|
|
49
|
-
const redisClient =
|
|
49
|
+
const redisClient = createClient({
|
|
50
50
|
url: redisConfig.url,
|
|
51
51
|
});
|
|
52
52
|
|
|
File without changes
|
package/tests/setup.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { randomBytes } from 'node:crypto';
|
|
4
4
|
import { MongoMemoryReplSet } from 'mongodb-memory-server';
|
|
5
5
|
import mongoose from 'mongoose';
|
|
6
|
-
import
|
|
6
|
+
import { createClient } from '@redis/client';
|
|
7
7
|
import Server from '../server.js';
|
|
8
8
|
|
|
9
9
|
import clearRedisNamespace from '../helpers/redis/clearNamespace.js';
|
|
@@ -31,7 +31,6 @@ beforeAll(async () => {
|
|
|
31
31
|
config: process.env.TEST_FOLDER_CONFIG || path.resolve('./config'),
|
|
32
32
|
controllers:
|
|
33
33
|
process.env.TEST_FOLDER_CONTROLLERS || path.resolve('./controllers'),
|
|
34
|
-
views: process.env.TEST_FOLDER_VIEWS || path.resolve('./views'),
|
|
35
34
|
models: process.env.TEST_FOLDER_MODELS || path.resolve('./models'),
|
|
36
35
|
emails:
|
|
37
36
|
process.env.TEST_FOLDER_EMAIL ||
|
|
@@ -93,7 +92,7 @@ beforeEach(() => {
|
|
|
93
92
|
afterEach(async () => {
|
|
94
93
|
if (global.server) {
|
|
95
94
|
const { url, namespace } = global.server.getConfig('redis');
|
|
96
|
-
const redisClient =
|
|
95
|
+
const redisClient = createClient({ url });
|
|
97
96
|
|
|
98
97
|
try {
|
|
99
98
|
await redisClient.connect();
|
package/tests/setupVitest.js
CHANGED
|
@@ -4,7 +4,7 @@ import { beforeAll, beforeEach, afterEach, afterAll } from 'vitest';
|
|
|
4
4
|
|
|
5
5
|
import mongoose from 'mongoose'; // we do not need create indexes on tests
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import { createClient } from '@redis/client';
|
|
8
8
|
import clearRedisNamespace from '../helpers/redis/clearNamespace.js';
|
|
9
9
|
import Server from '../server.js';
|
|
10
10
|
|
|
@@ -18,7 +18,6 @@ beforeAll(async () => {
|
|
|
18
18
|
config: process.env.TEST_FOLDER_CONFIG || path.resolve('./config'),
|
|
19
19
|
controllers:
|
|
20
20
|
process.env.TEST_FOLDER_CONTROLLERS || path.resolve('./controllers'),
|
|
21
|
-
views: process.env.TEST_FOLDER_VIEWS || path.resolve('./views'),
|
|
22
21
|
models: process.env.TEST_FOLDER_MODELS || path.resolve('./models'),
|
|
23
22
|
emails:
|
|
24
23
|
process.env.TEST_FOLDER_EMAIL ||
|
|
@@ -82,7 +81,7 @@ beforeEach(() => {
|
|
|
82
81
|
afterEach(async () => {
|
|
83
82
|
if (global.server) {
|
|
84
83
|
const { url, namespace } = global.server.getConfig('redis');
|
|
85
|
-
const redisClient =
|
|
84
|
+
const redisClient = createClient({ url });
|
|
86
85
|
|
|
87
86
|
try {
|
|
88
87
|
await redisClient.connect();
|
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
* @param config path to folder with config files
|
|
3
3
|
* @param models path to folder with moidels files
|
|
4
4
|
* @param controllers path to folder with controllers files
|
|
5
|
-
* @param
|
|
5
|
+
* @param migrations path to folder with migrations files
|
|
6
6
|
* @param locales path to folder with locales files
|
|
7
|
-
* @param
|
|
7
|
+
* @param migrations path to folder with migrations files
|
|
8
|
+
* @param [emails] path to folder with emails files. Optional
|
|
8
9
|
*/
|
|
9
10
|
type FolderConfig = {
|
|
10
11
|
config: string;
|
|
11
12
|
models: string;
|
|
12
13
|
controllers: string;
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
commands: string;
|
|
15
|
+
locales: string;
|
|
16
|
+
migrations: string;
|
|
17
|
+
emails?: string;
|
|
15
18
|
};
|
|
16
19
|
|
|
17
20
|
export default FolderConfig;
|
package/config/mail.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
from: 'Localhost <info@localhost>',
|
|
5
|
-
transports: {
|
|
6
|
-
sendMail: {
|
|
7
|
-
// path: "path to the sendmail command (defaults to 'sendmail')"
|
|
8
|
-
// args: 'an array of extra command line options to pass to the sendmail command (ie. ["-f", "foo@blurdybloop.com"]).'
|
|
9
|
-
},
|
|
10
|
-
stub: {},
|
|
11
|
-
smtp: {
|
|
12
|
-
// https://github.com/nodemailer/nodemailer#set-up-smtp
|
|
13
|
-
host: process.env.EMAIL_HOST || 'smtp.mailtrap.io',
|
|
14
|
-
port: process.env.EMAIL_PORT || 2525,
|
|
15
|
-
auth: {
|
|
16
|
-
user: process.env.EMAIL_USER,
|
|
17
|
-
pass: process.env.EMAIL_PASSWORD,
|
|
18
|
-
},
|
|
19
|
-
connectionTimeout: 10000, // timeout to 10 seconds
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
transport: process.env.EMAIL_TRANSPORT || 'smtp',
|
|
23
|
-
webResources: {
|
|
24
|
-
// https://github.com/jrit/web-resource-inliner path to find resources
|
|
25
|
-
relativeTo: path.resolve('src/services/messaging/email/resources'),
|
|
26
|
-
images: false,
|
|
27
|
-
},
|
|
28
|
-
globalVariablesToTemplates: {},
|
|
29
|
-
};
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import * as url from 'node:url';
|
|
4
|
-
import { promisify } from 'node:util';
|
|
5
|
-
import nodemailer from 'nodemailer';
|
|
6
|
-
import sendMail from 'nodemailer-sendmail-transport';
|
|
7
|
-
import stub from 'nodemailer-stub-transport';
|
|
8
|
-
import pug from 'pug';
|
|
9
|
-
import juice from 'juice';
|
|
10
|
-
import { convert } from 'html-to-text';
|
|
11
|
-
import Base from '../../../modules/Base.js';
|
|
12
|
-
|
|
13
|
-
const mailTransports = {
|
|
14
|
-
sendMail,
|
|
15
|
-
stub,
|
|
16
|
-
smtp: (data) => data,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
class Mail extends Base {
|
|
20
|
-
/**
|
|
21
|
-
* Construct mail class
|
|
22
|
-
* @param {object} app
|
|
23
|
-
* @param {string} template template name
|
|
24
|
-
* @param {object} [templateData={}] data to render in template. Object with value that available inside template
|
|
25
|
-
* @param {object} [i18n] data to render in template
|
|
26
|
-
*/
|
|
27
|
-
constructor(app, template, templateData = {}, i18n = null) {
|
|
28
|
-
super(app);
|
|
29
|
-
const dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
|
30
|
-
if (!path.isAbsolute(template)) {
|
|
31
|
-
if (
|
|
32
|
-
fs.existsSync(
|
|
33
|
-
`${this.app.foldersConfig.emails}/${path.basename(template)}`,
|
|
34
|
-
)
|
|
35
|
-
) {
|
|
36
|
-
this.template = `${this.app.foldersConfig.emails}/${path.basename(
|
|
37
|
-
template,
|
|
38
|
-
)}`;
|
|
39
|
-
} else if (
|
|
40
|
-
fs.existsSync(
|
|
41
|
-
path.join(dirname, `/templates/${path.basename(template)}`),
|
|
42
|
-
)
|
|
43
|
-
) {
|
|
44
|
-
this.template = path.join(
|
|
45
|
-
dirname,
|
|
46
|
-
`/templates/${path.basename(template)}`,
|
|
47
|
-
);
|
|
48
|
-
} else {
|
|
49
|
-
this.template = path.join(dirname, `/templates/emptyTemplate`);
|
|
50
|
-
this.logger.error(
|
|
51
|
-
`Template '${template}' not found. Using 'emptyTemplate' as a fallback`,
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
this.templateData = templateData;
|
|
56
|
-
this.i18n = i18n ?? {
|
|
57
|
-
t: (str) => str,
|
|
58
|
-
locale: 'en', // todo change it to config
|
|
59
|
-
};
|
|
60
|
-
this.locale = this.i18n?.language;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Render template
|
|
65
|
-
* @param {object} type and fullpath
|
|
66
|
-
* @param {object} templateData
|
|
67
|
-
* @returns string
|
|
68
|
-
*/
|
|
69
|
-
// eslint-disable-next-line class-methods-use-this
|
|
70
|
-
async #renderTemplateFile({ type, fullPath } = {}, templateData = {}) {
|
|
71
|
-
if (!type) {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
switch (type) {
|
|
76
|
-
case 'html':
|
|
77
|
-
case 'text':
|
|
78
|
-
case 'css':
|
|
79
|
-
return fs.promises.readFile(fullPath, { encoding: 'utf8' });
|
|
80
|
-
case 'pug': {
|
|
81
|
-
const compiledFunction = pug.compileFile(fullPath);
|
|
82
|
-
return compiledFunction(templateData);
|
|
83
|
-
}
|
|
84
|
-
default:
|
|
85
|
-
throw new Error(`Template type ${type} is not supported`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Render template
|
|
91
|
-
* @return {Promise}
|
|
92
|
-
*/
|
|
93
|
-
async renderTemplate() {
|
|
94
|
-
const files = await fs.promises.readdir(this.template);
|
|
95
|
-
const templates = {};
|
|
96
|
-
for (const file of files) {
|
|
97
|
-
const [name, extension] = file.split('.');
|
|
98
|
-
templates[name] = {
|
|
99
|
-
type: extension,
|
|
100
|
-
fullPath: path.join(this.template, file),
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (!templates.html || !templates.subject) {
|
|
105
|
-
throw new Error(
|
|
106
|
-
'Template HTML and Subject must be provided. Please follow documentation for details https://framework.adaptivestone.com/docs/email',
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
const mailConfig = this.app.getConfig('mail');
|
|
110
|
-
|
|
111
|
-
const templateDataToRender = {
|
|
112
|
-
locale: this.locale,
|
|
113
|
-
t: this.i18n.t.bind(this.i18n),
|
|
114
|
-
...mailConfig.globalVariablesToTemplates,
|
|
115
|
-
...this.templateData,
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
const [htmlRendered, subjectRendered, textRendered, extraCss] =
|
|
119
|
-
await Promise.all([
|
|
120
|
-
this.#renderTemplateFile(templates.html, templateDataToRender),
|
|
121
|
-
this.#renderTemplateFile(templates.subject, templateDataToRender),
|
|
122
|
-
this.#renderTemplateFile(templates.text, templateDataToRender),
|
|
123
|
-
this.#renderTemplateFile(templates.style),
|
|
124
|
-
]);
|
|
125
|
-
|
|
126
|
-
// @ts-ignore
|
|
127
|
-
juice.tableElements = ['TABLE'];
|
|
128
|
-
|
|
129
|
-
const juiceResourcesAsync = promisify(juice.juiceResources);
|
|
130
|
-
|
|
131
|
-
const inlinedHTML = await juiceResourcesAsync(htmlRendered, {
|
|
132
|
-
preserveImportant: true,
|
|
133
|
-
webResources: mailConfig.webResources,
|
|
134
|
-
extraCss,
|
|
135
|
-
});
|
|
136
|
-
return {
|
|
137
|
-
htmlRaw: htmlRendered,
|
|
138
|
-
subject: subjectRendered,
|
|
139
|
-
text: textRendered,
|
|
140
|
-
inlinedHTML,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Send email
|
|
145
|
-
* @param {string} to email send to
|
|
146
|
-
* @param {string} [from = mailConfig.from]
|
|
147
|
-
* @param {object} [aditionalNodemailerOptions = {}] additional option to nodemailer
|
|
148
|
-
* @return {Promise}
|
|
149
|
-
*/
|
|
150
|
-
async send(to, from = null, aditionalNodemailerOptions = {}) {
|
|
151
|
-
const { subject, text, inlinedHTML } = await this.renderTemplate();
|
|
152
|
-
|
|
153
|
-
return Mail.sendRaw(
|
|
154
|
-
this.app,
|
|
155
|
-
to,
|
|
156
|
-
subject,
|
|
157
|
-
inlinedHTML,
|
|
158
|
-
text,
|
|
159
|
-
from,
|
|
160
|
-
aditionalNodemailerOptions,
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Send provided text (html) to email. Low level function. All data should be prepared before sending (like inline styles)
|
|
166
|
-
* @param {import('../../../server.js').default['app']} app application
|
|
167
|
-
* @param {string} to send to
|
|
168
|
-
* @param {string} subject email topic
|
|
169
|
-
* @param {string} html hmlt body of emain
|
|
170
|
-
* @param {string} [text] if not provided will be generated from html string
|
|
171
|
-
* @param {string} [from = mailConfig.from] from. If not provided will be grabbed from config
|
|
172
|
-
* @param {object} [additionalNodeMailerOption = {}] any otipns to pass to nodemailer https://nodemailer.com/message/
|
|
173
|
-
*/
|
|
174
|
-
static async sendRaw(
|
|
175
|
-
app,
|
|
176
|
-
to,
|
|
177
|
-
subject,
|
|
178
|
-
html,
|
|
179
|
-
text = null,
|
|
180
|
-
from = null,
|
|
181
|
-
additionalNodeMailerOption = {},
|
|
182
|
-
) {
|
|
183
|
-
if (!app || !to || !subject || !html) {
|
|
184
|
-
throw new Error('App, to, subject and html is required fields.');
|
|
185
|
-
}
|
|
186
|
-
const mailConfig = app.getConfig('mail');
|
|
187
|
-
if (!from) {
|
|
188
|
-
// eslint-disable-next-line no-param-reassign
|
|
189
|
-
from = mailConfig.from;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (!text) {
|
|
193
|
-
// eslint-disable-next-line no-param-reassign
|
|
194
|
-
text = convert(html, {
|
|
195
|
-
selectors: [{ selector: 'img', format: 'skip' }],
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
const transportConfig = mailConfig.transports[mailConfig.transport];
|
|
199
|
-
const transport = mailTransports[mailConfig.transport];
|
|
200
|
-
const transporter = nodemailer.createTransport(transport(transportConfig));
|
|
201
|
-
|
|
202
|
-
return transporter.sendMail({
|
|
203
|
-
from,
|
|
204
|
-
to,
|
|
205
|
-
subject,
|
|
206
|
-
text,
|
|
207
|
-
html,
|
|
208
|
-
...additionalNodeMailerOption,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
static get loggerGroup() {
|
|
213
|
-
return 'email_';
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export default Mail;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
= `New message`
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
= `Good day`
|