Files
xtremflow/server/src/util/log_redactor.rs
T
MichaelandClaude Opus 5.5 3ea6fa0ae3 Serveur réécrit en Rust
Le serveur Dart (bin/) est remplacé par un serveur Rust (server/) :
tokio + axum, reqwest/rustls, SQLite embarqué via rusqlite. Même API,
mêmes routes et mêmes réponses : le front Flutter ne change pas.

La base de production est reprise telle quelle : schéma, migrations et
formats de dates (DateTime.toIso8601String de Dart) sont reproduits à
l'identique, les sessions et hachages existants restent valides.

Comportement conservé : cascade de routage, anti-SSRF, logs masqués,
rate limits, transcodage HLS/turbo.ts avec repli GPU, quotas fournisseur,
enregistrements (reprise, relances, fusion), season passes, EPG, logos de
repli, arrêt gracieux sur SIGTERM. Tests Dart portés (132 tests Rust) ;
la CI exécute fmt, test et clippy -D warnings, l'image Docker compile le
serveur dans un étage rust:1.98.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:29:57 +02:00

56 lines
1.8 KiB
Rust

//! Masque les identifiants IPTV des URL avant qu'elles n'atteignent les logs.
//!
//! Xtream Codes embarque les identifiants à la fois dans la query string
//! (`?username=u&password=p`) et dans le chemin
//! (`/live/<user>/<pass>/<id>.ts`, `/movie/...`, `/series/...`).
use regex::Regex;
use std::sync::LazyLock;
static QUERY_CREDS: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)(username|password)=[^&\s]*").unwrap());
static PATH_CREDS: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i)/(live|movie|series)/[^/\s]+/[^/\s]+/").unwrap());
/// Renvoie [url] avec les identifiants remplacés par `***`.
pub fn redact_url(url: &str) -> String {
let redacted = QUERY_CREDS.replace_all(url, "$1=***");
PATH_CREDS
.replace_all(&redacted, "/$1/***/***/")
.into_owned()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn masque_la_query_string() {
let out = redact_url(
"http://srv:8080/player_api.php?username=john&password=hunter2&action=get_live_streams",
);
assert!(!out.contains("john"));
assert!(!out.contains("hunter2"));
assert!(out.contains("username=***"));
assert!(out.contains("password=***"));
assert!(out.contains("action=get_live_streams"));
}
#[test]
fn masque_les_segments_de_chemin() {
for kind in ["live", "movie", "series"] {
let out = redact_url(&format!("http://srv:8080/{kind}/john/hunter2/12345.ts"));
assert!(!out.contains("john"), "{kind}");
assert!(!out.contains("hunter2"), "{kind}");
assert!(out.contains(&format!("/{kind}/***/***/12345.ts")), "{kind}");
}
}
#[test]
fn laisse_les_url_sans_identifiants() {
let url = "http://srv:8080/images/logo.png";
assert_eq!(redact_url(url), url);
}
}