Align directory structure perfectly with Ghaymah submission guidelines
فشلت بعض الفحوصات
Ghaymah CI/CD Pipeline / deploy-production (push) Has been cancelled
Ghaymah CI/CD Pipeline / build-and-push (push) Has been cancelled
Ghaymah CI/CD Pipeline / deploy-staging (push) Has been cancelled

هذا الالتزام موجود في:
2026-07-26 18:12:14 +03:00
الأصل 2e9f3135f7
التزام ae70713203
8 ملفات معدلة مع 28 إضافات و0 حذوفات

عرض الملف

@@ -0,0 +1,14 @@
# Question 5: mithal.space Engine Monitoring Dashboard
## Overview
Automated monitoring solution for `mithal.space` that tracks:
- **HTTP Latency** (Request response time in ms)
- **Uptime / Availability** (Status code verification)
- **SSL Certificate Expiration** (Remaining validity days)
- **DNS Resolution Time** (Name server resolution delay)
- **Search Response Latency** (End-to-end query performance)
## Architecture & Integration
1. **Collector (`monitor.py`):** Python script collecting operational metrics and appending structured logs to `metrics.json`.
2. **Web Dashboard (`dashboard.html`):** Frontend interface rendering active health metrics and interactive Chart.js response time graphs.
3. **Endpoint Integration:** Exposed via Flask on `/mithal-dashboard` and `/api/mithal-metrics`.

عرض الملف

