@duvdu-v1/duvdu 1.1.354 → 1.1.359
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/build/errors/pdf-generation-error.d.ts +9 -0
- package/build/errors/pdf-generation-error.js +15 -0
- package/build/guards/isEmailVerified.guard.d.ts +2 -0
- package/build/guards/isEmailVerified.guard.js +15 -0
- package/build/guards/isPhoneNumberVerified.guard.d.ts +2 -0
- package/build/guards/isPhoneNumberVerified.guard.js +15 -0
- package/build/index.d.ts +6 -0
- package/build/index.js +6 -0
- package/build/mailer/mailer.interface.d.ts +68 -0
- package/build/mailer/mailer.interface.js +12 -0
- package/build/mailer/mailer.service.d.ts +10 -0
- package/build/mailer/mailer.service.js +149 -0
- package/build/mailer/strategies/resend.strategy.d.ts +6 -0
- package/build/mailer/strategies/resend.strategy.js +48 -0
- package/build/mailer/strategies/smtp.strategy.d.ts +6 -0
- package/build/mailer/strategies/smtp.strategy.js +50 -0
- package/build/mailer/templates/layouts/main.hbs +103 -0
- package/build/mailer/templates/partials/footer.hbs +4 -0
- package/build/mailer/templates/partials/header.hbs +4 -0
- package/build/mailer/templates/views/account-update.hbs +20 -0
- package/build/mailer/templates/views/complaint-escalation.hbs +23 -0
- package/build/mailer/templates/views/invoice.hbs +29 -0
- package/build/mailer/templates/views/new-user.hbs +23 -0
- package/build/mailer/templates/views/organization-user-created.hbs +25 -0
- package/build/mailer/templates/views/reset-password.hbs +14 -0
- package/build/mailer/templates/views/verify-email.hbs +14 -0
- package/build/mailer/templates/views/welcome.hbs +22 -0
- package/build/middlewares/auth.middleware.js +2 -1
- package/build/middlewares/optional-auth.middleware.js +2 -1
- package/build/models/User.model.d.ts +2 -2
- package/build/models/User.model.js +15 -6
- package/build/models/all-contracts.model.js +1 -1
- package/build/models/contracts.model.d.ts +7 -0
- package/build/models/contracts.model.js +7 -0
- package/build/models/settings.model.d.ts +6 -0
- package/build/models/settings.model.js +6 -0
- package/build/services/pdf-templates/layouts/main.hbs +91 -0
- package/build/services/pdf-templates/partials/footer.hbs +4 -0
- package/build/services/pdf-templates/partials/header.hbs +4 -0
- package/build/services/pdf-templates/views/invoice.hbs +60 -0
- package/build/services/pdfGenerator.service.d.ts +16 -0
- package/build/services/pdfGenerator.service.js +198 -0
- package/build/services/rank.service.d.ts +2 -2
- package/build/types/JwtPayload.d.ts +2 -1
- package/build/types/User.d.ts +18 -7
- package/build/types/User.js +4 -0
- package/build/types/model-names.d.ts +1 -0
- package/build/types/model-names.js +1 -0
- package/build/types/pdf.types.d.ts +30 -0
- package/build/types/pdf.types.js +7 -0
- package/build/types/systemRoles.d.ts +1 -2
- package/build/types/systemRoles.js +3 -2
- package/build/utils/mask.d.ts +5 -0
- package/build/utils/mask.js +49 -0
- package/package.json +10 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PdfGenerationError = void 0;
|
|
4
|
+
const custom_error_1 = require("./custom-error");
|
|
5
|
+
class PdfGenerationError extends custom_error_1.CustomError {
|
|
6
|
+
constructor(message = 'Failed to generate PDF') {
|
|
7
|
+
super(message);
|
|
8
|
+
this.statusCode = 500;
|
|
9
|
+
Object.setPrototypeOf(this, PdfGenerationError.prototype);
|
|
10
|
+
}
|
|
11
|
+
serializeError() {
|
|
12
|
+
return [{ message: this.message }];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.PdfGenerationError = PdfGenerationError;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isEmailVerifiedGuard = void 0;
|
|
4
|
+
const isEmailVerifiedGuard = (req, res, next) => {
|
|
5
|
+
const loggedUser = req.loggedUser;
|
|
6
|
+
if (!loggedUser.isEmailVerified)
|
|
7
|
+
return res.status(403).json({
|
|
8
|
+
message: {
|
|
9
|
+
en: 'Your account is not verified yet. Please verify your email.',
|
|
10
|
+
ar: 'حسابك غير مفعل بعد. يرجى تفعيل بريدك الإلكتروني.',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
next();
|
|
14
|
+
};
|
|
15
|
+
exports.isEmailVerifiedGuard = isEmailVerifiedGuard;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPhoneNumberVerifiedGuard = void 0;
|
|
4
|
+
const isPhoneNumberVerifiedGuard = (req, res, next) => {
|
|
5
|
+
const loggedUser = req.loggedUser;
|
|
6
|
+
if (!loggedUser.isPhoneNumberVerified)
|
|
7
|
+
return res.status(403).json({
|
|
8
|
+
message: {
|
|
9
|
+
en: 'Your account is not verified yet. Please verify your phone number.',
|
|
10
|
+
ar: 'حسابك غير مفعل بعد. يرجى تفعيل رقم هاتفك.',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
next();
|
|
14
|
+
};
|
|
15
|
+
exports.isPhoneNumberVerifiedGuard = isPhoneNumberVerifiedGuard;
|
package/build/index.d.ts
CHANGED
|
@@ -6,11 +6,13 @@ export * from './errors/unauthorized-error';
|
|
|
6
6
|
export * from './errors/validation-error';
|
|
7
7
|
export * from './errors/generic-error';
|
|
8
8
|
export * from './errors/not-allowed-error';
|
|
9
|
+
export * from './errors/pdf-generation-error';
|
|
9
10
|
export * from './utils/api-feature';
|
|
10
11
|
export * from './utils/generateToken';
|
|
11
12
|
export * from './utils/bucket';
|
|
12
13
|
export * from './utils/file';
|
|
13
14
|
export * from './utils/date';
|
|
15
|
+
export * from './utils/mask';
|
|
14
16
|
export * from './middlewares/global-error-handling.middleware';
|
|
15
17
|
export * from './middlewares/database-connection';
|
|
16
18
|
export * from './middlewares/global-validator.middleware';
|
|
@@ -45,6 +47,7 @@ export * from './types/portfolio-post-order';
|
|
|
45
47
|
export * from './types/cycles';
|
|
46
48
|
export * from './types/booking-states';
|
|
47
49
|
export * from './types/socket-channel';
|
|
50
|
+
export * from './types/pdf.types';
|
|
48
51
|
export * from './models/producerPlatform.model';
|
|
49
52
|
export * from './models/Plan.model';
|
|
50
53
|
export * from './models/Role.model';
|
|
@@ -90,6 +93,7 @@ export * from './services/projectView.service';
|
|
|
90
93
|
export * from './services/rank.service';
|
|
91
94
|
export * from './services/checkUserFaceVerification';
|
|
92
95
|
export * from './services/paymob.service';
|
|
96
|
+
export * from './services/pdfGenerator.service';
|
|
93
97
|
export * from './types/notification.type';
|
|
94
98
|
export * from './types/notification_details';
|
|
95
99
|
export * from './events/new-notification.event';
|
|
@@ -100,3 +104,5 @@ export * from './events/subject';
|
|
|
100
104
|
export * from './events/topic-notification.event';
|
|
101
105
|
export * from './config/winston';
|
|
102
106
|
export * from './middlewares/pull.connection';
|
|
107
|
+
export * from './mailer/mailer.service';
|
|
108
|
+
export * from './mailer/mailer.interface';
|
package/build/index.js
CHANGED
|
@@ -22,11 +22,13 @@ __exportStar(require("./errors/unauthorized-error"), exports);
|
|
|
22
22
|
__exportStar(require("./errors/validation-error"), exports);
|
|
23
23
|
__exportStar(require("./errors/generic-error"), exports);
|
|
24
24
|
__exportStar(require("./errors/not-allowed-error"), exports);
|
|
25
|
+
__exportStar(require("./errors/pdf-generation-error"), exports);
|
|
25
26
|
__exportStar(require("./utils/api-feature"), exports);
|
|
26
27
|
__exportStar(require("./utils/generateToken"), exports);
|
|
27
28
|
__exportStar(require("./utils/bucket"), exports);
|
|
28
29
|
__exportStar(require("./utils/file"), exports);
|
|
29
30
|
__exportStar(require("./utils/date"), exports);
|
|
31
|
+
__exportStar(require("./utils/mask"), exports);
|
|
30
32
|
__exportStar(require("./middlewares/global-error-handling.middleware"), exports);
|
|
31
33
|
__exportStar(require("./middlewares/database-connection"), exports);
|
|
32
34
|
__exportStar(require("./middlewares/global-validator.middleware"), exports);
|
|
@@ -61,6 +63,7 @@ __exportStar(require("./types/portfolio-post-order"), exports);
|
|
|
61
63
|
__exportStar(require("./types/cycles"), exports);
|
|
62
64
|
__exportStar(require("./types/booking-states"), exports);
|
|
63
65
|
__exportStar(require("./types/socket-channel"), exports);
|
|
66
|
+
__exportStar(require("./types/pdf.types"), exports);
|
|
64
67
|
__exportStar(require("./models/producerPlatform.model"), exports);
|
|
65
68
|
__exportStar(require("./models/Plan.model"), exports);
|
|
66
69
|
__exportStar(require("./models/Role.model"), exports);
|
|
@@ -107,6 +110,7 @@ __exportStar(require("./services/projectView.service"), exports);
|
|
|
107
110
|
__exportStar(require("./services/rank.service"), exports);
|
|
108
111
|
__exportStar(require("./services/checkUserFaceVerification"), exports);
|
|
109
112
|
__exportStar(require("./services/paymob.service"), exports);
|
|
113
|
+
__exportStar(require("./services/pdfGenerator.service"), exports);
|
|
110
114
|
__exportStar(require("./types/notification.type"), exports);
|
|
111
115
|
__exportStar(require("./types/notification_details"), exports);
|
|
112
116
|
// events
|
|
@@ -118,3 +122,5 @@ __exportStar(require("./events/subject"), exports);
|
|
|
118
122
|
__exportStar(require("./events/topic-notification.event"), exports);
|
|
119
123
|
__exportStar(require("./config/winston"), exports);
|
|
120
124
|
__exportStar(require("./middlewares/pull.connection"), exports);
|
|
125
|
+
__exportStar(require("./mailer/mailer.service"), exports);
|
|
126
|
+
__exportStar(require("./mailer/mailer.interface"), exports);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export interface EmailJobData<T extends EmailTemplate> {
|
|
2
|
+
to: string;
|
|
3
|
+
subject: string;
|
|
4
|
+
body?: string;
|
|
5
|
+
template: T;
|
|
6
|
+
context?: EmailTemplateContext[T];
|
|
7
|
+
attachments?: Array<{
|
|
8
|
+
filename: string;
|
|
9
|
+
content: string | Buffer;
|
|
10
|
+
contentType?: string;
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
export declare enum EmailTemplate {
|
|
14
|
+
Welcome = "welcome",
|
|
15
|
+
VerifyEmail = "verify-email",
|
|
16
|
+
NewUser = "new-user",
|
|
17
|
+
ResetPassword = "reset-password",
|
|
18
|
+
AccountCreated = "account-created",
|
|
19
|
+
Invoice = "invoice"
|
|
20
|
+
}
|
|
21
|
+
export interface EmailTemplateContext {
|
|
22
|
+
[EmailTemplate.Welcome]?: {
|
|
23
|
+
name: string;
|
|
24
|
+
verificationLink?: string;
|
|
25
|
+
features?: string[];
|
|
26
|
+
};
|
|
27
|
+
[EmailTemplate.VerifyEmail]?: {
|
|
28
|
+
name: string;
|
|
29
|
+
verificationCode: string;
|
|
30
|
+
expiryTime?: string | number;
|
|
31
|
+
};
|
|
32
|
+
[EmailTemplate.NewUser]?: {
|
|
33
|
+
name: string;
|
|
34
|
+
verificationLink?: string;
|
|
35
|
+
};
|
|
36
|
+
[EmailTemplate.ResetPassword]?: {
|
|
37
|
+
name: string;
|
|
38
|
+
verificationCode: string;
|
|
39
|
+
expiryTime?: string;
|
|
40
|
+
};
|
|
41
|
+
[EmailTemplate.AccountCreated]?: {
|
|
42
|
+
name: string;
|
|
43
|
+
email: string;
|
|
44
|
+
loginUrl: string;
|
|
45
|
+
};
|
|
46
|
+
[EmailTemplate.Invoice]?: {
|
|
47
|
+
name: string;
|
|
48
|
+
transactionId: string;
|
|
49
|
+
amount: number;
|
|
50
|
+
currency: string;
|
|
51
|
+
date: string;
|
|
52
|
+
serviceLabel: string;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export interface MailOptions {
|
|
56
|
+
to: string;
|
|
57
|
+
subject: string;
|
|
58
|
+
html: string;
|
|
59
|
+
from?: string;
|
|
60
|
+
attachments?: Array<{
|
|
61
|
+
filename: string;
|
|
62
|
+
content: string | Buffer;
|
|
63
|
+
contentType?: string;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
export interface MailStrategy {
|
|
67
|
+
send(options: MailOptions): Promise<any>;
|
|
68
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EmailTemplate = void 0;
|
|
4
|
+
var EmailTemplate;
|
|
5
|
+
(function (EmailTemplate) {
|
|
6
|
+
EmailTemplate["Welcome"] = "welcome";
|
|
7
|
+
EmailTemplate["VerifyEmail"] = "verify-email";
|
|
8
|
+
EmailTemplate["NewUser"] = "new-user";
|
|
9
|
+
EmailTemplate["ResetPassword"] = "reset-password";
|
|
10
|
+
EmailTemplate["AccountCreated"] = "account-created";
|
|
11
|
+
EmailTemplate["Invoice"] = "invoice";
|
|
12
|
+
})(EmailTemplate || (exports.EmailTemplate = EmailTemplate = {}));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EmailJobData, EmailTemplate } from './mailer.interface';
|
|
2
|
+
declare class MailerServiceClass {
|
|
3
|
+
private strategy;
|
|
4
|
+
constructor();
|
|
5
|
+
private setStrategy;
|
|
6
|
+
private renderTemplate;
|
|
7
|
+
sendMail<T extends EmailTemplate>(options: EmailJobData<T>): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export declare const MailerService: MailerServiceClass;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.MailerService = void 0;
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path_1 = require("path");
|
|
48
|
+
const handlebars = __importStar(require("handlebars"));
|
|
49
|
+
const resend_strategy_1 = require("./strategies/resend.strategy");
|
|
50
|
+
const smtp_strategy_1 = require("./strategies/smtp.strategy");
|
|
51
|
+
class MailerServiceClass {
|
|
52
|
+
constructor() {
|
|
53
|
+
this.strategy = null;
|
|
54
|
+
if (process.env.SEND_MAIL) {
|
|
55
|
+
this.setStrategy();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
setStrategy() {
|
|
59
|
+
const driver = process.env.MAIL_DRIVER;
|
|
60
|
+
/* eslint-disable indent */
|
|
61
|
+
switch (driver) {
|
|
62
|
+
case 'smtp':
|
|
63
|
+
this.strategy = new smtp_strategy_1.SmtpStrategy();
|
|
64
|
+
console.log('Mailer strategy set to SMTP');
|
|
65
|
+
break;
|
|
66
|
+
case 'resend':
|
|
67
|
+
this.strategy = new resend_strategy_1.ResendStrategy();
|
|
68
|
+
console.log('Mailer strategy set to Resend');
|
|
69
|
+
break;
|
|
70
|
+
default:
|
|
71
|
+
console.warn(`Unsupported mail driver: ${driver}. Mail sending might fail.`);
|
|
72
|
+
this.strategy = null;
|
|
73
|
+
/* eslint-enable indent */
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
renderTemplate(templateName, context) {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
const templatesDir = (0, path_1.join)(__dirname, 'templates');
|
|
79
|
+
const layoutsDir = (0, path_1.join)(templatesDir, 'layouts');
|
|
80
|
+
const partialsDir = (0, path_1.join)(templatesDir, 'partials');
|
|
81
|
+
const viewsDir = (0, path_1.join)(templatesDir, 'views');
|
|
82
|
+
if (fs.existsSync(partialsDir)) {
|
|
83
|
+
const partials = yield fs.promises.readdir(partialsDir);
|
|
84
|
+
partials.forEach((file) => __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
if (file.endsWith('.hbs')) {
|
|
86
|
+
const partialName = file.replace('.hbs', '');
|
|
87
|
+
const partialContent = yield fs.promises.readFile((0, path_1.join)(partialsDir, file), 'utf-8');
|
|
88
|
+
handlebars.registerPartial(partialName, partialContent);
|
|
89
|
+
}
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
const layoutPath = (0, path_1.join)(layoutsDir, 'main.hbs');
|
|
93
|
+
let htmlContent = '';
|
|
94
|
+
const viewPath = (0, path_1.join)(viewsDir, `${templateName}.hbs`);
|
|
95
|
+
if (!fs.existsSync(viewPath)) {
|
|
96
|
+
throw new Error(`Template ${templateName} not found at ${viewPath}`);
|
|
97
|
+
}
|
|
98
|
+
const viewContent = fs.readFileSync(viewPath, 'utf-8');
|
|
99
|
+
const viewTemplate = handlebars.compile(viewContent);
|
|
100
|
+
const renderedView = viewTemplate(context);
|
|
101
|
+
if (fs.existsSync(layoutPath)) {
|
|
102
|
+
const layoutContent = fs.readFileSync(layoutPath, 'utf-8');
|
|
103
|
+
const layoutTemplate = handlebars.compile(layoutContent);
|
|
104
|
+
htmlContent = layoutTemplate(Object.assign(Object.assign({}, context), { body: renderedView }));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
htmlContent = renderedView;
|
|
108
|
+
}
|
|
109
|
+
return htmlContent;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
sendMail(options) {
|
|
113
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
114
|
+
if (!process.env.SEND_MAIL) {
|
|
115
|
+
console.log(`Dry run: Sending mail to ${options.to} with subject ${options.subject}`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!this.strategy) {
|
|
119
|
+
console.error('No mailer strategy configured.');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
let html = '';
|
|
123
|
+
if (options.template) {
|
|
124
|
+
try {
|
|
125
|
+
html = yield this.renderTemplate(options.template, options.context || {});
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
console.error(`Error rendering template ${options.template}: ${error.message}`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (options.body) {
|
|
133
|
+
html = options.body.includes('<') ? options.body : `<p>${options.body}</p>`;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
yield this.strategy.send({
|
|
137
|
+
to: options.to,
|
|
138
|
+
subject: options.subject,
|
|
139
|
+
html: html,
|
|
140
|
+
attachments: options.attachments,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.error(`Failed to send email via strategy: ${error.message}`);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
exports.MailerService = new MailerServiceClass();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ResendStrategy = void 0;
|
|
13
|
+
const resend_1 = require("resend");
|
|
14
|
+
class ResendStrategy {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.resend = new resend_1.Resend(process.env.RESEND_API_KEY);
|
|
17
|
+
}
|
|
18
|
+
send(options) {
|
|
19
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
const resendOptions = {
|
|
21
|
+
from: options.from || process.env.RESEND_FROM_ADDRESS,
|
|
22
|
+
to: options.to,
|
|
23
|
+
subject: options.subject,
|
|
24
|
+
html: options.html,
|
|
25
|
+
};
|
|
26
|
+
if (options.attachments && options.attachments.length > 0) {
|
|
27
|
+
resendOptions.attachments = options.attachments.map((attachment) => ({
|
|
28
|
+
filename: attachment.filename,
|
|
29
|
+
content: attachment.content,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const { data, error } = yield this.resend.emails.send(resendOptions);
|
|
34
|
+
if (error) {
|
|
35
|
+
console.error(`Error sending email via Resend: ${error.message}`);
|
|
36
|
+
throw new Error(error.message);
|
|
37
|
+
}
|
|
38
|
+
console.log(`Email sent via Resend: ${data === null || data === void 0 ? void 0 : data.id}`);
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error(`Unexpected error sending email via Resend: ${error.message}`);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.ResendStrategy = ResendStrategy;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SmtpStrategy = void 0;
|
|
13
|
+
const nodemailer_1 = require("nodemailer");
|
|
14
|
+
class SmtpStrategy {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.transporter = (0, nodemailer_1.createTransport)({
|
|
17
|
+
host: process.env.SMTP_HOST || 'smtp.gmail.com',
|
|
18
|
+
port: Number(process.env.SMTP_PORT) || 587,
|
|
19
|
+
secure: Number(process.env.SMTP_PORT) === 465,
|
|
20
|
+
auth: {
|
|
21
|
+
user: process.env.SMTP_USER,
|
|
22
|
+
pass: process.env.SMTP_PASS,
|
|
23
|
+
},
|
|
24
|
+
tls: {
|
|
25
|
+
rejectUnauthorized: false,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
send(options) {
|
|
30
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
const mailOptions = {
|
|
32
|
+
from: options.from || process.env.SMTP_FROM_ADDRESS,
|
|
33
|
+
to: options.to,
|
|
34
|
+
subject: options.subject,
|
|
35
|
+
html: options.html,
|
|
36
|
+
attachments: options.attachments,
|
|
37
|
+
};
|
|
38
|
+
try {
|
|
39
|
+
const info = yield this.transporter.sendMail(mailOptions);
|
|
40
|
+
console.log(`Email sent via SMTP: ${info.messageId}`);
|
|
41
|
+
return info;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error(`Error sending email via SMTP: ${error.message}`);
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.SmtpStrategy = SmtpStrategy;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{subject}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
10
|
+
line-height: 1.6;
|
|
11
|
+
color: #1f2937;
|
|
12
|
+
max-width: 600px;
|
|
13
|
+
margin: 0 auto;
|
|
14
|
+
padding: 24px;
|
|
15
|
+
background-color: #f5f3ff;
|
|
16
|
+
}
|
|
17
|
+
.email-container {
|
|
18
|
+
background-color: #ffffff;
|
|
19
|
+
border-radius: 12px;
|
|
20
|
+
padding: 32px;
|
|
21
|
+
box-shadow: 0 4px 20px rgba(108, 92, 231, 0.08);
|
|
22
|
+
}
|
|
23
|
+
.brand {
|
|
24
|
+
text-align: center;
|
|
25
|
+
padding-bottom: 20px;
|
|
26
|
+
margin-bottom: 28px;
|
|
27
|
+
border-bottom: 1px solid #ede9fe;
|
|
28
|
+
}
|
|
29
|
+
.brand h1 {
|
|
30
|
+
margin: 0;
|
|
31
|
+
font-size: 26px;
|
|
32
|
+
font-weight: 700;
|
|
33
|
+
letter-spacing: -0.5px;
|
|
34
|
+
background: linear-gradient(90deg, #6C5CE7, #a855f7);
|
|
35
|
+
-webkit-background-clip: text;
|
|
36
|
+
-webkit-text-fill-color: transparent;
|
|
37
|
+
background-clip: text;
|
|
38
|
+
}
|
|
39
|
+
.brand .tagline {
|
|
40
|
+
margin-top: 4px;
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
color: #6b7280;
|
|
43
|
+
letter-spacing: 1.5px;
|
|
44
|
+
text-transform: uppercase;
|
|
45
|
+
}
|
|
46
|
+
.content {
|
|
47
|
+
margin-bottom: 32px;
|
|
48
|
+
font-size: 15px;
|
|
49
|
+
color: #374151;
|
|
50
|
+
}
|
|
51
|
+
.content h2 {
|
|
52
|
+
color: #111827;
|
|
53
|
+
font-size: 20px;
|
|
54
|
+
margin-top: 0;
|
|
55
|
+
}
|
|
56
|
+
.content a {
|
|
57
|
+
color: #6C5CE7;
|
|
58
|
+
}
|
|
59
|
+
.footer {
|
|
60
|
+
text-align: center;
|
|
61
|
+
color: #9ca3af;
|
|
62
|
+
font-size: 12px;
|
|
63
|
+
border-top: 1px solid #ede9fe;
|
|
64
|
+
padding-top: 20px;
|
|
65
|
+
}
|
|
66
|
+
.footer a {
|
|
67
|
+
color: #6C5CE7;
|
|
68
|
+
text-decoration: none;
|
|
69
|
+
}
|
|
70
|
+
.button {
|
|
71
|
+
display: inline-block;
|
|
72
|
+
padding: 12px 28px;
|
|
73
|
+
background: linear-gradient(90deg, #6C5CE7, #a855f7);
|
|
74
|
+
color: #ffffff !important;
|
|
75
|
+
text-decoration: none;
|
|
76
|
+
border-radius: 8px;
|
|
77
|
+
margin: 12px 0;
|
|
78
|
+
font-weight: 600;
|
|
79
|
+
}
|
|
80
|
+
@media only screen and (max-width: 600px) {
|
|
81
|
+
body { padding: 12px; }
|
|
82
|
+
.email-container { padding: 22px; }
|
|
83
|
+
}
|
|
84
|
+
</style>
|
|
85
|
+
</head>
|
|
86
|
+
<body>
|
|
87
|
+
<div class="email-container">
|
|
88
|
+
<div class="brand">
|
|
89
|
+
<h1>Duvdu</h1>
|
|
90
|
+
<div class="tagline">Where creativity meets opportunity</div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="content">
|
|
94
|
+
{{{body}}}
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div class="footer">
|
|
98
|
+
<p>© {{currentYear}} Duvdu. All rights reserved.</p>
|
|
99
|
+
<p>This email was sent to {{email}}</p>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</body>
|
|
103
|
+
</html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<h2>Your account has been updated</h2>
|
|
2
|
+
|
|
3
|
+
<p>Hello {{#if user}}{{user}}{{else}}there{{/if}},</p>
|
|
4
|
+
|
|
5
|
+
<p>An administrator has made changes to your Duvdu account.</p>
|
|
6
|
+
|
|
7
|
+
<p>Your role: <strong>{{role}}</strong></p>
|
|
8
|
+
|
|
9
|
+
{{#if email}}
|
|
10
|
+
<p>Email: <strong>{{email}}</strong></p>
|
|
11
|
+
{{/if}}
|
|
12
|
+
|
|
13
|
+
{{#if tempPassword}}
|
|
14
|
+
<p>Password: <strong>{{tempPassword}}</strong></p>
|
|
15
|
+
{{/if}}
|
|
16
|
+
|
|
17
|
+
<p>If you didn't expect these changes, please contact our support team immediately.</p>
|
|
18
|
+
|
|
19
|
+
<p>Best regards,<br>
|
|
20
|
+
The Duvdu Team</p>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<h2 style="color: #d9534f;">Complaint Escalation Alert</h2>
|
|
2
|
+
|
|
3
|
+
<p>Dear {{userName}},</p>
|
|
4
|
+
|
|
5
|
+
<p>This is to inform you that the following complaint has exceeded its time limit and requires immediate attention:</p>
|
|
6
|
+
|
|
7
|
+
<div style="background-color: #fef2f2; padding: 16px 18px; border-left: 4px solid #d9534f; border-radius: 6px; margin: 20px 0;">
|
|
8
|
+
<h3 style="margin-top: 0; color: #111827;">{{complaintTitle}}</h3>
|
|
9
|
+
<p><strong>Complaint ID:</strong> {{complaintId}}</p>
|
|
10
|
+
<p><strong>Priority:</strong> <span style="text-transform: uppercase; color: #d9534f;">{{priority}}</span></p>
|
|
11
|
+
<p><strong>Current Status:</strong> {{statusName}}</p>
|
|
12
|
+
<p><strong>Time Exceeded:</strong> {{timeExceeded}}</p>
|
|
13
|
+
<p><strong>Description:</strong> {{complaintDescription}}</p>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<p>Please take immediate action to address this complaint so we can maintain the quality of service our community expects on Duvdu.</p>
|
|
17
|
+
|
|
18
|
+
<p><a href="{{frontendUrl}}/complaints/{{complaintId}}" class="button">View Complaint Details</a></p>
|
|
19
|
+
|
|
20
|
+
<p>Thank you for your prompt attention.</p>
|
|
21
|
+
|
|
22
|
+
<p>Best regards,<br>
|
|
23
|
+
The Duvdu Team</p>
|