From 91e3e0f709121a2d66b15e3bb75164a65630c7af Mon Sep 17 00:00:00 2001 From: ayashosha Date: Wed, 29 Jul 2026 05:13:59 +0300 Subject: [PATCH] initial files --- q5-mithal-monitor/Dockerfile | 5 + .../__pycache__/monitor.cpython-313.pyc | Bin 0 -> 5636 bytes q5-mithal-monitor/dashboard.html | 142 ++++++++++++++++++ q5-mithal-monitor/metrics.csv | 2 + q5-mithal-monitor/metrics.json | 41 +++++ q5-mithal-monitor/monitor.py | 122 +++++++++++++++ 6 files changed, 312 insertions(+) create mode 100644 q5-mithal-monitor/Dockerfile create mode 100644 q5-mithal-monitor/__pycache__/monitor.cpython-313.pyc create mode 100644 q5-mithal-monitor/dashboard.html create mode 100644 q5-mithal-monitor/metrics.csv create mode 100644 q5-mithal-monitor/metrics.json create mode 100644 q5-mithal-monitor/monitor.py diff --git a/q5-mithal-monitor/Dockerfile b/q5-mithal-monitor/Dockerfile new file mode 100644 index 0000000..f729810 --- /dev/null +++ b/q5-mithal-monitor/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.12-slim +WORKDIR /app +COPY monitor.py dashboard.html ./ +EXPOSE 5000 +CMD ["python", "monitor.py"] \ No newline at end of file diff --git a/q5-mithal-monitor/__pycache__/monitor.cpython-313.pyc b/q5-mithal-monitor/__pycache__/monitor.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90aad1b31536698627e4c08c4ccf8aa67a01ec5e GIT binary patch literal 5636 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 CK6wlP literal 0 HcmV?d00001 diff --git a/q5-mithal-monitor/dashboard.html b/q5-mithal-monitor/dashboard.html new file mode 100644 index 0000000..aa449d5 --- /dev/null +++ b/q5-mithal-monitor/dashboard.html @@ -0,0 +1,142 @@ + + + + + + mithal.space monitor + + + + +

mithal.space — Monitoring

+

loading...

+ +
+
Uptime (24h)
+
-
+
+ +
+
SSL (days left)
+
-
+
+
+ +
+
Latency last hour
+ +
+ +
+
Last 10 checks
+ + + + + + + + + + + + +
timelatencydnssearchstatusresult
+
+ + + + diff --git a/q5-mithal-monitor/metrics.csv b/q5-mithal-monitor/metrics.csv new file mode 100644 index 0000000..7e8bb1f --- /dev/null +++ b/q5-mithal-monitor/metrics.csv @@ -0,0 +1,2 @@ +timestamp,latency_ms,uptime,status_code,ssl_valid,ssl_expires,ssl_days_remaining,dns_ms,search_ms,search_status_code,overall +2026-07-29T01:55:40.917123+00:00,705.91,True,200,True,2026-09-15T13:10:47+00:00,48,0.54,1684.08,200,True diff --git a/q5-mithal-monitor/metrics.json b/q5-mithal-monitor/metrics.json new file mode 100644 index 0000000..fa7ade2 --- /dev/null +++ b/q5-mithal-monitor/metrics.json @@ -0,0 +1,41 @@ +[ + { + "timestamp": "2026-07-29T01:55:40.917123+00:00", + "latency_ms": 705.91, + "uptime": true, + "status_code": 200, + "ssl_valid": true, + "ssl_expires": "2026-09-15T13:10:47+00:00", + "ssl_days_remaining": 48, + "dns_ms": 0.54, + "search_ms": 1684.08, + "search_status_code": 200, + "overall": true + }, + { + "timestamp": "2026-07-29T02:06:20.105132Z", + "latency_ms": 515.49, + "uptime": true, + "status_code": 200, + "dns_ms": 10.14, + "ssl_valid": false, + "ssl_expires": "Sep 15 13:10:47 2026 GMT", + "ssl_days_remaining": null, + "search_ms": 476.59, + "search_status_code": 200, + "overall": false + }, + { + "timestamp": "2026-07-29T02:06:35.031662Z", + "latency_ms": 502.26, + "uptime": true, + "status_code": 200, + "dns_ms": 0.51, + "ssl_valid": false, + "ssl_expires": "Sep 15 13:10:47 2026 GMT", + "ssl_days_remaining": null, + "search_ms": 496.97, + "search_status_code": 200, + "overall": false + } +] \ No newline at end of file diff --git a/q5-mithal-monitor/monitor.py b/q5-mithal-monitor/monitor.py new file mode 100644 index 0000000..e4ecafa --- /dev/null +++ b/q5-mithal-monitor/monitor.py @@ -0,0 +1,122 @@ +import json +import os +import socket +import ssl +import threading +import time +from datetime import datetime +from http.server import HTTPServer, SimpleHTTPRequestHandler +from urllib.error import HTTPError +from urllib.request import urlopen + +URL = "https://mithal.space" +SEARCH_URL = "https://mithal.space/search?q=test" +HOST = "mithal.space" +METRICS_FILE = "metrics.json" + + +def check_http(url): + start = time.time() + status = 0 + ok = False + try: + res = urlopen(url, timeout=15) + status = res.status + ok = status == 200 + res.read(1024) + except HTTPError as e: + status = e.code + ok = status == 200 + except Exception: + ok = False + ms = round((time.time() - start) * 1000, 2) + return ms, status, ok + + +def check_dns(): + start = time.time() + ok = False + try: + socket.gethostbyname(HOST) + ok = True + except Exception: + ok = False + ms = round((time.time() - start) * 1000, 2) + return ms, ok + + +def check_ssl(): + valid = False + expires = None + days_left = None + try: + ctx = ssl.create_default_context() + with socket.create_connection((HOST, 443), timeout=10) as s: + with ctx.wrap_socket(s, server_hostname=HOST) as ss: + cert = ss.getpeercert() + expires = cert["notAfter"] + exp_date = datetime.strptime(expires, "%b %d %H:%M:%S %Y %GMT") + days_left = (exp_date - datetime.utcnow()).days + valid = True + except Exception: + pass + return valid, expires, days_left + + +def run_check(): + latency, status_code, uptime = check_http(URL) + dns_ms, dns_ok = check_dns() + ssl_ok, ssl_expires, ssl_days = check_ssl() + search_ms, search_code, search_ok = check_http(SEARCH_URL) + + row = { + "timestamp": datetime.utcnow().isoformat() + "Z", + "latency_ms": latency, + "uptime": uptime, + "status_code": status_code, + "dns_ms": dns_ms, + "ssl_valid": ssl_ok, + "ssl_expires": ssl_expires, + "ssl_days_remaining": ssl_days, + "search_ms": search_ms, + "search_status_code": search_code, + "overall": uptime and dns_ok and ssl_ok and search_ok, + } + return row + + +def save_row(row): + data = [] + if os.path.exists(METRICS_FILE): + with open(METRICS_FILE, "r", encoding="utf-8") as f: + data = json.load(f) + + data.append(row) + + # keep last 24 hours (1440 checks if every 1 min) + if len(data) > 1440: + data = data[-1440:] + + with open(METRICS_FILE, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2) + + +def monitor_loop(): + while True: + row = run_check() + save_row(row) + print(row) + time.sleep(60) + + +def start_server(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + server = HTTPServer(("0.0.0.0", 5000), SimpleHTTPRequestHandler) + print("dashboard: http://0.0.0.0:5000/dashboard.html") + server.serve_forever() + + +if __name__ == "__main__": + save_row(run_check()) + threading.Thread(target=monitor_loop, daemon=True).start() + start_server()