fix: HTTPS with self-signed cert for Web Crypto API support
Browsers disable crypto.subtle on non-localhost HTTP. The frontend server now generates a self-signed TLS cert at startup and serves over HTTPS by default (FRONTEND_TLS=true). FRONTEND_TLS=0 disables it for localhost dev. Added axum-server (tls-rustls) + rcgen deps to the frontend-server crate.
This commit is contained in:
parent
47000beef4
commit
043b4442a7
2 changed files with 38 additions and 8 deletions
|
|
@ -12,3 +12,5 @@ axum = "0.7"
|
|||
tokio = { version = "1", features = ["full"] }
|
||||
tower-http = { version = "0.5", features = ["fs", "trace"] }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
axum-server = { version = "0.7", features = ["tls-rustls"] }
|
||||
rcgen = "0.13"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
//! 1. Reverse-proxies `/api/*` requests to the backend container.
|
||||
//! 2. Serves the built Vite SPA bundle from `dist/` (static files).
|
||||
//! 3. Falls back to `index.html` for any unmatched route (SPA history mode).
|
||||
//! 4. Serves over HTTPS with a self-signed cert (required for Web Crypto API
|
||||
//! on non-localhost addresses; browsers disable crypto.subtle on plain HTTP).
|
||||
|
||||
use axum::{
|
||||
body::Body,
|
||||
|
|
@ -13,6 +15,7 @@ use axum::{
|
|||
Router,
|
||||
};
|
||||
use std::env;
|
||||
use std::sync::Arc;
|
||||
use tower_http::services::{ServeDir, ServeFile};
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
|
|
@ -30,8 +33,12 @@ async fn main() {
|
|||
.unwrap_or_else(|_| "8080".to_string())
|
||||
.parse()
|
||||
.expect("FRONTEND_PORT must be a number");
|
||||
let use_tls = env::var("FRONTEND_TLS")
|
||||
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
|
||||
.unwrap_or(true); // TLS on by default (Web Crypto requires HTTPS)
|
||||
|
||||
let client = reqwest::Client::builder()
|
||||
.danger_accept_invalid_certs(true) // backend is plain HTTP inside Docker
|
||||
.build()
|
||||
.expect("failed to build reqwest client");
|
||||
|
||||
|
|
@ -40,25 +47,46 @@ async fn main() {
|
|||
client,
|
||||
};
|
||||
|
||||
// Static file serving with SPA fallback.
|
||||
let serve_dir = ServeDir::new("dist").fallback(ServeFile::new("dist/index.html"));
|
||||
|
||||
let app = Router::new()
|
||||
// Proxy /api and everything under /api/ to the backend.
|
||||
.route("/api", any(proxy_to_backend))
|
||||
.route("/api/*path", any(proxy_to_backend))
|
||||
.with_state(state)
|
||||
// Everything else: static files + SPA fallback.
|
||||
.fallback_service(serve_dir)
|
||||
.layer(TraceLayer::new_for_http());
|
||||
|
||||
let addr = format!("0.0.0.0:{port}");
|
||||
eprintln!("Frontend server listening on {addr}");
|
||||
eprintln!("Proxying /api/* to {backend_url}");
|
||||
eprintln!("Serving static files from dist/");
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(&addr).await.expect("bind failed");
|
||||
axum::serve(listener, app).await.expect("server error");
|
||||
if use_tls {
|
||||
eprintln!("Generating self-signed TLS certificate...");
|
||||
let cert = rcgen::generate_simple_self_signed(vec!["localhost", "normogen"])
|
||||
.expect("failed to generate cert");
|
||||
let cert_der = rcgen::Certificate::der(&cert);
|
||||
let key_der = rcgen::Certificate::serialize_der_key(&cert);
|
||||
let rustls_cert = axum_server::tls_rustls::RustlsConfig::from_der(
|
||||
cert_der.to_vec(),
|
||||
key_der.to_vec(),
|
||||
)
|
||||
.await
|
||||
.expect("failed to build rustls config");
|
||||
|
||||
eprintln!("Frontend server listening on https://{addr} (TLS)");
|
||||
eprintln!("Proxying /api/* to {backend_url}");
|
||||
eprintln!("Serving static files from dist/");
|
||||
|
||||
axum_server::bind_rustls(addr.parse().expect("bad addr"), rustls_cert)
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.expect("server error");
|
||||
} else {
|
||||
eprintln!("Frontend server listening on http://{addr} (no TLS)");
|
||||
eprintln!("Proxying /api/* to {backend_url}");
|
||||
eprintln!("Serving static files from dist/");
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(&addr).await.expect("bind failed");
|
||||
axum::serve(listener, app).await.expect("server error");
|
||||
}
|
||||
}
|
||||
|
||||
/// Forward a request to the backend, preserving method, headers, body, and
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue