fixed bugs
This commit is contained in:
@@ -12,8 +12,8 @@ COPY requirements.txt .
|
|||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
# Copy application files
|
# Copy application files
|
||||||
COPY monitor.py index.html entrypoint.sh ./
|
COPY monitor.py index.html entrypoint.sh chart.umd.min.js ./
|
||||||
COPY data.jsonl data.csv monitoring_data.csv ./
|
COPY data.jsonl data.csv monitoring_data.csv data.js ./
|
||||||
|
|
||||||
# Create symlink so dashboard.html also works without code duplication
|
# Create symlink so dashboard.html also works without code duplication
|
||||||
RUN ln -sf index.html dashboard.html && chmod +x entrypoint.sh
|
RUN ln -sf index.html dashboard.html && chmod +x entrypoint.sh
|
||||||
|
|||||||
Vendored
+14
File diff suppressed because one or more lines are too long
@@ -881,3 +881,17 @@ timestamp,dns_time_ms,http_up,http_status_code,http_latency_ms,ssl_valid,ssl_day
|
|||||||
2026-07-28T04:32:24.769605+00:00,2.42,True,200,597.6,True,49,2026-09-15,True,200,671.85,
|
2026-07-28T04:32:24.769605+00:00,2.42,True,200,597.6,True,49,2026-09-15,True,200,671.85,
|
||||||
2026-07-28T04:33:26.739139+00:00,3.17,True,200,709.09,True,49,2026-09-15,True,200,613.71,
|
2026-07-28T04:33:26.739139+00:00,3.17,True,200,709.09,True,49,2026-09-15,True,200,613.71,
|
||||||
2026-07-28T04:34:28.659957+00:00,6.41,True,200,617.23,True,49,2026-09-15,True,200,671.04,
|
2026-07-28T04:34:28.659957+00:00,6.41,True,200,617.23,True,49,2026-09-15,True,200,671.04,
|
||||||
|
2026-07-28T04:35:30.526140+00:00,2.32,True,200,630.7,True,49,2026-09-15,True,200,598.95,
|
||||||
|
2026-07-28T04:36:32.364936+00:00,2.46,True,200,616.06,True,49,2026-09-15,True,200,593.11,
|
||||||
|
2026-07-28T04:37:34.295093+00:00,3.04,True,200,643.24,True,49,2026-09-15,True,200,653.17,
|
||||||
|
2026-07-28T04:38:36.392339+00:00,2.66,True,200,660.38,True,49,2026-09-15,True,200,682.75,
|
||||||
|
2026-07-28T04:39:29.189047+00:00,99.86,True,200,1010.9,True,49,2026-09-15,True,200,858.29,
|
||||||
|
2026-07-28T04:39:38.238266+00:00,3.46,True,200,605.77,True,49,2026-09-15,True,200,623.34,
|
||||||
|
2026-07-28T04:40:40.140083+00:00,2.66,True,200,613.5,True,49,2026-09-15,True,200,648.4,
|
||||||
|
2026-07-28T04:41:42.039580+00:00,2.65,True,200,642.36,True,49,2026-09-15,True,200,608.2,
|
||||||
|
2026-07-28T04:42:35.197388+00:00,5.17,True,200,931.97,True,49,2026-09-15,True,200,949.73,
|
||||||
|
2026-07-28T04:42:43.929046+00:00,2.32,True,200,654.17,True,49,2026-09-15,True,200,611.71,
|
||||||
|
2026-07-28T04:43:08.606710+00:00,9.07,True,200,907.32,True,49,2026-09-15,True,200,1103.37,
|
||||||
|
2026-07-28T04:43:45.921700+00:00,121.9,True,200,604.39,True,49,2026-09-15,True,200,644.85,
|
||||||
|
2026-07-28T04:44:47.853658+00:00,2.99,True,200,661.76,True,49,2026-09-15,True,200,627.05,
|
||||||
|
2026-07-28T04:45:49.750653+00:00,6.3,True,200,652.29,True,49,2026-09-15,True,200,611.18,
|
||||||
|
|||||||
|
File diff suppressed because one or more lines are too long
@@ -880,3 +880,17 @@
|
|||||||
{"timestamp": "2026-07-28T04:32:24.769605+00:00", "dns": {"time_ms": 2.42, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 597.6, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 671.85, "error": null}, "error": null}
|
{"timestamp": "2026-07-28T04:32:24.769605+00:00", "dns": {"time_ms": 2.42, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 597.6, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 671.85, "error": null}, "error": null}
|
||||||
{"timestamp": "2026-07-28T04:33:26.739139+00:00", "dns": {"time_ms": 3.17, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 709.09, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 613.71, "error": null}, "error": null}
|
{"timestamp": "2026-07-28T04:33:26.739139+00:00", "dns": {"time_ms": 3.17, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 709.09, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 613.71, "error": null}, "error": null}
|
||||||
{"timestamp": "2026-07-28T04:34:28.659957+00:00", "dns": {"time_ms": 6.41, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 617.23, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 671.04, "error": null}, "error": null}
|
{"timestamp": "2026-07-28T04:34:28.659957+00:00", "dns": {"time_ms": 6.41, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 617.23, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 671.04, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:35:30.526140+00:00", "dns": {"time_ms": 2.32, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 630.7, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 598.95, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:36:32.364936+00:00", "dns": {"time_ms": 2.46, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 616.06, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 593.11, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:37:34.295093+00:00", "dns": {"time_ms": 3.04, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 643.24, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 653.17, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:38:36.392339+00:00", "dns": {"time_ms": 2.66, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 660.38, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 682.75, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:39:29.189047+00:00", "dns": {"time_ms": 99.86, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 1010.9, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 858.29, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:39:38.238266+00:00", "dns": {"time_ms": 3.46, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 605.77, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 623.34, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:40:40.140083+00:00", "dns": {"time_ms": 2.66, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 613.5, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 648.4, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:41:42.039580+00:00", "dns": {"time_ms": 2.65, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 642.36, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 608.2, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:42:35.197388+00:00", "dns": {"time_ms": 5.17, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 931.97, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 949.73, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:42:43.929046+00:00", "dns": {"time_ms": 2.32, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 654.17, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 611.71, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:43:08.606710+00:00", "dns": {"time_ms": 9.07, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 907.32, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 1103.37, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:43:45.921700+00:00", "dns": {"time_ms": 121.9, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 604.39, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 644.85, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:44:47.853658+00:00", "dns": {"time_ms": 2.99, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 661.76, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 627.05, "error": null}, "error": null}
|
||||||
|
{"timestamp": "2026-07-28T04:45:49.750653+00:00", "dns": {"time_ms": 6.3, "error": null}, "http": {"up": true, "status_code": 200, "latency_ms": 652.29, "error": null}, "ssl": {"valid": true, "days_remaining": 49, "expiry_date": "2026-09-15", "error": null}, "search": {"up": true, "status_code": 200, "latency_ms": 611.18, "error": null}, "error": null}
|
||||||
|
|||||||
@@ -3,13 +3,19 @@ set -e
|
|||||||
|
|
||||||
export PYTHONUNBUFFERED=1
|
export PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
PORT="${PORT:-8080}"
|
||||||
|
|
||||||
# Ensure dashboard.html points to index.html to avoid code duplication
|
# Ensure dashboard.html points to index.html to avoid code duplication
|
||||||
if [ ! -L dashboard.html ] && [ ! -f dashboard.html ]; then
|
if [ ! -L dashboard.html ] && [ ! -f dashboard.html ]; then
|
||||||
ln -sf index.html dashboard.html
|
ln -sf index.html dashboard.html
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Starting Mithal website background monitor..."
|
echo "Running initial website check..."
|
||||||
|
python3 monitor.py --once || true
|
||||||
|
|
||||||
|
echo "Starting background website monitor..."
|
||||||
python3 monitor.py &
|
python3 monitor.py &
|
||||||
|
|
||||||
echo "Starting Mithal web dashboard on port 8080..."
|
echo "Starting web dashboard on port ${PORT}..."
|
||||||
exec python3 -m http.server 8080
|
exec python3 -m http.server "${PORT}"
|
||||||
|
|
||||||
|
|||||||
+101
-13
@@ -4,7 +4,9 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>mithal.space — لوحة المراقبة</title>
|
<title>mithal.space — لوحة المراقبة</title>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.4/chart.umd.min.js"></script>
|
<script src="chart.umd.min.js"></script>
|
||||||
|
<script>if (typeof Chart === 'undefined') { document.write('<script src="https://cdn.jsdelivr.net/npm/chart.js"><\/script>'); }</script>
|
||||||
|
<script src="data.js"></script>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
@@ -161,17 +163,75 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const REFRESH_MS = 60000; // إعادة تحميل تلقائي كل دقيقة
|
const REFRESH_MS = 15000; // إعادة تحميل تلقائي كل 15 ثانية لجلب البيانات الحديثة فوراً
|
||||||
|
|
||||||
let records = [];
|
let records = (typeof window !== 'undefined' && Array.isArray(window.MONITOR_DATA)) ? window.MONITOR_DATA : [];
|
||||||
let latencyChart = null;
|
let latencyChart = null;
|
||||||
|
|
||||||
function parseJsonl(text){
|
function parseJsonl(text){
|
||||||
return text.split('\n')
|
const items = text.split('\n')
|
||||||
.map(l => l.trim())
|
.map(l => l.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map(l => { try { return JSON.parse(l); } catch(e){ return null; } })
|
.map(l => { try { return JSON.parse(l); } catch(e){ return null; } })
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
return items.flat();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCsv(text) {
|
||||||
|
const lines = text.split('\n').map(l => l.trim()).filter(Boolean);
|
||||||
|
if (lines.length < 2) return [];
|
||||||
|
const headers = lines[0].split(',').map(h => h.trim().replace(/^"|"$/g, ''));
|
||||||
|
const parsed = [];
|
||||||
|
for (let i = 1; i < lines.length; i++) {
|
||||||
|
const cols = lines[i].split(',').map(c => c.trim().replace(/^"|"$/g, ''));
|
||||||
|
if (cols.length < headers.length) continue;
|
||||||
|
const row = {};
|
||||||
|
headers.forEach((h, idx) => { row[h] = cols[idx]; });
|
||||||
|
parsed.push({
|
||||||
|
timestamp: row.timestamp,
|
||||||
|
dns: { time_ms: row.dns_time_ms ? parseFloat(row.dns_time_ms) : (row.dns_latency_ms ? parseFloat(row.dns_latency_ms) : null) },
|
||||||
|
http: {
|
||||||
|
up: row.http_up === 'True' || row.http_up === 'true' || row.uptime === 'True' || row.uptime === 'true',
|
||||||
|
status_code: row.http_status_code ? parseInt(row.http_status_code) : (row.status_code ? parseInt(row.status_code) : null),
|
||||||
|
latency_ms: row.http_latency_ms ? parseFloat(row.http_latency_ms) : (row.latency_ms ? parseFloat(row.latency_ms) : null)
|
||||||
|
},
|
||||||
|
ssl: {
|
||||||
|
valid: row.ssl_valid === 'True' || row.ssl_valid === 'true',
|
||||||
|
days_remaining: row.ssl_days_remaining ? parseInt(row.ssl_days_remaining) : null,
|
||||||
|
expiry_date: row.ssl_expiry_date || row.ssl_expiry || null
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
up: row.search_up === 'True' || row.search_up === 'true' || row.search_success === 'True' || row.search_success === 'true',
|
||||||
|
status_code: row.search_status_code ? parseInt(row.search_status_code) : null,
|
||||||
|
latency_ms: row.search_latency_ms ? parseFloat(row.search_latency_ms) : null
|
||||||
|
},
|
||||||
|
error: row.error || null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFileContent(text) {
|
||||||
|
let trimmed = text.trim();
|
||||||
|
if (trimmed.startsWith('window.MONITOR_DATA')) {
|
||||||
|
const eqIdx = trimmed.indexOf('=');
|
||||||
|
if (eqIdx !== -1) {
|
||||||
|
trimmed = trimmed.substring(eqIdx + 1).trim().replace(/;$/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (trimmed.startsWith('[')) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(trimmed);
|
||||||
|
if (Array.isArray(parsed)) return parsed.flat();
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
if (trimmed.startsWith('{')) {
|
||||||
|
return parseJsonl(trimmed);
|
||||||
|
}
|
||||||
|
if (trimmed.includes(',')) {
|
||||||
|
return parseCsv(trimmed);
|
||||||
|
}
|
||||||
|
return parseJsonl(trimmed);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fmtTime(iso){
|
function fmtTime(iso){
|
||||||
@@ -202,9 +262,11 @@ function statusColorClass(days){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initChart() {
|
function initChart() {
|
||||||
|
if (typeof Chart === 'undefined') return;
|
||||||
const chartCanvas = document.getElementById('latencyChart');
|
const chartCanvas = document.getElementById('latencyChart');
|
||||||
if (!chartCanvas) return;
|
if (!chartCanvas) return;
|
||||||
const ctx = chartCanvas.getContext('2d');
|
const ctx = chartCanvas.getContext('2d');
|
||||||
|
try {
|
||||||
latencyChart = new Chart(ctx, {
|
latencyChart = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
@@ -244,6 +306,9 @@ function initChart() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} catch(e) {
|
||||||
|
console.warn('Could not initialize Chart:', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function render(){
|
function render(){
|
||||||
@@ -318,13 +383,13 @@ function render(){
|
|||||||
|
|
||||||
// --- update Chart smoothly ---
|
// --- update Chart smoothly ---
|
||||||
const lastHour = sorted.filter(r => nowMs - new Date(r.timestamp).getTime() <= 3600*1000);
|
const lastHour = sorted.filter(r => nowMs - new Date(r.timestamp).getTime() <= 3600*1000);
|
||||||
const chartSource = lastHour.length ? lastHour : sorted.slice(-30);
|
const chartSource = lastHour.length >= 2 ? lastHour : sorted.slice(-30);
|
||||||
|
|
||||||
const labels = chartSource.map(r => fmtShortTime(r.timestamp));
|
const labels = chartSource.map(r => fmtShortTime(r.timestamp));
|
||||||
const httpData = chartSource.map(r => r.http ? r.http.latency_ms : null);
|
const httpData = chartSource.map(r => r.http ? r.http.latency_ms : null);
|
||||||
const searchData = chartSource.map(r => r.search ? r.search.latency_ms : null);
|
const searchData = chartSource.map(r => r.search ? r.search.latency_ms : null);
|
||||||
|
|
||||||
if (!latencyChart) {
|
if (!latencyChart && typeof Chart !== 'undefined') {
|
||||||
initChart();
|
initChart();
|
||||||
}
|
}
|
||||||
if (latencyChart) {
|
if (latencyChart) {
|
||||||
@@ -354,15 +419,33 @@ function render(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadFromServer(){
|
async function fetchFile(filename) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('data.jsonl', { cache: 'no-store' });
|
const url = `${filename}?_t=${Date.now()}`;
|
||||||
if (!res.ok) throw new Error('not found');
|
const res = await fetch(url, { cache: 'no-store' });
|
||||||
|
if (!res.ok) return null;
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
records = parseJsonl(text);
|
const data = parseFileContent(text);
|
||||||
render();
|
return (data && data.length) ? data : null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Fallback when served directly as file:// or if fetch fails
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadFromServer(){
|
||||||
|
const filesToTry = ['data.jsonl', 'data.csv', 'monitoring_data.csv', 'data.js'];
|
||||||
|
for (const file of filesToTry) {
|
||||||
|
const fetched = await fetchFile(file);
|
||||||
|
if (fetched && fetched.length) {
|
||||||
|
records = fetched;
|
||||||
|
render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.MONITOR_DATA && window.MONITOR_DATA.length) {
|
||||||
|
records = window.MONITOR_DATA;
|
||||||
|
render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,13 +454,18 @@ document.getElementById('fileInput').addEventListener('change', (e) => {
|
|||||||
if (!file) return;
|
if (!file) return;
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (evt) => {
|
reader.onload = (evt) => {
|
||||||
records = parseJsonl(evt.target.result);
|
records = parseFileContent(evt.target.result);
|
||||||
render();
|
render();
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (typeof Chart !== 'undefined') {
|
||||||
initChart();
|
initChart();
|
||||||
|
}
|
||||||
|
if (records.length) {
|
||||||
|
render();
|
||||||
|
}
|
||||||
loadFromServer();
|
loadFromServer();
|
||||||
setInterval(loadFromServer, REFRESH_MS);
|
setInterval(loadFromServer, REFRESH_MS);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ CHECK_INTERVAL_SECONDS = 60 # كل دقيقة
|
|||||||
DATA_DIR = os.path.dirname(os.path.abspath(__file__))
|
DATA_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
JSONL_PATH = os.path.join(DATA_DIR, "data.jsonl")
|
JSONL_PATH = os.path.join(DATA_DIR, "data.jsonl")
|
||||||
CSV_PATH = os.path.join(DATA_DIR, "data.csv")
|
CSV_PATH = os.path.join(DATA_DIR, "data.csv")
|
||||||
|
JS_PATH = os.path.join(DATA_DIR, "data.js")
|
||||||
|
|
||||||
CSV_FIELDS = [
|
CSV_FIELDS = [
|
||||||
"timestamp",
|
"timestamp",
|
||||||
@@ -285,10 +286,31 @@ def save_csv(record: dict):
|
|||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
|
|
||||||
|
|
||||||
|
def save_js():
|
||||||
|
"""تحديث data.js تلقائياً لإتاحتها للمتصفح دون الحاجة لـ fetch/upload"""
|
||||||
|
if not os.path.isfile(JSONL_PATH):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
records = []
|
||||||
|
with open(JSONL_PATH, "r", encoding="utf-8") as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if line:
|
||||||
|
try:
|
||||||
|
records.append(json.loads(line))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
with open(JS_PATH, "w", encoding="utf-8") as f:
|
||||||
|
f.write(f"window.MONITOR_DATA = {json.dumps(records, ensure_ascii=False)};\n")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"تنبيه: تعذّر تحديث data.js: {e}", flush=True)
|
||||||
|
|
||||||
|
|
||||||
def run_once():
|
def run_once():
|
||||||
record = collect_metrics()
|
record = collect_metrics()
|
||||||
save_jsonl(record)
|
save_jsonl(record)
|
||||||
save_csv(record)
|
save_csv(record)
|
||||||
|
save_js()
|
||||||
status = "UP" if record["http"]["up"] else "DOWN"
|
status = "UP" if record["http"]["up"] else "DOWN"
|
||||||
print(
|
print(
|
||||||
f"[{record['timestamp']}] HTTP={status} "
|
f"[{record['timestamp']}] HTTP={status} "
|
||||||
|
|||||||
Reference in New Issue
Block a user