diff --git a/README.md b/README.md index dbacd9b..3957623 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,133 @@ -# Soul:23 coming soon page +# Landing Page y Monitor de Servicios "Soul:23" -A responsive landing page built with Bootstrap 4 that displays a countdown and a notification form. +Este repositorio contiene el código para una landing page de "próximamente" junto con un sistema de monitoreo de servicios integrado. La aplicación está construida con Node.js y Express, y es capaz de servir contenido estático y exponer una API con varios endpoints funcionales. -## Local Installation +## Características Principales -```bash -npm install -npm start +* **Landing Page Responsiva**: Una página de "próximamente" con un contador regresivo, construida con Bootstrap 4. +* **API de Frases**: Un endpoint que entrega una frase aleatoria en cada solicitud. +* **Monitor de Salud de Servicios**: Un endpoint avanzado que ejecuta un script de Python para verificar el estado de múltiples sitios y servicios web, categorizados en internos, de empresa y externos. +* **Servidor Flexible**: Configurado para servir archivos estáticos, HTML dinámico y endpoints JSON. +* **Contenerización**: Listo para desplegarse con Docker. + +## Estructura del Proyecto + +``` +/ +├─── data/ +│ ├─── quotes.json +│ └─── sites.json +├─── htmls/ +│ └─── telegram.html +├─── scripts/ +│ └─── health_checker.py +├─── css/ +├─── js/ +├─── img/ +├─── .gitignore +├─── docker-compose.yml +├─── Dockerfile +├─── index.html +├─── package.json +├─── server.js +└─── README.md ``` -The Express server serves all assets from the root and exposes a `/healthchecker` endpoint with the health script as `text/plain`, ready for operators to download with `curl`. +## Instalación y Ejecución Local -## Countdown and Form +Para ejecutar el proyecto en un entorno de desarrollo local, sigue estos pasos: -The time component reads the `data-date` attribute in `#countdown-timer`. Change it to any valid date: +1. **Clona el repositorio**: + ```bash + git clone + cd soul23_placeholder + ``` -```html -
-``` +2. **Instala las dependencias**: + Asegúrate de tener Node.js (v18 o superior) y npm instalados. + ```bash + npm install + ``` -If you prefer to program it with JavaScript, reassign the `countDownDate` variable inside `js/countdown.js` before the interval starts. +3. **Inicia el servidor**: + ```bash + npm start + ``` + El servidor se iniciará en `http://localhost:3001` por defecto. + +## Documentación Detallada de Componentes + +### `server.js` + +Es el núcleo de la aplicación. Configura un servidor Express que gestiona todas las rutas y la lógica principal. + +#### Endpoints de la API + +* **`GET /day-quote`** + * **Descripción**: Devuelve una frase motivacional aleatoria. + * **Lógica**: Lee el arreglo de frases de `data/quotes.json`, selecciona una al azar y la sirve. + * **Respuesta de Ejemplo**: + ```json + { + "phrase": "El universo trabaja mientras tú sigues avanzando." + } + ``` + +* **`GET /healthchecker`** + * **Descripción**: Ejecuta un script de monitoreo en Python (`scripts/health_checker.py`) y devuelve un reporte detallado del estado de los servicios definidos en `data/sites.json`. + * **Lógica**: Utiliza `child_process.exec` de Node.js para invocar el script de Python. Captura la salida JSON del script y la sirve como respuesta. Es ideal para tableros de monitoreo o webhooks. + * **Respuesta de Ejemplo (truncada)**: + ```json + { + "timestamp": "2025-12-18T01:34:53.516Z", + "internos": { + "vps_soul23_status": "🟢 OK (VPS Reachable)", + "coolify_status": 200 + }, + "empresa": { + "vanity_web_status": 200 + }, + "externos": { + "openai_status": "🟡 Advertencia (Partial System Outage)" + }, + "execution_time_seconds": 17.83 + } + ``` + +* **`GET /telegram`** + * **Descripción**: Sirve una página HTML (`htmls/telegram.html`) diseñada para gestionar redirecciones a la aplicación de Telegram, adaptándose a la plataforma del usuario. + +* **`GET /health`** + * **Descripción**: Un endpoint de salud básico que realiza una prueba de ping a una IP predefinida para una verificación rápida de conectividad. + +* **`GET /time-server`** + * **Descripción**: Proporciona la fecha y hora del servidor en múltiples formatos (ISO, Unix, legible). + +### Directorio `data/` + +Este directorio centraliza todos los datos que la aplicación necesita para funcionar. + +* **`quotes.json`**: Un archivo JSON que contiene un único arreglo de strings llamado `phrases`. Cada string es una frase que puede ser servida por el endpoint `/day-quote`. +* **`sites.json`**: El archivo de configuración para el monitor de salud. Contiene tres objetos principales: `internos`, `sitios_empresa` y `externos`. Cada objeto es un diccionario donde la clave es el nombre del servicio y el valor es su URL. + +### Directorio `scripts/` + +Contiene la lógica de negocio más compleja en forma de scripts. + +* **`health_checker.py`**: + * **Lenguaje**: Python 3. + * **Dependencias**: `requests`. + * **Lógica**: + 1. Carga la lista de sitios desde `../data/sites.json`. + 2. Itera sobre cada servicio y realiza diferentes tipos de verificaciones: + * **Verificación simple**: Para la mayoría de los sitios, comprueba si la URL devuelve un código de estado HTTP 200. + * **Endpoints de Salud Específicos**: Para servicios como `vps_soul23` y `formbricks`, realiza peticiones a sus endpoints `/health` y analiza la respuesta JSON para un estado más detallado. + * **APIs de StatusPage**: Para servicios como OpenAI y Cloudflare, consulta su API de `statuspage.io` para obtener el estado oficial del servicio. + 3. Consolida todos los resultados en un único objeto JSON. + 4. Si la variable de entorno `WEBHOOK_URLS` está definida (con una o más URLs separadas por comas), envía el resultado JSON a cada webhook. + 5. Imprime el resultado JSON en la salida estándar para que `server.js` pueda capturarlo. + +### Archivos de Contenerización + +* **`Dockerfile`**: Contiene las instrucciones para construir una imagen de Docker de la aplicación. Utiliza una imagen base de Node.js, copia los archivos del proyecto, instala las dependencias de `npm` y define el comando para iniciar el servidor. +* **`docker-compose.yml`**: Facilita la ejecución de la aplicación en un entorno local de Docker, gestionando la construcción de la imagen y la configuración de red. diff --git a/server.js b/server.js index 73d3e0a..a30a597 100644 --- a/server.js +++ b/server.js @@ -1,44 +1,63 @@ const express = require("express"); const path = require("path"); const fs = require("fs"); - const { exec } = require("child_process"); const app = express(); const port = process.env.PORT || 3001; const rootDir = path.join(__dirname); -// Serve static assets from the project root +// --- Middleware --- +// Sirve los archivos estáticos (HTML, CSS, JS) desde el directorio raíz del proyecto. +// 'index.html' se sirve como el archivo por defecto. app.use(express.static(rootDir, { index: "index.html" })); -// Health checker should always return the raw script with text/plain + +// --- Rutas de la API --- + +/** + * @route GET /healthchecker + * @description Ejecuta un script de Python para un chequeo de salud avanzado. + * Invoca el script `scripts/health_checker.py`, que monitorea múltiples servicios + * y devuelve un reporte detallado en formato JSON. + * @returns {object} Un objeto JSON con el estado de los servicios monitoreados. + * @throws 500 - Si el script de Python falla o su salida no es un JSON válido. + */ app.get("/healthchecker", (req, res) => { const pythonScriptPath = path.join(rootDir, "scripts", "health_checker.py"); exec(`python3 ${pythonScriptPath}`, (error, stdout, stderr) => { if (error) { - console.error(`Error executing health_checker.py: ${stderr}`); - return res.status(500).json({ error: "Failed to execute health checker" }); + console.error(`Error al ejecutar health_checker.py: ${stderr}`); + return res.status(500).json({ error: "No se pudo ejecutar el chequeo de salud" }); } try { const healthData = JSON.parse(stdout); res.status(200).json(healthData); } catch (parseErr) { - console.error(`Error parsing health checker output: ${parseErr}`); - res.status(500).json({ error: "Failed to parse health checker output" }); + console.error(`Error al parsear la salida del chequeo de salud: ${parseErr}`); + res.status(500).json({ error: "La respuesta del chequeo de salud no es un JSON válido" }); } }); }); -// Magic link para redirigir a la app de Telegram según plataforma +/** + * @route GET /telegram + * @description Sirve una página HTML para la redirección a Telegram. + * @returns {file} El archivo `htmls/telegram.html`. + */ app.get("/telegram", (req, res) => { res.sendFile(path.join(rootDir, "htmls", "telegram.html")); }); -// Standard health check endpoint for monitoring with VPS ping +/** + * @route GET /health + * @description Realiza un chequeo de salud simple haciendo ping a una IP. + * @returns {object} Un objeto JSON con el estado de la conectividad. + */ app.get("/health", (req, res) => { - const vpsIp = "31.97.41.188"; - // Ping with count 1 and timeout 1 second + const vpsIp = "31.97.41.188"; // IP a verificar + // Ejecuta un ping con un solo paquete y un timeout de 1 segundo. exec(`ping -c 1 -W 1 ${vpsIp}`, (error, stdout, stderr) => { const isAlive = !error; res.status(200).json({ @@ -48,57 +67,70 @@ app.get("/health", (req, res) => { vps_ping: { target: vpsIp, alive: isAlive, - output: isAlive ? "VPS Reachable" : "VPS Unreachable", + output: isAlive ? "VPS Alcanzable" : "VPS Inalcanzable", }, }, }); }); }); -// Endpoint to get a random phrase +/** + * @route GET /day-quote + * @description Devuelve una frase aleatoria del archivo de citas. + * @returns {object} Un objeto JSON con una única clave "phrase". + * @throws 500 - Si no se puede leer o parsear el archivo `data/quotes.json`. + */ app.get("/day-quote", (req, res) => { fs.readFile(path.join(rootDir, "data", "quotes.json"), "utf8", (err, data) => { if (err) { - console.error("Error reading quotes file:", err); - return res.status(500).json({ error: "Could not read quotes file" }); + console.error("Error al leer el archivo de frases:", err); + return res.status(500).json({ error: "No se pudo leer el archivo de frases" }); } try { const quotes = JSON.parse(data); const phrases = quotes.phrases; if (!phrases || phrases.length === 0) { - return res.status(500).json({ error: "No phrases found" }); + return res.status(500).json({ error: "No se encontraron frases en el archivo" }); } const randomPhrase = phrases[Math.floor(Math.random() * phrases.length)]; res.status(200).json({ phrase: randomPhrase }); } catch (parseErr) { - console.error("Error parsing quotes file:", parseErr); - return res.status(500).json({ error: "Could not parse quotes file" }); + console.error("Error al parsear el archivo de frases:", parseErr); + return res.status(500).json({ error: "No se pudo parsear el archivo de frases" }); } }); }); -// Endpoint to get the current time in multiple formats +/** + * @route GET /time-server + * @description Proporciona la hora actual del servidor en diferentes formatos. + * @returns {object} Un objeto JSON con la hora en formato ISO, Unix y legible. + */ app.get("/time-server", (req, res) => { const now = new Date(); const timezone = "America/Monterrey"; res.status(200).json({ - // Full UTC ISO 8601 string for machines utc_iso: now.toISOString(), - // Unix timestamp in seconds for machines unixtime: Math.floor(now.getTime() / 1000), - // Human-readable local time for debugging datetime_human: now.toLocaleString("en-US", { timeZone: timezone }), - // Timezone identifier timezone: timezone, }); }); -// Fallback to index.html for other routes (optional) +/** + * @route GET * + * @description Ruta "catch-all" que sirve la página principal. + * Útil para Single Page Applications (SPAs) donde el enrutamiento se maneja + * en el lado del cliente. + * @returns {file} El archivo `index.html`. + */ app.get("*", (req, res) => { res.sendFile(path.join(rootDir, "index.html")); }); + +// --- Inicio del Servidor --- app.listen(port, () => { - console.log(`Soul:23 server listening on port ${port}`); + console.log(`Servidor Soul:23 escuchando en el puerto ${port}`); });