@@ -0,0 +1,114 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>mithal.space Monitoring Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #0f172a; color: #f8fafc; margin: 0; padding: 20px; }
.container { max-width: 1100px; margin: auto; }
h1 { text-align: center; color: #38bdf8; margin-bottom: 30px; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px; margin-bottom: 30px; }
.card { background: #1e293b; padding: 20px; border-radius: 12px; border: 1px solid #334155; text-align: center; }
.card h3 { margin: 0; font-size: 14px; color: #94a3b8; }
.card p { margin: 10px 0 0; font-size: 28px; font-weight: bold; color: #38bdf8; }
.chart-container { background: #1e293b; padding: 20px; border-radius: 12px; border: 1px solid #334155; margin-bottom: 30px; }
table { width: 100%; border-collapse: collapse; background: #1e293b; border-radius: 12px; overflow: hidden; }
th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #334155; }
th { background-color: #0f172a; color: #38bdf8; }
.badge-up { background: #166534; color: #4ade80; padding: 4px 8px; border-radius: 4px; font-weight: bold; }
.badge-down { background: #991b1b; color: #f87171; padding: 4px 8px; border-radius: 4px; font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<h1>mithal.space Live Engine Monitoring</h1>
<div class="cards">
<div class="card"><h3>24h Uptime</h3><p id="uptime-val">100%</p></div>
<div class="card"><h3>Avg Latency</h3><p id="latency-val">-- ms</p></div>
<div class="card"><h3>SSL Cert Expiry</h3><p id="ssl-val">-- days</p></div>
<div class="card"><h3>DNS Lookup</h3><p id="dns-val">-- ms</p></div>
</div>
<div class="chart-container">
<canvas id="latencyChart" height="100"></canvas>
</div>
<h2>Recent 10 Checks Log</h2>
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>Status</th>
<th>HTTP Code</th>
<th>Latency</th>
<th>DNS Time</th>
<th>Search Response</th>
</tr>
</thead>
<tbody id="logs-body"></tbody>
</table>
</div>
<script>
async function fetchMetrics() {
try {
const res = await fetch('/api/mithal-metrics');
const data = await res.json();
if(!data || data.length === 0) return;
// Uptime calc
const upCount = data.filter(d => d.uptime).length;
const uptimePct = ((upCount / data.length) * 100).toFixed(1);
document.getElementById('uptime-val').innerText = `${uptimePct}%`;
const latest = data[data.length - 1];
document.getElementById('latency-val').innerText = `${latest.latency_ms} ms`;
document.getElementById('ssl-val').innerText = `${latest.ssl_days_left} Days`;
document.getElementById('dns-val').innerText = `${latest.dns_time_ms} ms`;
// Render Logs Table (Last 10)
const logs = data.slice(-10).reverse();
const tbody = document.getElementById('logs-body');
tbody.innerHTML = logs.map(l => `
<tr>
<td>${l.timestamp}</td>
<td><span class="${l.uptime ? 'badge-up' : 'badge-down'}">${l.uptime ? 'UP' : 'DOWN'}</span></td>
<td>${l.status_code}</td>
<td>${l.latency_ms} ms</td>
<td>${l.dns_time_ms} ms</td>
<td>${l.search_response_ms} ms</td>
</tr>
`).join('');
// Render Chart
const labels = data.map(d => d.timestamp.split(' ')[1]);
const latencies = data.map(d => d.latency_ms);
if (window.myChart) window.myChart.destroy();
const ctx = document.getElementById('latencyChart').getContext('2d');
window.myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'HTTP Latency (ms)',
data: latencies,
borderColor: '#38bdf8',
backgroundColor: 'rgba(56, 189, 248, 0.1)',
fill: true,
tension: 0.3
}]
},
options: { responsive: true, scales: { y: { beginAtZero: true } } }
});
} catch(e) { console.error("Error loading metrics:", e); }
}
fetchMetrics();
setInterval(fetchMetrics, 30000);
</script>
</body>
</html>

عرض الملف

@@ -0,0 +1,38 @@
[
{
"timestamp": "2026-07-26 15:01:38 UTC",
"uptime": true,
"status_code": 200,
"latency_ms": 2163.3,
"dns_time_ms": 342.46,
"ssl_days_left": 50,
"search_response_ms": 4654.36
},
{
"timestamp": "2026-07-26 15:02:26 UTC",
"uptime": true,
"status_code": 200,
"latency_ms": 1138.32,
"dns_time_ms": 79.74,
"ssl_days_left": 50,
"search_response_ms": 1107.08
},
{
"timestamp": "2026-07-26 15:05:57 UTC",
"uptime": true,
"status_code": 200,
"latency_ms": 1103.44,
"dns_time_ms": 70.71,
"ssl_days_left": 50,
"search_response_ms": 1058.72
},
{
"timestamp": "2026-07-26 15:09:33 UTC",
"uptime": true,
"status_code": 200,
"latency_ms": 1020.97,
"dns_time_ms": 201.71,
"ssl_days_left": 50,
"search_response_ms": 1073.96
}
]

عرض الملف

@@ -0,0 +1,89 @@
import os
import json
import socket
import ssl
import time
import requests
from datetime import datetime, timezone
TARGET_HOST = "mithal.space"
TARGET_URL = f"https://{TARGET_HOST}"
SEARCH_URL = f"{TARGET_URL}/search?q=test"
METRICS_FILE = os.path.join(os.path.dirname(__file__), "metrics.json")
def check_dns(host):
start = time.time()
try:
socket.gethostbyname(host)
dns_time = round((time.time() - start) * 1000, 2)
return dns_time
except Exception:
return -1
def check_ssl(host):
try:
context = ssl.create_default_context()
with socket.create_connection((host, 443), timeout=5) as sock:
with context.wrap_socket(sock, server_hostname=host) as ssock:
cert = ssock.getpeercert()
not_after_str = cert['notAfter']
# Parsing SSL expiration date
not_after = datetime.strptime(not_after_str, '%b %d %H:%M:%S %Y %Z').replace(tzinfo=timezone.utc)
days_left = (not_after - datetime.now(timezone.utc)).days
return days_left
except Exception:
return -1
def check_http():
try:
start = time.time()
res = requests.get(TARGET_URL, timeout=5)
latency = round((time.time() - start) * 1000, 2)
return res.status_code, latency, True
except Exception:
return 0, -1, False
def check_search():
try:
start = time.time()
res = requests.get(SEARCH_URL, timeout=5)
search_latency = round((time.time() - start) * 1000, 2)
return search_latency
except Exception:
return -1
def run_monitoring():
dns_time = check_dns(TARGET_HOST)
ssl_days = check_ssl(TARGET_HOST)
status_code, latency, is_up = check_http()
search_time = check_search()
metric_entry = {
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC"),
"uptime": is_up,
"status_code": status_code,
"latency_ms": latency,
"dns_time_ms": dns_time,
"ssl_days_left": ssl_days,
"search_response_ms": search_time
}
metrics = []
if os.path.exists(METRICS_FILE):
try:
with open(METRICS_FILE, "r") as f:
metrics = json.load(f)
except Exception:
metrics = []
metrics.append(metric_entry)
metrics = metrics[-100:] # Keep last 100 checks
with open(METRICS_FILE, "w") as f:
json.dump(metrics, f, indent=2)
return metric_entry
if __name__ == "__main__":
result = run_monitoring()
print("Monitoring check complete:", json.dumps(result, indent=2))