diff --git a/CHANGELOG.md b/CHANGELOG.md index 60dadf0..8610fed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ Todas as alterações notáveis neste projecto serão documentadas neste ficheiro. +## [2.4.0] - 2026-02-04 + +### Fixed +- ✅ **Monitor.tsx** - Corrigido URL da API de `/api/monitor.php` para `/api/monitor` +- ✅ **Monitor.tsx** - Adaptada estrutura de dados para nova resposta da API (items agrupados por categoria) +- ✅ **Monitoring Service** - Corrigido cálculo de stats (MySQL retornava strings) + +### Changed +- ✅ Interface `MonitorData` actualizada para reflectir estrutura real da API +- ✅ Mock data actualizado com dados realistas da BD +- ✅ Sumário usa `data.stats` em vez de `data.summary` para totais + +### Technical Notes +- API endpoint: `/api/monitor` retorna dados de `tbl_eal_monitoring` +- Categorias disponíveis: server, service, site, container, backup, wp_update, storage +- Stats calculados server-side com conversão explícita para números + +--- + ## [2.3.0] - 2026-02-04 ### Added diff --git a/api/services/monitoring.ts b/api/services/monitoring.ts index 8158b47..6a38bef 100644 --- a/api/services/monitoring.ts +++ b/api/services/monitoring.ts @@ -162,10 +162,13 @@ export async function getMonitoringData() { 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[]) { - total_critical += s.critical - total_warning += s.warning + // 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' @@ -178,7 +181,7 @@ export async function getMonitoringData() { stats: { total_critical, total_warning, - total_ok: items.length - total_critical - total_warning + total_ok } } } diff --git a/src/pages/Monitor.tsx b/src/pages/Monitor.tsx index 36cf3c0..fb5d93f 100644 --- a/src/pages/Monitor.tsx +++ b/src/pages/Monitor.tsx @@ -28,14 +28,23 @@ interface MonitorItem { last_check: string } +interface CategorySummary { + category: string + total: number + ok: number + warning: number + critical: number +} + interface MonitorData { - items: MonitorItem[] - summary: { - ok: number - warning: number - critical: number - } + items: Record + summary: CategorySummary[] overall: 'ok' | 'warning' | 'critical' + stats: { + total_ok: number + total_warning: number + total_critical: number + } } // Animation variants @@ -172,7 +181,7 @@ export default function Monitor() { const fetchData = useCallback(async () => { setRefreshing(true) try { - const response = await fetch('/api/monitor.php') + const response = await fetch('/api/monitor') if (!response.ok) throw new Error('Failed') const json = await response.json() setData(json) @@ -207,11 +216,8 @@ export default function Monitor() { if (!data) return null - const groupedItems = data.items.reduce((acc, item) => { - if (!acc[item.category]) acc[item.category] = [] - acc[item.category].push(item) - return acc - }, {} as Record) + // Items já vêm agrupados por categoria da API + const groupedItems = data.items const overallColor = { ok: 'text-emerald-400 bg-emerald-500/20', @@ -279,9 +285,9 @@ export default function Monitor() { > {/* Summary Grid */}
- - - + + +
{/* Monitor Grid */} @@ -412,40 +418,69 @@ export default function Monitor() { // Mock data function getMockData(): MonitorData { - return { - overall: 'ok', - summary: { ok: 18, warning: 2, critical: 0 }, - items: [ - // Servers - { id: 1, name: 'CWP Server', category: 'server', status: 'up', details: { cpu: 23, ram: 67, disk: 45 }, last_check: '' }, - { id: 2, name: 'EasyPanel', category: 'server', status: 'up', details: { cpu: 15, ram: 52, disk: 38 }, last_check: '' }, - - // Services - { id: 3, name: 'Nginx', category: 'service', status: 'ok', details: { response_time: 0.12 }, last_check: '' }, - { id: 4, name: 'MySQL', category: 'service', status: 'ok', details: { response_time: 0.05 }, last_check: '' }, - { id: 5, name: 'Redis', category: 'service', status: 'ok', details: { response_time: 0.02 }, last_check: '' }, - { id: 6, name: 'MCP Gateway', category: 'service', status: 'ok', details: { response_time: 0.18 }, last_check: '' }, - - // Sites - { id: 7, name: 'Descomplicar', category: 'site', status: 'up', details: { domain: 'descomplicar.pt' }, last_check: '' }, - { id: 8, name: 'SolarFV', category: 'site', status: 'up', details: { domain: 'solarfv360.pt' }, last_check: '' }, - { id: 9, name: 'Carstuff', category: 'site', status: 'up', details: { domain: 'carstuff.pt' }, last_check: '' }, - { id: 10, name: 'Emanuel Almeida', category: 'site', status: 'up', details: { domain: 'emanuelalmeida.pt' }, last_check: '' }, - - // Containers - { id: 11, name: 'Containers', category: 'container', status: 'ok', details: { up: 12, total: 12, down: 0 }, last_check: '' }, - - // Backups - { id: 12, name: 'BD Desk CRM', category: 'backup', status: 'ok', details: { age_hours: 4 }, last_check: '' }, - { id: 13, name: 'Ficheiros', category: 'backup', status: 'ok', details: { age_hours: 12 }, last_check: '' }, - { id: 14, name: 'Configs', category: 'backup', status: 'warning', details: { age_hours: 48 }, last_check: '' }, - - // Storage - { id: 15, name: 'CWP /home', category: 'storage', status: 'ok', details: { used: '89GB', total: '200GB', percent: 45 }, last_check: '' }, - { id: 16, name: 'EasyPanel', category: 'storage', status: 'ok', details: { used: '42GB', total: '100GB', percent: 42 }, last_check: '' }, - - // WP Updates - { id: 17, name: 'WP Updates', category: 'wp_update', status: 'ok', details: { manual_updates: 0 }, last_check: '' }, + const mockItems: Record = { + server: [ + { id: 5, name: 'CWP Server', category: 'server', status: 'up', details: { cpu: 7.3, ram: 10.2, disk: 39 }, last_check: '' }, + { id: 6, name: 'EasyPanel', category: 'server', status: 'up', details: { cpu: 20.5, ram: 20.2, disk: 41 }, last_check: '' }, + { id: 296, name: 'MCP Hub', category: 'server', status: 'up', details: { cpu: 1.0, load: 0.0 }, last_check: '' }, + { id: 297, name: 'Meet', category: 'server', status: 'up', details: { cpu: 4.7, load: 0.0 }, last_check: '' }, + { id: 298, name: 'WhatsApp', category: 'server', status: 'up', details: { cpu: 3.0, load: 0.04 }, last_check: '' }, + { id: 299, name: 'WhatSMS', category: 'server', status: 'up', details: { cpu: 2.1, load: 0.08 }, last_check: '' }, + ], + service: [ + { id: 1, name: 'Planeamento EAL', category: 'service', status: 'up', details: {}, last_check: '' }, + { id: 2, name: 'Desk CRM', category: 'service', status: 'up', details: {}, last_check: '' }, + { id: 3, name: 'Automator N8N', category: 'service', status: 'up', details: {}, last_check: '' }, + { id: 4, name: 'NextCloud', category: 'service', status: 'up', details: {}, last_check: '' }, + { id: 517, name: 'Gitea', category: 'service', status: 'up', details: {}, last_check: '' }, + { id: 518, name: 'Meet Jitsi', category: 'service', status: 'up', details: {}, last_check: '' }, + { id: 519, name: 'WikiJS', category: 'service', status: 'up', details: {}, last_check: '' }, + { id: 521, name: 'Google Docs', category: 'service', status: 'up', details: {}, last_check: '' }, + { id: 549, name: 'MCP Hub', category: 'service', status: 'up', details: {}, last_check: '' }, + { id: 515, name: 'WhatSMS', category: 'service', status: 'up', details: {}, last_check: '' }, + ], + site: [ + { id: 15960, name: 'Descomplicar', category: 'site', status: 'up', details: { domain: 'descomplicar.pt', response_time: 0.093 }, last_check: '' }, + { id: 15961, name: 'Emanuel Almeida', category: 'site', status: 'up', details: { domain: 'emanuelalmeida.pt', response_time: 1.687 }, last_check: '' }, + { id: 15959, name: 'Family Clinic', category: 'site', status: 'up', details: { domain: 'familyclinic.pt', response_time: 2.712 }, last_check: '' }, + { id: 15962, name: 'WTC', category: 'site', status: 'up', details: { domain: 'wtc-group.com' }, last_check: '' }, + { id: 15963, name: 'Carstuff', category: 'site', status: 'down', details: { domain: 'carstuff.pt' }, last_check: '' }, + { id: 15964, name: 'Espiral Senior', category: 'site', status: 'up', details: { domain: 'espiralsenior.pt' }, last_check: '' }, + { id: 15965, name: 'Karate Gaia', category: 'site', status: 'up', details: { domain: 'karategaia.pt' }, last_check: '' }, + ], + container: [ + { id: 7, name: 'EasyPanel Containers', category: 'container', status: 'warning', details: { up: 83, total: 87, down: 4 }, last_check: '' }, + ], + backup: [ + { id: 15967, name: 'MySQL Hourly', category: 'backup', status: 'ok', details: { age_hours: 1 }, last_check: '' }, + { id: 15968, name: 'CWP Accounts', category: 'backup', status: 'warning', details: { age_hours: 48 }, last_check: '' }, + { id: 15969, name: 'Easy Backup', category: 'backup', status: 'ok', details: { age_hours: 12 }, last_check: '' }, + { id: 15970, name: 'Server->Easy Sync', category: 'backup', status: 'failed', details: { age_hours: 72 }, last_check: '' }, + ], + storage: [ + { id: 15971, name: 'gordo', category: 'storage', status: 'ok', details: { used: '89GB', total: '200GB', percent: 45 }, last_check: '' }, + { id: 15972, name: 'gordito', category: 'storage', status: 'ok', details: { used: '42GB', total: '100GB', percent: 42 }, last_check: '' }, + ], + wp_update: [ + { id: 25, name: 'WordPress Plugins', category: 'wp_update', status: 'warning', details: { manual_updates: 3 }, last_check: '' }, ], } + + return { + overall: 'warning', + summary: [ + { category: 'server', total: 6, ok: 6, warning: 0, critical: 0 }, + { category: 'service', total: 10, ok: 10, warning: 0, critical: 0 }, + { category: 'site', total: 7, ok: 6, warning: 0, critical: 1 }, + { category: 'container', total: 1, ok: 0, warning: 1, critical: 0 }, + { category: 'backup', total: 4, ok: 2, warning: 1, critical: 1 }, + { category: 'storage', total: 2, ok: 2, warning: 0, critical: 0 }, + ], + stats: { + total_ok: 26, + total_warning: 2, + total_critical: 2 + }, + items: mockItems, + } }