@duvdu-v1/duvdu 1.1.355 → 1.1.360

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.
Files changed (56) hide show
  1. package/build/errors/pdf-generation-error.d.ts +9 -0
  2. package/build/errors/pdf-generation-error.js +15 -0
  3. package/build/guards/isEmailVerified.guard.d.ts +2 -0
  4. package/build/guards/isEmailVerified.guard.js +15 -0
  5. package/build/guards/isPhoneNumberVerified.guard.d.ts +2 -0
  6. package/build/guards/isPhoneNumberVerified.guard.js +15 -0
  7. package/build/index.d.ts +7 -1
  8. package/build/index.js +7 -1
  9. package/build/mailer/mailer.interface.d.ts +68 -0
  10. package/build/mailer/mailer.interface.js +12 -0
  11. package/build/mailer/mailer.service.d.ts +10 -0
  12. package/build/mailer/mailer.service.js +149 -0
  13. package/build/mailer/strategies/resend.strategy.d.ts +6 -0
  14. package/build/mailer/strategies/resend.strategy.js +48 -0
  15. package/build/mailer/strategies/smtp.strategy.d.ts +6 -0
  16. package/build/mailer/strategies/smtp.strategy.js +50 -0
  17. package/build/mailer/templates/layouts/main.hbs +103 -0
  18. package/build/mailer/templates/partials/footer.hbs +4 -0
  19. package/build/mailer/templates/partials/header.hbs +4 -0
  20. package/build/mailer/templates/views/account-update.hbs +20 -0
  21. package/build/mailer/templates/views/complaint-escalation.hbs +23 -0
  22. package/build/mailer/templates/views/invoice.hbs +29 -0
  23. package/build/mailer/templates/views/new-user.hbs +23 -0
  24. package/build/mailer/templates/views/organization-user-created.hbs +25 -0
  25. package/build/mailer/templates/views/reset-password.hbs +14 -0
  26. package/build/mailer/templates/views/verify-email.hbs +14 -0
  27. package/build/mailer/templates/views/welcome.hbs +22 -0
  28. package/build/middlewares/auth.middleware.js +2 -1
  29. package/build/middlewares/optional-auth.middleware.js +2 -1
  30. package/build/models/User.model.d.ts +2 -2
  31. package/build/models/User.model.js +23 -16
  32. package/build/models/contracts.model.d.ts +7 -0
  33. package/build/models/contracts.model.js +7 -0
  34. package/build/models/settings.model.d.ts +6 -0
  35. package/build/models/settings.model.js +6 -0
  36. package/build/services/pdf-templates/layouts/main.hbs +91 -0
  37. package/build/services/pdf-templates/partials/footer.hbs +4 -0
  38. package/build/services/pdf-templates/partials/header.hbs +4 -0
  39. package/build/services/pdf-templates/views/invoice.hbs +60 -0
  40. package/build/services/pdfGenerator.service.d.ts +16 -0
  41. package/build/services/pdfGenerator.service.js +198 -0
  42. package/build/services/rank.service.d.ts +2 -2
  43. package/build/types/JwtPayload.d.ts +2 -1
  44. package/build/types/User.d.ts +18 -7
  45. package/build/types/User.js +4 -0
  46. package/build/types/model-names.d.ts +1 -0
  47. package/build/types/model-names.js +1 -0
  48. package/build/types/pdf.types.d.ts +30 -0
  49. package/build/types/pdf.types.js +7 -0
  50. package/build/types/systemRoles.d.ts +1 -2
  51. package/build/types/systemRoles.js +3 -2
  52. package/build/utils/{bucket.d.ts → bucket-wasabi.d.ts} +10 -9
  53. package/build/utils/{bucket.js → bucket-wasabi.js} +102 -124
  54. package/build/utils/mask.d.ts +5 -0
  55. package/build/utils/mask.js +49 -0
  56. package/package.json +10 -2
@@ -0,0 +1,9 @@
1
+ import { CustomError } from './custom-error';
2
+ export declare class PdfGenerationError extends CustomError {
3
+ statusCode: number;
4
+ constructor(message?: string);
5
+ serializeError(): {
6
+ message: string;
7
+ field?: string | undefined;
8
+ }[];
9
+ }
@@ -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,2 @@
1
+ import type { Request, Response, NextFunction } from 'express';
2
+ export declare const isEmailVerifiedGuard: (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
@@ -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,2 @@
1
+ import type { Request, Response, NextFunction } from 'express';
2
+ export declare const isPhoneNumberVerifiedGuard: (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
@@ -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
- export * from './utils/bucket';
12
+ export * from './utils/bucket-wasabi';
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
- __exportStar(require("./utils/bucket"), exports);
28
+ __exportStar(require("./utils/bucket-wasabi"), 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,6 @@
1
+ import type { MailOptions, MailStrategy } from '../mailer.interface';
2
+ export declare class ResendStrategy implements MailStrategy {
3
+ private resend;
4
+ constructor();
5
+ send(options: MailOptions): Promise<any>;
6
+ }
@@ -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,6 @@
1
+ import type { MailOptions, MailStrategy } from '../mailer.interface';
2
+ export declare class SmtpStrategy implements MailStrategy {
3
+ private transporter;
4
+ constructor();
5
+ send(options: MailOptions): Promise<any>;
6
+ }
@@ -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>&copy; {{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,4 @@
1
+ <div class="footer">
2
+ <p>&copy; {{currentYear}} Duvdu. All rights reserved.</p>
3
+ <p>This email was sent to {{email}}</p>
4
+ </div>
@@ -0,0 +1,4 @@
1
+ <div class="brand">
2
+ <h1>Duvdu</h1>
3
+ <div class="tagline">Where creativity meets opportunity</div>
4
+ </div>
@@ -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>