401 lines
11 KiB
Rust
401 lines
11 KiB
Rust
use axum::{
|
|
body::Bytes,
|
|
extract::{Form, State},
|
|
response::{IntoResponse, Redirect},
|
|
};
|
|
use serde::Deserialize;
|
|
use tower_sessions::Session;
|
|
use form_urlencoded;
|
|
|
|
use crate::routes::{flash, get_and_clear_flash, render_html, AppState, AuthUser};
|
|
|
|
pub async fn settings_get(
|
|
_auth: AuthUser,
|
|
session: Session,
|
|
State(state): State<AppState>,
|
|
) -> impl IntoResponse {
|
|
let flash_messages = get_and_clear_flash(&session).await;
|
|
let (config, is_default_pw) = {
|
|
let cm = state.config_manager.lock().await;
|
|
let pw_default =
|
|
bcrypt::verify("admin", &cm.config.admin.password_hash).unwrap_or(false);
|
|
(cm.config.clone(), pw_default)
|
|
};
|
|
|
|
let smtp_password_masked = if config.smtp.password.is_empty() {
|
|
"".to_string()
|
|
} else {
|
|
"********".to_string()
|
|
};
|
|
|
|
let mut ctx = tera::Context::new();
|
|
ctx.insert("flash_messages", &flash_messages);
|
|
ctx.insert("is_authenticated", &true);
|
|
ctx.insert("active_page", "settings");
|
|
ctx.insert("config", &config);
|
|
ctx.insert("smtp", &config.smtp);
|
|
ctx.insert("smtp_password_masked", &smtp_password_masked);
|
|
ctx.insert("default_pw", &is_default_pw);
|
|
|
|
render_html(&state.tera, "settings.html", ctx)
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct ThresholdsForm {
|
|
pub cpu_percent: u32,
|
|
pub ram_percent: u32,
|
|
pub disk_percent: u32,
|
|
}
|
|
|
|
pub async fn update_thresholds(
|
|
_auth: AuthUser,
|
|
session: Session,
|
|
State(state): State<AppState>,
|
|
Form(form): Form<ThresholdsForm>,
|
|
) -> impl IntoResponse {
|
|
if !(1..=100).contains(&form.cpu_percent)
|
|
|| !(1..=100).contains(&form.ram_percent)
|
|
|| !(1..=100).contains(&form.disk_percent)
|
|
{
|
|
flash(
|
|
&session,
|
|
"danger",
|
|
"Les seuils doivent être entre 1 et 100.",
|
|
)
|
|
.await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
{
|
|
let mut cm = state.config_manager.lock().await;
|
|
cm.config.thresholds.cpu_percent = form.cpu_percent as f64;
|
|
cm.config.thresholds.ram_percent = form.ram_percent as f64;
|
|
cm.config.thresholds.disk_percent = form.disk_percent as f64;
|
|
cm.save();
|
|
}
|
|
flash(&session, "success", "Seuils mis à jour.").await;
|
|
Redirect::to("/settings")
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct MonitoringForm {
|
|
pub check_interval_minutes: u64,
|
|
pub alert_cooldown_minutes: u64,
|
|
}
|
|
|
|
pub async fn update_monitoring(
|
|
_auth: AuthUser,
|
|
session: Session,
|
|
State(state): State<AppState>,
|
|
Form(form): Form<MonitoringForm>,
|
|
) -> impl IntoResponse {
|
|
if form.check_interval_minutes < 1 {
|
|
flash(
|
|
&session,
|
|
"danger",
|
|
"L'intervalle doit être d'au moins 1 minute.",
|
|
)
|
|
.await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
if form.alert_cooldown_minutes < 1 {
|
|
flash(
|
|
&session,
|
|
"danger",
|
|
"Le cooldown doit être d'au moins 1 minute.",
|
|
)
|
|
.await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
{
|
|
let mut cm = state.config_manager.lock().await;
|
|
cm.config.check_interval_minutes = form.check_interval_minutes;
|
|
cm.config.alert_cooldown_minutes = form.alert_cooldown_minutes;
|
|
cm.save();
|
|
}
|
|
flash(
|
|
&session,
|
|
"success",
|
|
"Paramètres de monitoring mis à jour.",
|
|
)
|
|
.await;
|
|
Redirect::to("/settings")
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct SmtpForm {
|
|
pub smtp_server: String,
|
|
pub smtp_port: u16,
|
|
pub smtp_tls: Option<String>,
|
|
pub smtp_username: String,
|
|
pub smtp_password: Option<String>,
|
|
pub smtp_from: String,
|
|
pub smtp_to: String,
|
|
}
|
|
|
|
pub async fn update_smtp(
|
|
_auth: AuthUser,
|
|
session: Session,
|
|
State(state): State<AppState>,
|
|
Form(form): Form<SmtpForm>,
|
|
) -> impl IntoResponse {
|
|
let to_emails: Vec<String> = form
|
|
.smtp_to
|
|
.split(',')
|
|
.map(|s| s.trim().to_string())
|
|
.filter(|s| !s.is_empty())
|
|
.collect();
|
|
{
|
|
let mut cm = state.config_manager.lock().await;
|
|
let old_password = cm.config.smtp.password.clone();
|
|
cm.config.smtp.server = form.smtp_server.trim().to_string();
|
|
cm.config.smtp.port = form.smtp_port;
|
|
cm.config.smtp.use_tls = form.smtp_tls.is_some();
|
|
cm.config.smtp.username = form.smtp_username.trim().to_string();
|
|
cm.config.smtp.from_email = form.smtp_from.trim().to_string();
|
|
cm.config.smtp.to_emails = to_emails;
|
|
cm.config.smtp.password = match form.smtp_password {
|
|
Some(pw) if !pw.is_empty() => pw,
|
|
_ => old_password,
|
|
};
|
|
cm.save();
|
|
}
|
|
flash(&session, "success", "Configuration SMTP mise à jour.").await;
|
|
Redirect::to("/settings")
|
|
}
|
|
|
|
pub async fn test_smtp(
|
|
_auth: AuthUser,
|
|
session: Session,
|
|
State(state): State<AppState>,
|
|
) -> impl IntoResponse {
|
|
let smtp = {
|
|
let cm = state.config_manager.lock().await;
|
|
cm.config.smtp.clone()
|
|
};
|
|
let (ok, msg) = state.alerter.send_test(&smtp).await;
|
|
if ok {
|
|
flash(&session, "success", &format!("Test réussi : {}", msg)).await;
|
|
} else {
|
|
flash(&session, "danger", &format!("Test échoué : {}", msg)).await;
|
|
}
|
|
Redirect::to("/settings")
|
|
}
|
|
|
|
pub async fn update_processes(
|
|
_auth: AuthUser,
|
|
session: Session,
|
|
State(state): State<AppState>,
|
|
body: Bytes,
|
|
) -> impl IntoResponse {
|
|
use crate::config::ProcessConfig;
|
|
|
|
let mut names: Vec<String> = Vec::new();
|
|
let mut patterns: Vec<String> = Vec::new();
|
|
let mut mem_thresholds: Vec<String> = Vec::new();
|
|
let mut enableds: Vec<String> = Vec::new();
|
|
let mut alert_downs: Vec<String> = Vec::new();
|
|
|
|
for (key, value) in form_urlencoded::parse(body.as_ref()) {
|
|
match key.as_ref() {
|
|
"proc_name[]" => names.push(value.into_owned()),
|
|
"proc_pattern[]" => patterns.push(value.into_owned()),
|
|
"proc_mem_threshold[]" => mem_thresholds.push(value.into_owned()),
|
|
"proc_enabled[]" => enableds.push(value.into_owned()),
|
|
"proc_alert_down[]" => alert_downs.push(value.into_owned()),
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
let mut processes = Vec::new();
|
|
for (i, name) in names.iter().enumerate() {
|
|
let name = name.trim().to_string();
|
|
if name.is_empty() {
|
|
continue;
|
|
}
|
|
processes.push(ProcessConfig {
|
|
name,
|
|
pattern: patterns
|
|
.get(i)
|
|
.map(|s| s.trim().to_lowercase())
|
|
.unwrap_or_default(),
|
|
memory_threshold_mb: mem_thresholds
|
|
.get(i)
|
|
.and_then(|s| s.parse().ok())
|
|
.unwrap_or(0.0),
|
|
enabled: enableds.contains(&i.to_string()),
|
|
alert_on_down: alert_downs.contains(&i.to_string()),
|
|
});
|
|
}
|
|
{
|
|
let mut cm = state.config_manager.lock().await;
|
|
cm.config.processes = processes;
|
|
cm.save();
|
|
}
|
|
flash(&session, "success", "Processus surveillés mis à jour.").await;
|
|
Redirect::to("/settings")
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct PasswordForm {
|
|
pub current_password: String,
|
|
pub new_password: String,
|
|
pub confirm_password: String,
|
|
}
|
|
|
|
pub async fn update_password(
|
|
_auth: AuthUser,
|
|
session: Session,
|
|
State(state): State<AppState>,
|
|
Form(form): Form<PasswordForm>,
|
|
) -> impl IntoResponse {
|
|
let hash = {
|
|
let cm = state.config_manager.lock().await;
|
|
cm.config.admin.password_hash.clone()
|
|
};
|
|
if !bcrypt::verify(&form.current_password, &hash).unwrap_or(false) {
|
|
flash(&session, "danger", "Mot de passe actuel incorrect.").await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
if form.new_password.len() < 8 {
|
|
flash(
|
|
&session,
|
|
"danger",
|
|
"Le nouveau mot de passe doit faire au moins 8 caractères.",
|
|
)
|
|
.await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
if form.new_password != form.confirm_password {
|
|
flash(
|
|
&session,
|
|
"danger",
|
|
"Les mots de passe ne correspondent pas.",
|
|
)
|
|
.await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
let new_hash = match bcrypt::hash(&form.new_password, bcrypt::DEFAULT_COST) {
|
|
Ok(h) => h,
|
|
Err(_) => {
|
|
flash(
|
|
&session,
|
|
"danger",
|
|
"Erreur lors du hachage du mot de passe.",
|
|
)
|
|
.await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
};
|
|
{
|
|
let mut cm = state.config_manager.lock().await;
|
|
cm.config.admin.password_hash = new_hash;
|
|
cm.save();
|
|
}
|
|
flash(&session, "success", "Mot de passe mis à jour.").await;
|
|
Redirect::to("/settings")
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct PortForm {
|
|
pub port: u16,
|
|
}
|
|
|
|
pub async fn update_port(
|
|
_auth: AuthUser,
|
|
session: Session,
|
|
State(state): State<AppState>,
|
|
Form(form): Form<PortForm>,
|
|
) -> impl IntoResponse {
|
|
if !(1024..=65535).contains(&form.port) {
|
|
flash(
|
|
&session,
|
|
"danger",
|
|
"Le port doit être entre 1024 et 65535.",
|
|
)
|
|
.await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
{
|
|
let mut cm = state.config_manager.lock().await;
|
|
cm.config.port = form.port;
|
|
cm.save();
|
|
}
|
|
flash(
|
|
&session,
|
|
"warning",
|
|
&format!(
|
|
"Port mis à jour à {}. Redémarrez l'application pour appliquer.",
|
|
form.port
|
|
),
|
|
)
|
|
.await;
|
|
Redirect::to("/settings")
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct AmadeaLogPathForm {
|
|
pub amadea_log_path: String,
|
|
}
|
|
|
|
pub async fn update_amadea_log_path(
|
|
_auth: AuthUser,
|
|
session: Session,
|
|
State(state): State<AppState>,
|
|
Form(form): Form<AmadeaLogPathForm>,
|
|
) -> impl IntoResponse {
|
|
let path = form.amadea_log_path.trim().to_string();
|
|
if path.is_empty() {
|
|
flash(&session, "danger", "Le chemin ne peut pas être vide.").await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
{
|
|
let mut cm = state.config_manager.lock().await;
|
|
cm.config.amadea_log_path = path;
|
|
cm.save();
|
|
}
|
|
flash(&session, "success", "Chemin des logs Amadea mis à jour.").await;
|
|
Redirect::to("/settings")
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct UserThresholdsForm {
|
|
pub active_minutes: u64,
|
|
pub inactive_minutes: u64,
|
|
pub pause_threshold_minutes: u64,
|
|
}
|
|
|
|
pub async fn update_user_thresholds(
|
|
_auth: AuthUser,
|
|
session: Session,
|
|
State(state): State<AppState>,
|
|
Form(form): Form<UserThresholdsForm>,
|
|
) -> impl IntoResponse {
|
|
if form.active_minutes < 1 || form.inactive_minutes < 1 || form.pause_threshold_minutes < 1 {
|
|
flash(
|
|
&session,
|
|
"danger",
|
|
"Les seuils doivent être d'au moins 1 minute.",
|
|
)
|
|
.await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
if form.active_minutes >= form.inactive_minutes {
|
|
flash(
|
|
&session,
|
|
"danger",
|
|
"Le seuil 'actif' doit être inférieur au seuil 'inactif'.",
|
|
)
|
|
.await;
|
|
return Redirect::to("/settings");
|
|
}
|
|
{
|
|
let mut cm = state.config_manager.lock().await;
|
|
cm.config.user_status_thresholds.active_minutes = form.active_minutes;
|
|
cm.config.user_status_thresholds.inactive_minutes = form.inactive_minutes;
|
|
cm.config.user_status_thresholds.pause_threshold_minutes = form.pause_threshold_minutes;
|
|
cm.save();
|
|
}
|
|
flash(&session, "success", "Seuils utilisateurs mis à jour.").await;
|
|
Redirect::to("/settings")
|
|
}
|