Files
DashDescomplicar/api/services/monitoring.ts
Emanuel Almeida 75f29ee6d5 fix: Monitor page now uses real API data
- Changed fetch URL from /api/monitor.php to /api/monitor
- Updated MonitorData interface to match API response structure
- Fixed stats calculation (MySQL returning strings instead of numbers)
- Updated mock data with realistic values from production DB

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 23:34:43 +00:00

188 lines
4.4 KiB
TypeScript

/**
* Monitoring Queries Service
* @author Descomplicar® | @link descomplicar.pt | @copyright 2026
*/
import db from '../db.js'
import type { RowDataPacket } from 'mysql2'
interface MonitoringItem {
id: number
name: string
category: string
type: string
status: string
details: any
last_check: string
created_at: string
updated_at: string
}
interface CategorySummary {
category: string
total: number
ok: number
warning: number
critical: number
}
/**
* Check if a URL is accessible (HTTP HEAD request)
*/
export async function checkSiteAvailability(url: string, timeout = 10000): Promise<{
available: boolean
statusCode?: number
responseTime?: number
error?: string
}> {
const startTime = Date.now()
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
try {
const response = await fetch(url, {
method: 'HEAD',
signal: controller.signal,
headers: {
'User-Agent': 'Descomplicar-Monitor/1.0'
}
})
clearTimeout(timeoutId)
const responseTime = Date.now() - startTime
return {
available: response.ok || response.status < 500,
statusCode: response.status,
responseTime
}
} catch (error) {
clearTimeout(timeoutId)
return {
available: false,
error: (error as Error).message,
responseTime: Date.now() - startTime
}
}
}
/**
* Check all sites and update their availability status
*/
export async function checkAllSitesAvailability(): Promise<{
checked: number
up: number
down: number
results: any[]
}> {
// Get all sites from monitoring table
const [sites] = await db.query<RowDataPacket[]>(`
SELECT id, name, details FROM tbl_eal_monitoring
WHERE category = 'site'
`)
const results: any[] = []
let up = 0
let down = 0
for (const site of sites) {
const details = typeof site.details === 'string' ? JSON.parse(site.details) : site.details
const siteUrl = details?.site_url || `https://${site.name}`
const check = await checkSiteAvailability(siteUrl)
// Update status if site is down
if (!check.available) {
await db.query(
'UPDATE tbl_eal_monitoring SET status = ?, last_check = NOW() WHERE id = ?',
['down', site.id]
)
down++
} else {
// If was down and now is up, set to 'up' (will be replaced by plugin data later)
const currentStatus = details?.health?.status || 'ok'
if (currentStatus === 'down') {
await db.query(
'UPDATE tbl_eal_monitoring SET status = ?, last_check = NOW() WHERE id = ?',
['up', site.id]
)
}
up++
}
results.push({
name: site.name,
url: siteUrl,
...check
})
}
return {
checked: sites.length,
up,
down,
results
}
}
export async function getMonitoringData() {
// Get all items
const [items] = await db.query<RowDataPacket[]>(`
SELECT * FROM tbl_eal_monitoring
ORDER BY category, name
`)
// Get summary by category
const [summary] = await db.query<RowDataPacket[]>(`
SELECT
category,
COUNT(*) as total,
SUM(CASE WHEN status IN ('ok','up') THEN 1 ELSE 0 END) as ok,
SUM(CASE WHEN status = 'warning' THEN 1 ELSE 0 END) as warning,
SUM(CASE WHEN status IN ('failed','down') THEN 1 ELSE 0 END) as critical
FROM tbl_eal_monitoring
GROUP BY category
`)
// Parse details JSON and cast to MonitoringItem
const itemsParsed: MonitoringItem[] = items.map(item => ({
...item,
details: typeof item.details === 'string' ? JSON.parse(item.details) : item.details
} as MonitoringItem))
// Organize by category
const data: Record<string, MonitoringItem[]> = {}
for (const item of itemsParsed) {
if (!data[item.category]) {
data[item.category] = []
}
data[item.category].push(item)
}
// Calculate overall status
let overall: 'ok' | 'warning' | 'critical' = 'ok'
let total_critical = 0
let total_warning = 0
let total_ok = 0
for (const s of summary as CategorySummary[]) {
// MySQL pode retornar strings, converter para número
total_critical += Number(s.critical) || 0
total_warning += Number(s.warning) || 0
total_ok += Number(s.ok) || 0
}
if (total_critical > 0) overall = 'critical'
else if (total_warning > 0) overall = 'warning'
return {
items: data,
summary,
overall,
stats: {
total_critical,
total_warning,
total_ok
}
}
}