Compare commits

...

4 Commits

Author SHA1 Message Date
6442251c2d Migracion de sistema 2026-05-02 04:28:47 -05:00
03fc92b3fc fix: Detectar loop de reinicios en systemd como estado Failed
Un servicio que aparece como 'activating' con NRestarts > 3
se reporta como Failed en lugar de Activating, evitando que
loops de reinicio pasen desapercibidos en el monitor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 09:05:20 -05:00
400f677666 fix: Actualizar app sin crear duplicados usando update_app
Reemplaza el flujo soft_delete + add_app_full por un nuevo método
update_app() que modifica in-place la app existente, preservando
su id original y fecha de registro.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 09:05:17 -05:00
aa41c7dd75 feat: Agregar campo id (UUID v4) a MonitoredApp
Cada app registrada ahora tiene un identificador único UUID v4.
Se agrega la dependencia uuid al proyecto y se asegura que todas
las rutas de creación de MonitoredApp generen un id único.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 09:05:13 -05:00
11 changed files with 710 additions and 27 deletions

13
Cargo.lock generated
View File

@@ -1350,6 +1350,7 @@ dependencies = [
"tokio",
"tokio-stream",
"tower-http",
"uuid",
]
[[package]]
@@ -1711,6 +1712,18 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
dependencies = [
"getrandom 0.3.4",
"js-sys",
"serde_core",
"wasm-bindgen",
]
[[package]]
name = "vcpkg"
version = "0.2.15"

View File

@@ -11,6 +11,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sysinfo = "0.30"
chrono = "0.4"
uuid = { version = "1.0", features = ["v4", "serde"] }
tower-http = { version = "0.5", features = ["cors"] }
futures = "0.3"
tokio-stream = "0.1"

View File

