@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.
- package/package.json +5 -5
- package/src/cli/CLI.d.ts.map +1 -1
- package/src/cli/CLI.js +2 -0
- package/src/cli/cloudflare/CloudflareSecretSync.d.ts +9 -4
- package/src/cli/cloudflare/CloudflareSecretSync.d.ts.map +1 -1
- package/src/cli/cloudflare/CloudflareSecretSync.js +121 -16
- package/src/cli/commands/EmailProxyCommand.d.ts +6 -0
- package/src/cli/commands/EmailProxyCommand.d.ts.map +1 -0
- package/src/cli/commands/EmailProxyCommand.js +108 -0
- package/src/cli/commands/ProxyCommand.d.ts +1 -0
- package/src/cli/commands/ProxyCommand.d.ts.map +1 -1
- package/src/cli/commands/ProxyCommand.js +6 -0
- package/src/cli/commands/PutCommand.d.ts.map +1 -1
- package/src/cli/commands/PutCommand.js +25 -1
- package/src/cli/commands/WranglerProxyCommandUtils.d.ts +1 -0
- package/src/cli/commands/WranglerProxyCommandUtils.d.ts.map +1 -1
- package/src/cli/commands/WranglerProxyCommandUtils.js +23 -2
- package/src/cli/scaffolding/env.d.ts.map +1 -1
- package/src/cli/scaffolding/env.js +4 -0
- package/src/config/env.d.ts +18 -0
- package/src/config/env.d.ts.map +1 -1
- package/src/config/env.js +19 -0
- package/src/index.js +3 -3
- package/src/proxy/email/ZintrustEmailProxy.d.ts +20 -0
- package/src/proxy/email/ZintrustEmailProxy.d.ts.map +1 -0
- package/src/proxy/email/ZintrustEmailProxy.js +129 -0
- package/src/proxy/email/register.d.ts +2 -0
- package/src/proxy/email/register.d.ts.map +1 -0
- package/src/proxy/email/register.js +5 -0
- package/src/proxy/smtp/SmtpProxyServer.d.ts.map +1 -1
- package/src/proxy/smtp/SmtpProxyServer.js +1 -1
- package/src/proxy.d.ts +1 -0
- package/src/proxy.d.ts.map +1 -1
- package/src/proxy.js +1 -0
- package/src/tools/mail/MailMessage.d.ts +18 -0
- package/src/tools/mail/MailMessage.d.ts.map +1 -0
- package/src/tools/mail/MailMessage.js +75 -0
- package/src/tools/mail/drivers/Cloudflare.d.ts +1 -1
- package/src/tools/mail/drivers/Cloudflare.d.ts.map +1 -1
- package/src/tools/mail/drivers/Cloudflare.js +61 -1
- package/src/tools/mail/drivers/Smtp.d.ts +2 -18
- package/src/tools/mail/drivers/Smtp.d.ts.map +1 -1
- package/src/tools/mail/drivers/Smtp.js +1 -65
package/src/config/env.d.ts
CHANGED
|
@@ -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;
|
package/src/config/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAClF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAsNF,eAAO,MAAM,cAAc,QAAO,WAAW,GAAG,SAAwB,CAAC;AAEzE,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,MAKzE,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAKlD,CAAC;AAEF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,KAAG,OAGjC,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAKlD,CAAC;AAEF,eAAO,MAAM,eAAe,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAEvD,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAO,gBAAyC,CAAC;AAG9E,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAIxD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,MAMrC,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,EAAE,cAAc,MAAM,KAAG,MAI1D,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAI7D,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,EAAE,eAAe,OAAO,KAAG,OAI7D,CAAC;AAEF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,IAGhD,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,IAInC,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,QAAQ,SAAS,GAAG,IAAI,KAAG,IAEpD,CAAC;AAEF,eAAO,MAAM,QAAQ,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAOhD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAKjE,CAAC;AACF,eAAO,MAAM,mBAAmB,QAAuC,CAAC;AAKxE,eAAO,MAAM,GAAG;eAnES,MAAM,iBAAiB,MAAM,KAAG,MAAM;uBA1B9B,MAAM,KAAG,MAAM,GAAG,SAAS;kBAwChC,MAAM,gBAAgB,MAAM,KAAG,MAAM;mBAYpC,MAAM,iBAAiB,OAAO,KAAG,OAAO;oBANvC,MAAM,iBAAiB,MAAM,KAAG,MAAM;eAvC3C,MAAM,KAAG,OAAO;eAmDhB,MAAM,SAAS,MAAM,KAAG,IAAI;iBAK1B,MAAM,KAAG,IAAI;wBAMN,SAAS,GAAG,IAAI,KAAG,IAAI;oBAI7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;uBA7DjB,MAAM,KAAG,MAAM,GAAG,SAAS;2BAOzB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;4BAIrB,gBAAgB;cAuFN,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC
|
|
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.
|
|
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-
|
|
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-
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../../src/proxy/email/register.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmtpProxyServer.d.ts","sourceRoot":"","sources":["../../../../src/proxy/smtp/SmtpProxyServer.ts"],"names":[],"mappings":"
|
|
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
|
|
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';
|
package/src/proxy.d.ts.map
CHANGED
|
@@ -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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Cloudflare.d.ts","sourceRoot":"","sources":["../../../../../src/tools/mail/drivers/Cloudflare.ts"],"names":[],"mappings":"
|
|
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 '
|
|
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":"
|
|
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')
|