Clean Q5 files
هذا الالتزام موجود في:
@@ -1,98 +1,240 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Application Dashboard</title>
|
|
||||||
|
|
||||||
<style>
|
<meta charset="UTF-8">
|
||||||
body{
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
background:#f4f4f4;
|
|
||||||
text-align:center;
|
|
||||||
margin-top:50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card{
|
<title>Mithal Monitoring Dashboard</title>
|
||||||
width:400px;
|
|
||||||
margin:auto;
|
|
||||||
background:white;
|
|
||||||
padding:25px;
|
|
||||||
border-radius:12px;
|
|
||||||
box-shadow:0 0 10px rgba(0,0,0,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1{
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
color:#333;
|
|
||||||
}
|
|
||||||
|
|
||||||
p{
|
<style>
|
||||||
font-size:22px;
|
|
||||||
margin:15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
span{
|
body{
|
||||||
font-weight:bold;
|
font-family:Arial;
|
||||||
color:green;
|
background:#f4f4f4;
|
||||||
}
|
margin:30px;
|
||||||
</style>
|
}
|
||||||
|
|
||||||
|
h1{
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards{
|
||||||
|
display:flex;
|
||||||
|
justify-content:center;
|
||||||
|
gap:20px;
|
||||||
|
margin-bottom:30px;
|
||||||
|
flex-wrap:wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card{
|
||||||
|
|
||||||
|
background:white;
|
||||||
|
|
||||||
|
padding:20px;
|
||||||
|
|
||||||
|
width:250px;
|
||||||
|
|
||||||
|
border-radius:10px;
|
||||||
|
|
||||||
|
box-shadow:0 0 10px rgba(0,0,0,.2);
|
||||||
|
|
||||||
|
text-align:center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas{
|
||||||
|
|
||||||
|
background:white;
|
||||||
|
|
||||||
|
padding:20px;
|
||||||
|
|
||||||
|
border-radius:10px;
|
||||||
|
|
||||||
|
box-shadow:0 0 10px rgba(0,0,0,.2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
table{
|
||||||
|
|
||||||
|
margin-top:30px;
|
||||||
|
|
||||||
|
width:100%;
|
||||||
|
|
||||||
|
border-collapse:collapse;
|
||||||
|
|
||||||
|
background:white;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
th,td{
|
||||||
|
|
||||||
|
border:1px solid #ddd;
|
||||||
|
|
||||||
|
padding:10px;
|
||||||
|
|
||||||
|
text-align:center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
th{
|
||||||
|
|
||||||
|
background:#3498db;
|
||||||
|
|
||||||
|
color:white;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<h1>Mithal Monitoring Dashboard</h1>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|
||||||
<h1>Application Dashboard</h1>
|
<h2>Uptime</h2>
|
||||||
|
|
||||||
<p>
|
<h1 id="uptime">0%</h1>
|
||||||
Status:
|
|
||||||
<span id="status">Loading...</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Requests:
|
|
||||||
<span id="requests">0</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Response Time:
|
|
||||||
<span id="response">0</span> ms
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
|
||||||
|
<h2>SSL Remaining</h2>
|
||||||
|
|
||||||
|
<h1 id="ssl">0 Days</h1>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
|
||||||
|
<h2>Last Status</h2>
|
||||||
|
|
||||||
|
<h1 id="status">-</h1>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<canvas id="chart" height="100"></canvas>
|
||||||
|
|
||||||
|
<h2>Last 10 Checks</h2>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
|
||||||
|
<thead>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<th>Time</th>
|
||||||
|
|
||||||
|
<th>Status</th>
|
||||||
|
|
||||||
|
<th>Latency (ms)</th>
|
||||||
|
|
||||||
|
<th>DNS (ms)</th>
|
||||||
|
|
||||||
|
<th>Search (ms)</th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody id="tableBody">
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
async function updateDashboard() {
|
async function loadDashboard(){
|
||||||
|
|
||||||
try {
|
const response = await fetch("monitor_data.json");
|
||||||
|
|
||||||
const response = await fetch("http://127.0.0.1:8000/stats");
|
const data = await response.json();
|
||||||
|
|
||||||
const data = await response.json();
|
const total=data.length;
|
||||||
|
|
||||||
document.getElementById("status").textContent = data.status;
|
const up=data.filter(x=>x.uptime==="UP").length;
|
||||||
|
|
||||||
document.getElementById("requests").textContent = data.requests;
|
document.getElementById("uptime").innerHTML=((up/total)*100).toFixed(1)+"%";
|
||||||
|
|
||||||
document.getElementById("response").textContent = data.response_time;
|
const latest=data[data.length-1];
|
||||||
|
|
||||||
}
|
document.getElementById("ssl").innerHTML=latest.ssl_days_remaining+" Days";
|
||||||
|
|
||||||
catch(error){
|
document.getElementById("status").innerHTML=latest.uptime;
|
||||||
|
|
||||||
document.getElementById("status").textContent = "DOWN";
|
const last60=data.slice(-60);
|
||||||
|
|
||||||
}
|
const labels=last60.map(x=>x.timestamp.substring(11));
|
||||||
|
|
||||||
|
const latency=last60.map(x=>x.latency_ms);
|
||||||
|
|
||||||
|
new Chart(document.getElementById("chart"),{
|
||||||
|
|
||||||
|
type:"line",
|
||||||
|
|
||||||
|
data:{
|
||||||
|
|
||||||
|
labels:labels,
|
||||||
|
|
||||||
|
datasets:[{
|
||||||
|
|
||||||
|
label:"Latency",
|
||||||
|
|
||||||
|
data:latency
|
||||||
|
|
||||||
|
}]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDashboard();
|
});
|
||||||
|
|
||||||
setInterval(updateDashboard,30000);
|
const table=document.getElementById("tableBody");
|
||||||
|
|
||||||
|
table.innerHTML="";
|
||||||
|
|
||||||
|
const last10=data.slice(-10).reverse();
|
||||||
|
|
||||||
|
last10.forEach(item=>{
|
||||||
|
|
||||||
|
table.innerHTML+=`
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td>${item.timestamp}</td>
|
||||||
|
|
||||||
|
<td>${item.uptime}</td>
|
||||||
|
|
||||||
|
<td>${item.latency_ms}</td>
|
||||||
|
|
||||||
|
<td>${item.dns_ms}</td>
|
||||||
|
|
||||||
|
<td>${item.search_latency_ms}</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDashboard();
|
||||||
|
|
||||||
|
setInterval(loadDashboard,60000);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,25 +1,141 @@
|
|||||||
import requests
|
import requests
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
import json
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
URL = "https://mithal.space"
|
||||||
|
SEARCH_URL = "https://mithal.space/?q=test"
|
||||||
|
OUTPUT_FILE = "monitor_data.json"
|
||||||
|
|
||||||
|
|
||||||
|
def get_latency():
|
||||||
|
start = time.time()
|
||||||
|
response = requests.get(URL, timeout=10)
|
||||||
|
latency = round((time.time() - start) * 1000, 2)
|
||||||
|
return latency, response.status_code
|
||||||
|
|
||||||
|
|
||||||
|
def get_dns_time():
|
||||||
|
host = urlparse(URL).hostname
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
socket.gethostbyname(host)
|
||||||
|
dns_time = round((time.time() - start) * 1000, 2)
|
||||||
|
|
||||||
|
return dns_time
|
||||||
|
|
||||||
|
|
||||||
|
def get_ssl_info():
|
||||||
|
host = urlparse(URL).hostname
|
||||||
|
|
||||||
|
context = ssl.create_default_context()
|
||||||
|
|
||||||
|
with socket.create_connection((host, 443), timeout=10) as sock:
|
||||||
|
with context.wrap_socket(sock, server_hostname=host) as ssock:
|
||||||
|
cert = ssock.getpeercert()
|
||||||
|
|
||||||
|
expiry = cert["notAfter"]
|
||||||
|
|
||||||
|
expiry_date = datetime.strptime(
|
||||||
|
expiry,
|
||||||
|
"%b %d %H:%M:%S %Y %Z"
|
||||||
|
).replace(tzinfo=timezone.utc)
|
||||||
|
|
||||||
|
remaining = (expiry_date - datetime.now(timezone.utc)).days
|
||||||
|
|
||||||
|
return remaining, expiry
|
||||||
|
|
||||||
|
|
||||||
|
def get_search_latency():
|
||||||
|
start = time.time()
|
||||||
|
|
||||||
|
requests.get(SEARCH_URL, timeout=10)
|
||||||
|
|
||||||
|
return round((time.time() - start) * 1000, 2)
|
||||||
|
|
||||||
URL = "http://127.0.0.1:8000/health"
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
start = time.time()
|
|
||||||
|
|
||||||
response = requests.get(URL)
|
latency, status = get_latency()
|
||||||
|
|
||||||
end = time.time()
|
uptime = "UP" if status == 200 else "DOWN"
|
||||||
|
|
||||||
response_time = round((end - start) * 1000, 2)
|
dns_time = get_dns_time()
|
||||||
|
|
||||||
print("=" * 40)
|
ssl_days, ssl_expiry = get_ssl_info()
|
||||||
print("Status Code :", response.status_code)
|
|
||||||
print("Application :", "UP")
|
|
||||||
print("Response Time:", response_time, "ms")
|
|
||||||
|
|
||||||
except Exception:
|
search_latency = get_search_latency()
|
||||||
print("=" * 40)
|
|
||||||
print("Application : DOWN")
|
|
||||||
|
|
||||||
time.sleep(30)
|
result = {
|
||||||
|
|
||||||
|
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
|
||||||
|
"uptime": uptime,
|
||||||
|
|
||||||
|
"status_code": status,
|
||||||
|
|
||||||
|
"latency_ms": latency,
|
||||||
|
|
||||||
|
"dns_ms": dns_time,
|
||||||
|
|
||||||
|
"ssl_days_remaining": ssl_days,
|
||||||
|
|
||||||
|
"ssl_expiry": ssl_expiry,
|
||||||
|
|
||||||
|
"search_latency_ms": search_latency
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
|
||||||
|
result = {
|
||||||
|
|
||||||
|
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
|
||||||
|
"uptime": "DOWN",
|
||||||
|
|
||||||
|
"status_code": 0,
|
||||||
|
|
||||||
|
"latency_ms": 0,
|
||||||
|
|
||||||
|
"dns_ms": 0,
|
||||||
|
|
||||||
|
"ssl_days_remaining": 0,
|
||||||
|
|
||||||
|
"ssl_expiry": "Unavailable",
|
||||||
|
|
||||||
|
"search_latency_ms": 0,
|
||||||
|
|
||||||
|
"error": str(e)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load previous history
|
||||||
|
if os.path.exists(OUTPUT_FILE):
|
||||||
|
try:
|
||||||
|
with open(OUTPUT_FILE, "r") as f:
|
||||||
|
history = json.load(f)
|
||||||
|
except:
|
||||||
|
history = []
|
||||||
|
else:
|
||||||
|
history = []
|
||||||
|
|
||||||
|
# Add new result
|
||||||
|
history.append(result)
|
||||||
|
|
||||||
|
# Keep only last 24 hours (1440 checks)
|
||||||
|
history = history[-1440:]
|
||||||
|
|
||||||
|
# Save JSON
|
||||||
|
with open(OUTPUT_FILE, "w") as f:
|
||||||
|
json.dump(history, f, indent=4)
|
||||||
|
|
||||||
|
print(result)
|
||||||
|
|
||||||
|
time.sleep(60)
|
||||||
|
|||||||
82
q5-mithal-monitor/monitor_data.json
Normal file
82
q5-mithal-monitor/monitor_data.json
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"timestamp": "2026-07-29 13:21:04",
|
||||||
|
"uptime": "UP",
|
||||||
|
"status_code": 200,
|
||||||
|
"latency_ms": 742.61,
|
||||||
|
"dns_ms": 19.29,
|
||||||
|
"ssl_days_remaining": 47,
|
||||||
|
"ssl_expiry": "Sep 15 13:10:47 2026 GMT",
|
||||||
|
"search_latency_ms": 711.58
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2026-07-29 13:22:07",
|
||||||
|
"uptime": "UP",
|
||||||
|
"status_code": 200,
|
||||||
|
"latency_ms": 1359.55,
|
||||||
|
"dns_ms": 19.7,
|
||||||
|
"ssl_days_remaining": 47,
|
||||||
|
"ssl_expiry": "Sep 15 13:10:47 2026 GMT",
|
||||||
|
"search_latency_ms": 738.71
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2026-07-29 13:23:09",
|
||||||
|
"uptime": "UP",
|
||||||
|
"status_code": 200,
|
||||||
|
"latency_ms": 733.29,
|
||||||
|
"dns_ms": 27.96,
|
||||||
|
"ssl_days_remaining": 47,
|
||||||
|
"ssl_expiry": "Sep 15 13:10:47 2026 GMT",
|
||||||
|
"search_latency_ms": 690.43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2026-07-29 13:24:10",
|
||||||
|
"uptime": "UP",
|
||||||
|
"status_code": 200,
|
||||||
|
"latency_ms": 678.63,
|
||||||
|
"dns_ms": 17.48,
|
||||||
|
"ssl_days_remaining": 47,
|
||||||
|
"ssl_expiry": "Sep 15 13:10:47 2026 GMT",
|
||||||
|
"search_latency_ms": 704.29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2026-07-29 13:25:12",
|
||||||
|
"uptime": "UP",
|
||||||
|
"status_code": 200,
|
||||||
|
"latency_ms": 667.45,
|
||||||
|
"dns_ms": 18.25,
|
||||||
|
"ssl_days_remaining": 47,
|
||||||
|
"ssl_expiry": "Sep 15 13:10:47 2026 GMT",
|
||||||
|
"search_latency_ms": 700.19
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2026-07-29 13:26:14",
|
||||||
|
"uptime": "UP",
|
||||||
|
"status_code": 200,
|
||||||
|
"latency_ms": 712.69,
|
||||||
|
"dns_ms": 17.47,
|
||||||
|
"ssl_days_remaining": 47,
|
||||||
|
"ssl_expiry": "Sep 15 13:10:47 2026 GMT",
|
||||||
|
"search_latency_ms": 711.83
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2026-07-29 13:27:16",
|
||||||
|
"uptime": "UP",
|
||||||
|
"status_code": 200,
|
||||||
|
"latency_ms": 673.74,
|
||||||
|
"dns_ms": 18.32,
|
||||||
|
"ssl_days_remaining": 47,
|
||||||
|
"ssl_expiry": "Sep 15 13:10:47 2026 GMT",
|
||||||
|
"search_latency_ms": 696.53
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2026-07-29 13:28:18",
|
||||||
|
"uptime": "UP",
|
||||||
|
"status_code": 200,
|
||||||
|
"latency_ms": 855.31,
|
||||||
|
"dns_ms": 17.76,
|
||||||
|
"ssl_days_remaining": 47,
|
||||||
|
"ssl_expiry": "Sep 15 13:10:47 2026 GMT",
|
||||||
|
"search_latency_ms": 685.76
|
||||||
|
}
|
||||||
|
]
|
||||||
المرجع في مشكلة جديدة
حظر مستخدم