Skip to content

Commit b1a59d2

Browse files
authored
feat: reload SMTP server TLS certificates on change (#209)
1 parent c9ec8b7 commit b1a59d2

File tree

1 file changed

+50
-4
lines changed

1 file changed

+50
-4
lines changed

apps/smtp-server/src/server.ts

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { SMTPServer, SMTPServerOptions, SMTPServerSession } from "smtp-server";
22
import { Readable } from "stream";
33
import dotenv from "dotenv";
44
import { simpleParser } from "mailparser";
5-
import { readFileSync } from "fs";
5+
import { readFileSync, watch, FSWatcher } from "fs";
66

77
dotenv.config();
88

@@ -53,10 +53,19 @@ async function sendEmailToUnsend(emailData: any, apiKey: string) {
5353
}
5454
}
5555

56+
function loadCertificates(): { key?: Buffer; cert?: Buffer } {
57+
return {
58+
key: SSL_KEY_PATH ? readFileSync(SSL_KEY_PATH) : undefined,
59+
cert: SSL_CERT_PATH ? readFileSync(SSL_CERT_PATH) : undefined,
60+
};
61+
}
62+
63+
const initialCerts = loadCertificates();
64+
5665
const serverOptions: SMTPServerOptions = {
5766
secure: false,
58-
key: SSL_KEY_PATH ? readFileSync(SSL_KEY_PATH) : undefined,
59-
cert: SSL_CERT_PATH ? readFileSync(SSL_CERT_PATH) : undefined,
67+
key: initialCerts.key,
68+
cert: initialCerts.cert,
6069
onData(
6170
stream: Readable,
6271
session: SMTPServerSession,
@@ -109,6 +118,9 @@ const serverOptions: SMTPServerOptions = {
109118
};
110119

111120
function startServers() {
121+
const servers: SMTPServer[] = [];
122+
const watchers: FSWatcher[] = [];
123+
112124
if (SSL_KEY_PATH && SSL_CERT_PATH) {
113125
// Implicit SSL/TLS for ports 465 and 2465
114126
[465, 2465].forEach((port) => {
@@ -123,6 +135,8 @@ function startServers() {
123135
server.on("error", (err) => {
124136
console.error(`Error occurred on port ${port}:`, err);
125137
});
138+
139+
servers.push(server);
126140
});
127141
}
128142

@@ -137,7 +151,39 @@ function startServers() {
137151
server.on("error", (err) => {
138152
console.error(`Error occurred on port ${port}:`, err);
139153
});
154+
155+
servers.push(server);
140156
});
157+
158+
if (SSL_KEY_PATH && SSL_CERT_PATH) {
159+
const reloadCertificates = () => {
160+
try {
161+
const { key, cert } = loadCertificates();
162+
if (key && cert) {
163+
servers.forEach((srv) => srv.updateSecureContext({ key, cert }));
164+
console.log("TLS certificates reloaded");
165+
}
166+
} catch (err) {
167+
console.error("Failed to reload TLS certificates", err);
168+
}
169+
};
170+
171+
[SSL_KEY_PATH, SSL_CERT_PATH].forEach((file) => {
172+
watchers.push(watch(file, { persistent: false }, reloadCertificates));
173+
});
174+
}
175+
return { servers, watchers };
176+
}
177+
178+
const { servers, watchers } = startServers();
179+
180+
function shutdown() {
181+
console.log("Shutting down SMTP server...");
182+
watchers.forEach((w) => w.close());
183+
servers.forEach((s) => s.close());
184+
process.exit(0);
141185
}
142186

143-
startServers();
187+
["SIGINT", "SIGTERM", "SIGQUIT"].forEach((signal) => {
188+
process.on(signal, shutdown);
189+
});

0 commit comments

Comments
 (0)