welcome ghaymah

هذا الالتزام موجود في:
2026-07-27 23:06:16 +03:00
التزام 76f658bfab
17 ملفات معدلة مع 704 إضافات و0 حذوفات

عرض الملف

@@ -0,0 +1,17 @@
# استخدام صورة بايثون خفيفة
FROM python:3.9-slim
# تحديد مجلد العمل داخل الحاوية
WORKDIR /app
# تثبيت المكاتب المطلوبة مباشرة بدون الحاجة لملف requirements.txt منفصل
RUN pip install --no-cache-dir fastapi uvicorn requests
# نسخ باقي ملفات المشروع (مثل monitor.py و dashboard.html) إلى مجلد العمل
COPY . /app
# فتح البورت الذي يعمل عليه التطبيق
EXPOSE 8000
# أمر التشغيل المباشر باستخدام Uvicorn
CMD ["uvicorn", "monitor:app", "--host", "0.0.0.0", "--port", "8000"]

عرض الملف

@@ -0,0 +1,149 @@
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>لوحة مراقبة موقع مثال - Mithal Monitor</title>
<!-- مكتبة الرسم البياني Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: Arial, sans-serif; background: #0f172a; margin: 0; padding: 40px 20px; color: #ffffff; }
.container { max-width: 800px; margin: auto; text-align: center; }
h1 { color: #ffffff; font-size: 24px; margin-bottom: 30px; }
.metrics-box { display: flex; gap: 15px; margin-bottom: 20px; }
.card { background: #1e293b; border-radius: 8px; padding: 20px; flex: 1; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); }
.card h3 { margin: 0 0 8px; font-size: 14px; color: #38bdf8; }
.card p { font-size: 18px; margin: 0; font-weight: bold; color: #ffffff; }
.chart-container { background: #1e293b; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); }
.status-ok { color: #4ade80 !important; }
.status-down { color: #f87171 !important; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; background: #1e293b; border-radius: 8px; overflow: hidden; }
th, td { padding: 12px; text-align: center; font-size: 14px; border-bottom: 1px solid #334155; }
th { background: #0f172a; color: #38bdf8; }
tr:last-child td { border-bottom: none; }
</style>
</head>
<body>
<div class="container">
<h1>لوحة مراقبة موقع مثال (mithal.space)</h1>
<div class="metrics-box">
<div class="card">
<h3>حالة الموقع</h3>
<p id="siteStatus" class="status-ok">جاري التحميل...</p>
</div>
<div class="card">
<h3>حالة SSL وتاريخ الانتهاء</h3>
<p id="sslStatus" style="font-size: 14px;">جاري التحميل...</p>
</div>
<div class="card">
<h3>متوسط الـ Latency</h3>
<p id="latencyStatus">جاري التحميل...</p>
</div>
</div>
<!-- الرسم البياني (Line Chart) لزمن الاستجابة -->
<div class="chart-container">
<h3 style="color: #38bdf8; margin-top: 0; text-align: right;">رسم بياني لزمن الاستجابة (Latency - آخر ساعة)</h3>
<canvas id="latencyChart"></canvas>
</div>
<h2 style="font-size: 18px; margin-top: 30px; color: #38bdf8; text-align: right;">سجل آخر الفحصات</h2>
<table>
<thead>
<tr>
<th>الوقت</th>
<th>حالة الموقع</th>
<th>زمن الاستجابة</th>
<th>زمن البحث</th>
<th>حالة SSL</th>
</tr>
</thead>
<tbody id="logTableBody">
</tbody>
</table>
</div>
<script>
// إعداد الرسم البياني باستخدام Chart.js
const ctx = document.getElementById('latencyChart').getContext('2d');
const latencyChart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'زمن الاستجابة (ms)',
data: [],
borderColor: '#38bdf8',
backgroundColor: 'rgba(56, 189, 248, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.3
}]
},
options: {
responsive: true,
plugins: {
legend: { labels: { color: '#ffffff' } }
},
scales: {
x: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } },
y: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } }
}
}
});
async function fetchMetrics() {
try {
const response = await fetch('/api/metrics');
const data = await response.json();
if (data && data.length > 0) {
const latest = data[data.length - 1];
// تحديث الكروت العلوية
const statusElem = document.getElementById('siteStatus');
if (latest.status_code === 200) {
statusElem.textContent = "متصل (200 OK)";
statusElem.className = "status-ok";
} else {
statusElem.textContent = `غير متصل (${latest.status_code})`;
statusElem.className = "status-down";
}
document.getElementById('sslStatus').textContent = latest.ssl_status;
document.getElementById('latencyStatus').textContent = `${latest.latency_ms} ms`;
// تحديث بيانات الرسم البياني (Line Chart) من السكريبت
const timestamps = data.map(row => row.timestamp.split(' ')[1]); // أخذ الوقت فقط
const latencies = data.map(row => row.latency_ms);
latencyChart.data.labels = timestamps;
latencyChart.data.datasets[0].data = latencies;
latencyChart.update();
// تحديث الجدول
const tableBody = document.getElementById('logTableBody');
tableBody.innerHTML = '';
const reversedData = [...data].reverse().slice(0, 10);
reversedData.forEach(row => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${row.timestamp}</td>
<td class="${row.status_code === 200 ? 'status-ok' : 'status-down'}">${row.status_code} OK</td>
<td>${row.latency_ms} ms</td>
<td>${row.search_response_ms} ms</td>
<td>${row.ssl_status}</td>
`;
tableBody.appendChild(tr);
});
}
} catch (e) {
console.log("Error fetching metrics");
}
}
fetchMetrics();
setInterval(fetchMetrics, 5000); // تحديث الجراف والبيانات تلقائياً كل 5 ثوانٍ
</script>
</body>
</html>

عرض الملف

@@ -0,0 +1,74 @@
[
{
"timestamp": "2026-07-27 19:52:03",
"status_code": 200,
"latency_ms": 1041.82,
"dns_ms": 9.59,
"search_response_ms": 944.04,
"ssl_status": "صحيحة (تنتهي خلال 49 يوم)"
},
{
"timestamp": "2026-07-27 19:53:06",
"status_code": 200,
"latency_ms": 961.73,
"dns_ms": 1.01,
"search_response_ms": 951.24,
"ssl_status": "صحيحة (تنتهي خلال 49 يوم)"
},
{
"timestamp": "2026-07-27 19:54:09",
"status_code": 200,
"latency_ms": 1117.17,
"dns_ms": 21.45,
"search_response_ms": 849.41,
"ssl_status": "صحيحة (تنتهي خلال 49 يوم)"
},
{
"timestamp": "2026-07-27 19:55:12",
"status_code": 200,
"latency_ms": 1785.42,
"dns_ms": 0.0,
"search_response_ms": 846.86,
"ssl_status": "صحيحة (تنتهي خلال 49 يوم)"
},
{
"timestamp": "2026-07-27 19:56:23",
"status_code": 200,
"latency_ms": 3183.29,
"dns_ms": 1.12,
"search_response_ms": 4690.82,
"ssl_status": "صحيحة (تنتهي خلال 49 يوم)"
},
{
"timestamp": "2026-07-27 19:57:25",
"status_code": 200,
"latency_ms": 1111.06,
"dns_ms": 8.02,
"search_response_ms": 848.63,
"ssl_status": "صحيحة (تنتهي خلال 49 يوم)"
},
{
"timestamp": "2026-07-27 19:58:28",
"status_code": 200,
"latency_ms": 1030.14,
"dns_ms": 1.01,
"search_response_ms": 839.49,
"ssl_status": "صحيحة (تنتهي خلال 49 يوم)"
},
{
"timestamp": "2026-07-27 19:59:31",
"status_code": 200,
"latency_ms": 985.51,
"dns_ms": 18.2,
"search_response_ms": 931.7,
"ssl_status": "صحيحة (تنتهي خلال 49 يوم)"
},
{
"timestamp": "2026-07-27 20:00:33",
"status_code": 200,
"latency_ms": 936.12,
"dns_ms": 13.48,
"search_response_ms": 939.24,
"ssl_status": "صحيحة (تنتهي خلال 49 يوم)"
}
]

عرض الملف

@@ -0,0 +1,120 @@
import time
import requests
import json
import ssl
import socket
import os
import asyncio
from datetime import datetime, timezone
from urllib.parse import urlparse
from fastapi import FastAPI
from fastapi.responses import JSONResponse, FileResponse
from contextlib import asynccontextmanager
# --- تعريف الـ Lifespan لحل مشكلة الـ startup وتوليد الخلفية ---
@asynccontextmanager
async def lifespan(app: FastAPI):
task = asyncio.create_task(background_monitor())
yield
task.cancel()
app = FastAPI(lifespan=lifespan)
TARGET_URL = "https://mithal.space"
LOG_FILE = "metrics.json"
# --- دالة الفحص الخلفية بـ FastAPI ---
async def background_monitor():
while True:
parsed_url = urlparse(TARGET_URL)
hostname = parsed_url.netloc
start_time = time.time()
status_code = 0
latency = 0
ssl_expiry = "N/A"
dns_time = 0
search_time = 0
try:
dns_start = time.time()
socket.gethostbyname(hostname)
dns_time = round((time.time() - dns_start) * 1000, 2)
except:
dns_time = -1
try:
response = requests.get(TARGET_URL, timeout=10)
latency = round((time.time() - start_time) * 1000, 2)
status_code = response.status_code
except:
latency = -1
status_code = 503
try:
search_start = time.time()
requests.get(f"{TARGET_URL}/search?q=test", timeout=5)
search_time = round((time.time() - search_start) * 1000, 2)
except:
search_time = -1
try:
context = ssl.create_default_context()
with socket.create_connection((hostname, 443), timeout=5) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
expiry_date = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z').replace(tzinfo=timezone.utc)
days_left = (expiry_date - datetime.now(timezone.utc)).days
ssl_expiry = f"صحيحة (تنتهي خلال {days_left} يوم)"
except:
ssl_expiry = "خطأ في جلب SSL"
timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
record = {
"timestamp": timestamp_str,
"status_code": status_code,
"latency_ms": latency,
"dns_ms": dns_time,
"search_response_ms": search_time,
"ssl_status": ssl_expiry
}
try:
if os.path.exists(LOG_FILE):
with open(LOG_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
else:
data = []
except:
data = []
data.append(record)
if len(data) > 20:
data = data[-20:]
with open(LOG_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
print(f"[{timestamp_str}] Background Checked {TARGET_URL} - Status: {status_code} - Latency: {latency}ms")
# الانتظار لمدة 30 ثانية أو دقيقة بحسب رغبتك
await asyncio.sleep(60)
# --- المسارات (Endpoints) ---
@app.get("/")
def index():
if os.path.exists("dashboard.html"):
return FileResponse("dashboard.html")
return {"message": "Dashboard file not found"}
@app.get("/api/metrics")
def get_metrics():
if os.path.exists(LOG_FILE):
try:
with open(LOG_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
return JSONResponse(content=data)
except:
return JSONResponse(content=[])
return JSONResponse(content=[])