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", "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]] [[package]]
name = "argon2" name = "argon2"
version = "0.6.0" version = "0.6.0"
@@ -704,6 +713,28 @@ dependencies = [
"tracing", "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]] [[package]]
name = "base16ct" name = "base16ct"
version = "0.2.0" version = "0.2.0"
@@ -1753,6 +1784,16 @@ dependencies = [
"percent-encoding", "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]] [[package]]
name = "fs_extra" name = "fs_extra"
version = "1.3.0" version = "1.3.0"
@@ -1992,6 +2033,25 @@ dependencies = [
"tracing", "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]] [[package]]
name = "half" name = "half"
version = "2.7.1" version = "2.7.1"
@@ -2191,7 +2251,7 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2 0.3.27",
"http 0.2.12", "http 0.2.12",
"http-body 0.4.6", "http-body 0.4.6",
"httparse", "httparse",
@@ -2215,6 +2275,7 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"h2 0.4.19",
"http 1.5.0", "http 1.5.0",
"http-body 1.1.0", "http-body 1.1.0",
"httparse", "httparse",
@@ -2957,6 +3018,7 @@ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
"axum-extra", "axum-extra",
"axum-server",
"bitflags", "bitflags",
"chrono", "chrono",
"config", "config",
@@ -2967,6 +3029,7 @@ dependencies = [
"log", "log",
"migration", "migration",
"parking_lot", "parking_lot",
"rcgen 0.13.2",
"rustrtc", "rustrtc",
"sea-orm", "sea-orm",
"serde", "serde",
@@ -3482,6 +3545,19 @@ dependencies = [
"crossbeam-utils", "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]] [[package]]
name = "rcgen" name = "rcgen"
version = "0.14.10" version = "0.14.10"
@@ -3493,7 +3569,7 @@ dependencies = [
"rustls-pki-types", "rustls-pki-types",
"time", "time",
"x509-parser", "x509-parser",
"yasna", "yasna 0.6.0",
] ]
[[package]] [[package]]
@@ -3703,6 +3779,29 @@ dependencies = [
"windows-sys 0.61.2", "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]] [[package]]
name = "rustls-pki-types" name = "rustls-pki-types"
version = "1.15.1" version = "1.15.1"
@@ -3712,6 +3811,18 @@ dependencies = [
"zeroize", "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]] [[package]]
name = "rustrtc" name = "rustrtc"
version = "0.3.138" version = "0.3.138"
@@ -3735,7 +3846,7 @@ dependencies = [
"p256", "p256",
"parking_lot", "parking_lot",
"rand 0.10.2", "rand 0.10.2",
"rcgen", "rcgen 0.14.10",
"serde", "serde",
"serde_json", "serde_json",
"sha1 0.11.0", "sha1 0.11.0",
@@ -4635,6 +4746,16 @@ dependencies = [
"syn 3.0.3", "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]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.19" version = "0.1.19"
@@ -5446,6 +5567,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yasna"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
dependencies = [
"time",
]
[[package]] [[package]]
name = "yasna" name = "yasna"
version = "0.6.0" version = "0.6.0"
+2
View File
@@ -43,3 +43,5 @@ form_urlencoded = "1.2.2"
time = "0.3.55" time = "0.3.55"
sha2 = "0.11.0" sha2 = "0.11.0"
rustrtc = "0.3.138" 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 # Voice/Video port
udp_port = 8080 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] [database]
# DSN for database # DSN for database
# SQLite # SQLite
+15
View File
@@ -51,6 +51,12 @@ tcp_port = 8080
# WebRTC ICE/Media UDP multiplexing port # WebRTC ICE/Media UDP multiplexing port
udp_port = 8080 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] [database]
# DSN for database # DSN for database
# SQLite # SQLite
@@ -103,6 +109,15 @@ pub struct NetworkConfig {
pub hostv6: Option<Ipv6Addr>, pub hostv6: Option<Ipv6Addr>,
pub tcp_port: u16, pub tcp_port: u16,
pub udp_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)] #[derive(Clone, Deserialize)]
+1
View File
@@ -7,6 +7,7 @@ pub mod metrics;
pub mod middleware; pub mod middleware;
pub mod permissions; pub mod permissions;
pub mod server; pub mod server;
mod tls;
pub mod validation; pub mod validation;
pub use permissions::{RequireChannelPermission, RequireServerPermission}; 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::cors::CorsLayer;
use tower_http::trace::TraceLayer; use tower_http::trace::TraceLayer;
use crate::config::NetworkConfig; use crate::config::{NetworkConfig, TlsConfig};
use crate::core::AppState; use crate::core::AppState;
use crate::routes; use crate::routes;
use super::metrics::HttpMetrics; use super::metrics::HttpMetrics;
use super::middleware; use super::middleware;
use super::tls;
// ── Erreurs ─────────────────────────────────────────────────────────────────── // ── Erreurs ───────────────────────────────────────────────────────────────────
@@ -66,6 +67,7 @@ pub enum HttpServerError {
/// ``` /// ```
pub struct HttpServer { pub struct HttpServer {
bind_addr: SocketAddr, bind_addr: SocketAddr,
tls: Option<TlsConfig>,
app_state: AppState, app_state: AppState,
metrics: Arc<HttpMetrics>, metrics: Arc<HttpMetrics>,
shutdown_rx: broadcast::Receiver<()>, shutdown_rx: broadcast::Receiver<()>,
@@ -87,6 +89,7 @@ impl HttpServer {
( (
Self { Self {
bind_addr, bind_addr,
tls: network_config.tls.clone(),
app_state, app_state,
metrics, metrics,
shutdown_rx, shutdown_rx,
@@ -149,6 +152,22 @@ impl HttpServer {
// Outermost : intercepte les panics et retourne une 500 propre // Outermost : intercepte les panics et retourne une 500 propre
.layer(CatchPanicLayer::new()); .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 = let listener =
TcpListener::bind(self.bind_addr) TcpListener::bind(self.bind_addr)
.await .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, hostv6: None,
tcp_port: 8080, tcp_port: 8080,
udp_port: 9000, udp_port: 9000,
tls: None,
} }
} }