2026-02-09 02:05:22 -05:00
|
|
|
import { NextResponse } from 'next/server';
|
|
|
|
|
import { getDb } from '@/lib/db';
|
|
|
|
|
|
|
|
|
|
export const dynamic = 'force-dynamic';
|
|
|
|
|
|
|
|
|
|
export async function GET() {
|
|
|
|
|
try {
|
|
|
|
|
const db = await getDb();
|
|
|
|
|
const services = await db.all('SELECT id, name, url, created_at FROM monitored_services ORDER BY id ASC');
|
|
|
|
|
return NextResponse.json(services);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Services list error:', error);
|
|
|
|
|
return NextResponse.json([], { status: 500 });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function POST(request: Request) {
|
|
|
|
|
try {
|
|
|
|
|
const { name, url } = await request.json();
|
|
|
|
|
|
|
|
|
|
if (!name || !url) {
|
|
|
|
|
return NextResponse.json({ error: 'Name and URL are required' }, { status: 400 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Basic URL validation
|
|
|
|
|
try {
|
|
|
|
|
new URL(url);
|
|
|
|
|
} catch {
|
|
|
|
|
return NextResponse.json({ error: 'Invalid URL format' }, { status: 400 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const db = await getDb();
|
2026-02-09 03:28:24 -05:00
|
|
|
const trimmedName = name.trim();
|
|
|
|
|
const trimmedUrl = url.trim();
|
|
|
|
|
await db.run('INSERT INTO monitored_services (name, url) VALUES (?, ?)', trimmedName, trimmedUrl);
|
2026-02-09 02:05:22 -05:00
|
|
|
|
2026-02-09 03:28:24 -05:00
|
|
|
// Immediate ping so the service has data right away
|
|
|
|
|
let initialStatus = 'down';
|
|
|
|
|
let initialLatency = 0;
|
|
|
|
|
try {
|
|
|
|
|
const controller = new AbortController();
|
|
|
|
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
|
|
|
const start = performance.now();
|
|
|
|
|
const res = await fetch(trimmedUrl, {
|
|
|
|
|
method: 'HEAD',
|
|
|
|
|
signal: controller.signal,
|
|
|
|
|
});
|
|
|
|
|
clearTimeout(timeout);
|
|
|
|
|
initialLatency = Math.round(performance.now() - start);
|
|
|
|
|
initialStatus = res.status < 500 ? 'up' : 'down';
|
|
|
|
|
} catch {
|
|
|
|
|
initialStatus = 'down';
|
|
|
|
|
initialLatency = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await db.run(
|
|
|
|
|
'INSERT INTO uptime_logs (service_name, url, status, latency, timestamp) VALUES (?, ?, ?, ?, ?)',
|
|
|
|
|
trimmedName, trimmedUrl, initialStatus, initialLatency, new Date().toISOString()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return NextResponse.json({ success: true, initialStatus, initialLatency });
|
2026-02-09 02:05:22 -05:00
|
|
|
} catch (error: unknown) {
|
|
|
|
|
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
|
|
|
if (msg.includes('UNIQUE constraint')) {
|
|
|
|
|
return NextResponse.json({ error: 'A service with that name already exists' }, { status: 409 });
|
|
|
|
|
}
|
|
|
|
|
console.error('Service add error:', error);
|
|
|
|
|
return NextResponse.json({ error: 'Failed to add service' }, { status: 500 });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 02:23:19 -05:00
|
|
|
export async function PATCH(request: Request) {
|
|
|
|
|
try {
|
|
|
|
|
const { id, name } = await request.json();
|
|
|
|
|
|
|
|
|
|
if (!id || !name) {
|
|
|
|
|
return NextResponse.json({ error: 'ID and new name are required' }, { status: 400 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const db = await getDb();
|
|
|
|
|
|
|
|
|
|
const service = await db.get('SELECT name FROM monitored_services WHERE id = ?', id);
|
|
|
|
|
if (!service) {
|
|
|
|
|
return NextResponse.json({ error: 'Service not found' }, { status: 404 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const oldName = service.name;
|
|
|
|
|
const newName = name.trim();
|
|
|
|
|
|
|
|
|
|
await db.run('UPDATE monitored_services SET name = ? WHERE id = ?', newName, id);
|
|
|
|
|
// Update existing logs to match the new name
|
|
|
|
|
await db.run('UPDATE uptime_logs SET service_name = ? WHERE service_name = ?', newName, oldName);
|
|
|
|
|
|
|
|
|
|
return NextResponse.json({ success: true });
|
|
|
|
|
} catch (error: unknown) {
|
|
|
|
|
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
|
|
|
if (msg.includes('UNIQUE constraint')) {
|
|
|
|
|
return NextResponse.json({ error: 'A service with that name already exists' }, { status: 409 });
|
|
|
|
|
}
|
|
|
|
|
console.error('Service rename error:', error);
|
|
|
|
|
return NextResponse.json({ error: 'Failed to rename service' }, { status: 500 });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 02:05:22 -05:00
|
|
|
export async function DELETE(request: Request) {
|
|
|
|
|
try {
|
|
|
|
|
const { id } = await request.json();
|
|
|
|
|
|
|
|
|
|
if (!id) {
|
|
|
|
|
return NextResponse.json({ error: 'Service ID is required' }, { status: 400 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const db = await getDb();
|
|
|
|
|
|
|
|
|
|
// Get service name before deleting so we can clean up logs
|
|
|
|
|
const service = await db.get('SELECT name FROM monitored_services WHERE id = ?', id);
|
|
|
|
|
if (!service) {
|
|
|
|
|
return NextResponse.json({ error: 'Service not found' }, { status: 404 });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await db.run('DELETE FROM monitored_services WHERE id = ?', id);
|
|
|
|
|
await db.run('DELETE FROM uptime_logs WHERE service_name = ?', service.name);
|
|
|
|
|
|
|
|
|
|
return NextResponse.json({ success: true });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Service delete error:', error);
|
|
|
|
|
return NextResponse.json({ error: 'Failed to delete service' }, { status: 500 });
|
|
|
|
|
}
|
|
|
|
|
}
|