package com.mailserver.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.SmartLifecycle; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter; import reactor.netty.DisposableServer; import reactor.netty.http.server.HttpServer; import java.time.Duration; /** * The web client and API over HTTPS on the mail host itself (mailserver.https.port, in public compose 8443 → * published 443): https://mail.jambocheck.com opens the web mail, no second name through Jambotron's Caddy. * Same certificate as SMTP/IMAP (Let's Encrypt from the acme service, reloaded when renewed). The plain HTTP port * (8080) stays for a reverse proxy inside the network (staging: domain in Jambotron). */ @Configuration public class HttpsConfig { @Bean @ConditionalOnExpression("${mailserver.https.port:0} > 0 and ${mailserver.tls.enabled:true}") public HttpsServer httpsServer(HttpHandler handler, MailServerConfig.SmtpTlsContext tls, @Value("${mailserver.smtp.host:0.0.0.0}") String host, @Value("${mailserver.https.port}") int port) { return new HttpsServer(handler, tls, host, port); } public static class HttpsServer implements SmartLifecycle { private static final Logger log = LoggerFactory.getLogger(HttpsServer.class); private final HttpHandler handler; private final MailServerConfig.SmtpTlsContext tls; private final String host; private final int port; private volatile DisposableServer server; HttpsServer(HttpHandler handler, MailServerConfig.SmtpTlsContext tls, String host, int port) { this.handler = handler; this.tls = tls; this.host = host; this.port = port; } @Override public void start() { server = HttpServer.create() .host(host) .port(port) .secure(spec -> spec.sslContext(tls.context())) // HSTS: Spring Security adds it for HTTPS requests by itself .handle(new ReactorHttpHandlerAdapter(handler)) .bindNow(Duration.ofSeconds(30)); log.info("HTTPS (web client + API) listening on {}:{}", host, server.port()); } @Override public void stop() { DisposableServer current = server; if (current != null) { current.disposeNow(Duration.ofSeconds(10)); server = null; } } @Override public boolean isRunning() { return server != null; } public int port() { return server == null ? -1 : server.port(); } } }