@breadstone/archipel-platform-mailing 0.0.13 → 0.0.15

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 (40) hide show
  1. package/README.md +2 -0
  2. package/package.json +4 -3
  3. package/src/MailModule.js +5 -4
  4. package/src/env.d.ts +18 -18
  5. package/src/env.js +19 -19
  6. package/src/index.d.ts +1 -0
  7. package/src/index.js +1 -0
  8. package/src/mailgun/env.d.ts +3 -3
  9. package/src/mailgun/env.js +3 -3
  10. package/src/models/IMailAttachment.d.ts +31 -0
  11. package/src/models/IMailAttachment.js +29 -0
  12. package/src/postmark/env.d.ts +2 -2
  13. package/src/postmark/env.js +2 -2
  14. package/src/resend/env.d.ts +2 -2
  15. package/src/resend/env.js +2 -2
  16. package/src/sendgrid/env.d.ts +2 -2
  17. package/src/sendgrid/env.js +2 -2
  18. package/src/services/MailService.d.ts +10 -2
  19. package/src/services/MailService.js +31 -7
  20. package/src/services/MailVerificationService.d.ts +7 -2
  21. package/src/services/MailVerificationService.js +13 -6
  22. package/src/services/strategies/LogDeliveryStrategy.d.ts +2 -4
  23. package/src/services/strategies/LogDeliveryStrategy.js +3 -3
  24. package/src/services/strategies/MailgunDeliveryStrategy.d.ts +2 -2
  25. package/src/services/strategies/MailgunDeliveryStrategy.js +6 -2
  26. package/src/services/strategies/PostmarkDeliveryStrategy.d.ts +2 -2
  27. package/src/services/strategies/PostmarkDeliveryStrategy.js +6 -2
  28. package/src/services/strategies/ResendDeliveryStrategy.d.ts +2 -2
  29. package/src/services/strategies/ResendDeliveryStrategy.js +6 -2
  30. package/src/services/strategies/SendGridDeliveryStrategy.d.ts +2 -2
  31. package/src/services/strategies/SendGridDeliveryStrategy.js +6 -2
  32. package/src/services/strategies/SmtpDeliveryStrategy.d.ts +2 -2
  33. package/src/services/strategies/SmtpDeliveryStrategy.js +7 -3
  34. package/src/services/strategies/abstracts/DeliveryStrategyBase.d.ts +3 -1
  35. package/src/templating/MailTemplateEngine.d.ts +3 -1
  36. package/src/templating/MailTemplateEngine.js +23 -3
  37. package/src/templating/strategies/BlobTemplateFetchStrategy.d.ts +2 -1
  38. package/src/templating/strategies/BlobTemplateFetchStrategy.js +3 -2
  39. package/src/terminus/MailHealthIndicator.d.ts +2 -1
  40. package/src/terminus/MailHealthIndicator.js +2 -2
package/README.md CHANGED
@@ -70,6 +70,8 @@ Set `MAIL_DELIVERY_STRATEGY` to one of: `smtp`, `postmark`, `resend`, `sendgrid`
70
70
  - **Template engines**: File-based or blob-based template fetching with variable substitution
71
71
  - **Health checks**: Built-in `MailHealthIndicator` for readiness probes
72
72
  - **Email verification**: `MailVerificationService` for address verification flows
73
+ - **Attachment support**: Send file attachments with MIME-type validation and size limits (25 MB max)
74
+ - **Header injection prevention**: Subject lines are automatically sanitized against SMTP header injection
73
75
 
74
76
  ## Available Providers
75
77
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breadstone/archipel-platform-mailing",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "Email delivery with strategy-based transport and template engines for NestJS applications.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -75,8 +75,9 @@
75
75
  "README.md"
76
76
  ],
77
77
  "dependencies": {
78
- "@breadstone/archipel-platform-blob-storage": "0.0.13",
79
- "@breadstone/archipel-platform-core": "0.0.13",
78
+ "@breadstone/archipel-platform-blob-storage": "0.0.15",
79
+ "@breadstone/archipel-platform-configuration": "0.0.15",
80
+ "@breadstone/archipel-platform-core": "0.0.15",
80
81
  "form-data": "4.0.5",
81
82
  "nodemailer": "8.0.5",
82
83
  "tslib": "2.8.1"
package/src/MailModule.js CHANGED
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.MailModule = void 0;
5
5
  const tslib_1 = require("tslib");
6
6
  const archipel_platform_blob_storage_1 = require("@breadstone/archipel-platform-blob-storage");
7
+ const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
7
8
  const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
8
9
  const common_1 = require("@nestjs/common");
9
10
  const env_1 = require("./env");
@@ -28,7 +29,7 @@ exports.MailModule = MailModule = tslib_1.__decorate([
28
29
  (0, common_1.Global)(),
29
30
  (0, common_1.Module)({
30
31
  imports: [
31
- archipel_platform_core_1.ConfigModule.register('platform-mailing', env_1.PLATFORM_MAILING_CONFIG_ENTRIES),
32
+ archipel_platform_configuration_1.ConfigModule.register('platform-mailing', env_1.PLATFORM_MAILING_CONFIG_ENTRIES),
32
33
  archipel_platform_blob_storage_1.BlobModule,
33
34
  archipel_platform_core_1.EventModule,
34
35
  archipel_platform_core_1.HealthModule,
@@ -46,7 +47,7 @@ exports.MailModule = MailModule = tslib_1.__decorate([
46
47
  }
47
48
  throw new Error(`Unknown template strategy: ${templateStrategy}`);
48
49
  },
49
- inject: [archipel_platform_core_1.ConfigService, archipel_platform_blob_storage_1.BlobService, archipel_platform_core_1.ResourceManager],
50
+ inject: [archipel_platform_configuration_1.ConfigService, archipel_platform_blob_storage_1.BlobService, archipel_platform_core_1.ResourceManager],
50
51
  },
51
52
  {
52
53
  provide: MailTokens_1.MAIL_DELIVERY_STRATEGY_TOKEN,
@@ -54,7 +55,7 @@ exports.MailModule = MailModule = tslib_1.__decorate([
54
55
  const deliveryStrategy = configService.get(env_1.MAIL_DELIVERY_STRATEGY.key);
55
56
  if (deliveryStrategy === 'log') {
56
57
  const { LogDeliveryStrategy } = await Promise.resolve().then(() => tslib_1.__importStar(require('./services/strategies/LogDeliveryStrategy')));
57
- return new LogDeliveryStrategy(configService);
58
+ return new LogDeliveryStrategy();
58
59
  }
59
60
  if (deliveryStrategy === 'smtp') {
60
61
  const { SmtpDeliveryStrategy } = await Promise.resolve().then(() => tslib_1.__importStar(require('./services/strategies/SmtpDeliveryStrategy')));
@@ -78,7 +79,7 @@ exports.MailModule = MailModule = tslib_1.__decorate([
78
79
  }
79
80
  throw new Error(`Unknown delivery strategy: ${deliveryStrategy}`);
80
81
  },
81
- inject: [archipel_platform_core_1.ConfigService],
82
+ inject: [archipel_platform_configuration_1.ConfigService],
82
83
  },
83
84
  archipel_platform_core_1.ContentTemplateEngine,
84
85
  MailService_1.MailService,
package/src/env.d.ts CHANGED
@@ -1,37 +1,37 @@
1
- import { IConfigRegistryEntry } from '@breadstone/archipel-platform-core';
1
+ import { IConfigRegistryEntry } from '@breadstone/archipel-platform-configuration';
2
2
  /** Which template-fetch strategy to use (e.g. `file`, `blob`). */
3
- export declare const MAIL_TEMPLATE_STRATEGY: import("@breadstone/archipel-platform-core").IConfigKey<string>;
3
+ export declare const MAIL_TEMPLATE_STRATEGY: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
4
4
  /** Which delivery strategy to use (e.g. `smtp`, `postmark`, `resend`, `sendgrid`, `mailgun`, `log`). */
5
- export declare const MAIL_DELIVERY_STRATEGY: import("@breadstone/archipel-platform-core").IConfigKey<string>;
5
+ export declare const MAIL_DELIVERY_STRATEGY: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
6
6
  /** Sender e-mail address shown in the `From` header. */
7
- export declare const MAIL_SENDER_EMAIL: import("@breadstone/archipel-platform-core").IConfigKey<string>;
7
+ export declare const MAIL_SENDER_EMAIL: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
8
8
  /** Template engine format identifier (e.g. `handlebars`). */
9
- export declare const MAIL_TEMPLATE_ENGINE_FORMAT: import("@breadstone/archipel-platform-core").IConfigKey<string>;
9
+ export declare const MAIL_TEMPLATE_ENGINE_FORMAT: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
10
10
  /** API key for the Postmark delivery service. */
11
- export declare const MAIL_POSTMARK_API_KEY: import("@breadstone/archipel-platform-core").IConfigKey<string>;
11
+ export declare const MAIL_POSTMARK_API_KEY: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
12
12
  /** API key for the Resend delivery service. */
13
- export declare const MAIL_RESEND_API_KEY: import("@breadstone/archipel-platform-core").IConfigKey<string>;
13
+ export declare const MAIL_RESEND_API_KEY: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
14
14
  /** API key for the SendGrid delivery service. */
15
- export declare const MAIL_SENDGRID_API_KEY: import("@breadstone/archipel-platform-core").IConfigKey<string>;
15
+ export declare const MAIL_SENDGRID_API_KEY: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
16
16
  /** API key for the Mailgun delivery service. */
17
- export declare const MAIL_MAILGUN_API_KEY: import("@breadstone/archipel-platform-core").IConfigKey<string>;
17
+ export declare const MAIL_MAILGUN_API_KEY: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
18
18
  /** Mailgun domain (e.g. `mg.example.com`). */
19
- export declare const MAIL_MAILGUN_DOMAIN: import("@breadstone/archipel-platform-core").IConfigKey<string>;
19
+ export declare const MAIL_MAILGUN_DOMAIN: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
20
20
  /** SMTP server host. */
21
- export declare const MAIL_SMTP_HOST: import("@breadstone/archipel-platform-core").IConfigKey<string>;
21
+ export declare const MAIL_SMTP_HOST: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
22
22
  /** SMTP server port. */
23
- export declare const MAIL_SMTP_PORT: import("@breadstone/archipel-platform-core").IConfigKey<number>;
23
+ export declare const MAIL_SMTP_PORT: import("@breadstone/archipel-platform-configuration").IConfigKey<number>;
24
24
  /** Whether the SMTP connection uses TLS. */
25
- export declare const MAIL_SMTP_SECURE: import("@breadstone/archipel-platform-core").IConfigKey<boolean>;
25
+ export declare const MAIL_SMTP_SECURE: import("@breadstone/archipel-platform-configuration").IConfigKey<boolean>;
26
26
  /** SMTP authentication username. */
27
- export declare const MAIL_SMTP_USER: import("@breadstone/archipel-platform-core").IConfigKey<string>;
27
+ export declare const MAIL_SMTP_USER: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
28
28
  /** SMTP authentication password. */
29
- export declare const MAIL_SMTP_PASSWORD: import("@breadstone/archipel-platform-core").IConfigKey<string>;
29
+ export declare const MAIL_SMTP_PASSWORD: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
30
30
  /** Mail host checked by the health indicator. */
31
- export declare const MAIL_HOST: import("@breadstone/archipel-platform-core").IConfigKey<string>;
31
+ export declare const MAIL_HOST: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
32
32
  /** Mail user checked by the health indicator. */
33
- export declare const MAIL_USER: import("@breadstone/archipel-platform-core").IConfigKey<string>;
33
+ export declare const MAIL_USER: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
34
34
  /** Mail port checked by the health indicator. */
35
- export declare const MAIL_PORT: import("@breadstone/archipel-platform-core").IConfigKey<number>;
35
+ export declare const MAIL_PORT: import("@breadstone/archipel-platform-configuration").IConfigKey<number>;
36
36
  /** All configuration entries required by `platform-mailing`. */
37
37
  export declare const PLATFORM_MAILING_CONFIG_ENTRIES: ReadonlyArray<Omit<IConfigRegistryEntry, 'module'>>;
package/src/env.js CHANGED
@@ -2,63 +2,63 @@
2
2
  // #region Imports
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.PLATFORM_MAILING_CONFIG_ENTRIES = exports.MAIL_PORT = exports.MAIL_USER = exports.MAIL_HOST = exports.MAIL_SMTP_PASSWORD = exports.MAIL_SMTP_USER = exports.MAIL_SMTP_SECURE = exports.MAIL_SMTP_PORT = exports.MAIL_SMTP_HOST = exports.MAIL_MAILGUN_DOMAIN = exports.MAIL_MAILGUN_API_KEY = exports.MAIL_SENDGRID_API_KEY = exports.MAIL_RESEND_API_KEY = exports.MAIL_POSTMARK_API_KEY = exports.MAIL_TEMPLATE_ENGINE_FORMAT = exports.MAIL_SENDER_EMAIL = exports.MAIL_DELIVERY_STRATEGY = exports.MAIL_TEMPLATE_STRATEGY = void 0;
5
- const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
5
+ const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
6
6
  // #endregion
7
7
  // ──────────────────────────────────────────────────────────────
8
8
  // Delivery & Template Strategy
9
9
  // ──────────────────────────────────────────────────────────────
10
10
  /** Which template-fetch strategy to use (e.g. `file`, `blob`). */
11
- exports.MAIL_TEMPLATE_STRATEGY = (0, archipel_platform_core_1.createConfigKey)('MAIL_TEMPLATE_STRATEGY');
11
+ exports.MAIL_TEMPLATE_STRATEGY = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_TEMPLATE_STRATEGY');
12
12
  /** Which delivery strategy to use (e.g. `smtp`, `postmark`, `resend`, `sendgrid`, `mailgun`, `log`). */
13
- exports.MAIL_DELIVERY_STRATEGY = (0, archipel_platform_core_1.createConfigKey)('MAIL_DELIVERY_STRATEGY');
13
+ exports.MAIL_DELIVERY_STRATEGY = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_DELIVERY_STRATEGY');
14
14
  /** Sender e-mail address shown in the `From` header. */
15
- exports.MAIL_SENDER_EMAIL = (0, archipel_platform_core_1.createConfigKey)('MAIL_SENDER_EMAIL');
15
+ exports.MAIL_SENDER_EMAIL = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_SENDER_EMAIL');
16
16
  /** Template engine format identifier (e.g. `handlebars`). */
17
- exports.MAIL_TEMPLATE_ENGINE_FORMAT = (0, archipel_platform_core_1.createConfigKey)('MAIL_TEMPLATE_ENGINE_FORMAT');
17
+ exports.MAIL_TEMPLATE_ENGINE_FORMAT = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_TEMPLATE_ENGINE_FORMAT');
18
18
  // ──────────────────────────────────────────────────────────────
19
19
  // Postmark
20
20
  // ──────────────────────────────────────────────────────────────
21
21
  /** API key for the Postmark delivery service. */
22
- exports.MAIL_POSTMARK_API_KEY = (0, archipel_platform_core_1.createConfigKey)('MAIL_POSTMARK_API_KEY');
22
+ exports.MAIL_POSTMARK_API_KEY = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_POSTMARK_API_KEY');
23
23
  // ──────────────────────────────────────────────────────────────
24
24
  // Resend
25
25
  // ──────────────────────────────────────────────────────────────
26
26
  /** API key for the Resend delivery service. */
27
- exports.MAIL_RESEND_API_KEY = (0, archipel_platform_core_1.createConfigKey)('MAIL_RESEND_API_KEY');
27
+ exports.MAIL_RESEND_API_KEY = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_RESEND_API_KEY');
28
28
  // ──────────────────────────────────────────────────────────────
29
29
  // SendGrid
30
30
  // ──────────────────────────────────────────────────────────────
31
31
  /** API key for the SendGrid delivery service. */
32
- exports.MAIL_SENDGRID_API_KEY = (0, archipel_platform_core_1.createConfigKey)('MAIL_SENDGRID_API_KEY');
32
+ exports.MAIL_SENDGRID_API_KEY = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_SENDGRID_API_KEY');
33
33
  // ──────────────────────────────────────────────────────────────
34
34
  // Mailgun
35
35
  // ──────────────────────────────────────────────────────────────
36
36
  /** API key for the Mailgun delivery service. */
37
- exports.MAIL_MAILGUN_API_KEY = (0, archipel_platform_core_1.createConfigKey)('MAIL_MAILGUN_API_KEY');
37
+ exports.MAIL_MAILGUN_API_KEY = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_MAILGUN_API_KEY');
38
38
  /** Mailgun domain (e.g. `mg.example.com`). */
39
- exports.MAIL_MAILGUN_DOMAIN = (0, archipel_platform_core_1.createConfigKey)('MAIL_MAILGUN_DOMAIN');
39
+ exports.MAIL_MAILGUN_DOMAIN = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_MAILGUN_DOMAIN');
40
40
  // ──────────────────────────────────────────────────────────────
41
41
  // SMTP
42
42
  // ──────────────────────────────────────────────────────────────
43
43
  /** SMTP server host. */
44
- exports.MAIL_SMTP_HOST = (0, archipel_platform_core_1.createConfigKey)('MAIL_SMTP_HOST');
44
+ exports.MAIL_SMTP_HOST = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_SMTP_HOST');
45
45
  /** SMTP server port. */
46
- exports.MAIL_SMTP_PORT = (0, archipel_platform_core_1.createConfigKey)('MAIL_SMTP_PORT');
46
+ exports.MAIL_SMTP_PORT = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_SMTP_PORT');
47
47
  /** Whether the SMTP connection uses TLS. */
48
- exports.MAIL_SMTP_SECURE = (0, archipel_platform_core_1.createConfigKey)('MAIL_SMTP_SECURE');
48
+ exports.MAIL_SMTP_SECURE = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_SMTP_SECURE');
49
49
  /** SMTP authentication username. */
50
- exports.MAIL_SMTP_USER = (0, archipel_platform_core_1.createConfigKey)('MAIL_SMTP_USER');
50
+ exports.MAIL_SMTP_USER = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_SMTP_USER');
51
51
  /** SMTP authentication password. */
52
- exports.MAIL_SMTP_PASSWORD = (0, archipel_platform_core_1.createConfigKey)('MAIL_SMTP_PASSWORD');
52
+ exports.MAIL_SMTP_PASSWORD = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_SMTP_PASSWORD');
53
53
  // ──────────────────────────────────────────────────────────────
54
54
  // Health Indicator
55
55
  // ──────────────────────────────────────────────────────────────
56
56
  /** Mail host checked by the health indicator. */
57
- exports.MAIL_HOST = (0, archipel_platform_core_1.createConfigKey)('MAIL_HOST');
57
+ exports.MAIL_HOST = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_HOST');
58
58
  /** Mail user checked by the health indicator. */
59
- exports.MAIL_USER = (0, archipel_platform_core_1.createConfigKey)('MAIL_USER');
59
+ exports.MAIL_USER = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_USER');
60
60
  /** Mail port checked by the health indicator. */
61
- exports.MAIL_PORT = (0, archipel_platform_core_1.createConfigKey)('MAIL_PORT');
61
+ exports.MAIL_PORT = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_PORT');
62
62
  // ──────────────────────────────────────────────────────────────
63
63
  // Registry entries
64
64
  // ──────────────────────────────────────────────────────────────
@@ -105,7 +105,7 @@ exports.PLATFORM_MAILING_CONFIG_ENTRIES = [
105
105
  // SMTP
106
106
  { key: exports.MAIL_SMTP_HOST, required: false, defaultValue: 'localhost', description: 'SMTP server host.' },
107
107
  { key: exports.MAIL_SMTP_PORT, required: false, defaultValue: 587, description: 'SMTP server port.' },
108
- { key: exports.MAIL_SMTP_SECURE, required: false, defaultValue: false, description: 'Whether SMTP uses TLS.' },
108
+ { key: exports.MAIL_SMTP_SECURE, required: false, defaultValue: true, description: 'Whether SMTP uses TLS.' },
109
109
  { key: exports.MAIL_SMTP_USER, required: false, defaultValue: '', description: 'SMTP authentication username.' },
110
110
  { key: exports.MAIL_SMTP_PASSWORD, required: false, defaultValue: '', description: 'SMTP authentication password.' },
111
111
  // Health
package/src/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from './env';
2
2
  export * from './events';
3
3
  export * from './MailModule';
4
4
  export * from './MailTokens';
5
+ export * from './models/IMailAttachment';
5
6
  export * from './services/MailService';
6
7
  export * from './services/MailVerificationService';
7
8
  export * from './services/strategies/abstracts/DeliveryStrategyBase';
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ tslib_1.__exportStar(require("./env"), exports);
5
5
  tslib_1.__exportStar(require("./events"), exports);
6
6
  tslib_1.__exportStar(require("./MailModule"), exports);
7
7
  tslib_1.__exportStar(require("./MailTokens"), exports);
8
+ tslib_1.__exportStar(require("./models/IMailAttachment"), exports);
8
9
  tslib_1.__exportStar(require("./services/MailService"), exports);
9
10
  tslib_1.__exportStar(require("./services/MailVerificationService"), exports);
10
11
  tslib_1.__exportStar(require("./services/strategies/abstracts/DeliveryStrategyBase"), exports);
@@ -1,7 +1,7 @@
1
- import { IConfigRegistryEntry } from '@breadstone/archipel-platform-core';
1
+ import { IConfigRegistryEntry } from '@breadstone/archipel-platform-configuration';
2
2
  /** API key for the Mailgun delivery service. */
3
- export declare const MAILGUN_API_KEY: import("@breadstone/archipel-platform-core").IConfigKey<string>;
3
+ export declare const MAILGUN_API_KEY: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
4
4
  /** Mailgun domain (e.g. `mg.example.com`). */
5
- export declare const MAILGUN_DOMAIN: import("@breadstone/archipel-platform-core").IConfigKey<string>;
5
+ export declare const MAILGUN_DOMAIN: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
6
6
  /** Configuration entries required by the Mailgun delivery provider. */
7
7
  export declare const MAILGUN_CONFIG_ENTRIES: ReadonlyArray<Omit<IConfigRegistryEntry, 'module'>>;
@@ -2,15 +2,15 @@
2
2
  // #region Imports
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.MAILGUN_CONFIG_ENTRIES = exports.MAILGUN_DOMAIN = exports.MAILGUN_API_KEY = void 0;
5
- const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
5
+ const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
6
6
  // #endregion
7
7
  // ──────────────────────────────────────────────────────────────
8
8
  // Mailgun
9
9
  // ──────────────────────────────────────────────────────────────
10
10
  /** API key for the Mailgun delivery service. */
11
- exports.MAILGUN_API_KEY = (0, archipel_platform_core_1.createConfigKey)('MAIL_MAILGUN_API_KEY');
11
+ exports.MAILGUN_API_KEY = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_MAILGUN_API_KEY');
12
12
  /** Mailgun domain (e.g. `mg.example.com`). */
13
- exports.MAILGUN_DOMAIN = (0, archipel_platform_core_1.createConfigKey)('MAIL_MAILGUN_DOMAIN');
13
+ exports.MAILGUN_DOMAIN = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_MAILGUN_DOMAIN');
14
14
  // ──────────────────────────────────────────────────────────────
15
15
  // Registry entries
16
16
  // ──────────────────────────────────────────────────────────────
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Allowed MIME types for email attachments.
3
+ *
4
+ * @public
5
+ */
6
+ export declare const ALLOWED_ATTACHMENT_MIME_TYPES: readonly ["application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/zip", "image/png", "image/jpeg", "image/gif", "image/webp", "text/plain", "text/csv"];
7
+ /**
8
+ * Maximum attachment size in bytes (25 MB).
9
+ *
10
+ * @public
11
+ */
12
+ export declare const MAX_ATTACHMENT_SIZE_BYTES: number;
13
+ /**
14
+ * Represents an email attachment.
15
+ *
16
+ * @public
17
+ */
18
+ export interface IMailAttachment {
19
+ /**
20
+ * The filename of the attachment as displayed to the recipient.
21
+ */
22
+ readonly filename: string;
23
+ /**
24
+ * The raw content of the attachment.
25
+ */
26
+ readonly content: Buffer | string;
27
+ /**
28
+ * The MIME type of the attachment.
29
+ */
30
+ readonly contentType: string;
31
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_ATTACHMENT_SIZE_BYTES = exports.ALLOWED_ATTACHMENT_MIME_TYPES = void 0;
4
+ /**
5
+ * Allowed MIME types for email attachments.
6
+ *
7
+ * @public
8
+ */
9
+ exports.ALLOWED_ATTACHMENT_MIME_TYPES = [
10
+ 'application/pdf',
11
+ 'application/msword',
12
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
13
+ 'application/vnd.ms-excel',
14
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
15
+ 'application/zip',
16
+ 'image/png',
17
+ 'image/jpeg',
18
+ 'image/gif',
19
+ 'image/webp',
20
+ 'text/plain',
21
+ 'text/csv',
22
+ ];
23
+ /**
24
+ * Maximum attachment size in bytes (25 MB).
25
+ *
26
+ * @public
27
+ */
28
+ exports.MAX_ATTACHMENT_SIZE_BYTES = 25 * 1024 * 1024;
29
+ //# sourceMappingURL=IMailAttachment.js.map
@@ -1,5 +1,5 @@
1
- import { IConfigRegistryEntry } from '@breadstone/archipel-platform-core';
1
+ import { IConfigRegistryEntry } from '@breadstone/archipel-platform-configuration';
2
2
  /** API key for the Postmark delivery service. */
3
- export declare const POSTMARK_API_KEY: import("@breadstone/archipel-platform-core").IConfigKey<string>;
3
+ export declare const POSTMARK_API_KEY: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
4
4
  /** Configuration entries required by the Postmark delivery provider. */
5
5
  export declare const POSTMARK_CONFIG_ENTRIES: ReadonlyArray<Omit<IConfigRegistryEntry, 'module'>>;
@@ -2,13 +2,13 @@
2
2
  // #region Imports
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.POSTMARK_CONFIG_ENTRIES = exports.POSTMARK_API_KEY = void 0;
5
- const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
5
+ const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
6
6
  // #endregion
7
7
  // ──────────────────────────────────────────────────────────────
8
8
  // Postmark
9
9
  // ──────────────────────────────────────────────────────────────
10
10
  /** API key for the Postmark delivery service. */
11
- exports.POSTMARK_API_KEY = (0, archipel_platform_core_1.createConfigKey)('MAIL_POSTMARK_API_KEY');
11
+ exports.POSTMARK_API_KEY = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_POSTMARK_API_KEY');
12
12
  // ──────────────────────────────────────────────────────────────
13
13
  // Registry entries
14
14
  // ──────────────────────────────────────────────────────────────
@@ -1,5 +1,5 @@
1
- import { IConfigRegistryEntry } from '@breadstone/archipel-platform-core';
1
+ import { IConfigRegistryEntry } from '@breadstone/archipel-platform-configuration';
2
2
  /** API key for the Resend delivery service. */
3
- export declare const RESEND_API_KEY: import("@breadstone/archipel-platform-core").IConfigKey<string>;
3
+ export declare const RESEND_API_KEY: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
4
4
  /** Configuration entries required by the Resend delivery provider. */
5
5
  export declare const RESEND_CONFIG_ENTRIES: ReadonlyArray<Omit<IConfigRegistryEntry, 'module'>>;
package/src/resend/env.js CHANGED
@@ -2,13 +2,13 @@
2
2
  // #region Imports
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.RESEND_CONFIG_ENTRIES = exports.RESEND_API_KEY = void 0;
5
- const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
5
+ const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
6
6
  // #endregion
7
7
  // ──────────────────────────────────────────────────────────────
8
8
  // Resend
9
9
  // ──────────────────────────────────────────────────────────────
10
10
  /** API key for the Resend delivery service. */
11
- exports.RESEND_API_KEY = (0, archipel_platform_core_1.createConfigKey)('MAIL_RESEND_API_KEY');
11
+ exports.RESEND_API_KEY = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_RESEND_API_KEY');
12
12
  // ──────────────────────────────────────────────────────────────
13
13
  // Registry entries
14
14
  // ──────────────────────────────────────────────────────────────
@@ -1,5 +1,5 @@
1
- import { IConfigRegistryEntry } from '@breadstone/archipel-platform-core';
1
+ import { IConfigRegistryEntry } from '@breadstone/archipel-platform-configuration';
2
2
  /** API key for the SendGrid delivery service. */
3
- export declare const SENDGRID_API_KEY: import("@breadstone/archipel-platform-core").IConfigKey<string>;
3
+ export declare const SENDGRID_API_KEY: import("@breadstone/archipel-platform-configuration").IConfigKey<string>;
4
4
  /** Configuration entries required by the SendGrid delivery provider. */
5
5
  export declare const SENDGRID_CONFIG_ENTRIES: ReadonlyArray<Omit<IConfigRegistryEntry, 'module'>>;
@@ -2,13 +2,13 @@
2
2
  // #region Imports
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.SENDGRID_CONFIG_ENTRIES = exports.SENDGRID_API_KEY = void 0;
5
- const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
5
+ const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
6
6
  // #endregion
7
7
  // ──────────────────────────────────────────────────────────────
8
8
  // SendGrid
9
9
  // ──────────────────────────────────────────────────────────────
10
10
  /** API key for the SendGrid delivery service. */
11
- exports.SENDGRID_API_KEY = (0, archipel_platform_core_1.createConfigKey)('MAIL_SENDGRID_API_KEY');
11
+ exports.SENDGRID_API_KEY = (0, archipel_platform_configuration_1.createConfigKey)('MAIL_SENDGRID_API_KEY');
12
12
  // ──────────────────────────────────────────────────────────────
13
13
  // Registry entries
14
14
  // ──────────────────────────────────────────────────────────────
@@ -1,4 +1,5 @@
1
- import { ConfigService } from '@breadstone/archipel-platform-core';
1
+ import { ConfigService } from '@breadstone/archipel-platform-configuration';
2
+ import { type IMailAttachment } from '../models/IMailAttachment';
2
3
  import { MailTemplateEngine } from '../templating/MailTemplateEngine';
3
4
  import { DeliveryStrategyBase } from './strategies/abstracts/DeliveryStrategyBase';
4
5
  /**
@@ -31,7 +32,7 @@ export declare class MailService {
31
32
  from?: string;
32
33
  to: string;
33
34
  subject: string;
34
- }, content: string, isHtml: boolean): Promise<void>;
35
+ }, content: string, isHtml: boolean, attachments?: Array<IMailAttachment>): Promise<void>;
35
36
  /**
36
37
  * Sends an email using a predefined template (HTML or text).
37
38
  * Automatically uses the template format (either 'html' or 'txt') based on the environment variable.
@@ -57,4 +58,11 @@ export declare class MailService {
57
58
  * @throws Error if the format is invalid.
58
59
  */
59
60
  private ensureValidEmail;
61
+ /**
62
+ * Validates email attachments against MIME-type allowlist and size limits.
63
+ *
64
+ * @param attachments - Array of attachments to validate
65
+ * @throws Error if any attachment violates constraints
66
+ */
67
+ private validateAttachments;
60
68
  }
@@ -3,9 +3,10 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.MailService = void 0;
5
5
  const tslib_1 = require("tslib");
6
- const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
6
+ const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
7
7
  const common_1 = require("@nestjs/common");
8
8
  const env_1 = require("../env");
9
+ const IMailAttachment_1 = require("../models/IMailAttachment");
9
10
  const MailTokens_1 = require("../MailTokens");
10
11
  const MailTemplateEngine_1 = require("../templating/MailTemplateEngine");
11
12
  const DeliveryStrategyBase_1 = require("./strategies/abstracts/DeliveryStrategyBase");
@@ -45,13 +46,17 @@ let MailService = class MailService {
45
46
  * @param content - Email body content (plain text or HTML)
46
47
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
47
48
  */
48
- async send(mailOptions, content, isHtml) {
49
+ async send(mailOptions, content, isHtml, attachments) {
49
50
  this.ensureValidEmail(mailOptions.to);
50
51
  if (mailOptions.from) {
51
52
  this.ensureValidEmail(mailOptions.from);
52
53
  }
54
+ if (attachments) {
55
+ this.validateAttachments(attachments);
56
+ }
53
57
  const sender = mailOptions.from ?? this._defaultFrom;
54
- await this._deliveryStrategy.send(sender, mailOptions.to, mailOptions.subject, content, isHtml);
58
+ const sanitizedSubject = mailOptions.subject.replace(/[\r\n]/g, ' ');
59
+ await this._deliveryStrategy.send(sender, mailOptions.to, sanitizedSubject, content, isHtml, attachments);
55
60
  }
56
61
  /**
57
62
  * Sends an email using a predefined template (HTML or text).
@@ -75,18 +80,37 @@ let MailService = class MailService {
75
80
  * @throws Error if the format is invalid.
76
81
  */
77
82
  ensureValidEmail(email) {
78
- // Basic RFC 5322 compliant (simplified) regex deliberately strict but not over-engineered.
79
- const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
80
- if (!emailRegex.test(email)) {
83
+ // Rejects leading/trailing dots, consecutive dots, and requires valid domain structure.
84
+ const emailRegex = /^[A-Z0-9](?:[A-Z0-9._%+-]*[A-Z0-9])?@[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?(?:\.[A-Z0-9](?:[A-Z0-9-]*[A-Z0-9])?)*\.[A-Z]{2,}$/i;
85
+ if (!emailRegex.test(email) || email.includes('..')) {
81
86
  throw new Error(`Invalid email address: '${email}'`);
82
87
  }
83
88
  }
89
+ /**
90
+ * Validates email attachments against MIME-type allowlist and size limits.
91
+ *
92
+ * @param attachments - Array of attachments to validate
93
+ * @throws Error if any attachment violates constraints
94
+ */
95
+ validateAttachments(attachments) {
96
+ for (const attachment of attachments) {
97
+ if (!IMailAttachment_1.ALLOWED_ATTACHMENT_MIME_TYPES.includes(attachment.contentType)) {
98
+ throw new Error(`Unsupported attachment MIME type: '${attachment.contentType}'`);
99
+ }
100
+ const size = typeof attachment.content === 'string'
101
+ ? Buffer.byteLength(attachment.content, 'utf-8')
102
+ : attachment.content.length;
103
+ if (size > IMailAttachment_1.MAX_ATTACHMENT_SIZE_BYTES) {
104
+ throw new Error(`Attachment '${attachment.filename}' exceeds maximum size of ${IMailAttachment_1.MAX_ATTACHMENT_SIZE_BYTES} bytes`);
105
+ }
106
+ }
107
+ }
84
108
  };
85
109
  exports.MailService = MailService;
86
110
  exports.MailService = MailService = tslib_1.__decorate([
87
111
  (0, common_1.Injectable)(),
88
112
  tslib_1.__param(2, (0, common_1.Inject)(MailTokens_1.MAIL_DELIVERY_STRATEGY_TOKEN)),
89
- tslib_1.__metadata("design:paramtypes", [archipel_platform_core_1.ConfigService,
113
+ tslib_1.__metadata("design:paramtypes", [archipel_platform_configuration_1.ConfigService,
90
114
  MailTemplateEngine_1.MailTemplateEngine,
91
115
  DeliveryStrategyBase_1.DeliveryStrategyBase])
92
116
  ], MailService);
@@ -1,10 +1,15 @@
1
- import { ConfigService } from '@breadstone/archipel-platform-core';
1
+ import { ConfigService } from '@breadstone/archipel-platform-configuration';
2
2
  /**
3
- * Represents the `MailVerificationService` class.
3
+ * Verifies SMTP server connectivity. Does **not** validate individual email
4
+ * addresses — only checks whether the configured SMTP transport is reachable.
4
5
  *
5
6
  * @public
7
+ * @deprecated Renamed to `SmtpConnectionVerifier` in a future release to better
8
+ * reflect its actual purpose. Prefer wrapping this in a health-check rather
9
+ * than calling it directly.
6
10
  */
7
11
  export declare class MailVerificationService {
12
+ private readonly _logger;
8
13
  private readonly _transporter;
9
14
  /**
10
15
  * Constructs a new instance of the `MailVerificationService` class.
@@ -1,19 +1,24 @@
1
1
  "use strict";
2
2
  // #region Imports
3
+ var MailVerificationService_1;
3
4
  Object.defineProperty(exports, "__esModule", { value: true });
4
5
  exports.MailVerificationService = void 0;
5
6
  const tslib_1 = require("tslib");
6
- const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
7
+ const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
7
8
  const common_1 = require("@nestjs/common");
8
9
  const nodemailer = tslib_1.__importStar(require("nodemailer"));
9
10
  const env_1 = require("../env");
10
11
  // #endregion
11
12
  /**
12
- * Represents the `MailVerificationService` class.
13
+ * Verifies SMTP server connectivity. Does **not** validate individual email
14
+ * addresses — only checks whether the configured SMTP transport is reachable.
13
15
  *
14
16
  * @public
17
+ * @deprecated Renamed to `SmtpConnectionVerifier` in a future release to better
18
+ * reflect its actual purpose. Prefer wrapping this in a health-check rather
19
+ * than calling it directly.
15
20
  */
16
- let MailVerificationService = class MailVerificationService {
21
+ let MailVerificationService = MailVerificationService_1 = class MailVerificationService {
17
22
  // #endregion
18
23
  // #region Ctor
19
24
  /**
@@ -22,6 +27,8 @@ let MailVerificationService = class MailVerificationService {
22
27
  * @public
23
28
  */
24
29
  constructor(configService) {
30
+ // #region Fields
31
+ this._logger = new common_1.Logger(MailVerificationService_1.name);
25
32
  this._transporter = nodemailer.createTransport({
26
33
  host: configService.tryGet(env_1.MAIL_SMTP_HOST.key, 'localhost'),
27
34
  port: 465,
@@ -42,7 +49,7 @@ let MailVerificationService = class MailVerificationService {
42
49
  return new Promise((resolve, reject) => {
43
50
  this._transporter.verify((error, success) => {
44
51
  if (error) {
45
- common_1.Logger.error('SMTP verification error:', error);
52
+ this._logger.error('SMTP verification error:', error);
46
53
  resolve(false);
47
54
  return; // Couldn't connect or verify
48
55
  }
@@ -70,8 +77,8 @@ let MailVerificationService = class MailVerificationService {
70
77
  }
71
78
  };
72
79
  exports.MailVerificationService = MailVerificationService;
73
- exports.MailVerificationService = MailVerificationService = tslib_1.__decorate([
80
+ exports.MailVerificationService = MailVerificationService = MailVerificationService_1 = tslib_1.__decorate([
74
81
  (0, common_1.Injectable)(),
75
- tslib_1.__metadata("design:paramtypes", [archipel_platform_core_1.ConfigService])
82
+ tslib_1.__metadata("design:paramtypes", [archipel_platform_configuration_1.ConfigService])
76
83
  ], MailVerificationService);
77
84
  //# sourceMappingURL=MailVerificationService.js.map
@@ -1,4 +1,3 @@
1
- import { ConfigService } from '@breadstone/archipel-platform-core';
2
1
  import { DeliveryStrategyBase } from './abstracts/DeliveryStrategyBase';
3
2
  /**
4
3
  * The `LogDeliveryStrategy` class.
@@ -7,12 +6,11 @@ import { DeliveryStrategyBase } from './abstracts/DeliveryStrategyBase';
7
6
  */
8
7
  export declare class LogDeliveryStrategy extends DeliveryStrategyBase {
9
8
  private readonly _logger;
10
- private readonly _configService;
11
9
  /**
12
10
  * Constructs a new instance of the `LogDelivery` class.
13
11
  *
14
12
  * @public
15
13
  */
16
- constructor(configService: ConfigService);
17
- send(from: string, to: string, subject: string, content: string, isHtml: boolean): Promise<void>;
14
+ constructor();
15
+ send(from: string, to: string, subject: string, content: string, isHtml: boolean, _attachments?: Array<import('../../models/IMailAttachment').IMailAttachment>): Promise<void>;
18
16
  }
@@ -18,14 +18,14 @@ class LogDeliveryStrategy extends DeliveryStrategyBase_1.DeliveryStrategyBase {
18
18
  *
19
19
  * @public
20
20
  */
21
- constructor(configService) {
21
+ constructor() {
22
22
  super();
23
23
  // #region Fields
24
24
  this._logger = new common_1.Logger(LogDeliveryStrategy.name);
25
- this._configService = configService;
26
25
  }
27
26
  // #endregion
28
- send(from, to, subject, content, isHtml) {
27
+ // #region Methods
28
+ send(from, to, subject, content, isHtml, _attachments) {
29
29
  this._logger.log(`Logged email: ${JSON.stringify({
30
30
  from: from,
31
31
  to: to,
@@ -1,4 +1,4 @@
1
- import { ConfigService } from '@breadstone/archipel-platform-core';
1
+ import { ConfigService } from '@breadstone/archipel-platform-configuration';
2
2
  import { DeliveryStrategyBase } from './abstracts/DeliveryStrategyBase';
3
3
  /**
4
4
  * Delivers emails via the Mailgun API.
@@ -25,5 +25,5 @@ export declare class MailgunDeliveryStrategy extends DeliveryStrategyBase {
25
25
  * @param content - Email body content (plain text or HTML)
26
26
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
27
27
  */
28
- send(from: string, to: string, subject: string, content: string, isHtml: boolean): Promise<void>;
28
+ send(from: string, to: string, subject: string, content: string, isHtml: boolean, _attachments?: Array<import('../../models/IMailAttachment').IMailAttachment>): Promise<void>;
29
29
  }
@@ -45,14 +45,18 @@ class MailgunDeliveryStrategy extends DeliveryStrategyBase_1.DeliveryStrategyBas
45
45
  * @param content - Email body content (plain text or HTML)
46
46
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
47
47
  */
48
- async send(from, to, subject, content, isHtml) {
48
+ async send(from, to, subject, content, isHtml, _attachments) {
49
49
  try {
50
- await this._mailgunClient.messages.create(this._domain, {
50
+ const sendPromise = this._mailgunClient.messages.create(this._domain, {
51
51
  from,
52
52
  to,
53
53
  subject,
54
54
  ...(isHtml ? { html: content } : { text: content }),
55
55
  });
56
+ const timeoutPromise = new Promise((_resolve, reject) => {
57
+ setTimeout(() => reject(new Error('Mailgun send timed out after 30000ms')), 30_000);
58
+ });
59
+ await Promise.race([sendPromise, timeoutPromise]);
56
60
  this._logger.log('Email sent successfully', 'Mail');
57
61
  }
58
62
  catch (error) {
@@ -1,4 +1,4 @@
1
- import { ConfigService } from '@breadstone/archipel-platform-core';
1
+ import { ConfigService } from '@breadstone/archipel-platform-configuration';
2
2
  import { DeliveryStrategyBase } from './abstracts/DeliveryStrategyBase';
3
3
  /**
4
4
  * The `PostmarkDeliveryStrategy` class.
@@ -24,5 +24,5 @@ export declare class PostmarkDeliveryStrategy extends DeliveryStrategyBase {
24
24
  * @param content - Email body content (plain text or HTML)
25
25
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
26
26
  */
27
- send(from: string, to: string, subject: string, content: string, isHtml: boolean): Promise<void>;
27
+ send(from: string, to: string, subject: string, content: string, isHtml: boolean, _attachments?: Array<import('../../models/IMailAttachment').IMailAttachment>): Promise<void>;
28
28
  }
@@ -38,14 +38,18 @@ class PostmarkDeliveryStrategy extends DeliveryStrategyBase_1.DeliveryStrategyBa
38
38
  * @param content - Email body content (plain text or HTML)
39
39
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
40
40
  */
41
- async send(from, to, subject, content, isHtml) {
41
+ async send(from, to, subject, content, isHtml, _attachments) {
42
42
  try {
43
- await this._postmarkClient.sendEmail({
43
+ const sendPromise = this._postmarkClient.sendEmail({
44
44
  From: from,
45
45
  To: to,
46
46
  Subject: subject,
47
47
  ...(isHtml ? { HtmlBody: content } : { TextBody: content }),
48
48
  });
49
+ const timeoutPromise = new Promise((_resolve, reject) => {
50
+ setTimeout(() => reject(new Error('Postmark send timed out after 30000ms')), 30_000);
51
+ });
52
+ await Promise.race([sendPromise, timeoutPromise]);
49
53
  this._logger.log('Email sent successfully', 'Mail');
50
54
  }
51
55
  catch (error) {
@@ -1,4 +1,4 @@
1
- import { ConfigService } from '@breadstone/archipel-platform-core';
1
+ import { ConfigService } from '@breadstone/archipel-platform-configuration';
2
2
  import { DeliveryStrategyBase } from './abstracts/DeliveryStrategyBase';
3
3
  /**
4
4
  * Delivers emails via the Resend API.
@@ -24,5 +24,5 @@ export declare class ResendDeliveryStrategy extends DeliveryStrategyBase {
24
24
  * @param content - Email body content (plain text or HTML)
25
25
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
26
26
  */
27
- send(from: string, to: string, subject: string, content: string, isHtml: boolean): Promise<void>;
27
+ send(from: string, to: string, subject: string, content: string, isHtml: boolean, _attachments?: Array<import('../../models/IMailAttachment').IMailAttachment>): Promise<void>;
28
28
  }
@@ -38,14 +38,18 @@ class ResendDeliveryStrategy extends DeliveryStrategyBase_1.DeliveryStrategyBase
38
38
  * @param content - Email body content (plain text or HTML)
39
39
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
40
40
  */
41
- async send(from, to, subject, content, isHtml) {
41
+ async send(from, to, subject, content, isHtml, _attachments) {
42
42
  try {
43
- await this._resendClient.emails.send({
43
+ const sendPromise = this._resendClient.emails.send({
44
44
  from,
45
45
  to,
46
46
  subject,
47
47
  ...(isHtml ? { html: content } : { text: content }),
48
48
  });
49
+ const timeoutPromise = new Promise((_resolve, reject) => {
50
+ setTimeout(() => reject(new Error('Resend send timed out after 30000ms')), 30_000);
51
+ });
52
+ await Promise.race([sendPromise, timeoutPromise]);
49
53
  this._logger.log('Email sent successfully', 'Mail');
50
54
  }
51
55
  catch (error) {
@@ -1,4 +1,4 @@
1
- import { ConfigService } from '@breadstone/archipel-platform-core';
1
+ import { ConfigService } from '@breadstone/archipel-platform-configuration';
2
2
  import { DeliveryStrategyBase } from './abstracts/DeliveryStrategyBase';
3
3
  /**
4
4
  * Delivers emails via the SendGrid API.
@@ -23,5 +23,5 @@ export declare class SendGridDeliveryStrategy extends DeliveryStrategyBase {
23
23
  * @param content - Email body content (plain text or HTML)
24
24
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
25
25
  */
26
- send(from: string, to: string, subject: string, content: string, isHtml: boolean): Promise<void>;
26
+ send(from: string, to: string, subject: string, content: string, isHtml: boolean, _attachments?: Array<import('../../models/IMailAttachment').IMailAttachment>): Promise<void>;
27
27
  }
@@ -39,14 +39,18 @@ class SendGridDeliveryStrategy extends DeliveryStrategyBase_1.DeliveryStrategyBa
39
39
  * @param content - Email body content (plain text or HTML)
40
40
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
41
41
  */
42
- async send(from, to, subject, content, isHtml) {
42
+ async send(from, to, subject, content, isHtml, _attachments) {
43
43
  try {
44
- await mail_1.default.send({
44
+ const sendPromise = mail_1.default.send({
45
45
  from,
46
46
  to,
47
47
  subject,
48
48
  ...(isHtml ? { html: content } : { text: content }),
49
49
  });
50
+ const timeoutPromise = new Promise((_resolve, reject) => {
51
+ setTimeout(() => reject(new Error('SendGrid send timed out after 30000ms')), 30_000);
52
+ });
53
+ await Promise.race([sendPromise, timeoutPromise]);
50
54
  this._logger.log('Email sent successfully', 'Mail');
51
55
  }
52
56
  catch (error) {
@@ -1,4 +1,4 @@
1
- import { ConfigService } from '@breadstone/archipel-platform-core';
1
+ import { ConfigService } from '@breadstone/archipel-platform-configuration';
2
2
  import { DeliveryStrategyBase } from './abstracts/DeliveryStrategyBase';
3
3
  /**
4
4
  * The `SmtpDeliveryStrategy` class.
@@ -24,5 +24,5 @@ export declare class SmtpDeliveryStrategy extends DeliveryStrategyBase {
24
24
  * @param content - Email body content (plain text or HTML)
25
25
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
26
26
  */
27
- send(from: string, to: string, subject: string, content: string, isHtml: boolean): Promise<void>;
27
+ send(from: string, to: string, subject: string, content: string, isHtml: boolean, _attachments?: Array<import('../../models/IMailAttachment').IMailAttachment>): Promise<void>;
28
28
  }
@@ -27,7 +27,7 @@ class SmtpDeliveryStrategy extends DeliveryStrategyBase_1.DeliveryStrategyBase {
27
27
  const options = {
28
28
  host: configService.tryGet(env_1.MAIL_SMTP_HOST.key, 'localhost'),
29
29
  port: configService.tryGet(env_1.MAIL_SMTP_PORT.key, 587),
30
- secure: configService.tryGet(env_1.MAIL_SMTP_SECURE.key, false),
30
+ secure: configService.tryGet(env_1.MAIL_SMTP_SECURE.key, true),
31
31
  auth: {
32
32
  user: configService.tryGet(env_1.MAIL_SMTP_USER.key, ''),
33
33
  pass: configService.tryGet(env_1.MAIL_SMTP_PASSWORD.key, ''),
@@ -47,14 +47,18 @@ class SmtpDeliveryStrategy extends DeliveryStrategyBase_1.DeliveryStrategyBase {
47
47
  * @param content - Email body content (plain text or HTML)
48
48
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
49
49
  */
50
- async send(from, to, subject, content, isHtml) {
50
+ async send(from, to, subject, content, isHtml, _attachments) {
51
51
  try {
52
- await this._transporter.sendMail({
52
+ const sendPromise = this._transporter.sendMail({
53
53
  from,
54
54
  to,
55
55
  subject,
56
56
  ...(isHtml ? { html: content } : { text: content }),
57
57
  });
58
+ const timeoutPromise = new Promise((_resolve, reject) => {
59
+ setTimeout(() => reject(new Error('SMTP send timed out after 30000ms')), 30_000);
60
+ });
61
+ await Promise.race([sendPromise, timeoutPromise]);
58
62
  this._logger.log('Email sent successfully', 'Mail');
59
63
  }
60
64
  catch (error) {
@@ -1,3 +1,4 @@
1
+ import type { IMailAttachment } from '../../../models/IMailAttachment';
1
2
  /**
2
3
  * Represents the base class for all email delivery strategies.
3
4
  *
@@ -21,6 +22,7 @@ export declare abstract class DeliveryStrategyBase {
21
22
  * @param subject - Subject of the email
22
23
  * @param content - Email body content (plain text or HTML)
23
24
  * @param isHtml - Flag to indicate whether the email is HTML (true) or plain text (false)
25
+ * @param attachments - Optional array of file attachments
24
26
  */
25
- abstract send(from: string, to: string, subject: string, content: string, isHtml: boolean): Promise<void>;
27
+ abstract send(from: string, to: string, subject: string, content: string, isHtml: boolean, attachments?: Array<IMailAttachment>): Promise<void>;
26
28
  }
@@ -1,7 +1,7 @@
1
1
  import { ContentTemplateEngine } from '@breadstone/archipel-platform-core';
2
2
  import { OnModuleInit } from '@nestjs/common';
3
- import { TemplateFetchStrategyBase } from './strategies/abstracts/TemplateFetchStrategyBase';
4
3
  import { MailTemplateFormat } from '../MailTokens';
4
+ import { TemplateFetchStrategyBase } from './strategies/abstracts/TemplateFetchStrategyBase';
5
5
  /**
6
6
  * The `MailTemplateEngine` class.
7
7
  *
@@ -35,4 +35,6 @@ export declare class MailTemplateEngine implements OnModuleInit {
35
35
  * @returns The compiled template as a string.
36
36
  */
37
37
  compileTemplate(templateKey: string, params: Record<string, unknown>, format: MailTemplateFormat): string;
38
+ private escapeHtmlParams;
39
+ private escapeHtml;
38
40
  }
@@ -5,8 +5,8 @@ exports.MailTemplateEngine = void 0;
5
5
  const tslib_1 = require("tslib");
6
6
  const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
7
7
  const common_1 = require("@nestjs/common");
8
- const TemplateFetchStrategyBase_1 = require("./strategies/abstracts/TemplateFetchStrategyBase");
9
8
  const MailTokens_1 = require("../MailTokens");
9
+ const TemplateFetchStrategyBase_1 = require("./strategies/abstracts/TemplateFetchStrategyBase");
10
10
  // #endregion
11
11
  /**
12
12
  * The `MailTemplateEngine` class.
@@ -48,12 +48,17 @@ let MailTemplateEngine = class MailTemplateEngine {
48
48
  * @returns The compiled template as a string.
49
49
  */
50
50
  compileTemplate(templateKey, params, format) {
51
- const template = this._templates[templateKey][format];
51
+ const templateVariants = this._templates[templateKey];
52
+ if (!templateVariants) {
53
+ throw new Error(`Template with key '${templateKey}' not found.`);
54
+ }
55
+ const template = templateVariants[format];
52
56
  if (!template) {
53
57
  throw new Error(`Template with key '${templateKey}' and format '${format}' not found.`);
54
58
  }
55
59
  try {
56
- const interpolatedTemplate = this._contentTemplateEngine.compileTemplate(template, params);
60
+ const safeParams = format === 'html' ? this.escapeHtmlParams(params) : params;
61
+ const interpolatedTemplate = this._contentTemplateEngine.compileTemplate(template, safeParams);
57
62
  return interpolatedTemplate;
58
63
  }
59
64
  catch (error) {
@@ -61,6 +66,21 @@ let MailTemplateEngine = class MailTemplateEngine {
61
66
  throw error;
62
67
  }
63
68
  }
69
+ escapeHtmlParams(params) {
70
+ const escaped = {};
71
+ for (const [key, value] of Object.entries(params)) {
72
+ escaped[key] = typeof value === 'string' ? this.escapeHtml(value) : value;
73
+ }
74
+ return escaped;
75
+ }
76
+ escapeHtml(text) {
77
+ return text
78
+ .replace(/&/g, '&amp;')
79
+ .replace(/</g, '&lt;')
80
+ .replace(/>/g, '&gt;')
81
+ .replace(/"/g, '&quot;')
82
+ .replace(/'/g, '&#39;');
83
+ }
64
84
  };
65
85
  exports.MailTemplateEngine = MailTemplateEngine;
66
86
  exports.MailTemplateEngine = MailTemplateEngine = tslib_1.__decorate([
@@ -1,6 +1,6 @@
1
1
  import { BlobService } from '@breadstone/archipel-platform-blob-storage';
2
- import { TemplateFetchStrategyBase } from './abstracts/TemplateFetchStrategyBase';
3
2
  import { IMailTemplateVariants } from '../../MailTokens';
3
+ import { TemplateFetchStrategyBase } from './abstracts/TemplateFetchStrategyBase';
4
4
  /**
5
5
  * The `BlobTemplateFetchStrategy` class.
6
6
  *
@@ -8,6 +8,7 @@ import { IMailTemplateVariants } from '../../MailTokens';
8
8
  */
9
9
  export declare class BlobTemplateFetchStrategy extends TemplateFetchStrategyBase {
10
10
  private readonly _blobService;
11
+ private readonly _logger;
11
12
  /**
12
13
  * Constructs a new instance of the `BlobTemplateFetchStrategy` class.
13
14
  *
@@ -22,6 +22,7 @@ class BlobTemplateFetchStrategy extends TemplateFetchStrategyBase_1.TemplateFetc
22
22
  */
23
23
  constructor(blobService) {
24
24
  super();
25
+ this._logger = new common_1.Logger(BlobTemplateFetchStrategy.name);
25
26
  this._blobService = blobService;
26
27
  }
27
28
  // #endregion
@@ -44,10 +45,10 @@ class BlobTemplateFetchStrategy extends TemplateFetchStrategyBase_1.TemplateFetc
44
45
  templates[templateName].txt = templateContent;
45
46
  }
46
47
  else {
47
- common_1.Logger.warn(`Unknown template extension '${extension}' for '${file}'`, 'Mail');
48
+ this._logger.warn(`Unknown template extension '${extension}' for '${file}'`);
48
49
  }
49
50
  }
50
- common_1.Logger.log('Templates loaded from blob storage', 'Mail');
51
+ this._logger.log('Templates loaded from blob storage');
51
52
  return templates;
52
53
  }
53
54
  }
@@ -1,4 +1,5 @@
1
- import { ConfigService, IHealthIndicator } from '@breadstone/archipel-platform-core';
1
+ import { ConfigService } from '@breadstone/archipel-platform-configuration';
2
+ import { IHealthIndicator } from '@breadstone/archipel-platform-core';
2
3
  import { HealthIndicatorResult } from '@nestjs/terminus';
3
4
  /**
4
5
  * Health indicator for mail service.
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.MailHealthIndicator = void 0;
5
5
  const tslib_1 = require("tslib");
6
- const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
6
+ const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
7
7
  const common_1 = require("@nestjs/common");
8
8
  const env_1 = require("../env");
9
9
  // #endregion
@@ -50,6 +50,6 @@ let MailHealthIndicator = class MailHealthIndicator {
50
50
  exports.MailHealthIndicator = MailHealthIndicator;
51
51
  exports.MailHealthIndicator = MailHealthIndicator = tslib_1.__decorate([
52
52
  (0, common_1.Injectable)(),
53
- tslib_1.__metadata("design:paramtypes", [archipel_platform_core_1.ConfigService])
53
+ tslib_1.__metadata("design:paramtypes", [archipel_platform_configuration_1.ConfigService])
54
54
  ], MailHealthIndicator);
55
55
  //# sourceMappingURL=MailHealthIndicator.js.map