@@ -5,6 +5,7 @@ use axum::{
};
use std::sync::Arc;
use sysinfo::System;
use uuid::Uuid;
use crate::orchestrator::{AppManager, LifecycleManager};
use crate::models::{ServiceConfig, RestartPolicy, AppType};
use super::dto::*;
@@ -158,11 +159,9 @@ pub async fn update_app_handler(
.cloned()
.unwrap_or_else(|| "production".to_string());
// Primero intentar hacer soft delete de la app anterior
let _ = config_manager.soft_delete_app(&app_name, Some("Actualizada - versión anterior".to_string()));
// Luego agregar la nueva configuración
let monitored_app = crate::config::MonitoredApp {
// Actualizar la app existente (sin crear duplicados)
let updated_app = crate::config::MonitoredApp {
id: String::new(), // Se preservará el ID original
name: config.app_name.clone(),
service_name: service_name.clone(),
path: config.working_directory.clone(),
@@ -172,7 +171,7 @@ pub async fn update_app_handler(
mode,
user: config.user.clone(),
service_file_path,
registered_at: chrono::Local::now().to_rfc3339(),
registered_at: String::new(), // Se preservará la fecha original
deleted: false,
deleted_at: None,
deleted_reason: None,
@@ -181,7 +180,7 @@ pub async fn update_app_handler(
created_at: None,
};
match config_manager.add_app_full(monitored_app) {
match config_manager.update_app(&app_name, updated_app) {
Ok(_) => {
logger.info("API", "✅ JSON actualizado");
}

View File

@@ -2,10 +2,15 @@ use serde::{Serialize, Deserialize};
use std::fs::{self, create_dir_all};
use std::path::Path;
use std::sync::{Arc, RwLock, OnceLock};
use uuid::Uuid;
use crate::logger::get_logger;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MonitoredApp {
/// ID único de la aplicación (UUID v4)
#[serde(default = "generate_uuid")]
pub id: String,
/// Nombre de la aplicación
pub name: String,
@@ -70,6 +75,10 @@ pub struct MonitoredApp {
pub created_at: Option<String>,
}
fn generate_uuid() -> String {
Uuid::new_v4().to_string()
}
fn default_mode() -> String {
"production".to_string()
}
@@ -186,14 +195,20 @@ impl ConfigManager {
}
/// Agrega una app con información completa
pub fn add_app_full(&self, app: MonitoredApp) -> Result<(), String> {
pub fn add_app_full(&self, mut app: MonitoredApp) -> Result<(), String> {
let mut config = self.config.write().unwrap();
// Verificar si ya existe
if config.apps.iter().any(|a| a.name == app.name) {
// Verificar si ya existe una app ACTIVA con el mismo nombre
// (las apps eliminadas no cuentan, pueden tener el mismo nombre)
if config.apps.iter().any(|a| a.name == app.name && !a.deleted) {
return Err(format!("La app '{}' ya está siendo monitoreada", app.name));
}
// Asegurar que tenga un UUID único
if app.id.is_empty() {
app.id = Uuid::new_v4().to_string();
}
config.apps.push(app);
// Guardar en disco
@@ -210,6 +225,7 @@ impl ConfigManager {
let registered_at = chrono::Local::now().to_rfc3339();
let app = MonitoredApp {
id: Uuid::new_v4().to_string(),
name,
service_name,
path: String::new(),
@@ -231,6 +247,45 @@ impl ConfigManager {
self.add_app_full(app)
}
/// Actualiza una app existente (sin crear duplicados)
pub fn update_app(&self, name: &str, updated_app: MonitoredApp) -> Result<(), String> {
let mut config = self.config.write().unwrap();
// Buscar la app activa con ese nombre
let app = config.apps.iter_mut().find(|a| a.name == name && !a.deleted);
match app {
Some(app) => {
// Preservar el ID original y la fecha de registro
let original_id = app.id.clone();
let original_registered_at = app.registered_at.clone();
// Actualizar todos los campos
app.name = updated_app.name;
app.service_name = updated_app.service_name;
app.path = updated_app.path;
app.port = updated_app.port;
app.entry_point = updated_app.entry_point;
app.node_bin = updated_app.node_bin;
app.mode = updated_app.mode;
app.user = updated_app.user;
app.service_file_path = updated_app.service_file_path;
app.environment = updated_app.environment;
// Mantener el ID original y fecha de registro
app.id = original_id;
app.registered_at = original_registered_at;
// Guardar en disco
match Self::save_config_to_file(&self.config_path, &config) {
Ok(_) => Ok(()),
Err(e) => Err(format!("Error al guardar configuración: {}", e))
}
}
None => Err(format!("La app '{}' no se encontró", name))
}
}
/// Realiza un soft delete: marca la app como eliminada pero mantiene el registro
pub fn soft_delete_app(&self, name: &str, reason: Option<String>) -> Result<(), String> {
let mut config = self.config.write().unwrap();

View File

@@ -1,6 +1,7 @@
/// Módulo para descubrir servicios systemd existentes
use std::fs;
use std::path::Path;
use uuid::Uuid;
use crate::logger::get_logger;
use crate::config::{get_config_manager, MonitoredApp};
@@ -232,7 +233,8 @@ pub fn sync_discovered_services(services: Vec<DiscoveredService>) {
detect_port_from_name(&service.app_name)
});
// Verificar si ya existe en la configuración
// Verificar si ya existe una app ACTIVA en la configuración
// (get_apps() ya filtra las eliminadas, así que esto está bien)
let existing_apps = config_manager.get_apps();
let already_exists = existing_apps.iter().any(|app| app.name == service.app_name);
@@ -248,6 +250,7 @@ pub fn sync_discovered_services(services: Vec<DiscoveredService>) {
let registered_at = chrono::Local::now().to_rfc3339();
let app = MonitoredApp {
id: Uuid::new_v4().to_string(),
name: service.app_name.clone(),
service_name,
path: service.working_directory.unwrap_or_default(),

View File

@@ -60,6 +60,8 @@ pub fn create_web_router() -> Router {
.route("/clear-logs", post(clear_logs_handler))
.route("/health", get(health_handler))
.route("/api-docs", get(api_docs_handler))
.route("/terms", get(terms_handler))
.route("/privacy", get(privacy_handler))
.route("/install.sh", get(install_script_handler))
// Archivos estáticos embebidos
.route("/static/icon/logo_telco128.png", get(logo_telco_handler))
@@ -138,6 +140,14 @@ async fn health_handler() -> Html<String> {
Html(template.to_string())
}
async fn terms_handler() -> Html<&'static str> {
Html(include_str!("../web/terms.html"))
}
async fn privacy_handler() -> Html<&'static str> {
Html(include_str!("../web/privacy.html"))
}
async fn install_script_handler() -> ([(String, String); 2], String) {
let script = include_str!("../install-remote.sh");
(

View File

@@ -5,6 +5,7 @@ use crate::logger::get_logger;
use crate::config::{get_config_manager, MonitoredApp};
use dashmap::DashMap;
use std::sync::Arc;
use uuid::Uuid;
pub struct AppManager {
apps: Arc<DashMap<String, ServiceConfig>>,
@@ -79,6 +80,7 @@ impl AppManager {
.unwrap_or_else(|| "production".to_string());
let monitored_app = MonitoredApp {
id: Uuid::new_v4().to_string(),
name: config.app_name.clone(),
service_name: config.service_name(),
path: config.working_directory.clone(),

View File

@@ -148,12 +148,44 @@ impl SystemCtl {
match output {
Ok(out) => {
let status_str = String::from_utf8_lossy(&out.stdout).trim().to_string();
ServiceStatus::from_str(&status_str)
let status = ServiceStatus::from_str(&status_str);
// Si está "activating", verificar si está en un loop de reinicios
if status == ServiceStatus::Activating {
if Self::is_in_restart_loop(service_name) {
return ServiceStatus::Failed;
}
}
status
}
Err(_) => ServiceStatus::Unknown,
}
}
/// Verifica si un servicio está en un loop de reinicios (restart counter > 3)
fn is_in_restart_loop(service_name: &str) -> bool {
let output = Command::new("systemctl")
.arg("show")
.arg(service_name)
.arg("--property=NRestarts")
.output();
match output {
Ok(out) => {
let output_str = String::from_utf8_lossy(&out.stdout);
// Formato: NRestarts=48
if let Some(count_str) = output_str.trim().strip_prefix("NRestarts=") {
if let Ok(count) = count_str.parse::<u32>() {
return count > 3;
}
}
false
}
Err(_) => false,
}
}
pub fn is_service_exists(service_name: &str) -> bool {
let output = Command::new("systemctl")
.arg("list-unit-files")

View File

@@ -238,11 +238,12 @@
<!-- Tab Content: App Logs -->
<div
id="content-app-logs"
class="flex-1 bg-[#0a0f16] overflow-y-auto overflow-x-auto p-4 font-mono text-sm tab-content"
class="flex-1 bg-[#0a0f16] overflow-y-auto overflow-x-hidden p-4 font-mono text-sm tab-content"
>
<div
id="log-container"
class="space-y-1 break-words overflow-wrap-anywhere"
class="space-y-1"
style="overflow-x: auto; min-width: 0;"
>
<!-- Welcome Message -->
<div class="text-[#9dabb9] opacity-50">
@@ -259,11 +260,12 @@
<!-- Tab Content: System Errors -->
<div
id="content-system-errors"
class="hidden flex-1 bg-[#0a0f16] overflow-y-auto overflow-x-auto p-4 font-mono text-sm tab-content"
class="hidden flex-1 bg-[#0a0f16] overflow-y-auto overflow-x-hidden p-4 font-mono text-sm tab-content"
>
<div
id="system-errors-container"
class="space-y-1 break-words overflow-wrap-anywhere"
class="space-y-1"
style="overflow-x: auto; min-width: 0;"
>
<div class="text-[#9dabb9] opacity-50">
<span class="text-yellow-400"></span> Cargando logs
@@ -446,11 +448,9 @@
}
}
logEntry.innerHTML = `
<span class="text-[#9dabb9] opacity-50">[${timestamp}]</span>
<span class="${color}">${icon}</span>
<span class="${color}">${escapeHtml(message)}</span>
`;
logEntry.style.whiteSpace = "pre";
logEntry.style.overflowX = "auto";
logEntry.innerHTML = `<span class="text-[#9dabb9] opacity-50">[${timestamp}]</span> <span class="${color}">${icon}</span> <span class="${color}">${escapeHtml(message)}</span>`;
logContainer.appendChild(logEntry);
@@ -506,12 +506,13 @@
}
function clearLogViewer() {
if (currentTab === "app-logs") {
const logContainer = document.getElementById("log-container");
logContainer.innerHTML = `
<div class="text-[#9dabb9]">
<span class="text-blue-400"></span> Log viewer cleared
</div>
`;
logContainer.innerHTML = `<div class="text-[#9dabb9]"><span class="text-blue-400"></span> Log viewer cleared</div>`;
} else if (currentTab === "system-errors") {
const errContainer = document.getElementById("system-errors-container");
errContainer.innerHTML = `<div class="text-[#9dabb9]"><span class="text-blue-400"></span> Historial de errores limpiado</div>`;
}
}
// Tab switching

299
web/privacy.html Normal file
View File

@@ -0,0 +1,299 @@
<!doctype html>
<html class="dark" lang="es" dir="ltr">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Política de Privacidad - SIAX Monitor</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
rel="stylesheet"
/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#137fec",
"background-light": "#f6f7f8",
"background-dark": "#101922",
},
fontFamily: {
display: ["Inter", "sans-serif"],
},
borderRadius: {
DEFAULT: "0.25rem",
lg: "0.5rem",
xl: "0.75rem",
full: "9999px",
},
},
},
};
</script>
<style>
body {
font-family: "Inter", sans-serif;
}
.material-symbols-outlined {
font-variation-settings:
"FILL" 0,
"wght" 400,
"GRAD" 0,
"opsz" 24;
}
</style>
</head>
<body class="bg-background-dark font-display text-white min-h-screen flex flex-col">
<!-- Header -->
<header class="sticky top-0 z-50 w-full border-b border-solid border-[#283039] bg-background-dark/80 backdrop-blur-md px-4 md:px-10 py-3">
<div class="max-w-[1400px] mx-auto flex items-center justify-between">
<div class="flex items-center gap-4 text-white">
<div class="size-8 bg-primary rounded-lg flex items-center justify-center overflow-hidden">
<img src="/static/icon/logo.png" alt="Logo" class="w-full h-full object-cover" />
</div>
<h2 class="text-white text-lg font-bold leading-tight tracking-[-0.015em]">SIAX Monitor</h2>
</div>
<div class="flex items-center gap-6">
<nav class="hidden md:flex items-center gap-6">
<a class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors" href="/">Panel</a>
<a class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors" href="/logs">Registros</a>
<a class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors" href="/terms">Términos</a>
<a class="text-primary text-sm font-medium border-b-2 border-primary pb-1" href="/privacy">Privacidad</a>
</nav>
</div>
</div>
</header>
<!-- Content -->
<main class="flex-1 max-w-[860px] mx-auto w-full px-6 py-12">
<!-- Title -->
<div class="mb-10">
<div class="flex items-center gap-3 mb-3">
<span class="material-symbols-outlined text-primary text-3xl">shield</span>
<h1 class="text-3xl font-bold text-white">Política de Privacidad</h1>
</div>
<p class="text-[#9dabb9] text-sm">
Última actualización: <span class="text-white">21 de febrero de 2026</span>
</p>
<div class="mt-4 p-4 bg-green-500/10 border border-green-500/20 rounded-lg flex items-start gap-3">
<span class="material-symbols-outlined text-green-400 text-[20px] mt-0.5">verified_user</span>
<p class="text-[#9dabb9] text-sm">
<strong class="text-white">SIAX Monitor no recopila ni transmite datos personales a servidores externos.</strong>
Todo el procesamiento ocurre localmente en tu infraestructura.
</p>
</div>
</div>
<!-- Sections -->
<div class="space-y-8">
<!-- 1 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">01</span>
Responsable del Tratamiento
</h2>
<div class="space-y-2 text-sm text-[#9dabb9]">
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-primary text-[18px]">person</span>
<span><strong class="text-white">Responsable:</strong> Pablinux / Xsystem</span>
</div>
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-primary text-[18px]">location_on</span>
<span><strong class="text-white">País:</strong> Ecuador</span>
</div>
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-primary text-[18px]">mail</span>
<span>
<a href="mailto:pablinux@hotmail.es" class="text-primary hover:underline">pablinux@hotmail.es</a>
&nbsp;·&nbsp;
<a href="mailto:admin@telcotronics.com" class="text-primary hover:underline">admin@telcotronics.com</a>
</span>
</div>
</div>
</section>
<!-- 2 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">02</span>
Datos que Maneja el Software
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed mb-4">
SIAX Monitor almacena localmente la siguiente información de configuración:
</p>
<div class="grid gap-3">
<div class="flex items-start gap-3 p-3 bg-[#0a0f16] rounded-lg">
<span class="material-symbols-outlined text-blue-400 text-[18px] mt-0.5">settings</span>
<div>
<p class="text-white text-sm font-medium">Configuración de aplicaciones</p>
<p class="text-[#9dabb9] text-xs mt-0.5">Nombres, rutas, puertos y parámetros de inicio de las apps registradas.</p>
</div>
</div>
<div class="flex items-start gap-3 p-3 bg-[#0a0f16] rounded-lg">
<span class="material-symbols-outlined text-yellow-400 text-[18px] mt-0.5">terminal</span>
<div>
<p class="text-white text-sm font-medium">Logs del sistema</p>
<p class="text-[#9dabb9] text-xs mt-0.5">Registros de actividad de los servicios gestionados, almacenados localmente.</p>
</div>
</div>
<div class="flex items-start gap-3 p-3 bg-[#0a0f16] rounded-lg">
<span class="material-symbols-outlined text-purple-400 text-[18px] mt-0.5">key</span>
<div>
<p class="text-white text-sm font-medium">Variables de entorno</p>
<p class="text-[#9dabb9] text-xs mt-0.5">Variables de configuración de las aplicaciones, guardadas en archivos locales.</p>
</div>
</div>
</div>
</section>
<!-- 3 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">03</span>
Datos que NO se Recopilan
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed mb-3">
SIAX Monitor <strong class="text-white">no recopila, almacena ni transmite</strong>:
</p>
<ul class="space-y-2 text-sm text-[#9dabb9]">
<li class="flex items-center gap-2">
<span class="material-symbols-outlined text-green-400 text-[16px]">check</span>
Datos personales de los usuarios de las aplicaciones gestionadas.
</li>
<li class="flex items-center gap-2">
<span class="material-symbols-outlined text-green-400 text-[16px]">check</span>
Información de telemetría o uso del software.
</li>
<li class="flex items-center gap-2">
<span class="material-symbols-outlined text-green-400 text-[16px]">check</span>
Cookies de seguimiento o identificadores de sesión persistentes.
</li>
<li class="flex items-center gap-2">
<span class="material-symbols-outlined text-green-400 text-[16px]">check</span>
Direcciones IP, datos de geolocalización o información del dispositivo.
</li>
<li class="flex items-center gap-2">
<span class="material-symbols-outlined text-green-400 text-[16px]">check</span>
Credenciales, contraseñas ni tokens de autenticación externos.
</li>
</ul>
</section>
<!-- 4 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">04</span>
Almacenamiento y Seguridad
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed">
Toda la información gestionada por SIAX Monitor reside exclusivamente en el servidor
donde el software está instalado. La seguridad de dicha información depende de las
medidas de protección del sistema operativo y la red del usuario.
Recomendamos restringir el acceso a la interfaz web mediante firewall y autenticación
a nivel de red.
</p>
</section>
<!-- 5 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">05</span>
Terceros
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed">
SIAX Monitor no comparte, vende ni cede información a terceros. La interfaz web carga
recursos externos únicamente para estilos y fuentes (Tailwind CSS CDN, Google Fonts),
los cuales no reciben datos de configuración ni de logs.
</p>
</section>
<!-- 6 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">06</span>
Derechos del Usuario
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed mb-3">
De conformidad con la legislación ecuatoriana aplicable, el usuario tiene derecho a:
</p>
<ul class="space-y-2 text-sm text-[#9dabb9]">
<li class="flex items-center gap-2">
<span class="material-symbols-outlined text-primary text-[16px]">arrow_forward</span>
Acceder a los datos de configuración almacenados localmente.
</li>
<li class="flex items-center gap-2">
<span class="material-symbols-outlined text-primary text-[16px]">arrow_forward</span>
Rectificar o eliminar cualquier dato desde la propia interfaz del software.
</li>
<li class="flex items-center gap-2">
<span class="material-symbols-outlined text-primary text-[16px]">arrow_forward</span>
Solicitar información sobre el tratamiento de datos contactando al responsable.
</li>
</ul>
</section>
<!-- 7 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">07</span>
Cambios en esta Política
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed">
Xsystem puede actualizar esta política de privacidad en cualquier momento. La fecha de
última actualización se indica al inicio del documento. Se notificará al usuario sobre
cambios relevantes a través de la interfaz del software o por correo electrónico.
</p>
</section>
<!-- 8 - Contact -->
<section class="border border-primary/30 rounded-xl p-6 bg-primary/5">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="material-symbols-outlined text-primary text-[20px]">mail</span>
Contacto
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed mb-4">
Para ejercer tus derechos o realizar consultas sobre esta política:
</p>
<div class="space-y-2">
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-primary text-[18px]">person</span>
<span class="text-sm text-[#9dabb9]">Desarrollador:
<a href="mailto:pablinux@hotmail.es" class="text-primary hover:underline">pablinux@hotmail.es</a>
</span>
</div>
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-primary text-[18px]">business</span>
<span class="text-sm text-[#9dabb9]">Empresa:
<a href="mailto:admin@telcotronics.com" class="text-primary hover:underline">admin@telcotronics.com</a>
</span>
</div>
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-primary text-[18px]">location_on</span>
<span class="text-sm text-[#9dabb9]">Ecuador</span>
</div>
</div>
</section>
</div>
<!-- Footer nav -->
<div class="mt-10 pt-6 border-t border-[#283039] flex items-center justify-between text-sm text-[#9dabb9]">
<span>&copy; 2026 Xsystem / Pablinux. Todos los derechos reservados.</span>
<a href="/terms" class="text-primary hover:underline flex items-center gap-1">
<span class="material-symbols-outlined text-[16px]">gavel</span>
Términos y Condiciones
</a>
</div>
</main>
</body>
</html>

268
web/terms.html Normal file
View File

@@ -0,0 +1,268 @@
<!doctype html>
<html class="dark" lang="es" dir="ltr">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Términos y Condiciones - SIAX Monitor</title>
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
rel="stylesheet"
/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
primary: "#137fec",
"background-light": "#f6f7f8",
"background-dark": "#101922",
},
fontFamily: {
display: ["Inter", "sans-serif"],
},
borderRadius: {
DEFAULT: "0.25rem",
lg: "0.5rem",
xl: "0.75rem",
full: "9999px",
},
},
},
};
</script>
<style>
body {
font-family: "Inter", sans-serif;
}
.material-symbols-outlined {
font-variation-settings:
"FILL" 0,
"wght" 400,
"GRAD" 0,
"opsz" 24;
}
</style>
</head>
<body class="bg-background-dark font-display text-white min-h-screen flex flex-col">
<!-- Header -->
<header class="sticky top-0 z-50 w-full border-b border-solid border-[#283039] bg-background-dark/80 backdrop-blur-md px-4 md:px-10 py-3">
<div class="max-w-[1400px] mx-auto flex items-center justify-between">
<div class="flex items-center gap-4 text-white">
<div class="size-8 bg-primary rounded-lg flex items-center justify-center overflow-hidden">
<img src="/static/icon/logo.png" alt="Logo" class="w-full h-full object-cover" />
</div>
<h2 class="text-white text-lg font-bold leading-tight tracking-[-0.015em]">SIAX Monitor</h2>
</div>
<div class="flex items-center gap-6">
<nav class="hidden md:flex items-center gap-6">
<a class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors" href="/">Panel</a>
<a class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors" href="/logs">Registros</a>
<a class="text-primary text-sm font-medium border-b-2 border-primary pb-1" href="/terms">Términos</a>
<a class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors" href="/privacy">Privacidad</a>
</nav>
</div>
</div>
</header>
<!-- Content -->
<main class="flex-1 max-w-[860px] mx-auto w-full px-6 py-12">
<!-- Title -->
<div class="mb-10">
<div class="flex items-center gap-3 mb-3">
<span class="material-symbols-outlined text-primary text-3xl">gavel</span>
<h1 class="text-3xl font-bold text-white">Términos y Condiciones</h1>
</div>
<p class="text-[#9dabb9] text-sm">
Última actualización: <span class="text-white">21 de febrero de 2026</span>
</p>
<div class="mt-4 p-4 bg-primary/10 border border-primary/20 rounded-lg">
<p class="text-[#9dabb9] text-sm">
Al utilizar <strong class="text-white">SIAX Monitor</strong>, aceptas los presentes términos en su totalidad.
Si no estás de acuerdo con alguna parte, te pedimos que no uses el software.
</p>
</div>
</div>
<!-- Sections -->
<div class="space-y-8">
<!-- 1 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">01</span>
Descripción del Software
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed">
<strong class="text-white">SIAX Monitor</strong> es una herramienta de monitoreo y gestión de aplicaciones
desarrollada por <strong class="text-white">Pablinux</strong> para <strong class="text-white">Xsystem</strong>,
con sede en Ecuador. Permite registrar, iniciar, detener y supervisar servicios del sistema operativo
a través de una interfaz web local. El software opera exclusivamente dentro de la infraestructura
del usuario y no transmite datos a servidores externos.
</p>
</section>
<!-- 2 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">02</span>
Licencia de Uso
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed mb-3">
Se otorga una licencia personal, no exclusiva, no transferible y revocable para usar SIAX Monitor
en los entornos autorizados por Xsystem. Queda expresamente prohibido:
</p>
<ul class="space-y-2 text-sm text-[#9dabb9]">
<li class="flex items-start gap-2">
<span class="material-symbols-outlined text-red-400 text-[16px] mt-0.5">block</span>
Sublicenciar, vender o redistribuir el software sin autorización escrita.
</li>
<li class="flex items-start gap-2">
<span class="material-symbols-outlined text-red-400 text-[16px] mt-0.5">block</span>
Modificar o crear trabajos derivados sin consentimiento del autor.
</li>
<li class="flex items-start gap-2">
<span class="material-symbols-outlined text-red-400 text-[16px] mt-0.5">block</span>
Utilizar el software para fines ilegales o no autorizados.
</li>
<li class="flex items-start gap-2">
<span class="material-symbols-outlined text-red-400 text-[16px] mt-0.5">block</span>
Realizar ingeniería inversa del código fuente.
</li>
</ul>
</section>
<!-- 3 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">03</span>
Responsabilidades del Usuario
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed mb-3">
El usuario es responsable de:
</p>
<ul class="space-y-2 text-sm text-[#9dabb9]">
<li class="flex items-start gap-2">
<span class="material-symbols-outlined text-green-400 text-[16px] mt-0.5">check_circle</span>
Mantener la seguridad del servidor donde se ejecuta SIAX Monitor.
</li>
<li class="flex items-start gap-2">
<span class="material-symbols-outlined text-green-400 text-[16px] mt-0.5">check_circle</span>
Gestionar correctamente los permisos de acceso a la interfaz web.
</li>
<li class="flex items-start gap-2">
<span class="material-symbols-outlined text-green-400 text-[16px] mt-0.5">check_circle</span>
Realizar copias de seguridad de su configuración y datos.
</li>
<li class="flex items-start gap-2">
<span class="material-symbols-outlined text-green-400 text-[16px] mt-0.5">check_circle</span>
Usar el software conforme a las leyes vigentes en Ecuador y las aplicables en su jurisdicción.
</li>
</ul>
</section>
<!-- 4 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">04</span>
Limitación de Responsabilidad
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed">
SIAX Monitor se proporciona <strong class="text-white">"tal cual"</strong>, sin garantías de ningún tipo,
expresas o implícitas. Xsystem y Pablinux no se hacen responsables por daños directos, indirectos,
incidentales o consecuentes que resulten del uso o la imposibilidad de uso del software, incluyendo
pero no limitándose a: pérdida de datos, interrupciones de servicio, o daños en el sistema operativo
derivados de configuraciones incorrectas.
</p>
</section>
<!-- 5 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">05</span>
Propiedad Intelectual
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed">
Todos los derechos de propiedad intelectual sobre SIAX Monitor, incluyendo su código fuente,
diseño, logotipos y documentación, son propiedad exclusiva de <strong class="text-white">Pablinux / Xsystem</strong>.
Quedan reservados todos los derechos no otorgados expresamente en estos términos.
</p>
</section>
<!-- 6 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">06</span>
Modificaciones
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed">
Xsystem se reserva el derecho de modificar estos términos en cualquier momento. Los cambios
serán comunicados a través de la propia interfaz del software o mediante correo electrónico.
El uso continuado del software tras la publicación de cambios constituye aceptación de los
nuevos términos.
</p>
</section>
<!-- 7 -->
<section class="border border-[#283039] rounded-xl p-6 bg-[#161f2a]">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="text-primary font-mono text-sm bg-primary/10 px-2 py-0.5 rounded">07</span>
Legislación Aplicable
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed">
Estos términos se rigen por las leyes vigentes en la <strong class="text-white">República del Ecuador</strong>.
Cualquier disputa derivada del uso de este software será sometida a la jurisdicción de los
tribunales competentes de Ecuador.
</p>
</section>
<!-- 8 - Contact -->
<section class="border border-primary/30 rounded-xl p-6 bg-primary/5">
<h2 class="text-white text-lg font-semibold mb-3 flex items-center gap-2">
<span class="material-symbols-outlined text-primary text-[20px]">mail</span>
Contacto
</h2>
<p class="text-[#9dabb9] text-sm leading-relaxed mb-4">
Para cualquier consulta relacionada con estos términos, puedes contactarnos a través de:
</p>
<div class="space-y-2">
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-primary text-[18px]">person</span>
<span class="text-sm text-[#9dabb9]">Desarrollador:
<a href="mailto:pablinux@hotmail.es" class="text-primary hover:underline">pablinux@hotmail.es</a>
</span>
</div>
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-primary text-[18px]">business</span>
<span class="text-sm text-[#9dabb9]">Empresa:
<a href="mailto:admin@telcotronics.com" class="text-primary hover:underline">admin@telcotronics.com</a>
</span>
</div>
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-primary text-[18px]">location_on</span>
<span class="text-sm text-[#9dabb9]">Ecuador</span>
</div>
</div>
</section>
</div>
<!-- Footer nav -->
<div class="mt-10 pt-6 border-t border-[#283039] flex items-center justify-between text-sm text-[#9dabb9]">
<span>&copy; 2026 Xsystem / Pablinux. Todos los derechos reservados.</span>
<a href="/privacy" class="text-primary hover:underline flex items-center gap-1">
<span class="material-symbols-outlined text-[16px]">shield</span>
Política de Privacidad
</a>
</div>
</main>
</body>
</html>