use axum::{Json, Router, routing::post}; use serde::Serialize; use std::process::Command; use tower_http::services::ServeDir; #[derive(Serialize)] struct ApiResponse { status: String, message: String, } fn env_or_exit(key: &str) -> String { std::env::var(key).unwrap_or_else(|_| { eprintln!("Error : env {} missing", key); std::process::exit(1); }) } async fn wake() -> Json { let mac = env_or_exit("MAC_ADDRESS"); let output = Command::new("wakeonlan").arg(&mac).output(); match output { Ok(o) if o.status.success() => Json(ApiResponse { status: "ok".into(), message: "Magic packet send !".into(), }), Ok(o) => Json(ApiResponse { status: "error".into(), message: String::from_utf8_lossy(&o.stderr).to_string(), }), Err(e) => Json(ApiResponse { status: "error".into(), message: e.to_string(), }), } } async fn shutdown() -> Json { let pc_ip = env_or_exit("PC_IP"); let ssh_user = env_or_exit("SSH_USER"); let ssh_key = env_or_exit("SSH_KEY_PATH"); let output = Command::new("ssh") .args([ "-i", &ssh_key, "-o", "StrictHostKeyChecking=accept-new", "-o", "ConnectTimeout=5", &format!("{}@{}", ssh_user, pc_ip), "sudo /sbin/shutdown -h now", ]) .output(); match output { Ok(o) if o.status.success() => Json(ApiResponse { status: "ok".into(), message: "Shotdown...".into(), }), Ok(o) => Json(ApiResponse { status: "error".into(), message: String::from_utf8_lossy(&o.stderr).to_string(), }), Err(e) => Json(ApiResponse { status: "error".into(), message: e.to_string(), }), } } #[tokio::main] async fn main() { dotenvy::dotenv().ok(); env_or_exit("MAC_ADDRESS"); env_or_exit("PC_IP"); env_or_exit("SSH_USER"); env_or_exit("SSH_KEY_PATH"); let port: u16 = std::env::var("PORT") .unwrap_or_else(|_| "5050".into()) .parse() .expect("invalid port"); let app = Router::new() .route("/wake", post(wake)) .route("/shutdown", post(shutdown)) .nest_service("/", ServeDir::new("public")); let addr = format!("127.0.0.1:{}", port); println!("Started on http://{}", addr); let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); axum::serve(listener, app).await.unwrap(); }