Kein Pulse Logo

Kein Pulse

Monitoreo de servicios en tiempo real para northernbytes

App móvil que utiliza una API privada sobre Uptime Kuma para monitorear los servicios de northernbytes desde cualquier lugar

¿Cómo funciona?

Núcleo de monitoreo

La aplicación utiliza Uptime Kuma Beta 2 como base del sistema de monitorización. Esta infraestructura permite la detección de caídas de servicio y el seguimiento de métricas de rendimiento.

Base de datos

A diferencia de las versiones anteriores donde Uptime Kuma generaba su propia base de datos SQL de manera interna, la versión Beta 2 permite conectar directamente con una base de datos externa MySQL, lo que facilita la integración y el acceso a los datos.

Capa de acceso

Se ha desarrollado una API REST que consume los datos directamente de la base de datos de Uptime Kuma. Este enfoque permite la independencia entre la interfaz móvil y el backend de monitoreo, facilitando actualizaciones en ambos sistemas.

Uptime Kuma Beta 2

Características de la versión Beta 2

Uptime Kuma Beta 2 introduce cambios en la arquitectura del sistema de monitoreo. Esta versión implementa modificaciones en la gestión de datos que sirven como base para el desarrollo de Kein Pulse.

Implementación de MySQL

La versión Beta 2 sustituye el sistema de base de datos SQLite por la compatibilidad con MySQL. Este cambio permite el acceso directo a los datos de monitorización desde aplicaciones externas, permitiendo la implementación de la API utilizada por Kein Pulse.

Estructura de API

La reorganización del esquema de datos facilita la creación de interfaces programáticas externas. En el caso de Kein Pulse, se desarrolló una API REST que consulta directamente la base de datos MySQL para obtener información sobre los servicios monitorizados.

Capacidades técnicas

Uptime Kuma admite diversos protocolos de monitorización (HTTP, TCP, DNS, Ping) con intervalos configurables desde 1 segundo. La API de Kein Pulse implementa métodos para acceder a estos datos y transmitirlos a la aplicación móvil de forma eficiente.

Configuración de MySQL

mysql-config.sh
$ cat mysql.conf
DB_TYPE=mysql
DB_HOST=localhost
DB_PORT=3306
DB_USER=keinpulse
DB_PASSWORD=********
DB_NAME=uptimekuma
Para Kein Pulse, solo fue necesario configurar los parámetros de conexión a la instancia MySQL preexistente en el servidor.
mysql-terminal
$ mysql -u keinpulse -p uptimekuma -e "SHOW TABLES;"
Tables_in_uptimekuma
user
tag
monitor
monitor_tag
monitor_tls_info
heartbeat
stat_daily
stat_hourly
stat_minutely
Existen más tablas en la base de datos de Uptime Kuma, pero Kein Pulse solo utiliza estas tablas ya que la intención de la aplicación es únicamente consultar datos, no crear nuevos monitores ni modificar la configuración.
api-integration.sh
$ echo "Integración con la API"
Al usar MySQL, fue posible desarrollar una API independiente que accediera directamente a estas tablas, permitiendo una integración más sencilla con otras herramientas y realizar consultas complejas para la aplicación móvil.
api-request.sh
$ curl -X GET http://api.keinpulse.local/v1/monitors/1 | jq
"name": "Ejemplo API",
"type": "http",
"active": true,
"interval": 60,
"description": null,
"url": "https://ejemplo.com",
"hostname": null,
"port": null,
"id": 1,
"user_id": 1,
"created_date": "2025-01-15T10:30:00",
"tags": [
"id": 1,
"name": "Demo",
"color": "#3B82F6",
"value": ""
],
"tls_info":
"valid": true,
"subject_cn": "ejemplo.com",
"valid_to": "Jun 15 12:00:00 2025 GMT",
"days_remaining": 45

Creación de la API

