init
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Activity, Clock, FileText, AlertTriangle } from 'lucide-react';
|
||||
import { monitoringAPI } from '../services/api';
|
||||
|
||||
function Logs() {
|
||||
const [backendLog, setBackendLog] = useState('');
|
||||
const [healthHistory, setHealthHistory] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [tab, setTab] = useState('logs');
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const [logRes, healthRes] = await Promise.all([
|
||||
monitoringAPI.getBackendLogs(100),
|
||||
monitoringAPI.getHealthHistory(),
|
||||
]);
|
||||
setBackendLog(logRes.data.log || '');
|
||||
setHealthHistory(healthRes.data || []);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch monitoring data:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl font-bold text-gray-900">System Monitoring</h1>
|
||||
<p className="mt-2 text-gray-600">
|
||||
View backend logs and health history
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-4 flex gap-4">
|
||||
<button
|
||||
className={`px-4 py-2 rounded ${tab === 'logs' ? 'bg-primary-600 text-white' : 'bg-gray-200 text-gray-800'}`}
|
||||
onClick={() => setTab('logs')}
|
||||
>
|
||||
<FileText className="inline-block mr-2" /> Backend Logs
|
||||
</button>
|
||||
<button
|
||||
className={`px-4 py-2 rounded ${tab === 'health' ? 'bg-primary-600 text-white' : 'bg-gray-200 text-gray-800'}`}
|
||||
onClick={() => setTab('health')}
|
||||
>
|
||||
<Clock className="inline-block mr-2" /> Health History
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{tab === 'logs' && (
|
||||
<div className="card">
|
||||
<div className="flex items-center mb-4">
|
||||
<FileText className="h-6 w-6 text-primary-500 mr-2" />
|
||||
<h3 className="text-lg font-medium text-gray-900">Backend Logs (last 100 lines)</h3>
|
||||
</div>
|
||||
<div className="bg-gray-900 text-green-400 p-4 rounded-lg font-mono text-sm h-96 overflow-y-auto">
|
||||
<pre>{backendLog || 'No logs available.'}</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tab === 'health' && (
|
||||
<div className="card">
|
||||
<div className="flex items-center mb-4">
|
||||
<Clock className="h-6 w-6 text-primary-500 mr-2" />
|
||||
<h3 className="text-lg font-medium text-gray-900">Health History (last 100 checks)</h3>
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="px-2 py-1 text-left">Timestamp</th>
|
||||
<th className="px-2 py-1 text-left">Network</th>
|
||||
<th className="px-2 py-1 text-left">WireGuard</th>
|
||||
<th className="px-2 py-1 text-left">Email</th>
|
||||
<th className="px-2 py-1 text-left">Calendar</th>
|
||||
<th className="px-2 py-1 text-left">Files</th>
|
||||
<th className="px-2 py-1 text-left">Routing</th>
|
||||
<th className="px-2 py-1 text-left">Vault</th>
|
||||
<th className="px-2 py-1 text-left">Alerts</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{healthHistory.map((h, i) => (
|
||||
<tr key={i} className={h.alerts && h.alerts.length > 0 ? 'bg-red-100' : ''}>
|
||||
<td className="px-2 py-1 font-mono">{h.timestamp}</td>
|
||||
<td className="px-2 py-1">
|
||||
{h.network?.status === 'online' || h.network?.running === true ?
|
||||
<span className="text-green-600">OK</span> :
|
||||
<span className="text-red-600 font-bold">Down</span>
|
||||
}
|
||||
</td>
|
||||
<td className="px-2 py-1">
|
||||
{h.wireguard?.status === 'online' || h.wireguard?.running === true ?
|
||||
<span className="text-green-600">OK</span> :
|
||||
<span className="text-red-600 font-bold">Down</span>
|
||||
}
|
||||
</td>
|
||||
<td className="px-2 py-1">
|
||||
{h.email?.status === 'online' || h.email?.running === true ?
|
||||
<span className="text-green-600">OK</span> :
|
||||
<span className="text-red-600 font-bold">Down</span>
|
||||
}
|
||||
</td>
|
||||
<td className="px-2 py-1">
|
||||
{h.calendar?.status === 'online' || h.calendar?.running === true ?
|
||||
<span className="text-green-600">OK</span> :
|
||||
<span className="text-red-600 font-bold">Down</span>
|
||||
}
|
||||
</td>
|
||||
<td className="px-2 py-1">
|
||||
{h.files?.status === 'online' || h.files?.running === true ?
|
||||
<span className="text-green-600">OK</span> :
|
||||
<span className="text-red-600 font-bold">Down</span>
|
||||
}
|
||||
</td>
|
||||
<td className="px-2 py-1">
|
||||
{h.routing?.status === 'online' || h.routing?.running === true ?
|
||||
<span className="text-green-600">OK</span> :
|
||||
<span className="text-red-600 font-bold">Down</span>
|
||||
}
|
||||
</td>
|
||||
<td className="px-2 py-1">
|
||||
{h.vault?.status === 'online' || h.vault?.running === true ?
|
||||
<span className="text-green-600">OK</span> :
|
||||
<span className="text-red-600 font-bold">Down</span>
|
||||
}
|
||||
</td>
|
||||
<td className="px-2 py-1">
|
||||
{h.alerts && h.alerts.length > 0 ? (
|
||||
<div className="flex flex-col gap-1">
|
||||
{h.alerts.map((a, j) => (
|
||||
<span key={j} className="text-red-700 font-semibold flex items-center"><AlertTriangle className="inline-block h-4 w-4 mr-1 text-red-500" />{a}</span>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-green-600">None</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Logs;
|
||||
Reference in New Issue
Block a user