@serve.zone/platformclient 1.1.5 → 1.2.0
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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.platformclient.d.ts +1 -1
- package/dist_ts/classes.platformclient.js +1 -1
- package/dist_ts/email/classes.emailconnector.d.ts +84 -0
- package/dist_ts/email/classes.emailconnector.js +227 -1
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/dist_ts/plugins.d.ts +1 -0
- package/dist_ts/plugins.js +2 -1
- package/package.json +4 -3
- package/readme.md +39 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.platformclient.ts +1 -1
- package/ts/email/classes.emailconnector.ts +322 -0
- package/ts/index.ts +1 -0
- package/ts/plugins.ts +2 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/platformclient',
|
|
6
|
-
version: '1.
|
|
6
|
+
version: '1.2.0',
|
|
7
7
|
description: 'a module that makes it really easy to use the serve.zone platform inside your app'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSw0QkFBNEI7SUFDbEMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLG1GQUFtRjtDQUNqRyxDQUFBIn0=
|
|
@@ -38,6 +38,6 @@ export declare class SzPlatformClient {
|
|
|
38
38
|
private discoverPlatformBindings;
|
|
39
39
|
private parsePlatformBindingsEnv;
|
|
40
40
|
private mergePlatformBindings;
|
|
41
|
-
|
|
41
|
+
getConfiguredEnvVar(envNameArg: string): Promise<string | undefined>;
|
|
42
42
|
private getProcessEnvVar;
|
|
43
43
|
}
|
|
@@ -175,4 +175,4 @@ export class SzPlatformClient {
|
|
|
175
175
|
return processEnv?.[envNameArg];
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
178
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wbGF0Zm9ybWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMucGxhdGZvcm1jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDckUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLDhDQUE4QyxDQUFDO0FBQzNGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ3ZFLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBWXhDLE1BQU0sT0FBTyxnQkFBZ0I7SUFjM0IsWUFBWSxzQkFBMEQ7UUFiL0QsY0FBUyxHQUFHLEtBQUssQ0FBQztRQUdsQixxQkFBZ0IsR0FBNEQsRUFBRSxDQUFDO1FBQy9FLGdCQUFXLEdBQXFDLElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUV0RixpQkFBWSxHQUFHLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUV4QyxtQkFBYyxHQUFHLElBQUksZ0JBQWdCLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDNUMsaUJBQVksR0FBRyxJQUFJLGNBQWMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN4Qyw4QkFBeUIsR0FBRyxJQUFJLDJCQUEyQixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2xFLG9CQUFlLEdBQUcsSUFBSSxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUduRCxJQUFJLENBQUMsWUFBWSxDQUFDLHNCQUFzQixDQUFDLENBQUM7SUFDNUMsQ0FBQztJQUVNLEtBQUssQ0FBQyxJQUFJLENBQUMsc0JBQTBEO1FBQzFFLElBQUksQ0FBQyxZQUFZLENBQUMsc0JBQXNCLENBQUMsQ0FBQztRQUMxQyxJQUFJLENBQUMsSUFB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
|
|
@@ -1,9 +1,82 @@
|
|
|
1
1
|
import type { SzPlatformClient } from '../classes.platformclient.js';
|
|
2
2
|
import * as plugins from '../plugins.js';
|
|
3
|
+
export type TServiceMailTlsMode = 'plain' | 'starttls' | 'implicitTls';
|
|
4
|
+
export interface IServiceMailSmtpCredentials {
|
|
5
|
+
from: string;
|
|
6
|
+
host: string;
|
|
7
|
+
port: number;
|
|
8
|
+
tlsMode: TServiceMailTlsMode;
|
|
9
|
+
username: string;
|
|
10
|
+
password: string;
|
|
11
|
+
addressToken?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface IServiceMailStatus {
|
|
14
|
+
ready: boolean;
|
|
15
|
+
from?: string;
|
|
16
|
+
host?: string;
|
|
17
|
+
port?: number;
|
|
18
|
+
tlsMode?: TServiceMailTlsMode;
|
|
19
|
+
usernameConfigured: boolean;
|
|
20
|
+
passwordConfigured: boolean;
|
|
21
|
+
missing: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface IServiceMailAttachment {
|
|
24
|
+
filename: string;
|
|
25
|
+
contentType?: string;
|
|
26
|
+
content: string | Uint8Array;
|
|
27
|
+
}
|
|
28
|
+
export interface IServiceMailSendOptions {
|
|
29
|
+
from?: string;
|
|
30
|
+
to: string | string[];
|
|
31
|
+
cc?: string | string[];
|
|
32
|
+
bcc?: string | string[];
|
|
33
|
+
subject: string;
|
|
34
|
+
text?: string;
|
|
35
|
+
html?: string;
|
|
36
|
+
headers?: Record<string, string>;
|
|
37
|
+
attachments?: IServiceMailAttachment[];
|
|
38
|
+
}
|
|
39
|
+
export interface IServiceMailSendResult {
|
|
40
|
+
messageId?: string;
|
|
41
|
+
accepted?: string[];
|
|
42
|
+
rejected?: string[];
|
|
43
|
+
response?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface IServiceMailInboundMessage extends plugins.servezoneInterfaces.data.IMailInboundMessagePayload {
|
|
46
|
+
from: string;
|
|
47
|
+
to: string[];
|
|
48
|
+
receivedAtIso: string;
|
|
49
|
+
}
|
|
50
|
+
export interface IServiceMailInboundHandlerResult {
|
|
51
|
+
accepted?: boolean;
|
|
52
|
+
workAppMessageId?: string;
|
|
53
|
+
message?: string;
|
|
54
|
+
}
|
|
55
|
+
export type IServiceMailInboundHandleResult = plugins.servezoneInterfaces.requests.mail.IReq_DeliverInboundMail['response'];
|
|
56
|
+
export type TServiceMailInboundHandler = (messageArg: IServiceMailInboundMessage) => Promise<IServiceMailInboundHandlerResult | void> | IServiceMailInboundHandlerResult | void;
|
|
3
57
|
export declare class SzEmailConnector {
|
|
4
58
|
platformClientRef: SzPlatformClient;
|
|
5
59
|
constructor(platformClientRefArg: SzPlatformClient);
|
|
60
|
+
/**
|
|
61
|
+
* Legacy platform-service email path. New service-mail code should use sendMail().
|
|
62
|
+
*/
|
|
6
63
|
sendEmail(optionsArg: plugins.servezoneInterfaces.platform.email.IReq_SendEmail['request']): Promise<string | undefined>;
|
|
64
|
+
getServiceMailStatus(addressArg?: string): Promise<IServiceMailStatus>;
|
|
65
|
+
getServiceMailCredentials(addressArg?: string): Promise<IServiceMailSmtpCredentials>;
|
|
66
|
+
sendText(optionsArg: Omit<IServiceMailSendOptions, 'html'> & {
|
|
67
|
+
text: string;
|
|
68
|
+
}): Promise<IServiceMailSendResult>;
|
|
69
|
+
sendHtml(optionsArg: Omit<IServiceMailSendOptions, 'text'> & {
|
|
70
|
+
html: string;
|
|
71
|
+
}): Promise<IServiceMailSendResult>;
|
|
72
|
+
sendMail(optionsArg: IServiceMailSendOptions): Promise<IServiceMailSendResult>;
|
|
73
|
+
normalizeInboundMessage(payloadArg: unknown): IServiceMailInboundMessage;
|
|
74
|
+
handleInboundPayload(payloadArg: unknown, handlerArg: TServiceMailInboundHandler): Promise<IServiceMailInboundHandleResult>;
|
|
75
|
+
createInboundHandler(handlerArg: TServiceMailInboundHandler): (payloadArg: unknown) => Promise<{
|
|
76
|
+
accepted: boolean;
|
|
77
|
+
workAppMessageId?: string;
|
|
78
|
+
message?: string;
|
|
79
|
+
}>;
|
|
7
80
|
sendNotification(optionsArg: {
|
|
8
81
|
toArg: string;
|
|
9
82
|
subject: string;
|
|
@@ -14,4 +87,15 @@ export declare class SzEmailConnector {
|
|
|
14
87
|
subject: string;
|
|
15
88
|
text: string;
|
|
16
89
|
}): Promise<void>;
|
|
90
|
+
private readMailEnv;
|
|
91
|
+
private readMailEnvVar;
|
|
92
|
+
private getCredentialEnvVarFromBindings;
|
|
93
|
+
private hasAnyMailEnvValue;
|
|
94
|
+
private normalizeTlsMode;
|
|
95
|
+
private getMailEnvToken;
|
|
96
|
+
private listToArray;
|
|
97
|
+
private normalizeAttachmentContent;
|
|
98
|
+
private asRecord;
|
|
99
|
+
private normalizeHeaders;
|
|
100
|
+
private extractHeader;
|
|
17
101
|
}
|
|
@@ -4,6 +4,9 @@ export class SzEmailConnector {
|
|
|
4
4
|
constructor(platformClientRefArg) {
|
|
5
5
|
this.platformClientRef = platformClientRefArg;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Legacy platform-service email path. New service-mail code should use sendMail().
|
|
9
|
+
*/
|
|
7
10
|
async sendEmail(optionsArg) {
|
|
8
11
|
if (this.platformClientRef.debugMode) {
|
|
9
12
|
logger.log('info', `sent email with subject ${optionsArg.title} to ${optionsArg.to}
|
|
@@ -19,7 +22,230 @@ export class SzEmailConnector {
|
|
|
19
22
|
const response = await typedRequest.fire(optionsArg);
|
|
20
23
|
return response.responseId;
|
|
21
24
|
}
|
|
25
|
+
async getServiceMailStatus(addressArg) {
|
|
26
|
+
const requestedAddress = addressArg?.trim().toLowerCase();
|
|
27
|
+
const token = requestedAddress ? this.getMailEnvToken(requestedAddress) : undefined;
|
|
28
|
+
const scopedEnv = token ? await this.readMailEnv(token) : undefined;
|
|
29
|
+
const defaultEnv = token ? await this.readMailEnv() : undefined;
|
|
30
|
+
const useDefaultEnv = requestedAddress
|
|
31
|
+
&& !this.hasAnyMailEnvValue(scopedEnv)
|
|
32
|
+
&& defaultEnv?.from?.trim().toLowerCase() === requestedAddress;
|
|
33
|
+
const env = useDefaultEnv ? defaultEnv : scopedEnv || await this.readMailEnv();
|
|
34
|
+
const missingPrefix = useDefaultEnv || !token ? undefined : token;
|
|
35
|
+
const port = env.smtpPort ? Number(env.smtpPort) : undefined;
|
|
36
|
+
const tlsMode = this.normalizeTlsMode(env.smtpTlsMode || 'starttls');
|
|
37
|
+
const missing = [];
|
|
38
|
+
if (!env.from)
|
|
39
|
+
missing.push(missingPrefix ? `MAIL_${missingPrefix}_FROM` : 'MAIL_FROM');
|
|
40
|
+
if (!env.smtpHost)
|
|
41
|
+
missing.push(missingPrefix ? `MAIL_${missingPrefix}_SMTP_HOST` : 'SMTP_HOST');
|
|
42
|
+
if (!env.smtpPort || !Number.isInteger(port) || port < 1 || port > 65535) {
|
|
43
|
+
missing.push(missingPrefix ? `MAIL_${missingPrefix}_SMTP_PORT` : 'SMTP_PORT');
|
|
44
|
+
}
|
|
45
|
+
if (!env.smtpUsername)
|
|
46
|
+
missing.push(missingPrefix ? `MAIL_${missingPrefix}_SMTP_USERNAME` : 'SMTP_USERNAME');
|
|
47
|
+
if (!env.smtpPassword)
|
|
48
|
+
missing.push(missingPrefix ? `MAIL_${missingPrefix}_SMTP_PASSWORD` : 'SMTP_PASSWORD');
|
|
49
|
+
return {
|
|
50
|
+
ready: missing.length === 0,
|
|
51
|
+
from: env.from,
|
|
52
|
+
host: env.smtpHost,
|
|
53
|
+
port,
|
|
54
|
+
tlsMode,
|
|
55
|
+
usernameConfigured: Boolean(env.smtpUsername),
|
|
56
|
+
passwordConfigured: Boolean(env.smtpPassword),
|
|
57
|
+
missing,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async getServiceMailCredentials(addressArg) {
|
|
61
|
+
const requestedAddress = addressArg?.trim().toLowerCase();
|
|
62
|
+
const requestedToken = requestedAddress ? this.getMailEnvToken(requestedAddress) : undefined;
|
|
63
|
+
const scopedEnv = requestedToken ? await this.readMailEnv(requestedToken) : undefined;
|
|
64
|
+
const hasScopedConfig = scopedEnv && Object.values(scopedEnv).some(Boolean);
|
|
65
|
+
const env = hasScopedConfig ? scopedEnv : await this.readMailEnv();
|
|
66
|
+
const from = env.from?.trim().toLowerCase();
|
|
67
|
+
if (!from)
|
|
68
|
+
throw new Error('MAIL_FROM is required for service mail');
|
|
69
|
+
if (requestedAddress && from !== requestedAddress) {
|
|
70
|
+
throw new Error(`No service mail credentials are configured for ${requestedAddress}`);
|
|
71
|
+
}
|
|
72
|
+
const port = Number(env.smtpPort);
|
|
73
|
+
if (!env.smtpHost)
|
|
74
|
+
throw new Error('SMTP_HOST is required for service mail');
|
|
75
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
76
|
+
throw new Error('SMTP_PORT must be an integer between 1 and 65535');
|
|
77
|
+
}
|
|
78
|
+
if (!env.smtpUsername)
|
|
79
|
+
throw new Error('SMTP_USERNAME is required for service mail');
|
|
80
|
+
if (!env.smtpPassword)
|
|
81
|
+
throw new Error('SMTP_PASSWORD is required for service mail');
|
|
82
|
+
return {
|
|
83
|
+
from,
|
|
84
|
+
host: env.smtpHost,
|
|
85
|
+
port,
|
|
86
|
+
tlsMode: this.normalizeTlsMode(env.smtpTlsMode || 'starttls'),
|
|
87
|
+
username: env.smtpUsername,
|
|
88
|
+
password: env.smtpPassword,
|
|
89
|
+
addressToken: hasScopedConfig ? requestedToken : undefined,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async sendText(optionsArg) {
|
|
93
|
+
return await this.sendMail(optionsArg);
|
|
94
|
+
}
|
|
95
|
+
async sendHtml(optionsArg) {
|
|
96
|
+
return await this.sendMail(optionsArg);
|
|
97
|
+
}
|
|
98
|
+
async sendMail(optionsArg) {
|
|
99
|
+
if (!optionsArg.text && !optionsArg.html) {
|
|
100
|
+
throw new Error('Either text or html is required for service mail');
|
|
101
|
+
}
|
|
102
|
+
if (this.platformClientRef.debugMode) {
|
|
103
|
+
logger.log('info', `service mail debug send: ${optionsArg.subject} to ${this.listToArray(optionsArg.to).join(', ')}`);
|
|
104
|
+
return { messageId: 'debug' };
|
|
105
|
+
}
|
|
106
|
+
const credentials = await this.getServiceMailCredentials(optionsArg.from);
|
|
107
|
+
const smartsmtp = await plugins.getSmartsmtp();
|
|
108
|
+
const smtp = await smartsmtp.Smartsmtp.createSmartsmtpWithRelay({
|
|
109
|
+
smtpServer: credentials.host,
|
|
110
|
+
smtpPort: credentials.port,
|
|
111
|
+
smtpTlsMode: credentials.tlsMode,
|
|
112
|
+
smtpUser: credentials.username,
|
|
113
|
+
smtpPassword: credentials.password,
|
|
114
|
+
});
|
|
115
|
+
const result = (await smtp.nodemailerTransport.sendMail({
|
|
116
|
+
from: credentials.from,
|
|
117
|
+
to: this.listToArray(optionsArg.to),
|
|
118
|
+
cc: optionsArg.cc ? this.listToArray(optionsArg.cc) : undefined,
|
|
119
|
+
bcc: optionsArg.bcc ? this.listToArray(optionsArg.bcc) : undefined,
|
|
120
|
+
subject: optionsArg.subject,
|
|
121
|
+
text: optionsArg.text,
|
|
122
|
+
html: optionsArg.html,
|
|
123
|
+
headers: optionsArg.headers,
|
|
124
|
+
attachments: optionsArg.attachments?.map((attachmentArg) => ({
|
|
125
|
+
filename: attachmentArg.filename,
|
|
126
|
+
contentType: attachmentArg.contentType,
|
|
127
|
+
content: this.normalizeAttachmentContent(attachmentArg.content),
|
|
128
|
+
})),
|
|
129
|
+
}));
|
|
130
|
+
return {
|
|
131
|
+
messageId: result.messageId,
|
|
132
|
+
accepted: Array.isArray(result.accepted) ? result.accepted.map(String) : undefined,
|
|
133
|
+
rejected: Array.isArray(result.rejected) ? result.rejected.map(String) : undefined,
|
|
134
|
+
response: result.response,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
normalizeInboundMessage(payloadArg) {
|
|
138
|
+
const payload = this.asRecord(payloadArg);
|
|
139
|
+
const deliveryCandidate = this.asRecord(payload.delivery || payload.message || payload);
|
|
140
|
+
const envelope = this.asRecord(deliveryCandidate.envelope);
|
|
141
|
+
const rawMessage = typeof deliveryCandidate.rawMessage === 'string' ? deliveryCandidate.rawMessage : '';
|
|
142
|
+
const rcptTo = Array.isArray(envelope.rcptTo) ? envelope.rcptTo.map(String) : [];
|
|
143
|
+
const receivedAt = typeof deliveryCandidate.receivedAt === 'number' ? deliveryCandidate.receivedAt : Date.now();
|
|
144
|
+
return {
|
|
145
|
+
spoolItemId: String(deliveryCandidate.spoolItemId || deliveryCandidate.id || ''),
|
|
146
|
+
owner: deliveryCandidate.owner,
|
|
147
|
+
envelope: {
|
|
148
|
+
mailFrom: String(envelope.mailFrom || deliveryCandidate.from || ''),
|
|
149
|
+
rcptTo,
|
|
150
|
+
},
|
|
151
|
+
rawMessage,
|
|
152
|
+
sizeBytes: typeof deliveryCandidate.sizeBytes === 'number' ? deliveryCandidate.sizeBytes : rawMessage.length,
|
|
153
|
+
messageId: deliveryCandidate.messageId ? String(deliveryCandidate.messageId) : undefined,
|
|
154
|
+
subject: deliveryCandidate.subject ? String(deliveryCandidate.subject) : this.extractHeader(rawMessage, 'subject'),
|
|
155
|
+
headers: this.normalizeHeaders(deliveryCandidate.headers),
|
|
156
|
+
source: deliveryCandidate.source,
|
|
157
|
+
receivedAt,
|
|
158
|
+
from: String(envelope.mailFrom || deliveryCandidate.from || ''),
|
|
159
|
+
to: rcptTo,
|
|
160
|
+
receivedAtIso: new Date(receivedAt).toISOString(),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async handleInboundPayload(payloadArg, handlerArg) {
|
|
164
|
+
const message = this.normalizeInboundMessage(payloadArg);
|
|
165
|
+
const result = await handlerArg(message);
|
|
166
|
+
return {
|
|
167
|
+
accepted: result?.accepted !== false,
|
|
168
|
+
workAppMessageId: result?.workAppMessageId,
|
|
169
|
+
message: result?.message,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
createInboundHandler(handlerArg) {
|
|
173
|
+
return async (payloadArg) => await this.handleInboundPayload(payloadArg, handlerArg);
|
|
174
|
+
}
|
|
22
175
|
async sendNotification(optionsArg) { }
|
|
23
176
|
async sendActionLink(optionsArg) { }
|
|
177
|
+
async readMailEnv(tokenArg) {
|
|
178
|
+
const prefix = tokenArg ? `MAIL_${tokenArg}_` : '';
|
|
179
|
+
return {
|
|
180
|
+
from: await this.readMailEnvVar(tokenArg ? `${prefix}FROM` : 'MAIL_FROM'),
|
|
181
|
+
smtpHost: await this.readMailEnvVar(`${prefix}SMTP_HOST`),
|
|
182
|
+
smtpPort: await this.readMailEnvVar(`${prefix}SMTP_PORT`),
|
|
183
|
+
smtpTlsMode: await this.readMailEnvVar(`${prefix}SMTP_TLS_MODE`),
|
|
184
|
+
smtpUsername: await this.readMailEnvVar(`${prefix}SMTP_USERNAME`),
|
|
185
|
+
smtpPassword: await this.readMailEnvVar(`${prefix}SMTP_PASSWORD`),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async readMailEnvVar(envNameArg) {
|
|
189
|
+
return await this.platformClientRef.getConfiguredEnvVar(envNameArg)
|
|
190
|
+
|| this.getCredentialEnvVarFromBindings(envNameArg);
|
|
191
|
+
}
|
|
192
|
+
getCredentialEnvVarFromBindings(envNameArg) {
|
|
193
|
+
for (const binding of this.platformClientRef.platformBindings || []) {
|
|
194
|
+
if (binding.desiredState === 'disabled' || binding.status === 'failed')
|
|
195
|
+
continue;
|
|
196
|
+
if (binding.capability !== 'email')
|
|
197
|
+
continue;
|
|
198
|
+
for (const credential of binding.credentials || []) {
|
|
199
|
+
const value = credential.env?.[envNameArg];
|
|
200
|
+
if (value)
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
hasAnyMailEnvValue(envArg) {
|
|
206
|
+
return Boolean(envArg && Object.values(envArg).some(Boolean));
|
|
207
|
+
}
|
|
208
|
+
normalizeTlsMode(valueArg) {
|
|
209
|
+
if (valueArg === 'plain' || valueArg === 'starttls' || valueArg === 'implicitTls')
|
|
210
|
+
return valueArg;
|
|
211
|
+
throw new Error(`Invalid SMTP_TLS_MODE: ${valueArg}`);
|
|
212
|
+
}
|
|
213
|
+
getMailEnvToken(addressArg) {
|
|
214
|
+
const token = addressArg.trim().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '').toUpperCase();
|
|
215
|
+
return /^\d/.test(token) ? `ADDR_${token}` : token;
|
|
216
|
+
}
|
|
217
|
+
listToArray(valueArg) {
|
|
218
|
+
return Array.isArray(valueArg) ? valueArg : [valueArg];
|
|
219
|
+
}
|
|
220
|
+
normalizeAttachmentContent(contentArg) {
|
|
221
|
+
return typeof contentArg === 'string' ? contentArg : Buffer.from(contentArg);
|
|
222
|
+
}
|
|
223
|
+
asRecord(valueArg) {
|
|
224
|
+
return valueArg && typeof valueArg === 'object' ? valueArg : {};
|
|
225
|
+
}
|
|
226
|
+
normalizeHeaders(headersArg) {
|
|
227
|
+
if (!headersArg || typeof headersArg !== 'object')
|
|
228
|
+
return undefined;
|
|
229
|
+
const result = {};
|
|
230
|
+
for (const [key, value] of Object.entries(headersArg)) {
|
|
231
|
+
if (Array.isArray(value)) {
|
|
232
|
+
result[key] = value.map(String);
|
|
233
|
+
}
|
|
234
|
+
else if (value !== undefined && value !== null) {
|
|
235
|
+
result[key] = String(value);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
extractHeader(rawMessageArg, headerNameArg) {
|
|
241
|
+
const lowerName = `${headerNameArg.toLowerCase()}:`;
|
|
242
|
+
for (const line of rawMessageArg.split(/\r?\n/)) {
|
|
243
|
+
if (!line)
|
|
244
|
+
return undefined;
|
|
245
|
+
if (line.toLowerCase().startsWith(lowerName)) {
|
|
246
|
+
return line.slice(lowerName.length).trim();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
24
250
|
}
|
|
25
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbWFpbGNvbm5lY3Rvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2VtYWlsL2NsYXNzZXMuZW1haWxjb25uZWN0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFFekMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUV0QyxNQUFNLE9BQU8sZ0JBQWdCO0lBRzNCLFlBQVksb0JBQXNDO1FBQ2hELElBQUksQ0FBQyxpQkFBaUIsR0FBRyxvQkFBb0IsQ0FBQztJQUNoRCxDQUFDO0lBRU0sS0FBSyxDQUFDLFNBQVMsQ0FDcEIsVUFBZ0Y7UUFFaEYsSUFBSSxJQUFJLENBQUMsaUJBQWlCLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDckMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsMkJBQTJCLFVBQVUsQ0FBQyxLQUFLLE9BQU8sVUFBVSxDQUFDLEVBQUU7O1VBRTlFLFVBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLFVBQVUsSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDOztTQUVyRSxDQUFDLENBQUM7UUFDUCxDQUFDO1FBRUQsSUFBSSxJQUFJLENBQUMsaUJBQWlCLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDckMsT0FBTztRQUNULENBQUM7UUFFRCxNQUFNLFlBQVksR0FDaEIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFdBQVcsQ0FBQyxrQkFBa0IsQ0FDbkQsV0FBVyxDQUNaLENBQUM7UUFDSixNQUFNLFFBQVEsR0FBRyxNQUFNLFlBQVksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDckQsT0FBTyxRQUFRLENBQUMsVUFBVSxDQUFDO0lBQzdCLENBQUM7SUFFTSxLQUFLLENBQUMsZ0JBQWdCLENBQUMsVUFBNEQsSUFBRyxDQUFDO0lBRXZGLEtBQUssQ0FBQyxjQUFjLENBQUMsVUFBNEQsSUFBRyxDQUFDO0NBQzdGIn0=
|
|
251
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbWFpbGNvbm5lY3Rvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2VtYWlsL2NsYXNzZXMuZW1haWxjb25uZWN0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFFekMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGNBQWMsQ0FBQztBQTRFdEMsTUFBTSxPQUFPLGdCQUFnQjtJQUczQixZQUFZLG9CQUFzQztRQUNoRCxJQUFJLENBQUMsaUJBQWlCLEdBQUcsb0JBQW9CLENBQUM7SUFDaEQsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFNBQVMsQ0FDcEIsVUFBZ0Y7UUFFaEYsSUFBSSxJQUFJLENBQUMsaUJBQWlCLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDckMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsMkJBQTJCLFVBQVUsQ0FBQyxLQUFLLE9BQU8sVUFBVSxDQUFDLEVBQUU7O1VBRTlFLFVBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLFVBQVUsSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDOztTQUVyRSxDQUFDLENBQUM7UUFDUCxDQUFDO1FBRUQsSUFBSSxJQUFJLENBQUMsaUJBQWlCLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDckMsT0FBTztRQUNULENBQUM7UUFFRCxNQUFNLFlBQVksR0FDaEIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFdBQVcsQ0FBQyxrQkFBa0IsQ0FDbkQsV0FBVyxDQUNaLENBQUM7UUFDSixNQUFNLFFBQVEsR0FBRyxNQUFNLFlBQVksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDckQsT0FBTyxRQUFRLENBQUMsVUFBVSxDQUFDO0lBQzdCLENBQUM7SUFFTSxLQUFLLENBQUMs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
|
package/dist_ts/index.d.ts
CHANGED
package/dist_ts/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export * from './classes.platformclient.js';
|
|
2
|
-
|
|
2
|
+
export * from './email/classes.emailconnector.js';
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDZCQUE2QixDQUFDO0FBQzVDLGNBQWMsbUNBQW1DLENBQUMifQ==
|
package/dist_ts/plugins.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { servezoneInterfaces, };
|
|
|
3
3
|
import * as qenv from '@push.rocks/qenv';
|
|
4
4
|
import * as smartlog from '@push.rocks/smartlog';
|
|
5
5
|
export { qenv, smartlog, };
|
|
6
|
+
export declare const getSmartsmtp: () => Promise<typeof import("@push.rocks/smartsmtp")>;
|
|
6
7
|
import * as typedrequest from '@api.global/typedrequest';
|
|
7
8
|
import * as typedserver from '@api.global/typedserver';
|
|
8
9
|
import * as typedsocket from '@api.global/typedsocket';
|
package/dist_ts/plugins.js
CHANGED
|
@@ -5,9 +5,10 @@ export { servezoneInterfaces, };
|
|
|
5
5
|
import * as qenv from '@push.rocks/qenv';
|
|
6
6
|
import * as smartlog from '@push.rocks/smartlog';
|
|
7
7
|
export { qenv, smartlog, };
|
|
8
|
+
export const getSmartsmtp = async () => await import('@push.rocks/smartsmtp');
|
|
8
9
|
// @api.global scope
|
|
9
10
|
import * as typedrequest from '@api.global/typedrequest';
|
|
10
11
|
import * as typedserver from '@api.global/typedserver';
|
|
11
12
|
import * as typedsocket from '@api.global/typedsocket';
|
|
12
13
|
export { typedrequest, typedserver, typedsocket, };
|
|
13
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
14
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxtQkFBbUIsTUFBTSx3QkFBd0IsQ0FBQztBQUU5RCxPQUFPLEVBQ0wsbUJBQW1CLEdBQ3BCLENBQUE7QUFFRCxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLElBQUksTUFBTSxrQkFBa0IsQ0FBQztBQUN6QyxPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBRWpELE9BQU8sRUFDTCxJQUFJLEVBQ0osUUFBUSxHQUNULENBQUE7QUFFRCxNQUFNLENBQUMsTUFBTSxZQUFZLEdBQUcsS0FBSyxJQUFJLEVBQUUsQ0FBQyxNQUFNLE1BQU0sQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO0FBRzlFLG9CQUFvQjtBQUNwQixPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFDdkQsT0FBTyxLQUFLLFdBQVcsTUFBTSx5QkFBeUIsQ0FBQztBQUV2RCxPQUFPLEVBQ0wsWUFBWSxFQUNaLFdBQVcsRUFDWCxXQUFXLEdBQ1osQ0FBQSJ9
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/platformclient",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "a module that makes it really easy to use the serve.zone platform inside your app",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@git.zone/tsbuild": "^4.4.2",
|
|
13
|
-
"@git.zone/tsbundle": "^2.
|
|
13
|
+
"@git.zone/tsbundle": "^2.11.0",
|
|
14
14
|
"@git.zone/tsrun": "^2.0.4",
|
|
15
15
|
"@git.zone/tstest": "^3.6.6",
|
|
16
16
|
"@types/node": "^25.9.1"
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"@push.rocks/qenv": "^6.1.4",
|
|
23
23
|
"@push.rocks/smartlog": "^3.2.2",
|
|
24
24
|
"@push.rocks/smartntml": "^2.0.9",
|
|
25
|
-
"@
|
|
25
|
+
"@push.rocks/smartsmtp": "^3.1.0",
|
|
26
|
+
"@serve.zone/interfaces": "^7.27.0"
|
|
26
27
|
},
|
|
27
28
|
"repository": {
|
|
28
29
|
"type": "git",
|
package/readme.md
CHANGED
|
@@ -38,7 +38,7 @@ await platformClient.emailConnector.sendEmail({
|
|
|
38
38
|
|
|
39
39
|
| Connector | Main methods | Platform capability |
|
|
40
40
|
| --- | --- | --- |
|
|
41
|
-
| `emailConnector` | `sendEmail()` | `email` |
|
|
41
|
+
| `emailConnector` | `getServiceMailStatus()`, `getServiceMailCredentials()`, `sendMail()`, `sendText()`, `sendHtml()`, `normalizeInboundMessage()`, `handleInboundPayload()`, `createInboundHandler()`, `sendEmail()` | `email` |
|
|
42
42
|
| `smsConnector` | `sendSms()`, `sendSmsVerifcation()` | `sms` |
|
|
43
43
|
| `pushNotificationConnector` | `sendPushNotification()` | `pushnotification` |
|
|
44
44
|
| `letterConnector` | `sendLetter()` | `letter` |
|
|
@@ -57,6 +57,8 @@ The client needs an authorization string and a platform endpoint. You can provid
|
|
|
57
57
|
|
|
58
58
|
Binding environment variables must contain JSON encoded `IPlatformBinding` objects from `@serve.zone/interfaces`.
|
|
59
59
|
|
|
60
|
+
Service mail credentials can be provided directly as environment variables or through `email` platform binding credentials. The default sender uses `MAIL_FROM`, `SMTP_HOST`, `SMTP_PORT`, `SMTP_TLS_MODE`, `SMTP_USERNAME`, and `SMTP_PASSWORD`. Additional configured sender addresses use scoped variables such as `MAIL_TEST_SERVICE_GATED_ONE_FROM` and `MAIL_TEST_SERVICE_GATED_ONE_SMTP_USERNAME`.
|
|
61
|
+
|
|
60
62
|
```typescript
|
|
61
63
|
import { SzPlatformClient } from '@serve.zone/platformclient';
|
|
62
64
|
|
|
@@ -102,7 +104,41 @@ The current SMS verification method is spelled `sendSmsVerifcation()` in code. U
|
|
|
102
104
|
|
|
103
105
|
## Connector Examples
|
|
104
106
|
|
|
105
|
-
|
|
107
|
+
Service mail over managed SMTP credentials:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const status = await client.emailConnector.getServiceMailStatus();
|
|
111
|
+
|
|
112
|
+
if (status.ready) {
|
|
113
|
+
await client.emailConnector.sendText({
|
|
114
|
+
to: 'user@example.com',
|
|
115
|
+
subject: 'Service mail ready',
|
|
116
|
+
text: 'This message was sent through the managed service mail identity.',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Specific sender address:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
await client.emailConnector.sendHtml({
|
|
125
|
+
from: 'test-service@gated.one',
|
|
126
|
+
to: 'user@example.com',
|
|
127
|
+
subject: 'Platform capability test',
|
|
128
|
+
html: '<p>The service mail identity is working.</p>',
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Inbound message normalization for webhook or TypedRequest-style delivery:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
const inboundHandler = client.emailConnector.createInboundHandler(async (message) => {
|
|
136
|
+
console.log(message.from, message.to, message.subject);
|
|
137
|
+
return { accepted: true };
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Legacy platform-service email request:
|
|
106
142
|
|
|
107
143
|
```typescript
|
|
108
144
|
await client.emailConnector.sendEmail({
|
|
@@ -144,7 +180,7 @@ await client.letterConnector.sendLetter({
|
|
|
144
180
|
});
|
|
145
181
|
```
|
|
146
182
|
|
|
147
|
-
|
|
183
|
+
Service-mail helper option and result types are exported from `@serve.zone/platformclient`. The legacy platform-service request fields are defined in `@serve.zone/interfaces` under `platform.email`, `platform.sms`, `platform.pushnotification`, and `platform.letter`.
|
|
148
184
|
|
|
149
185
|
## Platform Bindings
|
|
150
186
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -217,7 +217,7 @@ export class SzPlatformClient {
|
|
|
217
217
|
return Array.from(bindingMap.values());
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
public async getConfiguredEnvVar(envNameArg: string) {
|
|
221
221
|
const processValue = this.getProcessEnvVar(envNameArg);
|
|
222
222
|
if (processValue) {
|
|
223
223
|
return processValue;
|
|
@@ -3,6 +3,80 @@ import * as plugins from '../plugins.js';
|
|
|
3
3
|
|
|
4
4
|
import { logger } from '../logger.js';
|
|
5
5
|
|
|
6
|
+
export type TServiceMailTlsMode = 'plain' | 'starttls' | 'implicitTls';
|
|
7
|
+
|
|
8
|
+
export interface IServiceMailSmtpCredentials {
|
|
9
|
+
from: string;
|
|
10
|
+
host: string;
|
|
11
|
+
port: number;
|
|
12
|
+
tlsMode: TServiceMailTlsMode;
|
|
13
|
+
username: string;
|
|
14
|
+
password: string;
|
|
15
|
+
addressToken?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface IServiceMailStatus {
|
|
19
|
+
ready: boolean;
|
|
20
|
+
from?: string;
|
|
21
|
+
host?: string;
|
|
22
|
+
port?: number;
|
|
23
|
+
tlsMode?: TServiceMailTlsMode;
|
|
24
|
+
usernameConfigured: boolean;
|
|
25
|
+
passwordConfigured: boolean;
|
|
26
|
+
missing: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IServiceMailAttachment {
|
|
30
|
+
filename: string;
|
|
31
|
+
contentType?: string;
|
|
32
|
+
content: string | Uint8Array;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface IServiceMailSendOptions {
|
|
36
|
+
from?: string;
|
|
37
|
+
to: string | string[];
|
|
38
|
+
cc?: string | string[];
|
|
39
|
+
bcc?: string | string[];
|
|
40
|
+
subject: string;
|
|
41
|
+
text?: string;
|
|
42
|
+
html?: string;
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
attachments?: IServiceMailAttachment[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface IServiceMailSendResult {
|
|
48
|
+
messageId?: string;
|
|
49
|
+
accepted?: string[];
|
|
50
|
+
rejected?: string[];
|
|
51
|
+
response?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface IServiceMailInboundMessage extends plugins.servezoneInterfaces.data.IMailInboundMessagePayload {
|
|
55
|
+
from: string;
|
|
56
|
+
to: string[];
|
|
57
|
+
receivedAtIso: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface IServiceMailInboundHandlerResult {
|
|
61
|
+
accepted?: boolean;
|
|
62
|
+
workAppMessageId?: string;
|
|
63
|
+
message?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type IServiceMailInboundHandleResult =
|
|
67
|
+
plugins.servezoneInterfaces.requests.mail.IReq_DeliverInboundMail['response'];
|
|
68
|
+
|
|
69
|
+
interface INodemailerSendResult {
|
|
70
|
+
messageId?: string;
|
|
71
|
+
accepted?: unknown[];
|
|
72
|
+
rejected?: unknown[];
|
|
73
|
+
response?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type TServiceMailInboundHandler = (
|
|
77
|
+
messageArg: IServiceMailInboundMessage,
|
|
78
|
+
) => Promise<IServiceMailInboundHandlerResult | void> | IServiceMailInboundHandlerResult | void;
|
|
79
|
+
|
|
6
80
|
export class SzEmailConnector {
|
|
7
81
|
public platformClientRef: SzPlatformClient;
|
|
8
82
|
|
|
@@ -10,6 +84,9 @@ export class SzEmailConnector {
|
|
|
10
84
|
this.platformClientRef = platformClientRefArg;
|
|
11
85
|
}
|
|
12
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Legacy platform-service email path. New service-mail code should use sendMail().
|
|
89
|
+
*/
|
|
13
90
|
public async sendEmail(
|
|
14
91
|
optionsArg: plugins.servezoneInterfaces.platform.email.IReq_SendEmail['request']
|
|
15
92
|
) {
|
|
@@ -33,7 +110,252 @@ export class SzEmailConnector {
|
|
|
33
110
|
return response.responseId;
|
|
34
111
|
}
|
|
35
112
|
|
|
113
|
+
public async getServiceMailStatus(addressArg?: string): Promise<IServiceMailStatus> {
|
|
114
|
+
const requestedAddress = addressArg?.trim().toLowerCase();
|
|
115
|
+
const token = requestedAddress ? this.getMailEnvToken(requestedAddress) : undefined;
|
|
116
|
+
const scopedEnv = token ? await this.readMailEnv(token) : undefined;
|
|
117
|
+
const defaultEnv = token ? await this.readMailEnv() : undefined;
|
|
118
|
+
const useDefaultEnv = requestedAddress
|
|
119
|
+
&& !this.hasAnyMailEnvValue(scopedEnv)
|
|
120
|
+
&& defaultEnv?.from?.trim().toLowerCase() === requestedAddress;
|
|
121
|
+
const env = useDefaultEnv ? defaultEnv! : scopedEnv || await this.readMailEnv();
|
|
122
|
+
const missingPrefix = useDefaultEnv || !token ? undefined : token;
|
|
123
|
+
const port = env.smtpPort ? Number(env.smtpPort) : undefined;
|
|
124
|
+
const tlsMode = this.normalizeTlsMode(env.smtpTlsMode || 'starttls');
|
|
125
|
+
const missing: string[] = [];
|
|
126
|
+
if (!env.from) missing.push(missingPrefix ? `MAIL_${missingPrefix}_FROM` : 'MAIL_FROM');
|
|
127
|
+
if (!env.smtpHost) missing.push(missingPrefix ? `MAIL_${missingPrefix}_SMTP_HOST` : 'SMTP_HOST');
|
|
128
|
+
if (!env.smtpPort || !Number.isInteger(port) || port! < 1 || port! > 65535) {
|
|
129
|
+
missing.push(missingPrefix ? `MAIL_${missingPrefix}_SMTP_PORT` : 'SMTP_PORT');
|
|
130
|
+
}
|
|
131
|
+
if (!env.smtpUsername) missing.push(missingPrefix ? `MAIL_${missingPrefix}_SMTP_USERNAME` : 'SMTP_USERNAME');
|
|
132
|
+
if (!env.smtpPassword) missing.push(missingPrefix ? `MAIL_${missingPrefix}_SMTP_PASSWORD` : 'SMTP_PASSWORD');
|
|
133
|
+
return {
|
|
134
|
+
ready: missing.length === 0,
|
|
135
|
+
from: env.from,
|
|
136
|
+
host: env.smtpHost,
|
|
137
|
+
port,
|
|
138
|
+
tlsMode,
|
|
139
|
+
usernameConfigured: Boolean(env.smtpUsername),
|
|
140
|
+
passwordConfigured: Boolean(env.smtpPassword),
|
|
141
|
+
missing,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public async getServiceMailCredentials(addressArg?: string): Promise<IServiceMailSmtpCredentials> {
|
|
146
|
+
const requestedAddress = addressArg?.trim().toLowerCase();
|
|
147
|
+
const requestedToken = requestedAddress ? this.getMailEnvToken(requestedAddress) : undefined;
|
|
148
|
+
const scopedEnv = requestedToken ? await this.readMailEnv(requestedToken) : undefined;
|
|
149
|
+
const hasScopedConfig = scopedEnv && Object.values(scopedEnv).some(Boolean);
|
|
150
|
+
const env = hasScopedConfig ? scopedEnv! : await this.readMailEnv();
|
|
151
|
+
const from = env.from?.trim().toLowerCase();
|
|
152
|
+
if (!from) throw new Error('MAIL_FROM is required for service mail');
|
|
153
|
+
if (requestedAddress && from !== requestedAddress) {
|
|
154
|
+
throw new Error(`No service mail credentials are configured for ${requestedAddress}`);
|
|
155
|
+
}
|
|
156
|
+
const port = Number(env.smtpPort);
|
|
157
|
+
if (!env.smtpHost) throw new Error('SMTP_HOST is required for service mail');
|
|
158
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
159
|
+
throw new Error('SMTP_PORT must be an integer between 1 and 65535');
|
|
160
|
+
}
|
|
161
|
+
if (!env.smtpUsername) throw new Error('SMTP_USERNAME is required for service mail');
|
|
162
|
+
if (!env.smtpPassword) throw new Error('SMTP_PASSWORD is required for service mail');
|
|
163
|
+
return {
|
|
164
|
+
from,
|
|
165
|
+
host: env.smtpHost,
|
|
166
|
+
port,
|
|
167
|
+
tlsMode: this.normalizeTlsMode(env.smtpTlsMode || 'starttls'),
|
|
168
|
+
username: env.smtpUsername,
|
|
169
|
+
password: env.smtpPassword,
|
|
170
|
+
addressToken: hasScopedConfig ? requestedToken : undefined,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public async sendText(optionsArg: Omit<IServiceMailSendOptions, 'html'> & { text: string }) {
|
|
175
|
+
return await this.sendMail(optionsArg);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
public async sendHtml(optionsArg: Omit<IServiceMailSendOptions, 'text'> & { html: string }) {
|
|
179
|
+
return await this.sendMail(optionsArg);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
public async sendMail(optionsArg: IServiceMailSendOptions): Promise<IServiceMailSendResult> {
|
|
183
|
+
if (!optionsArg.text && !optionsArg.html) {
|
|
184
|
+
throw new Error('Either text or html is required for service mail');
|
|
185
|
+
}
|
|
186
|
+
if (this.platformClientRef.debugMode) {
|
|
187
|
+
logger.log('info', `service mail debug send: ${optionsArg.subject} to ${this.listToArray(optionsArg.to).join(', ')}`);
|
|
188
|
+
return { messageId: 'debug' };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const credentials = await this.getServiceMailCredentials(optionsArg.from);
|
|
192
|
+
const smartsmtp = await plugins.getSmartsmtp();
|
|
193
|
+
const smtp = await smartsmtp.Smartsmtp.createSmartsmtpWithRelay({
|
|
194
|
+
smtpServer: credentials.host,
|
|
195
|
+
smtpPort: credentials.port,
|
|
196
|
+
smtpTlsMode: credentials.tlsMode,
|
|
197
|
+
smtpUser: credentials.username,
|
|
198
|
+
smtpPassword: credentials.password,
|
|
199
|
+
});
|
|
200
|
+
const result = (await smtp.nodemailerTransport.sendMail({
|
|
201
|
+
from: credentials.from,
|
|
202
|
+
to: this.listToArray(optionsArg.to),
|
|
203
|
+
cc: optionsArg.cc ? this.listToArray(optionsArg.cc) : undefined,
|
|
204
|
+
bcc: optionsArg.bcc ? this.listToArray(optionsArg.bcc) : undefined,
|
|
205
|
+
subject: optionsArg.subject,
|
|
206
|
+
text: optionsArg.text,
|
|
207
|
+
html: optionsArg.html,
|
|
208
|
+
headers: optionsArg.headers,
|
|
209
|
+
attachments: optionsArg.attachments?.map((attachmentArg) => ({
|
|
210
|
+
filename: attachmentArg.filename,
|
|
211
|
+
contentType: attachmentArg.contentType,
|
|
212
|
+
content: this.normalizeAttachmentContent(attachmentArg.content),
|
|
213
|
+
})),
|
|
214
|
+
})) as INodemailerSendResult;
|
|
215
|
+
return {
|
|
216
|
+
messageId: result.messageId,
|
|
217
|
+
accepted: Array.isArray(result.accepted) ? result.accepted.map(String) : undefined,
|
|
218
|
+
rejected: Array.isArray(result.rejected) ? result.rejected.map(String) : undefined,
|
|
219
|
+
response: result.response,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
public normalizeInboundMessage(payloadArg: unknown): IServiceMailInboundMessage {
|
|
224
|
+
const payload = this.asRecord(payloadArg);
|
|
225
|
+
const deliveryCandidate = this.asRecord(payload.delivery || payload.message || payload);
|
|
226
|
+
const envelope = this.asRecord(deliveryCandidate.envelope);
|
|
227
|
+
const rawMessage = typeof deliveryCandidate.rawMessage === 'string' ? deliveryCandidate.rawMessage : '';
|
|
228
|
+
const rcptTo = Array.isArray(envelope.rcptTo) ? envelope.rcptTo.map(String) : [];
|
|
229
|
+
const receivedAt = typeof deliveryCandidate.receivedAt === 'number' ? deliveryCandidate.receivedAt : Date.now();
|
|
230
|
+
return {
|
|
231
|
+
spoolItemId: String(deliveryCandidate.spoolItemId || deliveryCandidate.id || ''),
|
|
232
|
+
owner: deliveryCandidate.owner as plugins.servezoneInterfaces.data.IMailResourceOwner | undefined,
|
|
233
|
+
envelope: {
|
|
234
|
+
mailFrom: String(envelope.mailFrom || deliveryCandidate.from || ''),
|
|
235
|
+
rcptTo,
|
|
236
|
+
},
|
|
237
|
+
rawMessage,
|
|
238
|
+
sizeBytes: typeof deliveryCandidate.sizeBytes === 'number' ? deliveryCandidate.sizeBytes : rawMessage.length,
|
|
239
|
+
messageId: deliveryCandidate.messageId ? String(deliveryCandidate.messageId) : undefined,
|
|
240
|
+
subject: deliveryCandidate.subject ? String(deliveryCandidate.subject) : this.extractHeader(rawMessage, 'subject'),
|
|
241
|
+
headers: this.normalizeHeaders(deliveryCandidate.headers),
|
|
242
|
+
source: deliveryCandidate.source as plugins.servezoneInterfaces.data.IMailConnectionInfo | undefined,
|
|
243
|
+
receivedAt,
|
|
244
|
+
from: String(envelope.mailFrom || deliveryCandidate.from || ''),
|
|
245
|
+
to: rcptTo,
|
|
246
|
+
receivedAtIso: new Date(receivedAt).toISOString(),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public async handleInboundPayload(
|
|
251
|
+
payloadArg: unknown,
|
|
252
|
+
handlerArg: TServiceMailInboundHandler,
|
|
253
|
+
): Promise<IServiceMailInboundHandleResult> {
|
|
254
|
+
const message = this.normalizeInboundMessage(payloadArg);
|
|
255
|
+
const result = await handlerArg(message);
|
|
256
|
+
return {
|
|
257
|
+
accepted: result?.accepted !== false,
|
|
258
|
+
workAppMessageId: result?.workAppMessageId,
|
|
259
|
+
message: result?.message,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
public createInboundHandler(handlerArg: TServiceMailInboundHandler) {
|
|
264
|
+
return async (payloadArg: unknown) => await this.handleInboundPayload(payloadArg, handlerArg);
|
|
265
|
+
}
|
|
266
|
+
|
|
36
267
|
public async sendNotification(optionsArg: { toArg: string; subject: string; text: string }) {}
|
|
37
268
|
|
|
38
269
|
public async sendActionLink(optionsArg: { toArg: string; subject: string; text: string }) {}
|
|
270
|
+
|
|
271
|
+
private async readMailEnv(tokenArg?: string): Promise<{
|
|
272
|
+
from?: string;
|
|
273
|
+
smtpHost?: string;
|
|
274
|
+
smtpPort?: string;
|
|
275
|
+
smtpTlsMode?: string;
|
|
276
|
+
smtpUsername?: string;
|
|
277
|
+
smtpPassword?: string;
|
|
278
|
+
}> {
|
|
279
|
+
const prefix = tokenArg ? `MAIL_${tokenArg}_` : '';
|
|
280
|
+
return {
|
|
281
|
+
from: await this.readMailEnvVar(tokenArg ? `${prefix}FROM` : 'MAIL_FROM'),
|
|
282
|
+
smtpHost: await this.readMailEnvVar(`${prefix}SMTP_HOST`),
|
|
283
|
+
smtpPort: await this.readMailEnvVar(`${prefix}SMTP_PORT`),
|
|
284
|
+
smtpTlsMode: await this.readMailEnvVar(`${prefix}SMTP_TLS_MODE`),
|
|
285
|
+
smtpUsername: await this.readMailEnvVar(`${prefix}SMTP_USERNAME`),
|
|
286
|
+
smtpPassword: await this.readMailEnvVar(`${prefix}SMTP_PASSWORD`),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private async readMailEnvVar(envNameArg: string): Promise<string | undefined> {
|
|
291
|
+
return await this.platformClientRef.getConfiguredEnvVar(envNameArg)
|
|
292
|
+
|| this.getCredentialEnvVarFromBindings(envNameArg);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private getCredentialEnvVarFromBindings(envNameArg: string): string | undefined {
|
|
296
|
+
for (const binding of this.platformClientRef.platformBindings || []) {
|
|
297
|
+
if (binding.desiredState === 'disabled' || binding.status === 'failed') continue;
|
|
298
|
+
if (binding.capability !== 'email') continue;
|
|
299
|
+
for (const credential of binding.credentials || []) {
|
|
300
|
+
const value = credential.env?.[envNameArg];
|
|
301
|
+
if (value) return value;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private hasAnyMailEnvValue(envArg?: {
|
|
307
|
+
from?: string;
|
|
308
|
+
smtpHost?: string;
|
|
309
|
+
smtpPort?: string;
|
|
310
|
+
smtpTlsMode?: string;
|
|
311
|
+
smtpUsername?: string;
|
|
312
|
+
smtpPassword?: string;
|
|
313
|
+
}): boolean {
|
|
314
|
+
return Boolean(envArg && Object.values(envArg).some(Boolean));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private normalizeTlsMode(valueArg: string): TServiceMailTlsMode {
|
|
318
|
+
if (valueArg === 'plain' || valueArg === 'starttls' || valueArg === 'implicitTls') return valueArg;
|
|
319
|
+
throw new Error(`Invalid SMTP_TLS_MODE: ${valueArg}`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private getMailEnvToken(addressArg: string): string {
|
|
323
|
+
const token = addressArg.trim().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '').toUpperCase();
|
|
324
|
+
return /^\d/.test(token) ? `ADDR_${token}` : token;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private listToArray(valueArg: string | string[]): string[] {
|
|
328
|
+
return Array.isArray(valueArg) ? valueArg : [valueArg];
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private normalizeAttachmentContent(contentArg: string | Uint8Array): string | Buffer {
|
|
332
|
+
return typeof contentArg === 'string' ? contentArg : Buffer.from(contentArg);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private asRecord(valueArg: unknown): Record<string, unknown> {
|
|
336
|
+
return valueArg && typeof valueArg === 'object' ? valueArg as Record<string, unknown> : {};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private normalizeHeaders(headersArg: unknown): Record<string, string | string[]> | undefined {
|
|
340
|
+
if (!headersArg || typeof headersArg !== 'object') return undefined;
|
|
341
|
+
const result: Record<string, string | string[]> = {};
|
|
342
|
+
for (const [key, value] of Object.entries(headersArg as Record<string, unknown>)) {
|
|
343
|
+
if (Array.isArray(value)) {
|
|
344
|
+
result[key] = value.map(String);
|
|
345
|
+
} else if (value !== undefined && value !== null) {
|
|
346
|
+
result[key] = String(value);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private extractHeader(rawMessageArg: string, headerNameArg: string): string | undefined {
|
|
353
|
+
const lowerName = `${headerNameArg.toLowerCase()}:`;
|
|
354
|
+
for (const line of rawMessageArg.split(/\r?\n/)) {
|
|
355
|
+
if (!line) return undefined;
|
|
356
|
+
if (line.toLowerCase().startsWith(lowerName)) {
|
|
357
|
+
return line.slice(lowerName.length).trim();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
39
361
|
}
|
package/ts/index.ts
CHANGED