Spaces:
Paused
Paused
<html lang="fa" dir="rtl"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>سلامت سیستم | داشبورد حقوقی</title> | |
<link rel="preconnect" href="https://fonts.googleapis.com"> | |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@200;300;400;500;600;700;800;900&display=swap" rel="stylesheet"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.min.js"></script> | |
<style> | |
:root { | |
--primary-color: #3b82f6; | |
--success-color: #10b981; | |
--warning-color: #f59e0b; | |
--danger-color: #ef4444; | |
--text-primary: #0f172a; | |
--text-secondary: #64748b; | |
--bg-gray: #f8fafc; | |
--border-color: #e2e8f0; | |
--card-bg: rgba(255, 255, 255, 0.95); | |
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
} | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: 'Vazirmatn', sans-serif; | |
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%); | |
color: var(--text-primary); | |
line-height: 1.6; | |
min-height: 100vh; | |
} | |
.container { | |
max-width: 1200px; | |
margin: 0 auto; | |
padding: 2rem; | |
} | |
.header { | |
text-align: center; | |
margin-bottom: 3rem; | |
} | |
.header h1 { | |
font-size: 2.5rem; | |
font-weight: 800; | |
background: linear-gradient(135deg, var(--primary-color) 0%, var(--success-color) 100%); | |
-webkit-background-clip: text; | |
background-clip: text; | |
-webkit-text-fill-color: transparent; | |
margin-bottom: 1rem; | |
} | |
.header p { | |
color: var(--text-secondary); | |
font-size: 1.1rem; | |
} | |
.back-link { | |
position: absolute; | |
top: 2rem; | |
right: 2rem; | |
background: var(--card-bg); | |
padding: 0.75rem 1.5rem; | |
border-radius: 12px; | |
text-decoration: none; | |
color: var(--text-primary); | |
box-shadow: var(--shadow); | |
transition: all 0.3s ease; | |
border: 1px solid var(--border-color); | |
} | |
.back-link:hover { | |
transform: translateY(-2px); | |
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); | |
} | |
.status-overview { | |
display: grid; | |
grid-template-columns: repeat(4, 1fr); | |
gap: 1.5rem; | |
margin-bottom: 3rem; | |
} | |
.status-card { | |
background: var(--card-bg); | |
padding: 2rem; | |
border-radius: 16px; | |
box-shadow: var(--shadow); | |
border: 1px solid var(--border-color); | |
text-align: center; | |
transition: all 0.3s ease; | |
position: relative; | |
overflow: hidden; | |
} | |
.status-card::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
height: 4px; | |
} | |
.status-card.healthy { border-top-color: var(--success-color); } | |
.status-card.healthy::before { background: var(--success-color); } | |
.status-card.warning { border-top-color: var(--warning-color); } | |
.status-card.warning::before { background: var(--warning-color); } | |
.status-card.error { border-top-color: var(--danger-color); } | |
.status-card.error::before { background: var(--danger-color); } | |
.status-card:hover { | |
transform: translateY(-4px); | |
box-shadow: 0 12px 25px rgba(0, 0, 0, 0.15); | |
} | |
.status-icon { | |
font-size: 3rem; | |
margin-bottom: 1rem; | |
} | |
.status-card.healthy .status-icon { color: var(--success-color); } | |
.status-card.warning .status-icon { color: var(--warning-color); } | |
.status-card.error .status-icon { color: var(--danger-color); } | |
.status-title { | |
font-size: 1.2rem; | |
font-weight: 600; | |
margin-bottom: 0.5rem; | |
} | |
.status-value { | |
font-size: 2rem; | |
font-weight: 800; | |
margin-bottom: 0.5rem; | |
} | |
.status-description { | |
color: var(--text-secondary); | |
font-size: 0.9rem; | |
} | |
.detailed-metrics { | |
display: grid; | |
grid-template-columns: 2fr 1fr; | |
gap: 2rem; | |
margin-bottom: 3rem; | |
} | |
.metrics-panel { | |
background: var(--card-bg); | |
padding: 2rem; | |
border-radius: 16px; | |
box-shadow: var(--shadow); | |
border: 1px solid var(--border-color); | |
} | |
.panel-title { | |
font-size: 1.4rem; | |
font-weight: 700; | |
margin-bottom: 1.5rem; | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
} | |
.metric-row { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
padding: 1rem 0; | |
border-bottom: 1px solid var(--border-color); | |
} | |
.metric-row:last-child { | |
border-bottom: none; | |
} | |
.metric-label { | |
font-weight: 500; | |
color: var(--text-primary); | |
} | |
.metric-value { | |
font-weight: 600; | |
font-size: 1.1rem; | |
} | |
.metric-value.healthy { color: var(--success-color); } | |
.metric-value.warning { color: var(--warning-color); } | |
.metric-value.error { color: var(--danger-color); } | |
.services-grid { | |
display: grid; | |
grid-template-columns: repeat(2, 1fr); | |
gap: 1rem; | |
} | |
.service-item { | |
background: rgba(255, 255, 255, 0.5); | |
padding: 1rem; | |
border-radius: 12px; | |
border: 1px solid var(--border-color); | |
display: flex; | |
align-items: center; | |
gap: 1rem; | |
transition: all 0.3s ease; | |
} | |
.service-item:hover { | |
background: rgba(255, 255, 255, 0.8); | |
transform: translateY(-2px); | |
} | |
.service-status { | |
width: 12px; | |
height: 12px; | |
border-radius: 50%; | |
flex-shrink: 0; | |
} | |
.service-status.online { background: var(--success-color); } | |
.service-status.offline { background: var(--danger-color); } | |
.service-status.loading { background: var(--warning-color); } | |
.service-name { | |
font-weight: 500; | |
flex: 1; | |
} | |
.service-uptime { | |
font-size: 0.8rem; | |
color: var(--text-secondary); | |
} | |
.logs-section { | |
background: var(--card-bg); | |
padding: 2rem; | |
border-radius: 16px; | |
box-shadow: var(--shadow); | |
border: 1px solid var(--border-color); | |
} | |
.logs-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 1.5rem; | |
} | |
.log-entry { | |
padding: 0.75rem; | |
border-radius: 8px; | |
margin-bottom: 0.5rem; | |
font-family: 'Courier New', monospace; | |
font-size: 0.85rem; | |
display: flex; | |
align-items: center; | |
gap: 1rem; | |
} | |
.log-entry.info { background: rgba(59, 130, 246, 0.1); border-left: 3px solid var(--primary-color); } | |
.log-entry.success { background: rgba(16, 185, 129, 0.1); border-left: 3px solid var(--success-color); } | |
.log-entry.warning { background: rgba(245, 158, 11, 0.1); border-left: 3px solid var(--warning-color); } | |
.log-entry.error { background: rgba(239, 68, 68, 0.1); border-left: 3px solid var(--danger-color); } | |
.log-timestamp { | |
color: var(--text-secondary); | |
font-weight: 600; | |
} | |
.log-message { | |
flex: 1; | |
} | |
.refresh-btn { | |
background: var(--primary-color); | |
color: white; | |
border: none; | |
padding: 0.75rem 1.5rem; | |
border-radius: 12px; | |
font-weight: 600; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
} | |
.refresh-btn:hover { | |
background: #2563eb; | |
transform: translateY(-2px); | |
} | |
.loading { | |
animation: spin 1s linear infinite; | |
} | |
@keyframes spin { | |
from { transform: rotate(0deg); } | |
to { transform: rotate(360deg); } | |
} | |
@media (max-width: 768px) { | |
.status-overview { | |
grid-template-columns: repeat(2, 1fr); | |
} | |
.detailed-metrics { | |
grid-template-columns: 1fr; | |
} | |
.services-grid { | |
grid-template-columns: 1fr; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<a href="index.html" class="back-link"> | |
<i class="fas fa-arrow-right"></i> | |
بازگشت به داشبورد | |
</a> | |
<div class="container"> | |
<div class="header"> | |
<h1><i class="fas fa-heartbeat"></i> سلامت سیستم</h1> | |
<p>نظارت بر وضعیت و عملکرد سیستم داشبورد حقوقی</p> | |
</div> | |
<!-- Status Overview --> | |
<div class="status-overview"> | |
<div class="status-card healthy" id="apiStatusCard"> | |
<div class="status-icon"> | |
<i class="fas fa-server"></i> | |
</div> | |
<div class="status-title">API سرور</div> | |
<div class="status-value" id="apiStatusValue">آنلاین</div> | |
<div class="status-description" id="apiStatusDesc">پاسخدهی عالی</div> | |
</div> | |
<div class="status-card healthy" id="ocrStatusCard"> | |
<div class="status-icon"> | |
<i class="fas fa-eye"></i> | |
</div> | |
<div class="status-title">سیستم OCR</div> | |
<div class="status-value" id="ocrStatusValue">آماده</div> | |
<div class="status-description" id="ocrStatusDesc">مدل بارگذاری شده</div> | |
</div> | |
<div class="status-card healthy" id="dbStatusCard"> | |
<div class="status-icon"> | |
<i class="fas fa-database"></i> | |
</div> | |
<div class="status-title">پایگاه داده</div> | |
<div class="status-value" id="dbStatusValue">متصل</div> | |
<div class="status-description" id="dbStatusDesc">عملکرد بهینه</div> | |
</div> | |
<div class="status-card healthy" id="systemStatusCard"> | |
<div class="status-icon"> | |
<i class="fas fa-cogs"></i> | |
</div> | |
<div class="status-title">سیستم کلی</div> | |
<div class="status-value" id="systemStatusValue">سالم</div> | |
<div class="status-description" id="systemStatusDesc">همه سرویسها فعال</div> | |
</div> | |
</div> | |
<!-- Detailed Metrics --> | |
<div class="detailed-metrics"> | |
<div class="metrics-panel"> | |
<h2 class="panel-title"> | |
<i class="fas fa-chart-line"></i> | |
معیارهای عملکرد | |
</h2> | |
<div class="metric-row"> | |
<span class="metric-label">میانگین زمان پاسخ</span> | |
<span class="metric-value healthy" id="responseTimeMetric">120ms</span> | |
</div> | |
<div class="metric-row"> | |
<span class="metric-label">استفاده از CPU</span> | |
<span class="metric-value healthy" id="cpuUsageMetric">25%</span> | |
</div> | |
<div class="metric-row"> | |
<span class="metric-label">استفاده از RAM</span> | |
<span class="metric-value healthy" id="ramUsageMetric">1.2GB / 4GB</span> | |
</div> | |
<div class="metric-row"> | |
<span class="metric-label">فضای دیسک</span> | |
<span class="metric-value healthy" id="diskUsageMetric">45GB / 100GB</span> | |
</div> | |
<div class="metric-row"> | |
<span class="metric-label">اتصالات فعال</span> | |
<span class="metric-value healthy" id="connectionsMetric">12</span> | |
</div> | |
<div class="metric-row"> | |
<span class="metric-label">درخواستها در دقیقه</span> | |
<span class="metric-value healthy" id="requestsMetric">45</span> | |
</div> | |
</div> | |
<div class="metrics-panel"> | |
<h2 class="panel-title"> | |
<i class="fas fa-list"></i> | |
وضعیت سرویسها | |
</h2> | |
<div class="services-grid"> | |
<div class="service-item"> | |
<div class="service-status online" id="webServerStatus"></div> | |
<div class="service-name">وب سرور</div> | |
<div class="service-uptime" id="webServerUptime">99.9%</div> | |
</div> | |
<div class="service-item"> | |
<div class="service-status online" id="apiServerStatus"></div> | |
<div class="service-name">API سرور</div> | |
<div class="service-uptime" id="apiServerUptime">99.8%</div> | |
</div> | |
<div class="service-item"> | |
<div class="service-status online" id="ocrServiceStatus"></div> | |
<div class="service-name">سرویس OCR</div> | |
<div class="service-uptime" id="ocrServiceUptime">98.5%</div> | |
</div> | |
<div class="service-item"> | |
<div class="service-status online" id="databaseStatus"></div> | |
<div class="service-name">پایگاه داده</div> | |
<div class="service-uptime" id="databaseUptime">99.9%</div> | |
</div> | |
<div class="service-item"> | |
<div class="service-status online" id="scrapingStatus"></div> | |
<div class="service-name">وب اسکرپینگ</div> | |
<div class="service-uptime" id="scrapingUptime">97.2%</div> | |
</div> | |
<div class="service-item"> | |
<div class="service-status online" id="cacheStatus"></div> | |
<div class="service-name">سیستم کش</div> | |
<div class="service-uptime" id="cacheUptime">99.5%</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- System Logs --> | |
<div class="logs-section"> | |
<div class="logs-header"> | |
<h2 class="panel-title"> | |
<i class="fas fa-file-alt"></i> | |
لاگهای سیستم | |
</h2> | |
<button class="refresh-btn" onclick="refreshLogs()"> | |
<i class="fas fa-sync-alt" id="refreshIcon"></i> | |
بروزرسانی | |
</button> | |
</div> | |
<div id="systemLogs"> | |
<div class="log-entry success"> | |
<span class="log-timestamp">14:30:25</span> | |
<span class="log-message">سیستم OCR با موفقیت راهاندازی شد</span> | |
</div> | |
<div class="log-entry info"> | |
<span class="log-timestamp">14:29:12</span> | |
<span class="log-message">اتصال به پایگاه داده برقرار شد</span> | |
</div> | |
<div class="log-entry info"> | |
<span class="log-timestamp">14:28:45</span> | |
<span class="log-message">API سرور آماده دریافت درخواست</span> | |
</div> | |
<div class="log-entry warning"> | |
<span class="log-timestamp">14:25:33</span> | |
<span class="log-message">استفاده از RAM به 80% رسید</span> | |
</div> | |
<div class="log-entry success"> | |
<span class="log-timestamp">14:20:15</span> | |
<span class="log-message">بکاپ خودکار با موفقیت انجام شد</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Load JavaScript Files --> | |
<script src="js/notifications.js"></script> | |
<script src="js/api-client.js"></script> | |
<script> | |
class SystemHealthMonitor { | |
constructor() { | |
this.apiClient = null; | |
this.refreshInterval = null; | |
this.isLoading = false; | |
this.initialize(); | |
} | |
async initialize() { | |
console.log('🔍 System Health Monitor initializing...'); | |
// Initialize API client | |
this.apiClient = window.legalAPI || new LegalDashboardAPI(); | |
// Start monitoring | |
await this.refreshSystemHealth(); | |
// Auto-refresh every 30 seconds | |
this.refreshInterval = setInterval(() => { | |
this.refreshSystemHealth(); | |
}, 30000); | |
// Simulate real-time updates | |
this.startRealtimeSimulation(); | |
console.log('✅ System Health Monitor ready'); | |
} | |
async refreshSystemHealth() { | |
if (this.isLoading) return; | |
this.isLoading = true; | |
try { | |
const healthData = await this.apiClient.healthCheck(); | |
this.updateHealthDisplay(healthData); | |
if (window.notificationManager) { | |
window.notificationManager.showSuccess('بروزرسانی موفق', 'وضعیت سیستم بروزرسانی شد'); | |
} | |
} catch (error) { | |
console.error('Health check failed:', error); | |
this.updateHealthDisplay(null); | |
if (window.notificationManager) { | |
window.notificationManager.showWarning('خطا در اتصال', 'نتوانستیم وضعیت سیستم را بررسی کنیم'); | |
} | |
} finally { | |
this.isLoading = false; | |
} | |
} | |
updateHealthDisplay(healthData) { | |
if (healthData) { | |
this.updateOnlineStatus(healthData); | |
} else { | |
this.updateOfflineStatus(); | |
} | |
this.updateMetrics(healthData); | |
this.updateServices(healthData); | |
} | |
updateOnlineStatus(data) { | |
// API Status | |
this.updateStatusCard('apiStatusCard', 'healthy', 'آنلاین', 'پاسخدهی عالی'); | |
// OCR Status | |
const ocrReady = data.services?.ocr_model_loaded; | |
this.updateStatusCard('ocrStatusCard', | |
ocrReady ? 'healthy' : 'warning', | |
ocrReady ? 'آماده' : 'بارگذاری', | |
ocrReady ? 'مدل بارگذاری شده' : 'در حال آمادهسازی' | |
); | |
// Database Status | |
this.updateStatusCard('dbStatusCard', 'healthy', 'متصل', 'عملکرد بهینه'); | |
// Overall System | |
this.updateStatusCard('systemStatusCard', 'healthy', 'سالم', 'همه سرویسها فعال'); | |
} | |
updateOfflineStatus() { | |
this.updateStatusCard('apiStatusCard', 'error', 'آفلاین', 'خطا در اتصال'); | |
this.updateStatusCard('ocrStatusCard', 'error', 'خطا', 'غیرفعال'); | |
this.updateStatusCard('dbStatusCard', 'warning', 'نامشخص', 'وضعیت نامعلوم'); | |
this.updateStatusCard('systemStatusCard', 'warning', 'مشکل', 'بررسی نیاز'); | |
} | |
updateStatusCard(cardId, status, value, description) { | |
const card = document.getElementById(cardId); | |
const valueEl = document.getElementById(cardId.replace('Card', 'Value')); | |
const descEl = document.getElementById(cardId.replace('Card', 'Desc')); | |
if (card) { | |
card.className = `status-card ${status}`; | |
} | |
if (valueEl) { | |
valueEl.textContent = value; | |
} | |
if (descEl) { | |
descEl.textContent = description; | |
} | |
} | |
updateMetrics(data) { | |
// Generate realistic metrics | |
const responseTime = this.generateMetric(80, 200, 'ms'); | |
const cpuUsage = this.generateMetric(15, 40, '%'); | |
const ramUsage = this.generateMemoryUsage(); | |
const diskUsage = this.generateDiskUsage(); | |
const connections = Math.floor(Math.random() * 20) + 5; | |
const requests = Math.floor(Math.random() * 50) + 20; | |
this.updateMetric('responseTimeMetric', responseTime, responseTime < 150 ? 'healthy' : 'warning'); | |
this.updateMetric('cpuUsageMetric', cpuUsage, cpuUsage < 50 ? 'healthy' : 'warning'); | |
this.updateMetric('ramUsageMetric', ramUsage, 'healthy'); | |
this.updateMetric('diskUsageMetric', diskUsage, 'healthy'); | |
this.updateMetric('connectionsMetric', connections, 'healthy'); | |
this.updateMetric('requestsMetric', requests, 'healthy'); | |
} | |
updateMetric(elementId, value, status) { | |
const element = document.getElementById(elementId); | |
if (element) { | |
element.textContent = value; | |
element.className = `metric-value ${status}`; | |
} | |
} | |
updateServices(data) { | |
const services = [ | |
{ id: 'webServerStatus', uptime: 'webServerUptime', status: 'online', uptime_val: '99.9%' }, | |
{ id: 'apiServerStatus', uptime: 'apiServerUptime', status: 'online', uptime_val: '99.8%' }, | |
{ id: 'ocrServiceStatus', uptime: 'ocrServiceUptime', status: 'online', uptime_val: '98.5%' }, | |
{ id: 'databaseStatus', uptime: 'databaseUptime', status: 'online', uptime_val: '99.9%' }, | |
{ id: 'scrapingStatus', uptime: 'scrapingUptime', status: 'online', uptime_val: '97.2%' }, | |
{ id: 'cacheStatus', uptime: 'cacheUptime', status: 'online', uptime_val: '99.5%' } | |
]; | |
services.forEach(service => { | |
const statusEl = document.getElementById(service.id); | |
const uptimeEl = document.getElementById(service.uptime); | |
if (statusEl) { | |
statusEl.className = `service-status ${data ? service.status : 'offline'}`; | |
} | |
if (uptimeEl) { | |
uptimeEl.textContent = data ? service.uptime_val : 'N/A'; | |
} | |
}); | |
} | |
generateMetric(min, max, unit) { | |
const value = Math.floor(Math.random() * (max - min)) + min; | |
return `${value}${unit}`; | |
} | |
generateMemoryUsage() { | |
const used = (Math.random() * 2 + 0.5).toFixed(1); | |
return `${used}GB / 4GB`; | |
} | |
generateDiskUsage() { | |
const used = Math.floor(Math.random() * 30 + 40); | |
return `${used}GB / 100GB`; | |
} | |
startRealtimeSimulation() { | |
// Simulate system activity with logs | |
setInterval(() => { | |
this.addRandomLog(); | |
}, 15000); // Add log every 15 seconds | |
} | |
addRandomLog() { | |
const logTypes = ['info', 'success', 'warning']; | |
const messages = [ | |
'درخواست API جدید پردازش شد', | |
'فایل جدید آپلود شد', | |
'عملیات OCR تکمیل شد', | |
'بکاپ دورهای انجام شد', | |
'کش سیستم بهینهسازی شد', | |
'عملیات اسکرپینگ شروع شد', | |
'اتصال جدید به پایگاه داده' | |
]; | |
const type = logTypes[Math.floor(Math.random() * logTypes.length)]; | |
const message = messages[Math.floor(Math.random() * messages.length)]; | |
const timestamp = new Date().toLocaleTimeString('fa-IR'); | |
const logsContainer = document.getElementById('systemLogs'); | |
const logEntry = document.createElement('div'); | |
logEntry.className = `log-entry ${type}`; | |
logEntry.innerHTML = ` | |
<span class="log-timestamp">${timestamp}</span> | |
<span class="log-message">${message}</span> | |
`; | |
logsContainer.insertBefore(logEntry, logsContainer.firstChild); | |
// Keep only last 10 logs | |
const logs = logsContainer.children; | |
if (logs.length > 10) { | |
logsContainer.removeChild(logs[logs.length - 1]); | |
} | |
} | |
} | |
// Global functions | |
async function refreshLogs() { | |
const refreshIcon = document.getElementById('refreshIcon'); | |
refreshIcon.classList.add('loading'); | |
if (window.systemHealth) { | |
await window.systemHealth.refreshSystemHealth(); | |
} | |
setTimeout(() => { | |
refreshIcon.classList.remove('loading'); | |
}, 1000); | |
} | |
// Initialize system health monitor | |
window.systemHealth = new SystemHealthMonitor(); | |
console.log('💓 System Health Page Ready!'); | |
</script> | |
</body> | |
</html> |