Add uptime monitor features: fix history bars, lifetime stats, detail modal, custom services
- Fix 24-bar history rendering to always show 24 uniform segments with gray fill for missing hours - Add lifetime uptime % and avg latency to status API - Add clickable detail modal with expanded stats, large history bar, and service removal - Add monitored_services DB table with CRUD API (GET/POST/DELETE) - Monitor reads services from DB each interval, seeds defaults on first run - Add inline form to add custom services to track - Extend log retention from 7 days to 90 days for lifetime stats Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
48
monitor.js
48
monitor.js
@@ -2,12 +2,46 @@ const sqlite3 = require('sqlite3');
|
||||
const { open } = require('sqlite');
|
||||
// Node 18+ has global fetch built-in
|
||||
|
||||
const SERVICES = [
|
||||
const DEFAULT_SERVICES = [
|
||||
{ name: 'Website', url: 'https://akkolli.net' },
|
||||
{ name: 'Gitea', url: 'https://code.akkolli.net' },
|
||||
{ name: 'Nextcloud', url: 'http://host.docker.internal:6060' },
|
||||
];
|
||||
|
||||
async function getServices(db) {
|
||||
try {
|
||||
const rows = await db.all('SELECT name, url FROM monitored_services');
|
||||
if (rows && rows.length > 0) return rows;
|
||||
} catch (e) {
|
||||
// Table might not exist yet
|
||||
}
|
||||
return DEFAULT_SERVICES;
|
||||
}
|
||||
|
||||
async function seedDefaults(db) {
|
||||
// Ensure monitored_services table exists
|
||||
await db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS monitored_services (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
url TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
`);
|
||||
|
||||
// Seed defaults if table is empty
|
||||
const count = await db.get('SELECT COUNT(*) as cnt FROM monitored_services');
|
||||
if (count.cnt === 0) {
|
||||
for (const s of DEFAULT_SERVICES) {
|
||||
await db.run(
|
||||
'INSERT OR IGNORE INTO monitored_services (name, url) VALUES (?, ?)',
|
||||
s.name, s.url
|
||||
);
|
||||
}
|
||||
console.log('Seeded default services into monitored_services');
|
||||
}
|
||||
}
|
||||
|
||||
async function monitor() {
|
||||
console.log('Starting monitoring loop...');
|
||||
|
||||
@@ -30,11 +64,16 @@ async function monitor() {
|
||||
);
|
||||
`);
|
||||
|
||||
await seedDefaults(db);
|
||||
|
||||
setInterval(async () => {
|
||||
console.log('Running checks...');
|
||||
const now = new Date().toISOString();
|
||||
|
||||
for (const service of SERVICES) {
|
||||
// Re-read services each interval so new additions are picked up
|
||||
const services = await getServices(db);
|
||||
|
||||
for (const service of services) {
|
||||
const start = performance.now();
|
||||
let status = 'down';
|
||||
let latency = 0;
|
||||
@@ -57,7 +96,6 @@ async function monitor() {
|
||||
} catch (err) {
|
||||
status = 'down';
|
||||
latency = 0;
|
||||
// console.error(`Failed to reach ${service.name}:`, err.message);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -70,9 +108,9 @@ async function monitor() {
|
||||
}
|
||||
}
|
||||
|
||||
// Prune old logs (keep 7 days)
|
||||
// Prune old logs (keep 90 days for lifetime stats)
|
||||
try {
|
||||
await db.run(`DELETE FROM uptime_logs WHERE timestamp < datetime('now', '-7 days')`);
|
||||
await db.run(`DELETE FROM uptime_logs WHERE timestamp < datetime('now', '-90 days')`);
|
||||
} catch (e) { }
|
||||
|
||||
}, 60000); // Run every minute
|
||||
|
||||
Reference in New Issue
Block a user