276 lines
9.3 KiB
Rust
276 lines
9.3 KiB
Rust
mod config;
|
|
mod monitor;
|
|
mod alerter;
|
|
mod user_monitor;
|
|
mod routes;
|
|
|
|
use std::sync::Arc;
|
|
use tokio::sync::Mutex as AsyncMutex;
|
|
use tower_sessions::{MemoryStore, SessionManagerLayer};
|
|
use tower_http::services::ServeDir;
|
|
use axum::{
|
|
Router,
|
|
routing::{get, post},
|
|
http::HeaderValue,
|
|
};
|
|
|
|
use config::ConfigManager;
|
|
use monitor::SystemMonitor;
|
|
use alerter::Alerter;
|
|
use user_monitor::UserMonitor;
|
|
use routes::{
|
|
AppState,
|
|
auth::{login_get, login_post, logout},
|
|
dashboard::{dashboard, api_metrics, toggle_monitoring},
|
|
settings::{
|
|
settings_get, update_thresholds, update_monitoring, update_smtp,
|
|
test_smtp, update_processes, update_password, update_port,
|
|
update_amadea_log_path, update_user_thresholds,
|
|
},
|
|
alerts::{alerts_get, clear_alerts},
|
|
users::{users_get, api_users, api_users_weekly, api_users_day},
|
|
};
|
|
|
|
pub async fn run_server() {
|
|
// Init config
|
|
let config_manager = Arc::new(AsyncMutex::new(ConfigManager::new()));
|
|
|
|
// Init services
|
|
let alerter = Arc::new(Alerter);
|
|
let monitor = Arc::new(SystemMonitor::new(
|
|
config_manager.clone(),
|
|
alerter.clone(),
|
|
));
|
|
let user_monitor = Arc::new(UserMonitor::new(config_manager.clone()));
|
|
|
|
// Démarrer monitoring et user monitor
|
|
monitor.clone().start().await;
|
|
{
|
|
let m = monitor.clone();
|
|
let _ = m.collect().await;
|
|
}
|
|
user_monitor.clone().start().await;
|
|
|
|
// App state
|
|
let state = AppState::new(
|
|
config_manager.clone(),
|
|
monitor,
|
|
alerter,
|
|
user_monitor,
|
|
);
|
|
|
|
// Sessions
|
|
let session_store = MemoryStore::default();
|
|
let session_layer = SessionManagerLayer::new(session_store)
|
|
.with_secure(false)
|
|
.with_name("supervision_session");
|
|
|
|
let app = Router::new()
|
|
.route("/login", get(login_get))
|
|
.route("/login", post(login_post))
|
|
.route("/logout", get(logout))
|
|
.route("/", get(dashboard))
|
|
.route("/api/metrics", get(api_metrics))
|
|
.route("/api/monitoring/toggle", post(toggle_monitoring))
|
|
.route("/settings", get(settings_get))
|
|
.route("/settings/thresholds", post(update_thresholds))
|
|
.route("/settings/monitoring", post(update_monitoring))
|
|
.route("/settings/smtp", post(update_smtp))
|
|
.route("/settings/smtp/test", post(test_smtp))
|
|
.route("/settings/processes", post(update_processes))
|
|
.route("/settings/password", post(update_password))
|
|
.route("/settings/port", post(update_port))
|
|
.route("/settings/amadea-log-path", post(update_amadea_log_path))
|
|
.route("/settings/user-thresholds", post(update_user_thresholds))
|
|
.route("/alerts", get(alerts_get))
|
|
.route("/alerts/clear", post(clear_alerts))
|
|
.route("/users", get(users_get))
|
|
.route("/api/users", get(api_users))
|
|
.route("/api/users/activity/weekly", get(api_users_weekly))
|
|
.route("/api/users/day/:date", get(api_users_day))
|
|
.nest_service("/static", ServeDir::new("static"))
|
|
.layer(session_layer)
|
|
.with_state(state.clone())
|
|
.layer(axum::middleware::map_response(|mut response: axum::response::Response| async move {
|
|
let headers = response.headers_mut();
|
|
headers.insert("X-Content-Type-Options", HeaderValue::from_static("nosniff"));
|
|
headers.insert("X-Frame-Options", HeaderValue::from_static("DENY"));
|
|
headers.insert("X-XSS-Protection", HeaderValue::from_static("1; mode=block"));
|
|
response
|
|
}));
|
|
|
|
let port = {
|
|
let cm = state.config_manager.lock().await;
|
|
cm.config.port
|
|
};
|
|
|
|
let addr = format!("0.0.0.0:{}", port);
|
|
tracing::info!("Supervision démarré sur http://localhost:{}", port);
|
|
|
|
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
|
axum::serve(listener, app).await.unwrap();
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
tracing_subscriber::fmt::init();
|
|
|
|
let args: Vec<String> = std::env::args().collect();
|
|
|
|
#[cfg(windows)]
|
|
{
|
|
if args.get(1).map(|s| s.as_str()) == Some("install") {
|
|
service::install_service();
|
|
return;
|
|
}
|
|
if args.get(1).map(|s| s.as_str()) == Some("uninstall") {
|
|
service::uninstall_service();
|
|
return;
|
|
}
|
|
if service::is_running_as_service() {
|
|
service::run_service();
|
|
return;
|
|
}
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
let _ = args;
|
|
|
|
run_server().await;
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
mod service {
|
|
use std::ffi::OsString;
|
|
use std::time::Duration;
|
|
use windows_service::{
|
|
define_windows_service,
|
|
service::{
|
|
ServiceAccess, ServiceControl, ServiceControlAccept, ServiceErrorControl,
|
|
ServiceExitCode, ServiceInfo, ServiceStartType, ServiceState, ServiceStatus,
|
|
ServiceType,
|
|
},
|
|
service_control_handler::{self, ServiceControlHandlerResult},
|
|
service_dispatcher,
|
|
service_manager::{ServiceManager, ServiceManagerAccess},
|
|
};
|
|
|
|
const SERVICE_NAME: &str = "Supervision";
|
|
const SERVICE_DISPLAY: &str = "Supervision - Monitoring Système";
|
|
const SERVICE_DESCRIPTION: &str =
|
|
"Surveille CPU, RAM, disques et processus. Interface web sur http://localhost:5000";
|
|
|
|
pub fn install_service() {
|
|
let manager = ServiceManager::local_computer(
|
|
None::<&str>,
|
|
ServiceManagerAccess::CREATE_SERVICE,
|
|
)
|
|
.expect("Impossible d'ouvrir le Service Manager (lancer en administrateur)");
|
|
|
|
let exe_path = std::env::current_exe().unwrap();
|
|
|
|
let service_info = ServiceInfo {
|
|
name: OsString::from(SERVICE_NAME),
|
|
display_name: OsString::from(SERVICE_DISPLAY),
|
|
service_type: ServiceType::OWN_PROCESS,
|
|
start_type: ServiceStartType::AutoStart,
|
|
error_control: ServiceErrorControl::Normal,
|
|
executable_path: exe_path,
|
|
launch_arguments: vec![],
|
|
dependencies: vec![],
|
|
account_name: None,
|
|
account_password: None,
|
|
};
|
|
|
|
let service = manager
|
|
.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)
|
|
.expect("Impossible de créer le service");
|
|
|
|
service
|
|
.set_description(SERVICE_DESCRIPTION)
|
|
.expect("Impossible de définir la description");
|
|
|
|
println!("Service '{}' installé avec succès.", SERVICE_NAME);
|
|
println!("Démarrer avec: sc start {}", SERVICE_NAME);
|
|
}
|
|
|
|
pub fn uninstall_service() {
|
|
let manager = ServiceManager::local_computer(
|
|
None::<&str>,
|
|
ServiceManagerAccess::CONNECT,
|
|
)
|
|
.expect("Impossible d'ouvrir le Service Manager");
|
|
|
|
let service = manager
|
|
.open_service(SERVICE_NAME, ServiceAccess::DELETE)
|
|
.expect("Service introuvable");
|
|
|
|
service.delete().expect("Impossible de supprimer le service");
|
|
println!("Service '{}' désinstallé.", SERVICE_NAME);
|
|
}
|
|
|
|
pub fn is_running_as_service() -> bool {
|
|
// Heuristique : variable SESSIONNAME absente = pas de session interactive
|
|
std::env::var("SESSIONNAME").is_err()
|
|
}
|
|
|
|
define_windows_service!(ffi_service_main, service_main);
|
|
|
|
fn service_main(_arguments: Vec<OsString>) {
|
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
rt.block_on(async {
|
|
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>();
|
|
|
|
let mut shutdown_tx = Some(shutdown_tx);
|
|
let status_handle = service_control_handler::register(
|
|
SERVICE_NAME,
|
|
move |control| match control {
|
|
ServiceControl::Stop => {
|
|
if let Some(tx) = shutdown_tx.take() {
|
|
let _ = tx.send(());
|
|
}
|
|
ServiceControlHandlerResult::NoError
|
|
}
|
|
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
|
|
_ => ServiceControlHandlerResult::NotImplemented,
|
|
},
|
|
)
|
|
.unwrap();
|
|
|
|
status_handle
|
|
.set_service_status(ServiceStatus {
|
|
service_type: ServiceType::OWN_PROCESS,
|
|
current_state: ServiceState::Running,
|
|
controls_accepted: ServiceControlAccept::STOP,
|
|
exit_code: ServiceExitCode::Win32(0),
|
|
checkpoint: 0,
|
|
wait_hint: Duration::default(),
|
|
process_id: None,
|
|
})
|
|
.unwrap();
|
|
|
|
tokio::select! {
|
|
_ = crate::run_server() => {},
|
|
_ = shutdown_rx => {},
|
|
}
|
|
|
|
status_handle
|
|
.set_service_status(ServiceStatus {
|
|
service_type: ServiceType::OWN_PROCESS,
|
|
current_state: ServiceState::Stopped,
|
|
controls_accepted: ServiceControlAccept::empty(),
|
|
exit_code: ServiceExitCode::Win32(0),
|
|
checkpoint: 0,
|
|
wait_hint: Duration::default(),
|
|
process_id: None,
|
|
})
|
|
.unwrap();
|
|
});
|
|
}
|
|
|
|
pub fn run_service() {
|
|
service_dispatcher::start(SERVICE_NAME, ffi_service_main)
|
|
.expect("Impossible de démarrer le service dispatcher");
|
|
}
|
|
}
|