requirements.txt
$ cat requirements.txt
fastapi
uvicorn[standard]
mysql-connector-python
python-dotenv
passlib[bcrypt]
python-jose[cryptography]
python-multipart
La API está desarrollada con FastAPI para máximo rendimiento y Python como lenguaje principal. La conexión directa a MySQL permite acceder a los datos de monitorización sin necesidad de una capa intermedia.
router.py
$ python -c "from app.api.router import api_router; print(api_router.routes)"
Autenticación:
POST /api/v1/auth/token - Login For Access Token
GET /api/v1/auth/me - Read Users Me
Monitores:
GET /api/v1/monitors/ - Read Monitors
GET /api/v1/monitors/monitor_id - Read Monitor
GET /api/v1/monitors/monitor_id/heartbeats/ - Read Monitor Heartbeats
GET /api/v1/monitors/monitor_id/stats/ - Read Monitor Stats
Etiquetas:
GET /api/v1/tags/ - List Tags
Sistema:
GET / - Read Root
GET /health - Health Check
La API está organizada en routers independientes por dominio de negocio. Cada endpoint utiliza inyección de dependencias para gestionar la conexión a la base de datos y la autenticación del usuario mediante tokens JWT.
monitor_router.py
$ cat app/api/endpoints/monitors/router.py
import mysql.connector
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from ...core.database import get_db
from ...core.security import get_current_user_id
from . import schemas, service
router = APIRouter()
@router.get("/", response_model=List[schemas.MonitorRead])
def read_monitors(
db: mysql.connector.MySQLConnection = Depends(get_db),
current_user_id: int = Depends(get_current_user_id),
tag_ids: Optional[List[int]] = Query(None, alias="tag_id")
):
"""Retrieve monitors for the authenticated user, optionally filtered."""
if not db:
raise HTTPException(status_code=503, detail="DB unavailable")
monitors = service.get_monitors_by_user_id(
db=db, user_id=current_user_id, tag_ids=tag_ids
)
return monitors
Los routers implementan los endpoints para cada recurso usando decoradores de FastAPI. Cada función gestiona un endpoint específico y utiliza inyección de dependencias para acceder a la BD y verificar la autenticación.
monitor_service.py
$ cat app/api/endpoints/monitors/service.py
def get_monitors_by_user_id(
db: mysql.connector.MySQLConnection,
user_id: int,
tag_ids: Optional[List[int]] = None
) -> List[schemas.MonitorRead]:
"""Fetches monitors from the database for a specific user."""
cursor = None
try:
cursor = db.cursor(dictionary=True)
select_fields = "m.`id`, m.`name`, m.`type`, ..."
from_clause = "FROM `monitor` m"
where_clauses = ["m.`user_id` = %s"]
params = [user_id]
if tag_ids:
from_clause += " JOIN `monitor_tag` mt ON m.`id` = mt.`monitor_id`"
placeholders = ", ".join(["%s"] * len(tag_ids))
where_clauses.append("mt.`tag_id` IN (placeholders)")
params.extend(tag_ids)
query = """ SELECT select_fields FROM_CLAUSE WHERE AND_JOINED_CLAUSES ORDER BY m.`name` ASC """
cursor.execute(query, tuple(params))
results = cursor.fetchall()
monitors = []
for row in results:
monitor = schemas.MonitorRead(**row)
# Obtener los tags y otros detalles
monitor.tags = get_tags_for_monitor(db=db, monitor_id=monitor.id)
monitors.append(monitor)
return monitors
except mysql.connector.Error as err:
print("Error fetching monitors: error_details")
return []
La capa de servicio implementa la lógica de negocio y las consultas a MySQL. A diferencia de un ORM tradicional, utilizamos mysql-connector-python para ejecutar consultas SQL dinámicas y optimizadas para cada caso de uso específico.
schemas.py
$ cat app/api/endpoints/monitors/schemas.py
from pydantic import BaseModel, Field
from typing import Optional, List
from datetime import datetime
from ..tags.schemas import MonitorTagDetail
class FilteredTlsInfo(BaseModel):
valid: Optional[bool] = None
subject_cn: Optional[str] = None
valid_to: Optional[str] = None
days_remaining: Optional[int] = None
class MonitorBase(BaseModel):
name: str
type: str
active: bool = True
interval: int = 60
description: Optional[str] = None
url: Optional[str] = None
hostname: Optional[str] = None
port: Optional[int] = None
class MonitorRead(MonitorBase):
id: int
user_id: int
created_date: datetime
tags: List[MonitorTagDetail] = []
tls_info: Optional[FilteredTlsInfo] = None
latest_status: Optional[int] = None
class Config:
from_attributes = True
Pydantic se utiliza para la validación de datos y generación de esquemas. Cada modelo define los campos esperados, tipos y reglas de validación. FastAPI utiliza estos modelos para validar peticiones/respuestas y generar la documentación interactiva automáticamente.

Desarrollo con SwiftUI

Tecnología nativa de Apple

La aplicación Kein Pulse está construida usando SwiftUI y Swift Concurrency, aprovechando las últimas tecnologías de Apple para ofrecer una experiencia fluida y moderna.

Monitoreo en tiempo real

Con async/await y llamadas a la API optimizadas, la aplicación muestra actualizaciones en tiempo real del estado de tus servicios con mínimo consumo de batería y recursos del dispositivo.

Notificaciones y Widgets

La aplicación implementa un sistema de notificaciones push nativas para alertar sobre cambios de estado en los servicios. El proyecto también incluye widgets para iOS que permiten la visualización de monitores críticos directamente desde la pantalla de inicio.

Autenticación segura

La conexión con la API está protegida mediante JWT, garantizando que solo usuarios autorizados puedan acceder a la información de monitorizacion. Además, es compatible con autenticación biométrica para mayor comodidad.

Tecnologías Utilizadas

SwiftUI SwiftUI
Swift Swift 5.9
Async/Await
WidgetKit
JWT Auth
Swift Charts