inicio: dashboard de privacidad Bitcoin con análisis on-chain
This commit is contained in:
+23
@@ -0,0 +1,23 @@
|
|||||||
|
# Credenciales y configuración local — NUNCA subir al repo
|
||||||
|
system-metrics.local.js
|
||||||
|
config.local.js
|
||||||
|
*.env
|
||||||
|
.env*
|
||||||
|
|
||||||
|
# Archivos de sistema
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# Archivos temporales
|
||||||
|
*.tmp
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Node
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Txoko
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
# Txoko Node Dashboard
|
||||||
|
|
||||||
|
Suite de auditoría de privacidad Bitcoin para nodos propios.
|
||||||
|
|
||||||
|
**your node, your rules** — ninguna consulta sale de tu red.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Qué es
|
||||||
|
|
||||||
|
Txoko es una herramienta de auditoría de privacidad Bitcoin que corre sobre tu propio nodo. Te permite analizar tus transacciones y entender qué puede deducir un observador externo sobre tu actividad on-chain.
|
||||||
|
|
||||||
|
No es un explorador de bloques genérico. Es una herramienta para que el propietario de un nodo audite su propia privacidad con total control sobre sus datos.
|
||||||
|
|
||||||
|
A diferencia de OXT, Blockchair o am-i.exposed, **ninguna consulta sale de tu red**. Todo el análisis ocurre en tu nodo, en tu navegador.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Funcionalidades
|
||||||
|
|
||||||
|
- **Privacy Lab** — análisis completo de una transacción con 15 heurísticas ponderadas
|
||||||
|
- **Informe narrativo** — explicación en prosa de lo que puede deducir un analista, sin jerga
|
||||||
|
- **UTXO Map** — mapa de tus UTXOs con score de privacidad por moneda
|
||||||
|
- **Wallet fingerprinting** — detecta qué wallet generó la transacción
|
||||||
|
- **Detección de CoinJoin** — Whirlpool, WabiSabi, JoinMarket, genérico
|
||||||
|
- **Explorador de bloques** — bloques recientes y búsqueda on-chain
|
||||||
|
- **Mempool** — stats, fees, fee intelligence, bloques proyectados
|
||||||
|
- **Monitor del nodo** — estado de Bitcoin Core, sistema, logs en tiempo real
|
||||||
|
- **Herramientas** — conversor sat/BTC/EUR, validador de dirección, decodificadores
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Filosofía
|
||||||
|
|
||||||
|
- **Sin dependencias externas de datos** — todo viene de tu propio nodo
|
||||||
|
- **Transparencia sobre certeza vs inferencia** — siempre distingue lo que es hecho de lo que es probabilidad
|
||||||
|
- **Didáctica honesta** — explicar las limitaciones es tan importante como el análisis
|
||||||
|
- **Ligero por defecto** — no estresar el nodo
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requisitos
|
||||||
|
|
||||||
|
- Bitcoin Core con `txindex=1`
|
||||||
|
- [Mempool.space self-hosted](https://github.com/mempool/mempool) (backend API)
|
||||||
|
- [Fulcrum](https://github.com/cculianu/Fulcrum) (índice Electrum, recomendado)
|
||||||
|
- nginx como proxy inverso
|
||||||
|
- Acceso via Tailscale, red local o similar
|
||||||
|
|
||||||
|
Probado sobre Ubuntu Server 24.04 con HP EliteDesk (i5, 32GB RAM, 2TB NVMe).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Instalación
|
||||||
|
|
||||||
|
### 1. Clonar el repositorio
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://gitea.tu-comunidad/txoko-dashboard.git
|
||||||
|
cd txoko-dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Copiar el dashboard
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp dashboard.html /var/www/txoko/dashboard.html
|
||||||
|
# o donde lo sirvas con nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Copiar el backend de métricas
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp system-metrics.js /home/armg/txoko/system-metrics.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Editar `system-metrics.js` y añadir tus credenciales RPC de Bitcoin Core
|
||||||
|
(el archivo del repo usa placeholders).
|
||||||
|
|
||||||
|
### 4. Configurar el servicio systemd
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp txoko-metrics.service /etc/systemd/system/
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable txoko-metrics
|
||||||
|
sudo systemctl start txoko-metrics
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Configurar nginx
|
||||||
|
|
||||||
|
Añadir a tu configuración de nginx:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# Dashboard
|
||||||
|
location /dashboard {
|
||||||
|
alias /var/www/txoko/dashboard.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API Mempool (ajusta el puerto según tu instalación)
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://localhost:8999;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Métricas del sistema
|
||||||
|
location /system/ {
|
||||||
|
proxy_pass http://127.0.0.1:4082;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Abrir en el navegador
|
||||||
|
|
||||||
|
```
|
||||||
|
http://TU-IP-TAILSCALE:4080/dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
Introduce la URL de tu Mempool en el modal de configuración y empieza a analizar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Estructura del repositorio
|
||||||
|
|
||||||
|
```
|
||||||
|
txoko-dashboard/
|
||||||
|
├── dashboard.html # Frontend completo (HTML + CSS + JS en un solo archivo)
|
||||||
|
├── system-metrics.js # Backend de métricas del nodo (Node.js)
|
||||||
|
├── txoko-metrics.service # Servicio systemd
|
||||||
|
├── README.md
|
||||||
|
└── LICENSE
|
||||||
|
```
|
||||||
|
|
||||||
|
El frontend es un único archivo HTML autocontenido. Sin bundler, sin npm, sin proceso de build. Babel transpila el JSX en el navegador. Puedes auditarlo todo abriendo el archivo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Limitaciones conocidas
|
||||||
|
|
||||||
|
- Análisis de transacción aislada — sin acceso al grafo completo, algunas heurísticas tienen techo
|
||||||
|
- Sin base de datos de entidades — no identifica exchanges o servicios KYC (en desarrollo)
|
||||||
|
- El score de privacidad muestra bandas (ALTA/MEDIA/BAJA), no un número exacto — la precisión numérica sería falsa
|
||||||
|
- El fingerprinting de wallet requiere mínimo varias señales coincidentes para aparecer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comparativa
|
||||||
|
|
||||||
|
| | Txoko | Mempool self-hosted | OXT | Blockchair | am-i.exposed |
|
||||||
|
|--|-------|-------------------|-----|------------|--------------|
|
||||||
|
| Self-hosted | ✓ | ✓ | ✗ | ✗ | Parcial |
|
||||||
|
| Sin exponer consultas | ✓✓ | ✓✓ | ✗ | ✗ | Parcial |
|
||||||
|
| Análisis de privacidad | ✓✓ | ✗ | ✓✓ | ✓ | ✓✓ |
|
||||||
|
| Informe narrativo | ✓✓ | ✗ | ✗ | ✗ | ✗ |
|
||||||
|
| Wallet fingerprinting | ✓ | ✗ | ✗ | ✗ | ✓ |
|
||||||
|
| UTXO Map con score | ✓ | ✗ | Parcial | ✗ | ✗ |
|
||||||
|
| Didáctica transparente | ✓✓ | ✗ | ✗ | ✗ | ✓ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contribuir
|
||||||
|
|
||||||
|
Proyecto en desarrollo activo. Issues y PRs bienvenidos.
|
||||||
|
|
||||||
|
Si encuentras un false positive en alguna heurística o tienes un caso de prueba interesante, abre un issue con el txid y lo que esperabas ver.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Licencia
|
||||||
|
|
||||||
|
MIT — ver [LICENSE](LICENSE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*₿ txoko · your node, your rules*
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
# Cómo subir Txoko a Gitea — paso a paso
|
||||||
|
|
||||||
|
Instrucciones exactas. Copiar y pegar en la terminal del Mac.
|
||||||
|
No hace falta entender git para seguir esto.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PASO 1 — Crear el repo en Gitea (una sola vez)
|
||||||
|
|
||||||
|
1. Abre tu instancia de Gitea en el navegador
|
||||||
|
2. Clic en el **+** (arriba a la derecha) → "New Repository"
|
||||||
|
3. Rellena:
|
||||||
|
- **Repository Name:** `txoko-dashboard`
|
||||||
|
- **Description:** `Suite de auditoría de privacidad Bitcoin para nodos propios`
|
||||||
|
- **Visibility:** Private (o Public si quieres compartirlo con la comunidad)
|
||||||
|
- **Initialize repository:** NO marcar (ya traemos nuestros archivos)
|
||||||
|
4. Clic en "Create Repository"
|
||||||
|
5. Gitea te muestra una página con instrucciones — copia la URL del repo,
|
||||||
|
será algo como: `https://gitea.tu-comunidad/tu-usuario/txoko-dashboard.git`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PASO 2 — Configurar git en el Mac (una sola vez, si no lo tienes)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config --global user.name "tu nombre"
|
||||||
|
git config --global user.email "tu@email.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
Comprueba que git está instalado:
|
||||||
|
```bash
|
||||||
|
git --version
|
||||||
|
```
|
||||||
|
Si no está: `brew install git`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PASO 3 — Crear el repo local y primer commit (una sola vez)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Crear carpeta del proyecto en el Mac
|
||||||
|
mkdir ~/txoko-dashboard
|
||||||
|
cd ~/txoko-dashboard
|
||||||
|
|
||||||
|
# Copiar los archivos del repo que te he preparado
|
||||||
|
# (descarga los archivos de esta conversación y cópialos aquí)
|
||||||
|
|
||||||
|
# Inicializar git
|
||||||
|
git init
|
||||||
|
git branch -M main
|
||||||
|
|
||||||
|
# Añadir todos los archivos
|
||||||
|
git add .
|
||||||
|
|
||||||
|
# Primer commit — el historial empieza aquí
|
||||||
|
git commit -m "inicio: dashboard de privacidad Bitcoin con análisis on-chain"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PASO 4 — Conectar con Gitea y subir (una sola vez)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Sustituye la URL por la de tu repo de Gitea
|
||||||
|
git remote add origin https://gitea.tu-comunidad/tu-usuario/txoko-dashboard.git
|
||||||
|
|
||||||
|
# Subir
|
||||||
|
git push -u origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
Gitea te pedirá usuario y contraseña la primera vez.
|
||||||
|
Si quieres evitar introducirlos cada vez, crea un token en
|
||||||
|
Gitea → Settings → Applications → "Generate Token" y úsalo como contraseña.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PASO 5 — Flujo de trabajo normal (cada vez que yo te dé un archivo nuevo)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/txoko-dashboard
|
||||||
|
|
||||||
|
# Copiar el archivo actualizado descargado de esta conversación
|
||||||
|
cp ~/Downloads/txoko-dashboard.html ./dashboard.html
|
||||||
|
|
||||||
|
# Ver qué cambió (opcional pero útil)
|
||||||
|
git diff dashboard.html
|
||||||
|
|
||||||
|
# Registrar el cambio con un mensaje descriptivo
|
||||||
|
git add dashboard.html
|
||||||
|
git commit -m "fix: descripción breve de lo que se arregló"
|
||||||
|
|
||||||
|
# Subir a Gitea
|
||||||
|
git push
|
||||||
|
|
||||||
|
# Copiar al nodo (igual que antes)
|
||||||
|
scp dashboard.html armg@100.116.19.86:/home/armg/txoko/dashboard.html
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mensajes de commit — ejemplos
|
||||||
|
|
||||||
|
El mensaje va después de `-m` y describe QUÉ cambiaste.
|
||||||
|
No tiene que ser perfecto, solo útil para ti en el futuro.
|
||||||
|
|
||||||
|
```
|
||||||
|
"fix: timeout en consultas al nodo"
|
||||||
|
"feat: score por bandas ALTA/MEDIA/BAJA"
|
||||||
|
"fix: responsive pestaña Mempool en móvil"
|
||||||
|
"fix: detección Whirlpool con tolerancia 2%"
|
||||||
|
"feat: fingerprinting wallet completo"
|
||||||
|
"fix: umbral dust por tipo de salida"
|
||||||
|
"docs: actualizar README con nuevas funcionalidades"
|
||||||
|
```
|
||||||
|
|
||||||
|
Convención (opcional pero ordenada):
|
||||||
|
- `fix:` — corrige algo que no funcionaba bien
|
||||||
|
- `feat:` — añade algo nuevo
|
||||||
|
- `docs:` — solo documentación
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Si algo sale mal
|
||||||
|
|
||||||
|
**Subiste algo que no querías:**
|
||||||
|
```bash
|
||||||
|
git revert HEAD
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
**Quieres volver a una versión anterior:**
|
||||||
|
```bash
|
||||||
|
git log --oneline # ver el historial
|
||||||
|
git checkout HASH_DEL_COMMIT -- dashboard.html # recuperar ese archivo
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ver el historial:**
|
||||||
|
```bash
|
||||||
|
git log --oneline
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resumen del flujo
|
||||||
|
|
||||||
|
```
|
||||||
|
Claude te da dashboard.html
|
||||||
|
↓
|
||||||
|
cp ~/Downloads/dashboard.html ./dashboard.html
|
||||||
|
↓
|
||||||
|
git add . && git commit -m "descripción"
|
||||||
|
↓
|
||||||
|
git push
|
||||||
|
↓
|
||||||
|
scp dashboard.html armg@100.116.19.86:/home/armg/txoko/
|
||||||
|
```
|
||||||
|
|
||||||
|
Cuatro comandos después de la descarga. Siempre los mismos.
|
||||||
+2850
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
* txoko-metrics — Backend de métricas del nodo Bitcoin
|
||||||
|
*
|
||||||
|
* Servidor HTTP Node.js que escucha en 127.0.0.1:4082.
|
||||||
|
* Sirve métricas del sistema y estado de Bitcoin Core al dashboard.
|
||||||
|
*
|
||||||
|
* CONFIGURACIÓN:
|
||||||
|
* Editar las variables de la sección "CONFIGURACIÓN LOCAL" con
|
||||||
|
* los datos de tu nodo antes de arrancar.
|
||||||
|
*
|
||||||
|
* ARRANCAR:
|
||||||
|
* node system-metrics.js
|
||||||
|
*
|
||||||
|
* O como servicio systemd:
|
||||||
|
* sudo systemctl start txoko-metrics
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ── CONFIGURACIÓN LOCAL ──────────────────────────────────────────────
|
||||||
|
// Copia este archivo como system-metrics.local.js y edita estos valores.
|
||||||
|
// El archivo .local.js está en .gitignore — nunca se sube al repo.
|
||||||
|
|
||||||
|
const RPC_HOST = '127.0.0.1';
|
||||||
|
const RPC_PORT = 8332;
|
||||||
|
const RPC_USER = 'TU_RPC_USER'; // Ver bitcoin.conf → rpcuser
|
||||||
|
const RPC_PASSWORD = 'TU_RPC_PASSWORD'; // Ver bitcoin.conf → rpcpassword
|
||||||
|
// Alternativa: autenticación por cookie (más seguro)
|
||||||
|
// const RPC_COOKIE = '/data/bitcoin/.cookie';
|
||||||
|
|
||||||
|
const METRICS_PORT = 4082;
|
||||||
|
const METRICS_HOST = '127.0.0.1'; // Solo localhost — no exponer al exterior
|
||||||
|
|
||||||
|
// ── FIN CONFIGURACIÓN ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const http = require('http');
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
|
// Cliente RPC Bitcoin Core
|
||||||
|
async function rpcCall(method, params = []) {
|
||||||
|
const body = JSON.stringify({ jsonrpc: '1.0', id: 'txoko', method, params });
|
||||||
|
const auth = Buffer.from(`${RPC_USER}:${RPC_PASSWORD}`).toString('base64');
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http.request({
|
||||||
|
host: RPC_HOST, port: RPC_PORT, method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json', 'Authorization': `Basic ${auth}` }
|
||||||
|
}, res => {
|
||||||
|
let data = '';
|
||||||
|
res.on('data', chunk => data += chunk);
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(data);
|
||||||
|
if (parsed.error) reject(parsed.error);
|
||||||
|
else resolve(parsed.result);
|
||||||
|
} catch (e) { reject(e); }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req.on('error', reject);
|
||||||
|
req.write(body);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoints
|
||||||
|
const routes = {
|
||||||
|
|
||||||
|
'/system/info': async () => {
|
||||||
|
const cpus = os.cpus();
|
||||||
|
const totalMem = os.totalmem();
|
||||||
|
const freeMem = os.freemem();
|
||||||
|
const usedMem = totalMem - freeMem;
|
||||||
|
|
||||||
|
// CPU usage (snapshot simple)
|
||||||
|
const cpuUsage = cpus.reduce((sum, cpu) => {
|
||||||
|
const total = Object.values(cpu.times).reduce((a, b) => a + b, 0);
|
||||||
|
const idle = cpu.times.idle;
|
||||||
|
return sum + ((total - idle) / total) * 100;
|
||||||
|
}, 0) / cpus.length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
cpu: { usage: cpuUsage.toFixed(1), cores: cpus.length, model: cpus[0]?.model },
|
||||||
|
memory: {
|
||||||
|
total: totalMem, free: freeMem, used: usedMem,
|
||||||
|
usedPercent: ((usedMem / totalMem) * 100).toFixed(1)
|
||||||
|
},
|
||||||
|
uptime: os.uptime(),
|
||||||
|
platform: os.platform(),
|
||||||
|
hostname: os.hostname()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
'/system/bitcoin': async () => {
|
||||||
|
const [info, netinfo, mempoolinfo] = await Promise.all([
|
||||||
|
rpcCall('getblockchaininfo'),
|
||||||
|
rpcCall('getnetworkinfo'),
|
||||||
|
rpcCall('getmempoolinfo')
|
||||||
|
]);
|
||||||
|
return { blockchain: info, network: netinfo, mempool: mempoolinfo };
|
||||||
|
},
|
||||||
|
|
||||||
|
'/system/logs/bitcoin': async () => {
|
||||||
|
try {
|
||||||
|
const lines = execSync('journalctl -u bitcoind -n 80 --no-pager -o short-iso 2>/dev/null || tail -80 /data/bitcoin/debug.log 2>/dev/null || echo ""')
|
||||||
|
.toString().trim().split('\n').filter(Boolean);
|
||||||
|
return { logs: lines.map(line => ({
|
||||||
|
line,
|
||||||
|
level: line.includes('error') || line.includes('ERROR') ? 'error'
|
||||||
|
: line.includes('warn') || line.includes('WARN') ? 'warn'
|
||||||
|
: 'info'
|
||||||
|
}))};
|
||||||
|
} catch { return { logs: [] }; }
|
||||||
|
},
|
||||||
|
|
||||||
|
'/system/logs/fulcrum': async () => {
|
||||||
|
try {
|
||||||
|
const lines = execSync('journalctl -u fulcrum -n 80 --no-pager -o short-iso 2>/dev/null || echo ""')
|
||||||
|
.toString().trim().split('\n').filter(Boolean);
|
||||||
|
return { logs: lines.map(line => ({
|
||||||
|
line,
|
||||||
|
level: line.includes('Error') || line.includes('error') ? 'error'
|
||||||
|
: line.includes('Warn') || line.includes('warn') ? 'warn'
|
||||||
|
: 'info'
|
||||||
|
}))};
|
||||||
|
} catch { return { logs: [] }; }
|
||||||
|
},
|
||||||
|
|
||||||
|
'/system/health': async () => ({ status: 'ok', ts: Date.now() })
|
||||||
|
};
|
||||||
|
|
||||||
|
// Servidor HTTP
|
||||||
|
const server = http.createServer(async (req, res) => {
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
res.setHeader('Content-Type', 'application/json');
|
||||||
|
|
||||||
|
// Extraer ruta sin query string
|
||||||
|
const path = req.url.split('?')[0];
|
||||||
|
const handler = routes[path];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
res.writeHead(404);
|
||||||
|
return res.end(JSON.stringify({ error: 'Not found' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await handler();
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify(data));
|
||||||
|
} catch (err) {
|
||||||
|
res.writeHead(500);
|
||||||
|
res.end(JSON.stringify({ error: err.message || 'Internal error' }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(METRICS_PORT, METRICS_HOST, () => {
|
||||||
|
console.log(`txoko-metrics escuchando en ${METRICS_HOST}:${METRICS_PORT}`);
|
||||||
|
});
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Txoko Node Metrics — backend de métricas para el dashboard
|
||||||
|
After=network.target bitcoind.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=armg
|
||||||
|
ExecStart=/usr/bin/node /home/armg/txoko/system-metrics.js
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
# Seguridad básica
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user