feat: Implementación completa Fase 4 - Sistema de monitoreo con API REST y WebSocket
✨ Nuevas funcionalidades: - API REST unificada en puerto 8080 (eliminado CORS) - WebSocket para logs en tiempo real desde journalctl - Integración completa con systemd para gestión de servicios - Escaneo automático de procesos Node.js y Python - Rate limiting (1 operación/segundo por app) - Interface web moderna con Tailwind CSS (tema oscuro) - Documentación API estilo Swagger completamente en español 🎨 Interface Web (todas las páginas en español): - Dashboard con estadísticas en tiempo real - Visor de escaneo de procesos con filtros - Formulario de registro de aplicaciones con variables de entorno - Visor de logs en tiempo real con WebSocket y sidebar - Página de selección de apps detectadas - Documentación completa de API REST 🏗️ Arquitectura: - Módulo models: ServiceConfig, ManagedApp, AppStatus - Módulo systemd: wrapper de systemctl, generador de .service, parser - Módulo orchestrator: AppManager, LifecycleManager con validaciones - Módulo api: handlers REST, WebSocket manager, DTOs - Servidor unificado en puerto 8080 (Web + API + WS) 🔧 Mejoras técnicas: - Eliminación de CORS mediante servidor unificado - Separación clara frontend/backend con carga dinámica - Thread-safe con Arc<DashMap> para estado compartido - Reconciliación de estados: sysinfo vs systemd - Validaciones de paths, usuarios y configuraciones - Manejo robusto de errores con thiserror 📝 Documentación: - README.md actualizado con arquitectura completa - EJEMPLOS.md con casos de uso detallados - ESTADO_PROYECTO.md con progreso de Fase 4 - API docs interactiva en /api-docs - Script de despliegue mejorado con health checks 🚀 Producción: - Deployment script con validaciones - Health checks y rollback capability - Configuración de sudoers para systemctl - Hardening de seguridad en servicios systemd
This commit is contained in:
565
web/api-docs.html
Normal file
565
web/api-docs.html
Normal file
@@ -0,0 +1,565 @@
|
||||
<!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>Documentación API - SIAX Monitor</title>
|
||||
<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=JetBrains+Mono:wght@400;500;600&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"],
|
||||
mono: ["JetBrains Mono", "monospace"],
|
||||
},
|
||||
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;
|
||||
}
|
||||
.code-block {
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body
|
||||
class="bg-background-light dark:bg-background-dark font-display text-white min-h-screen flex flex-col"
|
||||
>
|
||||
<!-- Sticky Top Navigation -->
|
||||
<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 whitespace-nowrap"
|
||||
>
|
||||
<div class="flex items-center gap-8">
|
||||
<div class="flex items-center gap-4 text-white">
|
||||
<div
|
||||
class="size-8 bg-primary rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<span class="material-symbols-outlined text-white"
|
||||
>monitoring</span
|
||||
>
|
||||
</div>
|
||||
<h2
|
||||
class="text-white text-lg font-bold leading-tight tracking-[-0.015em]"
|
||||
>
|
||||
SIAX Monitor
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-1 justify-end gap-6 items-center">
|
||||
<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="/scan"
|
||||
>Escanear</a
|
||||
>
|
||||
<a
|
||||
class="text-[#9dabb9] text-sm font-medium hover:text-white transition-colors"
|
||||
href="/register"
|
||||
>Registrar Nueva</a
|
||||
>
|
||||
<a
|
||||
class="text-primary text-sm font-medium border-b-2 border-primary pb-1"
|
||||
href="/api-docs"
|
||||
>Documentación API</a
|
||||
>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="flex-1 flex max-w-[1400px] mx-auto w-full">
|
||||
<!-- Sidebar - Table of Contents -->
|
||||
<aside class="w-64 border-r border-[#283039] bg-[#161f2a] p-6 space-y-6 overflow-y-auto">
|
||||
<div>
|
||||
<h3 class="text-white font-bold text-sm mb-3">CONTENIDO</h3>
|
||||
<nav class="space-y-2">
|
||||
<a href="#intro" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Introducción</a>
|
||||
<a href="#auth" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Autenticación</a>
|
||||
<a href="#apps" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Gestión de Apps</a>
|
||||
<a href="#scan" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Escaneo</a>
|
||||
<a href="#lifecycle" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Ciclo de Vida</a>
|
||||
<a href="#websocket" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">WebSocket</a>
|
||||
<a href="#errors" class="block text-[#9dabb9] text-sm hover:text-primary transition-colors">Códigos de Error</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="pt-6 border-t border-[#283039]">
|
||||
<h3 class="text-white font-bold text-sm mb-3">INFO</h3>
|
||||
<div class="space-y-2 text-xs">
|
||||
<div>
|
||||
<span class="text-[#9dabb9]">Versión:</span>
|
||||
<span class="text-white font-mono">v1.0.0</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-[#9dabb9]">Base URL:</span>
|
||||
<span class="text-white font-mono">localhost:8080</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-[#9dabb9]">Protocolo:</span>
|
||||
<span class="text-white font-mono">HTTP/WS</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content - API Documentation -->
|
||||
<main class="flex-1 p-8 overflow-y-auto">
|
||||
<!-- Introduction -->
|
||||
<section id="intro" class="mb-12">
|
||||
<h1 class="text-white text-4xl font-black mb-4">Documentación API REST</h1>
|
||||
<p class="text-[#9dabb9] text-lg mb-6">
|
||||
API para gestión y monitoreo de aplicaciones Node.js y Python con systemd.
|
||||
</p>
|
||||
|
||||
<div class="rounded-xl border border-primary/30 bg-primary/10 p-4 mb-6">
|
||||
<div class="flex items-start gap-3">
|
||||
<span class="material-symbols-outlined text-primary mt-0.5">info</span>
|
||||
<div>
|
||||
<p class="text-white font-semibold mb-1">Endpoint Base</p>
|
||||
<code class="text-primary font-mono text-sm">http://localhost:8080/api</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
|
||||
<span class="material-symbols-outlined text-green-400 mb-2">check_circle</span>
|
||||
<p class="text-white font-semibold text-sm">REST API</p>
|
||||
<p class="text-[#9dabb9] text-xs">JSON responses</p>
|
||||
</div>
|
||||
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
|
||||
<span class="material-symbols-outlined text-blue-400 mb-2">bolt</span>
|
||||
<p class="text-white font-semibold text-sm">WebSocket</p>
|
||||
<p class="text-[#9dabb9] text-xs">Logs en tiempo real</p>
|
||||
</div>
|
||||
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
|
||||
<span class="material-symbols-outlined text-purple-400 mb-2">schedule</span>
|
||||
<p class="text-white font-semibold text-sm">Rate Limiting</p>
|
||||
<p class="text-[#9dabb9] text-xs">1 op/segundo</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Authentication -->
|
||||
<section id="auth" class="mb-12">
|
||||
<h2 class="text-white text-2xl font-bold mb-4 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">lock</span>
|
||||
Autenticación
|
||||
</h2>
|
||||
<p class="text-[#9dabb9] mb-4">
|
||||
Actualmente la API no requiere autenticación ya que está diseñada para acceso local vía VPN.
|
||||
</p>
|
||||
<div class="rounded-xl border border-yellow-500/30 bg-yellow-500/10 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<span class="material-symbols-outlined text-yellow-400">warning</span>
|
||||
<div>
|
||||
<p class="text-yellow-400 font-semibold">Nota de Seguridad</p>
|
||||
<p class="text-[#9dabb9] text-sm">Esta API debe ser accesible solo desde redes privadas o VPN.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Apps Management -->
|
||||
<section id="apps" class="mb-12">
|
||||
<h2 class="text-white text-2xl font-bold mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">apps</span>
|
||||
Gestión de Aplicaciones
|
||||
</h2>
|
||||
|
||||
<!-- List Apps -->
|
||||
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
|
||||
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 rounded bg-green-500/20 text-green-400 text-xs font-bold font-mono">GET</span>
|
||||
<code class="text-white font-mono text-sm">/api/apps</code>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm mt-2">Listar todas las aplicaciones registradas</p>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<p class="text-white font-semibold text-sm mb-2">Respuesta exitosa (200)</p>
|
||||
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto">{
|
||||
"success": true,
|
||||
"data": {
|
||||
"apps": ["app_tareas", "fidelizacion"],
|
||||
"total": 2
|
||||
},
|
||||
"error": null
|
||||
}</pre>
|
||||
</div>
|
||||
<button onclick="tryEndpoint('GET', '/api/apps')" class="flex items-center gap-2 px-4 py-2 bg-primary hover:brightness-110 rounded-lg text-white text-sm font-medium transition-all">
|
||||
<span class="material-symbols-outlined text-sm">play_arrow</span>
|
||||
Probar endpoint
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Register App -->
|
||||
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
|
||||
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono">POST</span>
|
||||
<code class="text-white font-mono text-sm">/api/apps</code>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm mt-2">Registrar una nueva aplicación</p>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<p class="text-white font-semibold text-sm mb-2">Body (JSON)</p>
|
||||
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-blue-400 overflow-x-auto">{
|
||||
"app_name": "mi-app",
|
||||
"script_path": "/opt/apps/mi-app/index.js",
|
||||
"working_directory": "/opt/apps/mi-app",
|
||||
"user": "nodejs",
|
||||
"environment": {
|
||||
"NODE_ENV": "production",
|
||||
"PORT": "3000"
|
||||
},
|
||||
"restart_policy": "always",
|
||||
"app_type": "nodejs",
|
||||
"description": "Mi aplicación Node.js"
|
||||
}</pre>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-white font-semibold text-sm mb-2">Respuesta exitosa (200)</p>
|
||||
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto">{
|
||||
"success": true,
|
||||
"data": {
|
||||
"app_name": "mi-app",
|
||||
"operation": "register",
|
||||
"success": true,
|
||||
"message": "Aplicación registrada exitosamente"
|
||||
},
|
||||
"error": null
|
||||
}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete App -->
|
||||
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
|
||||
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono">DELETE</span>
|
||||
<code class="text-white font-mono text-sm">/api/apps/:name</code>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm mt-2">Eliminar una aplicación registrada</p>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<p class="text-white font-semibold text-sm mb-2">Parámetros</p>
|
||||
<ul class="space-y-2">
|
||||
<li class="flex items-start gap-2">
|
||||
<code class="text-primary font-mono text-sm">name</code>
|
||||
<span class="text-[#9dabb9] text-sm">- Nombre de la aplicación</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Get Status -->
|
||||
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
|
||||
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 rounded bg-green-500/20 text-green-400 text-xs font-bold font-mono">GET</span>
|
||||
<code class="text-white font-mono text-sm">/api/apps/:name/status</code>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm mt-2">Obtener estado de una aplicación</p>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<p class="text-white font-semibold text-sm mb-2">Respuesta exitosa (200)</p>
|
||||
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto">{
|
||||
"success": true,
|
||||
"data": {
|
||||
"name": "mi-app",
|
||||
"status": "Running",
|
||||
"pid": 12345,
|
||||
"cpu_usage": 2.5,
|
||||
"memory_usage": "128.50 MB",
|
||||
"systemd_status": "active",
|
||||
"last_updated": "2026-01-13T12:34:56"
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Scan -->
|
||||
<section id="scan" class="mb-12">
|
||||
<h2 class="text-white text-2xl font-bold mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">search</span>
|
||||
Escaneo de Procesos
|
||||
</h2>
|
||||
|
||||
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
|
||||
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 rounded bg-green-500/20 text-green-400 text-xs font-bold font-mono">GET</span>
|
||||
<code class="text-white font-mono text-sm">/api/scan</code>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm mt-2">Escanear procesos Node.js y Python en ejecución</p>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<p class="text-white font-semibold text-sm mb-2">Respuesta exitosa (200)</p>
|
||||
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-green-400 overflow-x-auto">{
|
||||
"success": true,
|
||||
"data": {
|
||||
"processes": [
|
||||
{
|
||||
"pid": 5769,
|
||||
"name": "node",
|
||||
"user": "1000",
|
||||
"cpu_usage": 2.5,
|
||||
"memory_mb": 112.54,
|
||||
"process_type": "nodejs"
|
||||
}
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
<button onclick="tryEndpoint('GET', '/api/scan')" class="flex items-center gap-2 px-4 py-2 bg-primary hover:brightness-110 rounded-lg text-white text-sm font-medium transition-all">
|
||||
<span class="material-symbols-outlined text-sm">play_arrow</span>
|
||||
Probar endpoint
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Lifecycle -->
|
||||
<section id="lifecycle" class="mb-12">
|
||||
<h2 class="text-white text-2xl font-bold mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">settings_power</span>
|
||||
Ciclo de Vida
|
||||
</h2>
|
||||
|
||||
<!-- Start -->
|
||||
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
|
||||
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono">POST</span>
|
||||
<code class="text-white font-mono text-sm">/api/apps/:name/start</code>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm mt-2">Iniciar una aplicación</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stop -->
|
||||
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
|
||||
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono">POST</span>
|
||||
<code class="text-white font-mono text-sm">/api/apps/:name/stop</code>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm mt-2">Detener una aplicación</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Restart -->
|
||||
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
|
||||
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 text-xs font-bold font-mono">POST</span>
|
||||
<code class="text-white font-mono text-sm">/api/apps/:name/restart</code>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm mt-2">Reiniciar una aplicación</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-yellow-500/30 bg-yellow-500/10 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<span class="material-symbols-outlined text-yellow-400">schedule</span>
|
||||
<div>
|
||||
<p class="text-yellow-400 font-semibold">Rate Limiting</p>
|
||||
<p class="text-[#9dabb9] text-sm">Las operaciones están limitadas a 1 por segundo por aplicación.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- WebSocket -->
|
||||
<section id="websocket" class="mb-12">
|
||||
<h2 class="text-white text-2xl font-bold mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">cable</span>
|
||||
WebSocket (Logs en tiempo real)
|
||||
</h2>
|
||||
|
||||
<div class="mb-8 rounded-xl border border-[#283039] bg-[#161f2a] overflow-hidden">
|
||||
<div class="bg-[#1c2730] px-6 py-4 border-b border-[#283039]">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 rounded bg-purple-500/20 text-purple-400 text-xs font-bold font-mono">WS</span>
|
||||
<code class="text-white font-mono text-sm">ws://localhost:8080/api/apps/:name/logs</code>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm mt-2">Stream de logs en tiempo real desde journalctl</p>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<p class="text-white font-semibold text-sm mb-2">Ejemplo JavaScript</p>
|
||||
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-purple-400 overflow-x-auto">const ws = new WebSocket('ws://localhost:8080/api/apps/mi-app/logs');
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('Conectado a logs');
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const log = JSON.parse(event.data);
|
||||
console.log(log.MESSAGE);
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error('Error:', error);
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('Desconectado');
|
||||
};</pre>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-white font-semibold text-sm mb-2">Límites</p>
|
||||
<ul class="space-y-2">
|
||||
<li class="flex items-start gap-2">
|
||||
<span class="material-symbols-outlined text-primary text-sm">check</span>
|
||||
<span class="text-[#9dabb9] text-sm">Máximo 5 conexiones concurrentes por aplicación</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2">
|
||||
<span class="material-symbols-outlined text-primary text-sm">check</span>
|
||||
<span class="text-[#9dabb9] text-sm">Formato JSON desde systemd journalctl</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Error Codes -->
|
||||
<section id="errors" class="mb-12">
|
||||
<h2 class="text-white text-2xl font-bold mb-6 flex items-center gap-2">
|
||||
<span class="material-symbols-outlined text-primary">error</span>
|
||||
Códigos de Error
|
||||
</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<span class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono">400</span>
|
||||
<p class="text-white font-semibold">Bad Request</p>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm">Datos de entrada inválidos o faltantes</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<span class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono">404</span>
|
||||
<p class="text-white font-semibold">Not Found</p>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm">Aplicación no encontrada</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<span class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono">429</span>
|
||||
<p class="text-white font-semibold">Too Many Requests</p>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm">Rate limit excedido (1 operación/segundo)</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-[#283039] bg-[#161f2a] p-4">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<span class="px-2 py-1 rounded bg-red-500/20 text-red-400 text-xs font-bold font-mono">500</span>
|
||||
<p class="text-white font-semibold">Internal Server Error</p>
|
||||
</div>
|
||||
<p class="text-[#9dabb9] text-sm">Error interno del servidor</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<p class="text-white font-semibold text-sm mb-2">Estructura de error</p>
|
||||
<pre class="code-block bg-[#0a0f16] p-4 rounded-lg text-sm text-red-400 overflow-x-auto">{
|
||||
"success": false,
|
||||
"data": null,
|
||||
"error": "Descripción del error"
|
||||
}</pre>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function tryEndpoint(method, path) {
|
||||
const resultDiv = event.target.parentElement.querySelector('.result') ||
|
||||
event.target.parentElement.appendChild(document.createElement('div'));
|
||||
resultDiv.className = 'result mt-4 code-block bg-[#0a0f16] p-4 rounded-lg text-sm overflow-x-auto';
|
||||
resultDiv.textContent = 'Ejecutando...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:8080${path}`, {
|
||||
method: method
|
||||
});
|
||||
const data = await response.json();
|
||||
resultDiv.innerHTML = `<pre class="text-green-400">${JSON.stringify(data, null, 2)}</pre>`;
|
||||
} catch (error) {
|
||||
resultDiv.innerHTML = `<pre class="text-red-400">Error: ${error.message}</pre>`;
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth scroll for anchor links
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
if (target) {
|
||||
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user