feat: Auto-detección de hostname del servidor

Reemplaza el hostname hardcodeado 'siax-intel' por detección automática
del hostname real del sistema.

PROBLEMA RESUELTO:
- server_name estaba hardcodeado como 'siax-intel'
- En servidor de producción (server-web) se reportaba con nombre incorrecto
- Imposible distinguir entre múltiples servidores en API central

SOLUCIÓN IMPLEMENTADA:
1. Función get_hostname() con fallbacks:
   - Método 1: Ejecuta comando 'hostname'
   - Método 2: Lee /etc/hostname
   - Método 3: Fallback a 'siax-agent'
2. Cache con OnceLock en interface.rs (una sola lectura)
3. Detección al inicio en main.rs con logging

COMPORTAMIENTO:
- Desarrollo (siax-intel): Detecta 'siax-intel'
- Producción (server-web): Detecta 'server-web'
- Sin /etc/hostname: Usa 'siax-agent' como fallback

BENEFICIOS:
 Cada servidor se identifica correctamente en API central
 No requiere configuración manual
 Funciona en cualquier distribución Linux
 Log al inicio muestra hostname detectado
 Interface web muestra nombre correcto del servidor

Archivos modificados:
- src/main.rs: +28 líneas (función get_hostname)
- src/interface.rs: +35 líneas (función get_hostname con cache)
This commit is contained in:
2026-01-15 03:14:56 -05:00
parent d18cb7c3dd
commit 0db45187cb
2 changed files with 114 additions and 3 deletions

View File

@@ -1,11 +1,46 @@
use axum::{
routing::{get, post},
response::Html,
response::{Html, IntoResponse},
Router,
extract::Form,
http::header,
};
use serde::Deserialize;
use crate::logger::get_logger;
use std::sync::OnceLock;
/// Cache del hostname del sistema (se calcula una sola vez)
static HOSTNAME: OnceLock<String> = OnceLock::new();
/// Obtiene el hostname del sistema (cached)
fn get_hostname() -> &'static str {
HOSTNAME.get_or_init(|| {
use std::process::Command;
// Intentar con comando hostname
if let Ok(output) = Command::new("hostname").output() {
if output.status.success() {
if let Ok(hostname) = String::from_utf8(output.stdout) {
let hostname = hostname.trim();
if !hostname.is_empty() {
return hostname.to_string();
}
}
}
}
// Fallback: leer /etc/hostname
if let Ok(hostname) = std::fs::read_to_string("/etc/hostname") {
let hostname = hostname.trim();
if !hostname.is_empty() {
return hostname.to_string();
}
}
// Último fallback
"siax-agent".to_string()
})
}
#[derive(Deserialize)]
struct ProcessForm {
@@ -22,12 +57,19 @@ pub fn create_web_router() -> Router {
.route("/add-process", post(add_process_handler))
.route("/logs", get(logs_handler))
.route("/clear-logs", post(clear_logs_handler))
.route("/health", get(health_handler))
.route("/api-docs", get(api_docs_handler))
.route("/install.sh", get(install_script_handler))
// Archivos estáticos embebidos
.route("/static/icon/logo_telco128.png", get(logo_telco_handler))
.route("/static/icon/logo.png", get(logo_handler))
.route("/static/icon/favicon.svg", get(favicon_svg_handler))
.route("/static/icon/favicon.ico", get(favicon_ico_handler))
}
async fn index_handler() -> Html<String> {
let template = include_str!("../web/index.html");
let html = template.replace("{{SERVER_NAME}}", "siax-intel");
let html = template.replace("{{SERVER_NAME}}", get_hostname());
Html(html)
}
@@ -84,3 +126,40 @@ async fn api_docs_handler() -> Html<String> {
let template = include_str!("../web/api-docs.html");
Html(template.to_string())
}
async fn health_handler() -> Html<String> {
let template = include_str!("../web/health.html");
Html(template.to_string())
}
async fn install_script_handler() -> ([(String, String); 2], String) {
let script = include_str!("../install-remote.sh");
(
[
("Content-Type".to_string(), "text/plain; charset=utf-8".to_string()),
("Content-Disposition".to_string(), "inline; filename=\"install.sh\"".to_string()),
],
script.to_string(),
)
}
// Handlers para archivos estáticos embebidos
async fn logo_telco_handler() -> impl IntoResponse {
let image = include_bytes!("../web/static/icon/logo_telco128.png");
([(header::CONTENT_TYPE, "image/png")], image.as_ref())
}
async fn logo_handler() -> impl IntoResponse {
let image = include_bytes!("../web/static/icon/logo.png");
([(header::CONTENT_TYPE, "image/png")], image.as_ref())
}
async fn favicon_svg_handler() -> impl IntoResponse {
let image = include_bytes!("../web/static/icon/favicon.svg");
([(header::CONTENT_TYPE, "image/svg+xml")], image.as_ref())
}
async fn favicon_ico_handler() -> impl IntoResponse {
let image = include_bytes!("../web/static/icon/favicon.ico");
([(header::CONTENT_TYPE, "image/x-icon")], image.as_ref())
}

View File

@@ -29,7 +29,10 @@ async fn main() {
let apps = config_manager.get_apps();
println!("📋 Apps a monitorear: {:?}", apps);
let server_name = "siax-intel".to_string();
// Detectar hostname del sistema automáticamente
let server_name = get_hostname();
logger.info("Sistema", &format!("Servidor detectado: {}", server_name));
let api_key = "ak_VVeNzGxK2mCq8s7YpFtHjL3b9dR4TuZ6".to_string();
let cloud_url = "https://api.siax-system.net/api/apps_servcs/apps".to_string();
@@ -54,6 +57,7 @@ async fn main() {
let web_api_handle = tokio::spawn(async move {
// Router para la API REST
let api_router = Router::new()
.route("/api/health", get(api::health_handler))
.route("/api/apps", get(api::list_apps_handler).post(api::register_app_handler))
.route("/api/apps/:name", delete(api::unregister_app_handler))
.route("/api/apps/:name/status", get(api::get_app_status_handler))
@@ -93,3 +97,31 @@ async fn main() {
// Esperamos a ambos
let _ = tokio::join!(monitor_handle, web_api_handle);
}
/// Obtiene el hostname del sistema
fn get_hostname() -> String {
use std::process::Command;
// Intentar obtener hostname con el comando 'hostname'
if let Ok(output) = Command::new("hostname").output() {
if output.status.success() {
if let Ok(hostname) = String::from_utf8(output.stdout) {
let hostname = hostname.trim();
if !hostname.is_empty() {
return hostname.to_string();
}
}
}
}
// Fallback: leer /etc/hostname
if let Ok(hostname) = std::fs::read_to_string("/etc/hostname") {
let hostname = hostname.trim();
if !hostname.is_empty() {
return hostname.to_string();
}
}
// Último fallback: nombre genérico
"siax-agent".to_string()
}