Really-amin commited on
Commit
aef06e5
·
verified ·
1 Parent(s): efc5f32

Upload 2 files

Browse files
app/static/index.html CHANGED
The diff for this file is too large to render. See raw diff
 
app/static/system-health.html ADDED
@@ -0,0 +1,738 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fa" dir="rtl">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>سلامت سیستم | داشبورد حقوقی</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
11
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.min.js"></script>
12
+
13
+ <style>
14
+ :root {
15
+ --primary-color: #3b82f6;
16
+ --success-color: #10b981;
17
+ --warning-color: #f59e0b;
18
+ --danger-color: #ef4444;
19
+ --text-primary: #0f172a;
20
+ --text-secondary: #64748b;
21
+ --bg-gray: #f8fafc;
22
+ --border-color: #e2e8f0;
23
+ --card-bg: rgba(255, 255, 255, 0.95);
24
+ --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
25
+ }
26
+
27
+ * {
28
+ margin: 0;
29
+ padding: 0;
30
+ box-sizing: border-box;
31
+ }
32
+
33
+ body {
34
+ font-family: 'Vazirmatn', sans-serif;
35
+ background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
36
+ color: var(--text-primary);
37
+ line-height: 1.6;
38
+ min-height: 100vh;
39
+ }
40
+
41
+ .container {
42
+ max-width: 1200px;
43
+ margin: 0 auto;
44
+ padding: 2rem;
45
+ }
46
+
47
+ .header {
48
+ text-align: center;
49
+ margin-bottom: 3rem;
50
+ }
51
+
52
+ .header h1 {
53
+ font-size: 2.5rem;
54
+ font-weight: 800;
55
+ background: linear-gradient(135deg, var(--primary-color) 0%, var(--success-color) 100%);
56
+ -webkit-background-clip: text;
57
+ background-clip: text;
58
+ -webkit-text-fill-color: transparent;
59
+ margin-bottom: 1rem;
60
+ }
61
+
62
+ .header p {
63
+ color: var(--text-secondary);
64
+ font-size: 1.1rem;
65
+ }
66
+
67
+ .back-link {
68
+ position: absolute;
69
+ top: 2rem;
70
+ right: 2rem;
71
+ background: var(--card-bg);
72
+ padding: 0.75rem 1.5rem;
73
+ border-radius: 12px;
74
+ text-decoration: none;
75
+ color: var(--text-primary);
76
+ box-shadow: var(--shadow);
77
+ transition: all 0.3s ease;
78
+ border: 1px solid var(--border-color);
79
+ }
80
+
81
+ .back-link:hover {
82
+ transform: translateY(-2px);
83
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
84
+ }
85
+
86
+ .status-overview {
87
+ display: grid;
88
+ grid-template-columns: repeat(4, 1fr);
89
+ gap: 1.5rem;
90
+ margin-bottom: 3rem;
91
+ }
92
+
93
+ .status-card {
94
+ background: var(--card-bg);
95
+ padding: 2rem;
96
+ border-radius: 16px;
97
+ box-shadow: var(--shadow);
98
+ border: 1px solid var(--border-color);
99
+ text-align: center;
100
+ transition: all 0.3s ease;
101
+ position: relative;
102
+ overflow: hidden;
103
+ }
104
+
105
+ .status-card::before {
106
+ content: '';
107
+ position: absolute;
108
+ top: 0;
109
+ left: 0;
110
+ right: 0;
111
+ height: 4px;
112
+ }
113
+
114
+ .status-card.healthy { border-top-color: var(--success-color); }
115
+ .status-card.healthy::before { background: var(--success-color); }
116
+ .status-card.warning { border-top-color: var(--warning-color); }
117
+ .status-card.warning::before { background: var(--warning-color); }
118
+ .status-card.error { border-top-color: var(--danger-color); }
119
+ .status-card.error::before { background: var(--danger-color); }
120
+
121
+ .status-card:hover {
122
+ transform: translateY(-4px);
123
+ box-shadow: 0 12px 25px rgba(0, 0, 0, 0.15);
124
+ }
125
+
126
+ .status-icon {
127
+ font-size: 3rem;
128
+ margin-bottom: 1rem;
129
+ }
130
+
131
+ .status-card.healthy .status-icon { color: var(--success-color); }
132
+ .status-card.warning .status-icon { color: var(--warning-color); }
133
+ .status-card.error .status-icon { color: var(--danger-color); }
134
+
135
+ .status-title {
136
+ font-size: 1.2rem;
137
+ font-weight: 600;
138
+ margin-bottom: 0.5rem;
139
+ }
140
+
141
+ .status-value {
142
+ font-size: 2rem;
143
+ font-weight: 800;
144
+ margin-bottom: 0.5rem;
145
+ }
146
+
147
+ .status-description {
148
+ color: var(--text-secondary);
149
+ font-size: 0.9rem;
150
+ }
151
+
152
+ .detailed-metrics {
153
+ display: grid;
154
+ grid-template-columns: 2fr 1fr;
155
+ gap: 2rem;
156
+ margin-bottom: 3rem;
157
+ }
158
+
159
+ .metrics-panel {
160
+ background: var(--card-bg);
161
+ padding: 2rem;
162
+ border-radius: 16px;
163
+ box-shadow: var(--shadow);
164
+ border: 1px solid var(--border-color);
165
+ }
166
+
167
+ .panel-title {
168
+ font-size: 1.4rem;
169
+ font-weight: 700;
170
+ margin-bottom: 1.5rem;
171
+ display: flex;
172
+ align-items: center;
173
+ gap: 0.5rem;
174
+ }
175
+
176
+ .metric-row {
177
+ display: flex;
178
+ justify-content: space-between;
179
+ align-items: center;
180
+ padding: 1rem 0;
181
+ border-bottom: 1px solid var(--border-color);
182
+ }
183
+
184
+ .metric-row:last-child {
185
+ border-bottom: none;
186
+ }
187
+
188
+ .metric-label {
189
+ font-weight: 500;
190
+ color: var(--text-primary);
191
+ }
192
+
193
+ .metric-value {
194
+ font-weight: 600;
195
+ font-size: 1.1rem;
196
+ }
197
+
198
+ .metric-value.healthy { color: var(--success-color); }
199
+ .metric-value.warning { color: var(--warning-color); }
200
+ .metric-value.error { color: var(--danger-color); }
201
+
202
+ .services-grid {
203
+ display: grid;
204
+ grid-template-columns: repeat(2, 1fr);
205
+ gap: 1rem;
206
+ }
207
+
208
+ .service-item {
209
+ background: rgba(255, 255, 255, 0.5);
210
+ padding: 1rem;
211
+ border-radius: 12px;
212
+ border: 1px solid var(--border-color);
213
+ display: flex;
214
+ align-items: center;
215
+ gap: 1rem;
216
+ transition: all 0.3s ease;
217
+ }
218
+
219
+ .service-item:hover {
220
+ background: rgba(255, 255, 255, 0.8);
221
+ transform: translateY(-2px);
222
+ }
223
+
224
+ .service-status {
225
+ width: 12px;
226
+ height: 12px;
227
+ border-radius: 50%;
228
+ flex-shrink: 0;
229
+ }
230
+
231
+ .service-status.online { background: var(--success-color); }
232
+ .service-status.offline { background: var(--danger-color); }
233
+ .service-status.loading { background: var(--warning-color); }
234
+
235
+ .service-name {
236
+ font-weight: 500;
237
+ flex: 1;
238
+ }
239
+
240
+ .service-uptime {
241
+ font-size: 0.8rem;
242
+ color: var(--text-secondary);
243
+ }
244
+
245
+ .logs-section {
246
+ background: var(--card-bg);
247
+ padding: 2rem;
248
+ border-radius: 16px;
249
+ box-shadow: var(--shadow);
250
+ border: 1px solid var(--border-color);
251
+ }
252
+
253
+ .logs-header {
254
+ display: flex;
255
+ justify-content: space-between;
256
+ align-items: center;
257
+ margin-bottom: 1.5rem;
258
+ }
259
+
260
+ .log-entry {
261
+ padding: 0.75rem;
262
+ border-radius: 8px;
263
+ margin-bottom: 0.5rem;
264
+ font-family: 'Courier New', monospace;
265
+ font-size: 0.85rem;
266
+ display: flex;
267
+ align-items: center;
268
+ gap: 1rem;
269
+ }
270
+
271
+ .log-entry.info { background: rgba(59, 130, 246, 0.1); border-left: 3px solid var(--primary-color); }
272
+ .log-entry.success { background: rgba(16, 185, 129, 0.1); border-left: 3px solid var(--success-color); }
273
+ .log-entry.warning { background: rgba(245, 158, 11, 0.1); border-left: 3px solid var(--warning-color); }
274
+ .log-entry.error { background: rgba(239, 68, 68, 0.1); border-left: 3px solid var(--danger-color); }
275
+
276
+ .log-timestamp {
277
+ color: var(--text-secondary);
278
+ font-weight: 600;
279
+ }
280
+
281
+ .log-message {
282
+ flex: 1;
283
+ }
284
+
285
+ .refresh-btn {
286
+ background: var(--primary-color);
287
+ color: white;
288
+ border: none;
289
+ padding: 0.75rem 1.5rem;
290
+ border-radius: 12px;
291
+ font-weight: 600;
292
+ cursor: pointer;
293
+ transition: all 0.3s ease;
294
+ display: flex;
295
+ align-items: center;
296
+ gap: 0.5rem;
297
+ }
298
+
299
+ .refresh-btn:hover {
300
+ background: #2563eb;
301
+ transform: translateY(-2px);
302
+ }
303
+
304
+ .loading {
305
+ animation: spin 1s linear infinite;
306
+ }
307
+
308
+ @keyframes spin {
309
+ from { transform: rotate(0deg); }
310
+ to { transform: rotate(360deg); }
311
+ }
312
+
313
+ @media (max-width: 768px) {
314
+ .status-overview {
315
+ grid-template-columns: repeat(2, 1fr);
316
+ }
317
+
318
+ .detailed-metrics {
319
+ grid-template-columns: 1fr;
320
+ }
321
+
322
+ .services-grid {
323
+ grid-template-columns: 1fr;
324
+ }
325
+ }
326
+ </style>
327
+ </head>
328
+ <body>
329
+ <a href="index.html" class="back-link">
330
+ <i class="fas fa-arrow-right"></i>
331
+ بازگشت به داشبورد
332
+ </a>
333
+
334
+ <div class="container">
335
+ <div class="header">
336
+ <h1><i class="fas fa-heartbeat"></i> سلامت سیستم</h1>
337
+ <p>نظارت بر وضعیت و عملکرد سیستم داشبورد حقوقی</p>
338
+ </div>
339
+
340
+ <!-- Status Overview -->
341
+ <div class="status-overview">
342
+ <div class="status-card healthy" id="apiStatusCard">
343
+ <div class="status-icon">
344
+ <i class="fas fa-server"></i>
345
+ </div>
346
+ <div class="status-title">API سرور</div>
347
+ <div class="status-value" id="apiStatusValue">آنلاین</div>
348
+ <div class="status-description" id="apiStatusDesc">پاسخ‌دهی عالی</div>
349
+ </div>
350
+
351
+ <div class="status-card healthy" id="ocrStatusCard">
352
+ <div class="status-icon">
353
+ <i class="fas fa-eye"></i>
354
+ </div>
355
+ <div class="status-title">سیستم OCR</div>
356
+ <div class="status-value" id="ocrStatusValue">آماده</div>
357
+ <div class="status-description" id="ocrStatusDesc">مدل بارگذاری شده</div>
358
+ </div>
359
+
360
+ <div class="status-card healthy" id="dbStatusCard">
361
+ <div class="status-icon">
362
+ <i class="fas fa-database"></i>
363
+ </div>
364
+ <div class="status-title">پایگاه داده</div>
365
+ <div class="status-value" id="dbStatusValue">متصل</div>
366
+ <div class="status-description" id="dbStatusDesc">عملکرد بهینه</div>
367
+ </div>
368
+
369
+ <div class="status-card healthy" id="systemStatusCard">
370
+ <div class="status-icon">
371
+ <i class="fas fa-cogs"></i>
372
+ </div>
373
+ <div class="status-title">سیستم کلی</div>
374
+ <div class="status-value" id="systemStatusValue">سالم</div>
375
+ <div class="status-description" id="systemStatusDesc">همه سرویس‌ها فعال</div>
376
+ </div>
377
+ </div>
378
+
379
+ <!-- Detailed Metrics -->
380
+ <div class="detailed-metrics">
381
+ <div class="metrics-panel">
382
+ <h2 class="panel-title">
383
+ <i class="fas fa-chart-line"></i>
384
+ معیارهای عملکرد
385
+ </h2>
386
+
387
+ <div class="metric-row">
388
+ <span class="metric-label">میانگین زمان پاسخ</span>
389
+ <span class="metric-value healthy" id="responseTimeMetric">120ms</span>
390
+ </div>
391
+
392
+ <div class="metric-row">
393
+ <span class="metric-label">استفاده از CPU</span>
394
+ <span class="metric-value healthy" id="cpuUsageMetric">25%</span>
395
+ </div>
396
+
397
+ <div class="metric-row">
398
+ <span class="metric-label">استفاده از RAM</span>
399
+ <span class="metric-value healthy" id="ramUsageMetric">1.2GB / 4GB</span>
400
+ </div>
401
+
402
+ <div class="metric-row">
403
+ <span class="metric-label">فضای دیسک</span>
404
+ <span class="metric-value healthy" id="diskUsageMetric">45GB / 100GB</span>
405
+ </div>
406
+
407
+ <div class="metric-row">
408
+ <span class="metric-label">اتصالات فعال</span>
409
+ <span class="metric-value healthy" id="connectionsMetric">12</span>
410
+ </div>
411
+
412
+ <div class="metric-row">
413
+ <span class="metric-label">درخواست‌ها در دقیقه</span>
414
+ <span class="metric-value healthy" id="requestsMetric">45</span>
415
+ </div>
416
+ </div>
417
+
418
+ <div class="metrics-panel">
419
+ <h2 class="panel-title">
420
+ <i class="fas fa-list"></i>
421
+ وضعیت سرویس‌ها
422
+ </h2>
423
+
424
+ <div class="services-grid">
425
+ <div class="service-item">
426
+ <div class="service-status online" id="webServerStatus"></div>
427
+ <div class="service-name">وب سرور</div>
428
+ <div class="service-uptime" id="webServerUptime">99.9%</div>
429
+ </div>
430
+
431
+ <div class="service-item">
432
+ <div class="service-status online" id="apiServerStatus"></div>
433
+ <div class="service-name">API سرور</div>
434
+ <div class="service-uptime" id="apiServerUptime">99.8%</div>
435
+ </div>
436
+
437
+ <div class="service-item">
438
+ <div class="service-status online" id="ocrServiceStatus"></div>
439
+ <div class="service-name">سرویس OCR</div>
440
+ <div class="service-uptime" id="ocrServiceUptime">98.5%</div>
441
+ </div>
442
+
443
+ <div class="service-item">
444
+ <div class="service-status online" id="databaseStatus"></div>
445
+ <div class="service-name">پایگاه داده</div>
446
+ <div class="service-uptime" id="databaseUptime">99.9%</div>
447
+ </div>
448
+
449
+ <div class="service-item">
450
+ <div class="service-status online" id="scrapingStatus"></div>
451
+ <div class="service-name">وب اسکرپینگ</div>
452
+ <div class="service-uptime" id="scrapingUptime">97.2%</div>
453
+ </div>
454
+
455
+ <div class="service-item">
456
+ <div class="service-status online" id="cacheStatus"></div>
457
+ <div class="service-name">سیستم کش</div>
458
+ <div class="service-uptime" id="cacheUptime">99.5%</div>
459
+ </div>
460
+ </div>
461
+ </div>
462
+ </div>
463
+
464
+ <!-- System Logs -->
465
+ <div class="logs-section">
466
+ <div class="logs-header">
467
+ <h2 class="panel-title">
468
+ <i class="fas fa-file-alt"></i>
469
+ لاگ‌های سیستم
470
+ </h2>
471
+ <button class="refresh-btn" onclick="refreshLogs()">
472
+ <i class="fas fa-sync-alt" id="refreshIcon"></i>
473
+ بروزرسانی
474
+ </button>
475
+ </div>
476
+
477
+ <div id="systemLogs">
478
+ <div class="log-entry success">
479
+ <span class="log-timestamp">14:30:25</span>
480
+ <span class="log-message">سیستم OCR با موفقیت راه‌اندازی شد</span>
481
+ </div>
482
+ <div class="log-entry info">
483
+ <span class="log-timestamp">14:29:12</span>
484
+ <span class="log-message">اتصال به پایگاه داده برقرار شد</span>
485
+ </div>
486
+ <div class="log-entry info">
487
+ <span class="log-timestamp">14:28:45</span>
488
+ <span class="log-message">API سرور آماده دریافت درخواست</span>
489
+ </div>
490
+ <div class="log-entry warning">
491
+ <span class="log-timestamp">14:25:33</span>
492
+ <span class="log-message">استفاده از RAM به 80% رسید</span>
493
+ </div>
494
+ <div class="log-entry success">
495
+ <span class="log-timestamp">14:20:15</span>
496
+ <span class="log-message">بکاپ خودکار با موفقیت انجام شد</span>
497
+ </div>
498
+ </div>
499
+ </div>
500
+ </div>
501
+
502
+ <!-- Load JavaScript Files -->
503
+ <script src="js/notifications.js"></script>
504
+ <script src="js/api-client.js"></script>
505
+
506
+ <script>
507
+ class SystemHealthMonitor {
508
+ constructor() {
509
+ this.apiClient = null;
510
+ this.refreshInterval = null;
511
+ this.isLoading = false;
512
+
513
+ this.initialize();
514
+ }
515
+
516
+ async initialize() {
517
+ console.log('🔍 System Health Monitor initializing...');
518
+
519
+ // Initialize API client
520
+ this.apiClient = window.legalAPI || new LegalDashboardAPI();
521
+
522
+ // Start monitoring
523
+ await this.refreshSystemHealth();
524
+
525
+ // Auto-refresh every 30 seconds
526
+ this.refreshInterval = setInterval(() => {
527
+ this.refreshSystemHealth();
528
+ }, 30000);
529
+
530
+ // Simulate real-time updates
531
+ this.startRealtimeSimulation();
532
+
533
+ console.log('✅ System Health Monitor ready');
534
+ }
535
+
536
+ async refreshSystemHealth() {
537
+ if (this.isLoading) return;
538
+
539
+ this.isLoading = true;
540
+
541
+ try {
542
+ const healthData = await this.apiClient.healthCheck();
543
+ this.updateHealthDisplay(healthData);
544
+
545
+ if (window.notificationManager) {
546
+ window.notificationManager.showSuccess('بروزرسانی موفق', 'وضعیت سیستم بروزرسانی شد');
547
+ }
548
+
549
+ } catch (error) {
550
+ console.error('Health check failed:', error);
551
+ this.updateHealthDisplay(null);
552
+
553
+ if (window.notificationManager) {
554
+ window.notificationManager.showWarning('خطا در اتصال', 'نتوانستیم وضعیت سیستم را بررسی کنیم');
555
+ }
556
+ } finally {
557
+ this.isLoading = false;
558
+ }
559
+ }
560
+
561
+ updateHealthDisplay(healthData) {
562
+ if (healthData) {
563
+ this.updateOnlineStatus(healthData);
564
+ } else {
565
+ this.updateOfflineStatus();
566
+ }
567
+
568
+ this.updateMetrics(healthData);
569
+ this.updateServices(healthData);
570
+ }
571
+
572
+ updateOnlineStatus(data) {
573
+ // API Status
574
+ this.updateStatusCard('apiStatusCard', 'healthy', 'آنلاین', 'پاسخ‌دهی عالی');
575
+
576
+ // OCR Status
577
+ const ocrReady = data.services?.ocr_model_loaded;
578
+ this.updateStatusCard('ocrStatusCard',
579
+ ocrReady ? 'healthy' : 'warning',
580
+ ocrReady ? 'آماده' : 'بارگذاری',
581
+ ocrReady ? 'مدل بارگذاری شده' : 'در حال آماده‌سازی'
582
+ );
583
+
584
+ // Database Status
585
+ this.updateStatusCard('dbStatusCard', 'healthy', 'متصل', 'عملکرد بهینه');
586
+
587
+ // Overall System
588
+ this.updateStatusCard('systemStatusCard', 'healthy', 'سالم', 'همه سرویس‌ها فعال');
589
+ }
590
+
591
+ updateOfflineStatus() {
592
+ this.updateStatusCard('apiStatusCard', 'error', 'آفلاین', 'خطا در اتصال');
593
+ this.updateStatusCard('ocrStatusCard', 'error', 'خطا', 'غیرفعال');
594
+ this.updateStatusCard('dbStatusCard', 'warning', 'نامشخص', 'وضعیت نامعلوم');
595
+ this.updateStatusCard('systemStatusCard', 'warning', 'مشکل', 'بررسی نیاز');
596
+ }
597
+
598
+ updateStatusCard(cardId, status, value, description) {
599
+ const card = document.getElementById(cardId);
600
+ const valueEl = document.getElementById(cardId.replace('Card', 'Value'));
601
+ const descEl = document.getElementById(cardId.replace('Card', 'Desc'));
602
+
603
+ if (card) {
604
+ card.className = `status-card ${status}`;
605
+ }
606
+ if (valueEl) {
607
+ valueEl.textContent = value;
608
+ }
609
+ if (descEl) {
610
+ descEl.textContent = description;
611
+ }
612
+ }
613
+
614
+ updateMetrics(data) {
615
+ // Generate realistic metrics
616
+ const responseTime = this.generateMetric(80, 200, 'ms');
617
+ const cpuUsage = this.generateMetric(15, 40, '%');
618
+ const ramUsage = this.generateMemoryUsage();
619
+ const diskUsage = this.generateDiskUsage();
620
+ const connections = Math.floor(Math.random() * 20) + 5;
621
+ const requests = Math.floor(Math.random() * 50) + 20;
622
+
623
+ this.updateMetric('responseTimeMetric', responseTime, responseTime < 150 ? 'healthy' : 'warning');
624
+ this.updateMetric('cpuUsageMetric', cpuUsage, cpuUsage < 50 ? 'healthy' : 'warning');
625
+ this.updateMetric('ramUsageMetric', ramUsage, 'healthy');
626
+ this.updateMetric('diskUsageMetric', diskUsage, 'healthy');
627
+ this.updateMetric('connectionsMetric', connections, 'healthy');
628
+ this.updateMetric('requestsMetric', requests, 'healthy');
629
+ }
630
+
631
+ updateMetric(elementId, value, status) {
632
+ const element = document.getElementById(elementId);
633
+ if (element) {
634
+ element.textContent = value;
635
+ element.className = `metric-value ${status}`;
636
+ }
637
+ }
638
+
639
+ updateServices(data) {
640
+ const services = [
641
+ { id: 'webServerStatus', uptime: 'webServerUptime', status: 'online', uptime_val: '99.9%' },
642
+ { id: 'apiServerStatus', uptime: 'apiServerUptime', status: 'online', uptime_val: '99.8%' },
643
+ { id: 'ocrServiceStatus', uptime: 'ocrServiceUptime', status: 'online', uptime_val: '98.5%' },
644
+ { id: 'databaseStatus', uptime: 'databaseUptime', status: 'online', uptime_val: '99.9%' },
645
+ { id: 'scrapingStatus', uptime: 'scrapingUptime', status: 'online', uptime_val: '97.2%' },
646
+ { id: 'cacheStatus', uptime: 'cacheUptime', status: 'online', uptime_val: '99.5%' }
647
+ ];
648
+
649
+ services.forEach(service => {
650
+ const statusEl = document.getElementById(service.id);
651
+ const uptimeEl = document.getElementById(service.uptime);
652
+
653
+ if (statusEl) {
654
+ statusEl.className = `service-status ${data ? service.status : 'offline'}`;
655
+ }
656
+ if (uptimeEl) {
657
+ uptimeEl.textContent = data ? service.uptime_val : 'N/A';
658
+ }
659
+ });
660
+ }
661
+
662
+ generateMetric(min, max, unit) {
663
+ const value = Math.floor(Math.random() * (max - min)) + min;
664
+ return `${value}${unit}`;
665
+ }
666
+
667
+ generateMemoryUsage() {
668
+ const used = (Math.random() * 2 + 0.5).toFixed(1);
669
+ return `${used}GB / 4GB`;
670
+ }
671
+
672
+ generateDiskUsage() {
673
+ const used = Math.floor(Math.random() * 30 + 40);
674
+ return `${used}GB / 100GB`;
675
+ }
676
+
677
+ startRealtimeSimulation() {
678
+ // Simulate system activity with logs
679
+ setInterval(() => {
680
+ this.addRandomLog();
681
+ }, 15000); // Add log every 15 seconds
682
+ }
683
+
684
+ addRandomLog() {
685
+ const logTypes = ['info', 'success', 'warning'];
686
+ const messages = [
687
+ 'درخواست API جدید پردازش شد',
688
+ 'فایل جدید آپلود شد',
689
+ 'عملیات OCR تکمیل شد',
690
+ 'بکاپ دوره‌ای انجام شد',
691
+ 'کش سیستم بهینه‌سازی شد',
692
+ 'عملیات اسکرپینگ شروع شد',
693
+ 'اتصال جدید به پایگاه داده'
694
+ ];
695
+
696
+ const type = logTypes[Math.floor(Math.random() * logTypes.length)];
697
+ const message = messages[Math.floor(Math.random() * messages.length)];
698
+ const timestamp = new Date().toLocaleTimeString('fa-IR');
699
+
700
+ const logsContainer = document.getElementById('systemLogs');
701
+ const logEntry = document.createElement('div');
702
+ logEntry.className = `log-entry ${type}`;
703
+ logEntry.innerHTML = `
704
+ <span class="log-timestamp">${timestamp}</span>
705
+ <span class="log-message">${message}</span>
706
+ `;
707
+
708
+ logsContainer.insertBefore(logEntry, logsContainer.firstChild);
709
+
710
+ // Keep only last 10 logs
711
+ const logs = logsContainer.children;
712
+ if (logs.length > 10) {
713
+ logsContainer.removeChild(logs[logs.length - 1]);
714
+ }
715
+ }
716
+ }
717
+
718
+ // Global functions
719
+ async function refreshLogs() {
720
+ const refreshIcon = document.getElementById('refreshIcon');
721
+ refreshIcon.classList.add('loading');
722
+
723
+ if (window.systemHealth) {
724
+ await window.systemHealth.refreshSystemHealth();
725
+ }
726
+
727
+ setTimeout(() => {
728
+ refreshIcon.classList.remove('loading');
729
+ }, 1000);
730
+ }
731
+
732
+ // Initialize system health monitor
733
+ window.systemHealth = new SystemHealthMonitor();
734
+
735
+ console.log('💓 System Health Page Ready!');
736
+ </script>
737
+ </body>
738
+ </html>