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>
This commit is contained in:
2026-02-04 23:34:43 +00:00
parent bd21a8d511
commit 75f29ee6d5
3 changed files with 109 additions and 52 deletions

View File

@@ -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

View File

@@ -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
}
}
}

View File

@@ -28,14 +28,23 @@ interface MonitorItem {
last_check: string
}
interface MonitorData {
items: MonitorItem[]
summary: {
interface CategorySummary {
category: string
total: number
ok: number
warning: number
critical: number
}
interface MonitorData {
items: Record<string, MonitorItem[]>
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<string, MonitorItem[]>)
// 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 */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mb-8">
<SummaryCard value={data.summary.ok} label="Operacionais" color="text-emerald-400" icon={CheckCircle2} />
<SummaryCard value={data.summary.warning} label="Avisos" color="text-amber-400" icon={AlertTriangle} />
<SummaryCard value={data.summary.critical} label="Críticos" color="text-red-400" icon={XCircle} />
<SummaryCard value={data.stats.total_ok} label="Operacionais" color="text-emerald-400" icon={CheckCircle2} />
<SummaryCard value={data.stats.total_warning} label="Avisos" color="text-amber-400" icon={AlertTriangle} />
<SummaryCard value={data.stats.total_critical} label="Críticos" color="text-red-400" icon={XCircle} />
</div>
{/* 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<string, MonitorItem[]> = {
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,
}
}