feat(mailer-smtp): implement in-process SMTP server and management IPC integration

This commit is contained in:
2026-02-10 22:00:44 +00:00
parent fc2e6d44f4
commit 5220ee0857
24 changed files with 4390 additions and 274 deletions

View File

@@ -73,6 +73,60 @@ interface IVersionInfo {
smtp: string;
}
// --- SMTP Server types ---
interface ISmtpServerConfig {
hostname: string;
ports: number[];
securePort?: number;
tlsCertPem?: string;
tlsKeyPem?: string;
maxMessageSize?: number;
maxConnections?: number;
maxRecipients?: number;
connectionTimeoutSecs?: number;
dataTimeoutSecs?: number;
authEnabled?: boolean;
maxAuthFailures?: number;
socketTimeoutSecs?: number;
processingTimeoutSecs?: number;
rateLimits?: IRateLimitConfig;
}
interface IRateLimitConfig {
maxConnectionsPerIp?: number;
maxMessagesPerSender?: number;
maxAuthFailuresPerIp?: number;
windowSecs?: number;
}
interface IEmailData {
type: 'inline' | 'file';
base64?: string;
path?: string;
}
interface IEmailReceivedEvent {
correlationId: string;
sessionId: string;
mailFrom: string;
rcptTo: string[];
data: IEmailData;
remoteAddr: string;
clientHostname: string | null;
secure: boolean;
authenticatedUser: string | null;
securityResults: any | null;
}
interface IAuthRequestEvent {
correlationId: string;
sessionId: string;
username: string;
password: string;
remoteAddr: string;
}
/**
* Type-safe command map for the mailer-bin IPC bridge.
*/
@@ -128,6 +182,35 @@ type TMailerCommands = {
};
result: IEmailSecurityResult;
};
startSmtpServer: {
params: ISmtpServerConfig;
result: { started: boolean };
};
stopSmtpServer: {
params: Record<string, never>;
result: { stopped: boolean; wasRunning?: boolean };
};
emailProcessingResult: {
params: {
correlationId: string;
accepted: boolean;
smtpCode?: number;
smtpMessage?: string;
};
result: { resolved: boolean };
};
authResult: {
params: {
correlationId: string;
success: boolean;
message?: string;
};
result: { resolved: boolean };
};
configureRateLimits: {
params: IRateLimitConfig;
result: { configured: boolean };
};
};
// ---------------------------------------------------------------------------
@@ -314,6 +397,85 @@ export class RustSecurityBridge {
}): Promise<IEmailSecurityResult> {
return this.bridge.sendCommand('verifyEmail', opts);
}
// -----------------------------------------------------------------------
// SMTP Server lifecycle
// -----------------------------------------------------------------------
/**
* Start the Rust SMTP server.
* The server will listen on the configured ports and emit events for
* emailReceived and authRequest that must be handled by the caller.
*/
public async startSmtpServer(config: ISmtpServerConfig): Promise<boolean> {
const result = await this.bridge.sendCommand('startSmtpServer', config);
return result?.started === true;
}
/** Stop the Rust SMTP server. */
public async stopSmtpServer(): Promise<void> {
await this.bridge.sendCommand('stopSmtpServer', {} as any);
}
/**
* Send the result of email processing back to the Rust SMTP server.
* This resolves a pending correlation-ID callback, allowing the Rust
* server to send the SMTP response to the client.
*/
public async sendEmailProcessingResult(opts: {
correlationId: string;
accepted: boolean;
smtpCode?: number;
smtpMessage?: string;
}): Promise<void> {
await this.bridge.sendCommand('emailProcessingResult', opts);
}
/**
* Send the result of authentication validation back to the Rust SMTP server.
*/
public async sendAuthResult(opts: {
correlationId: string;
success: boolean;
message?: string;
}): Promise<void> {
await this.bridge.sendCommand('authResult', opts);
}
/** Update rate limit configuration at runtime. */
public async configureRateLimits(config: IRateLimitConfig): Promise<void> {
await this.bridge.sendCommand('configureRateLimits', config);
}
// -----------------------------------------------------------------------
// Event registration — delegates to the underlying bridge EventEmitter
// -----------------------------------------------------------------------
/**
* Register a handler for emailReceived events from the Rust SMTP server.
* These events fire when a complete email has been received and needs processing.
*/
public onEmailReceived(handler: (data: IEmailReceivedEvent) => void): void {
this.bridge.on('management:emailReceived', handler);
}
/**
* Register a handler for authRequest events from the Rust SMTP server.
* The handler must call sendAuthResult() with the correlationId.
*/
public onAuthRequest(handler: (data: IAuthRequestEvent) => void): void {
this.bridge.on('management:authRequest', handler);
}
/** Remove an emailReceived event handler. */
public offEmailReceived(handler: (data: IEmailReceivedEvent) => void): void {
this.bridge.off('management:emailReceived', handler);
}
/** Remove an authRequest event handler. */
public offAuthRequest(handler: (data: IAuthRequestEvent) => void): void {
this.bridge.off('management:authRequest', handler);
}
}
// Re-export interfaces for consumers
@@ -327,4 +489,9 @@ export type {
IContentScanResult,
IReputationResult as IRustReputationResult,
IVersionInfo,
ISmtpServerConfig,
IRateLimitConfig,
IEmailData,
IEmailReceivedEvent,
IAuthRequestEvent,
};