add event_bus_typed

This commit is contained in:
2026-09-23 21:11:35 +02:00
parent b830abe3f4
commit 5bac3174df
8 changed files with 241 additions and 4 deletions
Generated
+133 -3
View File
@@ -189,6 +189,15 @@ dependencies = [
"derive_arbitrary",
]
[[package]]
name = "arc-swap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b"
dependencies = [
"rustversion",
]
[[package]]
name = "argon2"
version = "0.6.0"
@@ -704,6 +713,28 @@ dependencies = [
"tracing",
]
[[package]]
name = "axum-server"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ab4a3ec9ea8a657c72d99a03a824af695bd0fb5ec639ccbd9cd3543b41a5f9"
dependencies = [
"arc-swap",
"bytes",
"fs-err",
"http 1.5.0",
"http-body 1.1.0",
"hyper 1.11.0",
"hyper-util",
"pin-project-lite",
"rustls",
"rustls-pemfile",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tower-service",
]
[[package]]
name = "base16ct"
version = "0.2.0"
@@ -1753,6 +1784,16 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fs-err"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91aa448ca50d7e79433bdf3ee8d99215430d2ec02ade5aefab2a073a1822e8a"
dependencies = [
"autocfg",
"tokio",
]
[[package]]
name = "fs_extra"
version = "1.3.0"
@@ -1992,6 +2033,25 @@ dependencies = [
"tracing",
]
[[package]]
name = "h2"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http 1.5.0",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "half"
version = "2.7.1"
@@ -2191,7 +2251,7 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"h2",
"h2 0.3.27",
"http 0.2.12",
"http-body 0.4.6",
"httparse",
@@ -2215,6 +2275,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-core",
"h2 0.4.19",
"http 1.5.0",
"http-body 1.1.0",
"httparse",
@@ -2957,6 +3018,7 @@ dependencies = [
"async-trait",
"axum",
"axum-extra",
"axum-server",
"bitflags",
"chrono",
"config",
@@ -2967,6 +3029,7 @@ dependencies = [
"log",
"migration",
"parking_lot",
"rcgen 0.13.2",
"rustrtc",
"sea-orm",
"serde",
@@ -3482,6 +3545,19 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rcgen"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2"
dependencies = [
"pem 3.0.6",
"ring",
"rustls-pki-types",
"time",
"yasna 0.5.2",
]
[[package]]
name = "rcgen"
version = "0.14.10"
@@ -3493,7 +3569,7 @@ dependencies = [
"rustls-pki-types",
"time",
"x509-parser",
"yasna",
"yasna 0.6.0",
]
[[package]]
@@ -3703,6 +3779,29 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "rustls"
version = "0.23.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634"
dependencies = [
"aws-lc-rs",
"once_cell",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.15.1"
@@ -3712,6 +3811,18 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
dependencies = [
"aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted 0.9.0",
]
[[package]]
name = "rustrtc"
version = "0.3.138"
@@ -3735,7 +3846,7 @@ dependencies = [
"p256",
"parking_lot",
"rand 0.10.2",
"rcgen",
"rcgen 0.14.10",
"serde",
"serde_json",
"sha1 0.11.0",
@@ -4635,6 +4746,16 @@ dependencies = [
"syn 3.0.3",
]
[[package]]
name = "tokio-rustls"
version = "0.26.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67"
dependencies = [
"rustls",
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.19"
@@ -5446,6 +5567,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yasna"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
dependencies = [
"time",
]
[[package]]
name = "yasna"
version = "0.6.0"
+2
View File
@@ -43,3 +43,5 @@ form_urlencoded = "1.2.2"
time = "0.3.55"
sha2 = "0.11.0"
rustrtc = "0.3.138"
axum-server = { version = "0.7", features = ["tls-rustls"] }
rcgen = "0.13"
+6
View File
@@ -8,6 +8,12 @@ tcp_port = 8080
# Voice/Video port
udp_port = 8080
# Uncomment for native self-signed HTTPS/WSS. Keep both files between restarts.
# [network.tls]
# cert_path = "certs/server.pem"
# key_path = "certs/server-key.pem"
# names = ["localhost", "127.0.0.1"] # include the host clients actually use
[database]
# DSN for database
# SQLite
+15
View File
@@ -51,6 +51,12 @@ tcp_port = 8080
# WebRTC ICE/Media UDP multiplexing port
udp_port = 8080
# Optional native HTTPS (omit this section to keep plain HTTP behind a proxy).
# [network.tls]
# cert_path = "certs/server.pem"
# key_path = "certs/server-key.pem"
# names = ["localhost", "127.0.0.1"]
[database]
# DSN for database
# SQLite
@@ -103,6 +109,15 @@ pub struct NetworkConfig {
pub hostv6: Option<Ipv6Addr>,
pub tcp_port: u16,
pub udp_port: u16,
#[serde(default)]
pub tls: Option<TlsConfig>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct TlsConfig {
pub cert_path: std::path::PathBuf,
pub key_path: std::path::PathBuf,
pub names: Vec<String>,
}
#[derive(Clone, Deserialize)]
+1
View File
@@ -7,6 +7,7 @@ pub mod metrics;
pub mod middleware;
pub mod permissions;
pub mod server;
mod tls;
pub mod validation;
pub use permissions::{RequireChannelPermission, RequireServerPermission};
+20 -1
View File
@@ -15,12 +15,13 @@ use tower_http::catch_panic::CatchPanicLayer;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
use crate::config::NetworkConfig;
use crate::config::{NetworkConfig, TlsConfig};
use crate::core::AppState;
use crate::routes;
use super::metrics::HttpMetrics;
use super::middleware;
use super::tls;
// ── Erreurs ───────────────────────────────────────────────────────────────────
@@ -66,6 +67,7 @@ pub enum HttpServerError {
/// ```
pub struct HttpServer {
bind_addr: SocketAddr,
tls: Option<TlsConfig>,
app_state: AppState,
metrics: Arc<HttpMetrics>,
shutdown_rx: broadcast::Receiver<()>,
@@ -87,6 +89,7 @@ impl HttpServer {
(
Self {
bind_addr,
tls: network_config.tls.clone(),
app_state,
metrics,
shutdown_rx,
@@ -149,6 +152,22 @@ impl HttpServer {
// Outermost : intercepte les panics et retourne une 500 propre
.layer(CatchPanicLayer::new());
if let Some(tls_config) = &self.tls {
let rustls = tls::load_or_create(tls_config).await?;
let handle = axum_server::Handle::new();
let shutdown = handle.clone();
tokio::spawn(async move {
let _ = self.shutdown_rx.recv().await;
shutdown.graceful_shutdown(Some(std::time::Duration::from_secs(10)));
});
tracing::info!(addr = %self.bind_addr, "HTTPS server listening");
axum_server::bind_rustls(self.bind_addr, rustls)
.handle(handle)
.serve(app.into_make_service())
.await?;
return Ok(());
}
let listener =
TcpListener::bind(self.bind_addr)
.await
+63
View File
@@ -0,0 +1,63 @@
use crate::config::TlsConfig;
use axum_server::tls_rustls::RustlsConfig;
use std::io;
pub async fn load_or_create(config: &TlsConfig) -> io::Result<RustlsConfig> {
if config.names.is_empty() || config.names.iter().any(|name| name.is_empty()) {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "TLS requires at least one non-empty name"));
}
if config.cert_path == config.key_path {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "certificate and key paths must differ"));
}
let cert_exists = config.cert_path.exists();
let key_exists = config.key_path.exists();
if cert_exists != key_exists {
return Err(io::Error::new(io::ErrorKind::InvalidData, "only one TLS file exists; restore the missing file or remove both to regenerate"));
}
if !cert_exists {
let generated = rcgen::generate_simple_self_signed(config.names.clone())
.map_err(io::Error::other)?;
for path in [&config.cert_path, &config.key_path] {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
}
std::fs::write(&config.cert_path, generated.cert.pem())?;
let key = generated.key_pair.serialize_pem();
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
use std::io::Write;
let mut file = std::fs::OpenOptions::new().write(true).create_new(true).mode(0o600).open(&config.key_path)?;
file.write_all(key.as_bytes())?;
}
#[cfg(not(unix))]
std::fs::write(&config.key_path, key)?;
tracing::info!(cert = %config.cert_path.display(), "Generated self-signed TLS certificate");
}
RustlsConfig::from_pem_file(&config.cert_path, &config.key_path).await
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn certificate_is_reused_and_partial_pair_is_rejected() {
let dir = std::env::temp_dir().join(format!("oxspeak-tls-{}", uuid::Uuid::new_v4()));
let config = TlsConfig {
cert_path: dir.join("cert.pem"), key_path: dir.join("key.pem"),
names: vec!["localhost".into()],
};
load_or_create(&config).await.unwrap();
let cert = std::fs::read(&config.cert_path).unwrap();
let key = std::fs::read(&config.key_path).unwrap();
load_or_create(&config).await.unwrap();
assert_eq!(cert, std::fs::read(&config.cert_path).unwrap());
assert_eq!(key, std::fs::read(&config.key_path).unwrap());
std::fs::remove_file(&config.key_path).unwrap();
assert!(load_or_create(&config).await.is_err());
std::fs::remove_dir_all(dir).unwrap();
}
}
+1
View File
@@ -341,6 +341,7 @@ mod tests {
hostv6: None,
tcp_port: 8080,
udp_port: 9000,
tls: None,
}
}