@zintrust/core 1.8.4 → 1.8.6

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 (43) hide show
  1. package/package.json +5 -5
  2. package/src/cli/CLI.d.ts.map +1 -1
  3. package/src/cli/CLI.js +2 -0
  4. package/src/cli/cloudflare/CloudflareSecretSync.d.ts +9 -4
  5. package/src/cli/cloudflare/CloudflareSecretSync.d.ts.map +1 -1
  6. package/src/cli/cloudflare/CloudflareSecretSync.js +121 -16
  7. package/src/cli/commands/EmailProxyCommand.d.ts +6 -0
  8. package/src/cli/commands/EmailProxyCommand.d.ts.map +1 -0
  9. package/src/cli/commands/EmailProxyCommand.js +108 -0
  10. package/src/cli/commands/ProxyCommand.d.ts +1 -0
  11. package/src/cli/commands/ProxyCommand.d.ts.map +1 -1
  12. package/src/cli/commands/ProxyCommand.js +6 -0
  13. package/src/cli/commands/PutCommand.d.ts.map +1 -1
  14. package/src/cli/commands/PutCommand.js +25 -1
  15. package/src/cli/commands/WranglerProxyCommandUtils.d.ts +1 -0
  16. package/src/cli/commands/WranglerProxyCommandUtils.d.ts.map +1 -1
  17. package/src/cli/commands/WranglerProxyCommandUtils.js +23 -2
  18. package/src/cli/scaffolding/env.d.ts.map +1 -1
  19. package/src/cli/scaffolding/env.js +4 -0
  20. package/src/config/env.d.ts +18 -0
  21. package/src/config/env.d.ts.map +1 -1
  22. package/src/config/env.js +19 -0
  23. package/src/index.js +3 -3
  24. package/src/proxy/email/ZintrustEmailProxy.d.ts +20 -0
  25. package/src/proxy/email/ZintrustEmailProxy.d.ts.map +1 -0
  26. package/src/proxy/email/ZintrustEmailProxy.js +129 -0
  27. package/src/proxy/email/register.d.ts +2 -0
  28. package/src/proxy/email/register.d.ts.map +1 -0
  29. package/src/proxy/email/register.js +5 -0
  30. package/src/proxy/smtp/SmtpProxyServer.d.ts.map +1 -1
  31. package/src/proxy/smtp/SmtpProxyServer.js +1 -1
  32. package/src/proxy.d.ts +1 -0
  33. package/src/proxy.d.ts.map +1 -1
  34. package/src/proxy.js +1 -0
  35. package/src/tools/mail/MailMessage.d.ts +18 -0
  36. package/src/tools/mail/MailMessage.d.ts.map +1 -0
  37. package/src/tools/mail/MailMessage.js +75 -0
  38. package/src/tools/mail/drivers/Cloudflare.d.ts +1 -1
  39. package/src/tools/mail/drivers/Cloudflare.d.ts.map +1 -1
  40. package/src/tools/mail/drivers/Cloudflare.js +61 -1
  41. package/src/tools/mail/drivers/Smtp.d.ts +2 -18
  42. package/src/tools/mail/drivers/Smtp.d.ts.map +1 -1
  43. package/src/tools/mail/drivers/Smtp.js +1 -65
@@ -116,6 +116,24 @@ export declare const Env: Readonly<{
116
116
  SMTP_PROXY_REQUIRE_SIGNING: boolean;
117
117
  SMTP_PROXY_SIGNING_WINDOW_MS: number;
118
118
  USE_SMTP_PROXY: boolean;
119
+ MAIL_DRIVER: string;
120
+ MAIL_CONNECTION: string;
121
+ MAIL_FROM_ADDRESS: string;
122
+ MAIL_FROM_NAME: string;
123
+ MAIL_CLOUDFLARE_BINDING: string;
124
+ MAIL_CLOUDFLARE_PROXY_URL: string;
125
+ MAIL_CLOUDFLARE_PROXY_KEY_ID: string;
126
+ MAIL_CLOUDFLARE_PROXY_SECRET: string;
127
+ MAIL_CLOUDFLARE_PROXY_TIMEOUT_MS: number;
128
+ MAIL_HOST: string;
129
+ MAIL_PORT: number;
130
+ MAIL_USERNAME: string;
131
+ MAIL_PASSWORD: string;
132
+ MAIL_SECURE: string;
133
+ SENDGRID_API_KEY: string;
134
+ MAILGUN_API_KEY: string;
135
+ MAILGUN_DOMAIN: string;
136
+ MAILGUN_BASE_URL: string;
119
137
  MONGODB_PROXY_URL: string;
120
138
  MONGODB_PROXY_HOST: string;
121
139
  MONGODB_PROXY_PORT: number;
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"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"}
package/src/config/env.js CHANGED
@@ -356,6 +356,25 @@ export const Env = Object.freeze({
356
356
  SMTP_PROXY_REQUIRE_SIGNING: getBool('SMTP_PROXY_REQUIRE_SIGNING', true),
357
357
  SMTP_PROXY_SIGNING_WINDOW_MS: getInt('SMTP_PROXY_SIGNING_WINDOW_MS', getInt('ZT_PROXY_SIGNING_WINDOW_MS', 60000)),
358
358
  USE_SMTP_PROXY: getBool('USE_SMTP_PROXY', false),
359
+ // Mail
360
+ MAIL_DRIVER: get('MAIL_DRIVER', 'disabled'),
361
+ MAIL_CONNECTION: get('MAIL_CONNECTION', ''),
362
+ MAIL_FROM_ADDRESS: get('MAIL_FROM_ADDRESS', ''),
363
+ MAIL_FROM_NAME: get('MAIL_FROM_NAME', ''),
364
+ MAIL_CLOUDFLARE_BINDING: get('MAIL_CLOUDFLARE_BINDING', get('MAIL_CL_BINDING', 'SEND_EMAIL')),
365
+ MAIL_CLOUDFLARE_PROXY_URL: get('MAIL_CLOUDFLARE_PROXY_URL', ''),
366
+ MAIL_CLOUDFLARE_PROXY_KEY_ID: get('MAIL_CLOUDFLARE_PROXY_KEY_ID', PROXY_KEY_ID_FALLBACK),
367
+ MAIL_CLOUDFLARE_PROXY_SECRET: get('MAIL_CLOUDFLARE_PROXY_SECRET', PROXY_SECRET_FALLBACK),
368
+ MAIL_CLOUDFLARE_PROXY_TIMEOUT_MS: getInt('MAIL_CLOUDFLARE_PROXY_TIMEOUT_MS', ZT_PROXY_TIMEOUT_MS),
369
+ MAIL_HOST: get('MAIL_HOST', ''),
370
+ MAIL_PORT: getInt('MAIL_PORT', 587),
371
+ MAIL_USERNAME: get('MAIL_USERNAME', ''),
372
+ MAIL_PASSWORD: get('MAIL_PASSWORD', ''),
373
+ MAIL_SECURE: get('MAIL_SECURE', ''),
374
+ SENDGRID_API_KEY: get('SENDGRID_API_KEY', ''),
375
+ MAILGUN_API_KEY: get('MAILGUN_API_KEY', ''),
376
+ MAILGUN_DOMAIN: get('MAILGUN_DOMAIN', ''),
377
+ MAILGUN_BASE_URL: get('MAILGUN_BASE_URL', 'https://api.mailgun.net'),
359
378
  MONGODB_PROXY_URL: get('MONGODB_PROXY_URL', ''),
360
379
  MONGODB_PROXY_HOST: get('MONGODB_PROXY_HOST', '127.0.0.1'),
361
380
  MONGODB_PROXY_PORT: getInt('MONGODB_PROXY_PORT', 8792),
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v1.8.4
2
+ * @zintrust/core v1.8.6
3
3
  *
4
4
  * ZinTrust Framework - Production-Grade TypeScript Backend
5
5
  * Built for performance, type safety, and exceptional developer experience
6
6
  *
7
7
  * Build Information:
8
- * Built: 2026-05-15T14:53:27.229Z
8
+ * Built: 2026-05-19T17:44:54.279Z
9
9
  * Node: >=20.0.0
10
10
  * License: MIT
11
11
  *
@@ -21,7 +21,7 @@
21
21
  * Available at runtime for debugging and health checks
22
22
  */
23
23
  export const ZINTRUST_VERSION = '0.1.41';
24
- export const ZINTRUST_BUILD_DATE = '2026-05-15T14:53:27.194Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-05-19T17:44:54.246Z'; // Replaced during build
25
25
  export { Application } from './boot/Application.js';
26
26
  export { AwsSigV4 } from './common/index.js';
27
27
  export { SignedRequest } from './security/SignedRequest.js';
@@ -0,0 +1,20 @@
1
+ type SendEmailBinding = {
2
+ send: (message: unknown) => Promise<unknown>;
3
+ };
4
+ type EmailProxyEnv = {
5
+ APP_KEY?: string;
6
+ MAIL_CLOUDFLARE_PROXY_SECRET?: string;
7
+ MAIL_CLOUDFLARE_BINDING?: string;
8
+ SEND_EMAIL?: SendEmailBinding;
9
+ SEB?: SendEmailBinding;
10
+ ZT_NONCES?: unknown;
11
+ ZT_PROXY_SIGNING_WINDOW_MS?: string;
12
+ ZT_MAX_BODY_BYTES?: string;
13
+ } & Record<string, unknown>;
14
+ export declare const ZintrustEmailProxy: Readonly<{
15
+ _ZINTRUST_CLOUDFLARE_EMAIL_PROXY_VERSION: "0.1.15";
16
+ _ZINTRUST_CLOUDFLARE_EMAIL_PROXY_BUILD_DATE: "__BUILD_DATE__";
17
+ fetch(request: Request, env: EmailProxyEnv): Promise<Response>;
18
+ }>;
19
+ export default ZintrustEmailProxy;
20
+ //# sourceMappingURL=ZintrustEmailProxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZintrustEmailProxy.d.ts","sourceRoot":"","sources":["../../../../src/proxy/email/ZintrustEmailProxy.ts"],"names":[],"mappings":"AAUA,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9C,CAAC;AAMF,KAAK,aAAa,GAAG;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACtC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,GAAG,CAAC,EAAE,gBAAgB,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA8I5B,eAAO,MAAM,kBAAkB;;;mBAGR,OAAO,OAAO,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;EAcpE,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,129 @@
1
+ import { ErrorFactory } from '../../exceptions/ZintrustError.js';
2
+ import { isArray, isNonEmptyString, isObject } from '../../helper/index.js';
3
+ import { readAndVerifyJson, toErrorResponse } from '../CloudflareProxyShared.js';
4
+ import { RequestValidator } from '../RequestValidator.js';
5
+ import { buildRfc2822Message, } from '../../tools/mail/MailMessage.js';
6
+ const DEFAULT_SIGNING_WINDOW_MS = 60_000;
7
+ const DEFAULT_MAX_BODY_BYTES = 128 * 1024;
8
+ const normalizeRecipients = (to) => (Array.isArray(to) ? to : [to]);
9
+ const importCloudflareEmail = async () => {
10
+ try {
11
+ return (await import('cloudflare:email'));
12
+ }
13
+ catch {
14
+ throw ErrorFactory.createConfigError('Cloudflare email runtime API is unavailable');
15
+ }
16
+ };
17
+ const resolveBinding = (env, requestedBinding) => {
18
+ const candidateNames = [
19
+ requestedBinding,
20
+ env.MAIL_CLOUDFLARE_BINDING,
21
+ 'SEND_EMAIL',
22
+ 'SEB',
23
+ ].filter((value, index, values) => typeof value === 'string' && value.trim() !== '' && values.indexOf(value) === index);
24
+ for (const name of candidateNames) {
25
+ const binding = env[name];
26
+ if (binding !== undefined && typeof binding.send === 'function')
27
+ return binding;
28
+ }
29
+ throw ErrorFactory.createConfigError(`Missing Cloudflare send_email binding (tried: ${candidateNames.length > 0 ? candidateNames.join(', ') : 'none'})`);
30
+ };
31
+ const decodeAttachment = (payload) => ({
32
+ filename: payload.filename,
33
+ content: Buffer.from(payload.contentBase64, 'base64'),
34
+ });
35
+ const parseAttachments = (value) => {
36
+ if (value === undefined)
37
+ return undefined;
38
+ if (!isArray(value))
39
+ throw ErrorFactory.createValidationError('attachments must be an array');
40
+ return value.map((entry) => {
41
+ if (!isObject(entry)) {
42
+ throw ErrorFactory.createValidationError('attachments entries must be objects');
43
+ }
44
+ const filename = entry['filename'];
45
+ const contentBase64 = entry['contentBase64'];
46
+ if (!isNonEmptyString(filename)) {
47
+ throw ErrorFactory.createValidationError('attachment filename is required');
48
+ }
49
+ if (!isNonEmptyString(contentBase64)) {
50
+ throw ErrorFactory.createValidationError('attachment contentBase64 is required');
51
+ }
52
+ return decodeAttachment({ filename, contentBase64 });
53
+ });
54
+ };
55
+ const parseMessage = (payload) => {
56
+ if (!isObject(payload))
57
+ throw ErrorFactory.createValidationError('message is required');
58
+ const fromRaw = payload['from'];
59
+ if (!isObject(fromRaw) || !isNonEmptyString(fromRaw['email'])) {
60
+ throw ErrorFactory.createValidationError('message.from.email is required');
61
+ }
62
+ const toRaw = payload['to'];
63
+ if (!isNonEmptyString(payload['subject']) ||
64
+ !isNonEmptyString(payload['text']) ||
65
+ !(isNonEmptyString(toRaw) || (isArray(toRaw) && toRaw.every((item) => isNonEmptyString(item))))) {
66
+ throw ErrorFactory.createValidationError('message.to, message.subject, and message.text are required');
67
+ }
68
+ return {
69
+ to: toRaw,
70
+ from: {
71
+ email: fromRaw['email'],
72
+ name: isNonEmptyString(fromRaw['name']) ? fromRaw['name'] : undefined,
73
+ },
74
+ subject: payload['subject'],
75
+ text: payload['text'],
76
+ html: isNonEmptyString(payload['html']) ? payload['html'] : undefined,
77
+ attachments: parseAttachments(payload['attachments']),
78
+ };
79
+ };
80
+ const handleSend = async (request, env) => {
81
+ const check = await readAndVerifyJson(request, env, {
82
+ secretEnvVar: 'MAIL_CLOUDFLARE_PROXY_SECRET',
83
+ missingSecretStatus: 401,
84
+ missingSecretMessage: 'Missing signing secret (MAIL_CLOUDFLARE_PROXY_SECRET or APP_KEY)',
85
+ defaultSigningWindowMs: DEFAULT_SIGNING_WINDOW_MS,
86
+ defaultMaxBodyBytes: DEFAULT_MAX_BODY_BYTES,
87
+ });
88
+ if (!check.ok)
89
+ return check.response;
90
+ try {
91
+ const payload = check.payload;
92
+ if (!isObject(payload)) {
93
+ return toErrorResponse(400, 'VALIDATION_ERROR', 'Invalid body');
94
+ }
95
+ const message = parseMessage(payload['message']);
96
+ const requestedBinding = isNonEmptyString(payload['binding']) ? payload['binding'] : undefined;
97
+ const binding = resolveBinding(env, requestedBinding);
98
+ const { EmailMessage } = await importCloudflareEmail();
99
+ const raw = buildRfc2822Message(message);
100
+ await Promise.all(normalizeRecipients(message.to).map(async (recipient) => binding.send(new EmailMessage(message.from.email, recipient, raw))));
101
+ return new Response(JSON.stringify({ ok: true }), {
102
+ status: 200,
103
+ headers: {
104
+ 'Content-Type': 'application/json; charset=utf-8',
105
+ 'Cache-Control': 'no-store',
106
+ },
107
+ });
108
+ }
109
+ catch (error) {
110
+ const message = error instanceof Error ? error.message : 'Email send failed';
111
+ return toErrorResponse(500, 'EMAIL_SEND_ERROR', message);
112
+ }
113
+ };
114
+ export const ZintrustEmailProxy = Object.freeze({
115
+ _ZINTRUST_CLOUDFLARE_EMAIL_PROXY_VERSION: '0.1.15',
116
+ _ZINTRUST_CLOUDFLARE_EMAIL_PROXY_BUILD_DATE: '__BUILD_DATE__',
117
+ async fetch(request, env) {
118
+ const url = new URL(request.url);
119
+ const methodError = RequestValidator.requirePost(request.method);
120
+ if (methodError !== null) {
121
+ return toErrorResponse(405, methodError.code, 'Method not allowed');
122
+ }
123
+ if (url.pathname === '/zin/mail/cloudflare/send') {
124
+ return handleSend(request, env);
125
+ }
126
+ return toErrorResponse(404, 'NOT_FOUND', 'Not found');
127
+ },
128
+ });
129
+ export default ZintrustEmailProxy;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../../src/proxy/email/register.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import { ProxyRegistry } from '../ProxyRegistry.js';
2
+ ProxyRegistry.register({
3
+ name: 'email',
4
+ description: 'Cloudflare email Worker proxy endpoint',
5
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"SmtpProxyServer.d.ts","sourceRoot":"","sources":["../../../../src/proxy/smtp/SmtpProxyServer.ts"],"names":[],"mappings":"AA6BA,KAAK,cAAc,GAAG,OAAO,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC,CAAC;AA6UH,eAAO,MAAM,eAAe;sBACH,cAAc,GAAQ,OAAO,CAAC,IAAI,CAAC;EA2B1D,CAAC;AAEH,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"SmtpProxyServer.d.ts","sourceRoot":"","sources":["../../../../src/proxy/smtp/SmtpProxyServer.ts"],"names":[],"mappings":"AAyBA,KAAK,cAAc,GAAG,OAAO,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC,CAAC;AA6UH,eAAO,MAAM,eAAe;sBACH,cAAc,GAAQ,OAAO,CAAC,IAAI,CAAC;EA2B1D,CAAC;AAEH,eAAe,eAAe,CAAC"}
@@ -3,7 +3,7 @@ import { Env } from '../../config/env.js';
3
3
  import { Logger } from '../../config/logger.js';
4
4
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
5
5
  import { isNonEmptyString, isObject } from '../../helper/index.js';
6
- import { SmtpDriver, } from '../../tools/mail/drivers/Smtp.js';
6
+ import { SmtpDriver } from '../../tools/mail/drivers/Smtp.js';
7
7
  import { ErrorHandler } from '../ErrorHandler.js';
8
8
  import { createProxyServer } from '../ProxyServer.js';
9
9
  import { resolveProxySigningConfig } from '../ProxySigningConfigResolver.js';
package/src/proxy.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { ZintrustD1Proxy } from './proxy/d1/ZintrustD1Proxy';
2
+ export { ZintrustEmailProxy } from './proxy/email/ZintrustEmailProxy';
2
3
  export { ErrorHandler } from './proxy/ErrorHandler';
3
4
  export { ZintrustKvProxy } from './proxy/kv/ZintrustKvProxy';
4
5
  export { RequestValidator } from './proxy/RequestValidator';
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
package/src/proxy.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { ZintrustD1Proxy } from './proxy/d1/ZintrustD1Proxy.js';
2
+ export { ZintrustEmailProxy } from './proxy/email/ZintrustEmailProxy.js';
2
3
  export { ErrorHandler } from './proxy/ErrorHandler.js';
3
4
  export { ZintrustKvProxy } from './proxy/kv/ZintrustKvProxy.js';
4
5
  export { RequestValidator } from './proxy/RequestValidator.js';
@@ -0,0 +1,18 @@
1
+ export type MailAddress = {
2
+ email: string;
3
+ name?: string;
4
+ };
5
+ export type MailAttachment = {
6
+ filename: string;
7
+ content: Buffer;
8
+ };
9
+ export type MailMessage = {
10
+ to: string | string[];
11
+ from: MailAddress;
12
+ subject: string;
13
+ text: string;
14
+ html?: string;
15
+ attachments?: MailAttachment[];
16
+ };
17
+ export declare const buildRfc2822Message: (msg: MailMessage) => string;
18
+ //# sourceMappingURL=MailMessage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MailMessage.d.ts","sourceRoot":"","sources":["../../../../src/tools/mail/MailMessage.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC,CAAC;AAgBF,eAAO,MAAM,mBAAmB,GAAI,KAAK,WAAW,KAAG,MAuFtD,CAAC"}
@@ -0,0 +1,75 @@
1
+ import { generateUuid } from '../../common/utility.js';
2
+ import { isNonEmptyString } from '../../helper/index.js';
3
+ const normalizeRecipients = (to) => (Array.isArray(to) ? to : [to]);
4
+ const resolveMessageIdDomain = (senderEmail) => {
5
+ const domainCandidate = senderEmail.split('@')[1]?.trim().toLowerCase() ?? '';
6
+ if (!isNonEmptyString(domainCandidate))
7
+ return 'localhost';
8
+ const normalizedDomain = domainCandidate.replace(/^<+|>+$/g, '').replace(/\.+$/g, '');
9
+ if (!isNonEmptyString(normalizedDomain) || !normalizedDomain.includes('.')) {
10
+ return normalizedDomain === '' ? 'localhost' : normalizedDomain;
11
+ }
12
+ return normalizedDomain;
13
+ };
14
+ export const buildRfc2822Message = (msg) => {
15
+ const toList = normalizeRecipients(msg.to);
16
+ const fromNameRaw = msg.from.name;
17
+ const fromName = typeof fromNameRaw === 'string' ? fromNameRaw.trim() : '';
18
+ const fromHeader = fromName === '' ? msg.from.email : `${fromName} <${msg.from.email}>`;
19
+ const toHeader = toList.join(', ');
20
+ const subject = msg.subject;
21
+ const messageId = `<${generateUuid().replaceAll('-', '')}@${resolveMessageIdDomain(msg.from.email)}>`;
22
+ const headers = [
23
+ `From: ${fromHeader}`,
24
+ `To: ${toHeader}`,
25
+ `Subject: ${subject}`,
26
+ `Date: ${new Date().toUTCString()}`,
27
+ `Message-ID: ${messageId}`,
28
+ 'MIME-Version: 1.0',
29
+ ];
30
+ const attachParts = (attachments, innerBody) => {
31
+ const mixedBoundary = `mixed_${generateUuid().replaceAll('-', '')}`;
32
+ const lines = [];
33
+ lines.push(`Content-Type: multipart/mixed; boundary="${mixedBoundary}"`, '', `--${mixedBoundary}`, innerBody);
34
+ for (const attachment of attachments) {
35
+ const b64 = attachment.content.toString('base64');
36
+ lines.push(`--${mixedBoundary}`, `Content-Type: application/octet-stream; name="${attachment.filename}"`, 'Content-Transfer-Encoding: base64', `Content-Disposition: attachment; filename="${attachment.filename}"`, '');
37
+ for (let index = 0; index < b64.length; index += 76) {
38
+ lines.push(b64.slice(index, index + 76));
39
+ }
40
+ }
41
+ lines.push(`--${mixedBoundary}--`, '');
42
+ return lines.join('\r\n');
43
+ };
44
+ if (typeof msg.html === 'string' && msg.html !== '') {
45
+ const boundary = `zintrust_${generateUuid().replaceAll('-', '')}`;
46
+ headers.push(`Content-Type: multipart/alternative; boundary="${boundary}"`);
47
+ const parts = [
48
+ `--${boundary}`,
49
+ 'Content-Type: text/plain; charset=utf-8',
50
+ 'Content-Transfer-Encoding: 7bit',
51
+ '',
52
+ msg.text,
53
+ `--${boundary}`,
54
+ 'Content-Type: text/html; charset=utf-8',
55
+ 'Content-Transfer-Encoding: 7bit',
56
+ '',
57
+ msg.html,
58
+ `--${boundary}--`,
59
+ '',
60
+ ];
61
+ const inner = `${parts.join('\r\n')}`;
62
+ if (msg.attachments && msg.attachments.length > 0) {
63
+ const mixed = attachParts(msg.attachments, inner);
64
+ return `${headers.join('\r\n')}\r\n\r\n${mixed}`;
65
+ }
66
+ return `${headers.join('\r\n')}\r\n\r\n${inner}`;
67
+ }
68
+ if (msg.attachments && msg.attachments.length > 0) {
69
+ const inner = ['Content-Type: text/plain; charset=utf-8', '', msg.text, ''].join('\r\n');
70
+ const mixed = attachParts(msg.attachments, inner);
71
+ return `${headers.join('\r\n')}\r\n\r\n${mixed}`;
72
+ }
73
+ headers.push('Content-Type: text/plain; charset=utf-8');
74
+ return `${headers.join('\r\n')}\r\n\r\n${msg.text}\r\n`;
75
+ };
@@ -1,4 +1,4 @@
1
- import { type MailMessage } from '../drivers/Smtp';
1
+ import { type MailMessage } from '../../mail/MailMessage';
2
2
  export type CloudflareMailConfig = {
3
3
  driver: 'cl';
4
4
  binding: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Cloudflare.d.ts","sourceRoot":"","sources":["../../../../../src/tools/mail/drivers/Cloudflare.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE3E,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AA6CF,eAAO,MAAM,gBAAgB;iBAEjB,oBAAoB,WACnB,WAAW,GACnB,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;EA0B/C,CAAC;AAEH,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"Cloudflare.d.ts","sourceRoot":"","sources":["../../../../../src/tools/mail/drivers/Cloudflare.ts"],"names":[],"mappings":"AAKA,OAAO,EAAyC,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAElG,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAuIF,eAAO,MAAM,gBAAgB;iBAEjB,oBAAoB,WACnB,WAAW,GACnB,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;EA8B/C,CAAC;AAEH,eAAe,gBAAgB,CAAC"}
@@ -1,7 +1,64 @@
1
+ import { RemoteSignedJson } from '../../../common/RemoteSignedJson.js';
1
2
  import { Cloudflare } from '../../../config/cloudflare.js';
3
+ import { Env } from '../../../config/env.js';
2
4
  import { ErrorFactory } from '../../../exceptions/ZintrustError.js';
3
- import { buildRfc2822Message } from '../drivers/Smtp.js';
5
+ import { buildRfc2822Message } from '../../mail/MailMessage.js';
6
+ const resolveSigningPrefix = (baseUrl) => {
7
+ try {
8
+ const parsed = new URL(baseUrl);
9
+ const path = parsed.pathname.endsWith('/') ? parsed.pathname.slice(0, -1) : parsed.pathname;
10
+ if (path === '' || path === '/')
11
+ return undefined;
12
+ return path;
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ };
4
18
  const normalizeRecipients = (to) => (Array.isArray(to) ? to : [to]);
19
+ const shouldUseProxy = () => Env.get('MAIL_CLOUDFLARE_PROXY_URL', '').trim() !== '';
20
+ const createRemoteConfig = () => {
21
+ const baseUrl = Env.get('MAIL_CLOUDFLARE_PROXY_URL', '');
22
+ const timeoutMs = Env.getInt('MAIL_CLOUDFLARE_PROXY_TIMEOUT_MS', Env.getInt('ZT_PROXY_TIMEOUT_MS', Env.REQUEST_TIMEOUT));
23
+ return {
24
+ baseUrl,
25
+ keyId: Env.get('MAIL_CLOUDFLARE_PROXY_KEY_ID', ''),
26
+ secret: Env.get('MAIL_CLOUDFLARE_PROXY_SECRET', ''),
27
+ timeoutMs,
28
+ signaturePathPrefixToStrip: resolveSigningPrefix(baseUrl),
29
+ missingUrlMessage: 'Cloudflare mail proxy URL is missing (MAIL_CLOUDFLARE_PROXY_URL)',
30
+ missingCredentialsMessage: 'Cloudflare mail proxy signing credentials are missing (MAIL_CLOUDFLARE_PROXY_KEY_ID / MAIL_CLOUDFLARE_PROXY_SECRET). Fallbacks: APP_NAME and APP_KEY.',
31
+ messages: {
32
+ unauthorized: 'Cloudflare mail proxy unauthorized',
33
+ forbidden: 'Cloudflare mail proxy forbidden',
34
+ rateLimited: 'Cloudflare mail proxy rate limited',
35
+ rejected: 'Cloudflare mail proxy rejected request',
36
+ error: 'Cloudflare mail proxy error',
37
+ timedOut: 'Cloudflare mail proxy request timed out',
38
+ },
39
+ };
40
+ };
41
+ const serializeMessage = (message) => ({
42
+ to: message.to,
43
+ from: message.from,
44
+ subject: message.subject,
45
+ text: message.text,
46
+ html: message.html,
47
+ attachments: message.attachments?.map((attachment) => ({
48
+ filename: attachment.filename,
49
+ contentBase64: attachment.content.toString('base64'),
50
+ })),
51
+ });
52
+ const sendViaProxy = async (config, message) => {
53
+ const response = await RemoteSignedJson.request(createRemoteConfig(), '/zin/mail/cloudflare/send', {
54
+ binding: config.binding,
55
+ message: serializeMessage(message),
56
+ });
57
+ return {
58
+ ok: response.ok === true,
59
+ messageId: response.messageId,
60
+ };
61
+ };
5
62
  const resolveBinding = (config) => {
6
63
  const env = Cloudflare.getWorkersEnv();
7
64
  if (env === null) {
@@ -35,6 +92,9 @@ export const CloudflareDriver = Object.freeze({
35
92
  if (recipients.length === 0) {
36
93
  throw ErrorFactory.createConfigError('Cloudflare mail driver requires at least one recipient');
37
94
  }
95
+ if (shouldUseProxy()) {
96
+ return sendViaProxy(config, message);
97
+ }
38
98
  const binding = resolveBinding(config);
39
99
  const { EmailMessage } = await importCloudflareEmail();
40
100
  const raw = buildRfc2822Message(message);
@@ -1,3 +1,4 @@
1
+ import { type MailAddress, type MailAttachment, type MailMessage } from '../../mail/MailMessage';
1
2
  export type SmtpConfig = {
2
3
  host: string;
3
4
  port: number;
@@ -5,29 +6,12 @@ export type SmtpConfig = {
5
6
  password?: string;
6
7
  secure?: boolean | 'starttls';
7
8
  };
8
- export type MailAddress = {
9
- email: string;
10
- name?: string;
11
- };
12
- export type MailAttachment = {
13
- filename: string;
14
- content: Buffer;
15
- };
16
- export type MailMessage = {
17
- to: string | string[];
18
- from: MailAddress;
19
- subject: string;
20
- text: string;
21
- html?: string;
22
- attachments?: MailAttachment[];
23
- };
9
+ export type { MailAddress, MailAttachment, MailMessage };
24
10
  export type SendResult = {
25
11
  ok: boolean;
26
12
  provider: 'smtp';
27
13
  messageId?: string;
28
14
  };
29
- declare const buildRfc2822Message: (msg: MailMessage) => string;
30
- export { buildRfc2822Message };
31
15
  export declare const SmtpDriver: Readonly<{
32
16
  /**
33
17
  * NOTE: This is a minimal SMTP implementation intended for Node.js runtimes.
@@ -1 +1 @@
1
- {"version":3,"file":"Smtp.d.ts","sourceRoot":"","sources":["../../../../../src/tools/mail/drivers/Smtp.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,cAAc,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAskBF,QAAA,MAAM,mBAAmB,GAAI,KAAK,WAAW,KAAG,MAwF/C,CAAC;AAEF,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAS/B,eAAO,MAAM,UAAU;IACrB;;;;OAIG;iBACgB,UAAU,WAAW,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;EAuDzE,CAAC;AAEH,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"Smtp.d.ts","sourceRoot":"","sources":["../../../../../src/tools/mail/drivers/Smtp.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,WAAW,EACjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;CAC/B,CAAC;AAEF,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC;AAEzD,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA6kBF,eAAO,MAAM,UAAU;IACrB;;;;OAIG;iBACgB,UAAU,WAAW,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;EAuDzE,CAAC;AAEH,eAAe,UAAU,CAAC"}
@@ -1,4 +1,3 @@
1
- import { generateUuid } from '../../../common/utility.js';
2
1
  import { RemoteSignedJson } from '../../../common/RemoteSignedJson.js';
3
2
  import { Cloudflare } from '../../../config/cloudflare.js';
4
3
  import { Env } from '../../../config/env.js';
@@ -7,6 +6,7 @@ import { ErrorFactory } from '../../../exceptions/ZintrustError.js';
7
6
  import * as net from '../../../node-singletons/net.js';
8
7
  import * as tls from '../../../node-singletons/tls.js';
9
8
  import { normalizeSigningCredentials } from '../../../proxy/SigningService.js';
9
+ import { buildRfc2822Message, } from '../../mail/MailMessage.js';
10
10
  const resolveSigningPrefix = (baseUrl) => {
11
11
  try {
12
12
  const parsed = new URL(baseUrl);
@@ -434,70 +434,6 @@ const doQuit = async (socket, reader) => {
434
434
  const quit = await reader.readResponse();
435
435
  assertCode(quit, [221, 250], 'QUIT');
436
436
  };
437
- const buildRfc2822Message = (msg) => {
438
- const toList = normalizeRecipients(msg.to);
439
- const fromNameRaw = msg.from.name;
440
- const fromName = typeof fromNameRaw === 'string' ? fromNameRaw.trim() : '';
441
- const fromHeader = fromName === '' ? msg.from.email : `${fromName} <${msg.from.email}>`;
442
- const toHeader = toList.join(', ');
443
- const subject = msg.subject;
444
- const headers = [
445
- `From: ${fromHeader}`,
446
- `To: ${toHeader}`,
447
- `Subject: ${subject}`,
448
- 'MIME-Version: 1.0',
449
- ];
450
- const attachParts = (attachments, innerBody) => {
451
- const mixedBoundary = `mixed_${generateUuid().replaceAll('-', '')}`;
452
- const lines = [];
453
- lines.push(`Content-Type: multipart/mixed; boundary="${mixedBoundary}"`, '', `--${mixedBoundary}`, innerBody);
454
- // attachments
455
- for (const a of attachments) {
456
- const b64 = a.content.toString('base64');
457
- lines.push(`--${mixedBoundary}`, `Content-Type: application/octet-stream; name="${a.filename}"`, 'Content-Transfer-Encoding: base64', `Content-Disposition: attachment; filename="${a.filename}"`, '');
458
- // break base64 into 76 char lines per RFC
459
- for (let i = 0; i < b64.length; i += 76) {
460
- lines.push(b64.slice(i, i + 76));
461
- }
462
- }
463
- lines.push(`--${mixedBoundary}--`, '');
464
- return lines.join('\r\n');
465
- };
466
- if (typeof msg.html === 'string' && msg.html !== '') {
467
- const boundary = `zintrust_${generateUuid().replaceAll('-', '')}`;
468
- headers.push(`Content-Type: multipart/alternative; boundary="${boundary}"`);
469
- const parts = [
470
- `--${boundary}`,
471
- 'Content-Type: text/plain; charset=utf-8',
472
- 'Content-Transfer-Encoding: 7bit',
473
- '',
474
- msg.text,
475
- `--${boundary}`,
476
- 'Content-Type: text/html; charset=utf-8',
477
- 'Content-Transfer-Encoding: 7bit',
478
- '',
479
- msg.html,
480
- `--${boundary}--`,
481
- '',
482
- ];
483
- const inner = `${parts.join('\r\n')}`;
484
- if (msg.attachments && msg.attachments.length > 0) {
485
- // wrap in multipart/mixed
486
- const mixed = attachParts(msg.attachments, inner);
487
- return `${headers.join('\r\n')}\r\n\r\n${mixed}`;
488
- }
489
- return `${headers.join('\r\n')}\r\n\r\n${inner}`;
490
- }
491
- // plain text
492
- if (msg.attachments && msg.attachments.length > 0) {
493
- const inner = ['Content-Type: text/plain; charset=utf-8', '', msg.text, ''].join('\r\n');
494
- const mixed = attachParts(msg.attachments, inner);
495
- return `${headers.join('\r\n')}\r\n\r\n${mixed}`;
496
- }
497
- headers.push('Content-Type: text/plain; charset=utf-8');
498
- return `${headers.join('\r\n')}\r\n\r\n${msg.text}\r\n`;
499
- };
500
- export { buildRfc2822Message };
501
437
  const dotStuff = (data) => data
502
438
  .replaceAll(/\r?\n/g, '\r\n')
503
439
  .split('\r\n')