Restructure repository for final submission
هذا الالتزام موجود في:
72
common-mortakaz/integration-1.md
Normal file
72
common-mortakaz/integration-1.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Integration Proposal - Product 1: Green Framework
|
||||
|
||||
## 1. Selected Product Description
|
||||
|
||||
- **Product Name:** Green Framework
|
||||
- **Overview:** A modern PHP framework designed for building web applications and REST APIs efficiently. It helps developers create scalable backend applications with a clean and organized structure.
|
||||
|
||||
---
|
||||
|
||||
## 2. How it integrates with `ghaymah.systems`
|
||||
|
||||
### Proposed Integration
|
||||
|
||||
1. Developers can deploy Green Framework applications on Ghaymah cloud infrastructure.
|
||||
2. Source code can be connected to a CI/CD pipeline to automate building and deployment.
|
||||
3. Docker containers can be used to package the application for consistent deployments.
|
||||
4. Monitoring and logging tools can be connected to monitor application health and performance.
|
||||
|
||||
---
|
||||
|
||||
## 3. Added Value for the End User
|
||||
|
||||
- Faster application deployment.
|
||||
- Easier application maintenance.
|
||||
- More reliable hosting environment.
|
||||
- Better visibility into application health and availability.
|
||||
- Simplified deployment workflow for development teams.
|
||||
|
||||
---
|
||||
|
||||
## 4. Architecture Sketch
|
||||
|
||||
```text
|
||||
Developer
|
||||
│
|
||||
▼
|
||||
Git Repository
|
||||
│
|
||||
▼
|
||||
CI/CD Pipeline
|
||||
│
|
||||
▼
|
||||
Docker Container
|
||||
│
|
||||
▼
|
||||
Ghaymah Infrastructure
|
||||
│
|
||||
▼
|
||||
Green Framework Application
|
||||
│
|
||||
▼
|
||||
Monitoring & Logs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Potential Technical or Commercial Challenges
|
||||
|
||||
### Technical
|
||||
- Managing environment variables securely.
|
||||
- Handling database migrations during deployments.
|
||||
- Monitoring application performance across environments.
|
||||
|
||||
### Commercial
|
||||
- Encouraging developers to adopt a new hosting platform.
|
||||
- Providing clear migration guides for existing applications.
|
||||
|
||||
---
|
||||
|
||||
## 6. Which product is more viable?
|
||||
|
||||
**Green Framework** is the stronger integration candidate because every application built with the framework eventually needs a reliable deployment environment. Integrating it with Ghaymah would provide developers with a complete deployment workflow while supporting modern DevOps practices.
|
||||
67
common-mortakaz/integration-2.md
Normal file
67
common-mortakaz/integration-2.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Integration Proposal - Product 2: Jadel (جَدَل)
|
||||
|
||||
## 1. Selected Product Description
|
||||
|
||||
- **Product Name:** Jadel
|
||||
- **Overview:** A SaaS platform for managing fitness centers and gyms, including memberships, subscriptions, attendance, and daily business operations.
|
||||
|
||||
---
|
||||
|
||||
## 2. How it integrates with `ghaymah.systems`
|
||||
|
||||
### Proposed Integration
|
||||
|
||||
1. Host the Jadel platform on Ghaymah infrastructure to improve service availability.
|
||||
2. Run the application and its database in a secure cloud environment.
|
||||
3. Connect monitoring tools to observe application health and detect failures.
|
||||
4. Schedule regular backups to reduce the risk of data loss.
|
||||
|
||||
---
|
||||
|
||||
## 3. Added Value for the End User
|
||||
|
||||
- Higher application availability.
|
||||
- Improved system reliability.
|
||||
- Better monitoring for administrators.
|
||||
- Easier infrastructure management.
|
||||
- Ability to support business growth without major infrastructure changes.
|
||||
|
||||
---
|
||||
|
||||
## 4. Architecture Sketch
|
||||
|
||||
```text
|
||||
Gym Staff
|
||||
│
|
||||
▼
|
||||
Jadel Platform
|
||||
│
|
||||
▼
|
||||
Ghaymah Infrastructure
|
||||
│
|
||||
┌───┴──────────┐
|
||||
▼ ▼
|
||||
Application Database
|
||||
│
|
||||
▼
|
||||
Monitoring & Backups
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Potential Technical or Commercial Challenges
|
||||
|
||||
### Technical
|
||||
- Protecting customer information.
|
||||
- Database migration and backup management.
|
||||
- Maintaining service availability during updates.
|
||||
|
||||
### Commercial
|
||||
- Convincing businesses to migrate from existing systems.
|
||||
- Training users on the new platform.
|
||||
|
||||
---
|
||||
|
||||
## 6. Which product is more viable?
|
||||
|
||||
Although Jadel would benefit from cloud hosting, its adoption depends mainly on business demand within the fitness industry. Compared to Green Framework, it targets a more specific market, making its integration slightly less flexible.
|
||||
4
common-qabilah/qabilah-profile.txt
Normal file
4
common-qabilah/qabilah-profile.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Name: Mohammed Hamdy Mahmoud
|
||||
|
||||
Qabilah Profile:
|
||||
https://qabilah.com/profile/mohammedhamdy102003/posts
|
||||
179
q1-deploy-monitor/README.md
Normal file
179
q1-deploy-monitor/README.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# Q1 - Deploy and Monitor an API on Ghaymah Cloud
|
||||
|
||||
## Overview
|
||||
|
||||
This project demonstrates deploying a Dockerized Python API to **Ghaymah Cloud** and implementing a simple monitoring solution.
|
||||
|
||||
The application exposes a `/health` endpoint that is continuously monitored using a Python script. A lightweight HTML dashboard displays the application's health status, response time, uptime, and request statistics.
|
||||
|
||||
---
|
||||
|
||||
# Project Structure
|
||||
|
||||
```
|
||||
Q1-Deploy-and-Monitoring/
|
||||
│
|
||||
├── screenshots/
|
||||
│ ├── dashboard-local.png
|
||||
│ └── dashboard-ghaymah.png
|
||||
│
|
||||
├── Dockerfile
|
||||
├── app.py
|
||||
├── health-check.py
|
||||
├── dashboard.html
|
||||
├── monitor-log.csv
|
||||
├── requirements.txt
|
||||
├── install-docker.sh
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Task Requirements
|
||||
|
||||
This implementation satisfies all requirements of Question 1.
|
||||
|
||||
| Requirement | Status |
|
||||
|------------|--------|
|
||||
| Dockerize the API | ✅ |
|
||||
| Deploy to Ghaymah Cloud | ✅ |
|
||||
| Implement `/health` endpoint | ✅ |
|
||||
| Monitoring script (every 30 seconds) | ✅ |
|
||||
| Monitoring Dashboard | ✅ |
|
||||
|
||||
---
|
||||
|
||||
# Technologies
|
||||
|
||||
- Python
|
||||
- Flask
|
||||
- Docker
|
||||
- HTML
|
||||
- CSS
|
||||
- JavaScript
|
||||
- Ghaymah Cloud
|
||||
- Ghaymah CLI
|
||||
|
||||
---
|
||||
|
||||
# API
|
||||
|
||||
## Health Endpoint
|
||||
|
||||
```
|
||||
GET /health
|
||||
```
|
||||
|
||||
Example response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "healthy"
|
||||
}
|
||||
```
|
||||
|
||||
The monitoring script periodically sends requests to this endpoint to verify application availability.
|
||||
|
||||
---
|
||||
|
||||
# Docker
|
||||
|
||||
Build the Docker image
|
||||
|
||||
```bash
|
||||
docker build -t exam-api .
|
||||
```
|
||||
|
||||
Run the container
|
||||
|
||||
```bash
|
||||
docker run -d -p 5000:5000 exam-api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Monitoring Script
|
||||
|
||||
The monitoring script (`health-check.py`) executes every **30 seconds** and performs the following operations:
|
||||
|
||||
- Sends an HTTP request to `/health`
|
||||
- Measures response latency
|
||||
- Detects application availability
|
||||
- Records monitoring results
|
||||
- Updates `monitor-log.csv`
|
||||
|
||||
Run the monitor
|
||||
|
||||
```bash
|
||||
python3 health-check.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Monitoring Dashboard
|
||||
|
||||
The dashboard was implemented using HTML, CSS and JavaScript.
|
||||
|
||||
Displayed metrics include:
|
||||
|
||||
- Current application status
|
||||
- Response time
|
||||
- Total requests
|
||||
- Application uptime
|
||||
- Monitoring history
|
||||
|
||||
---
|
||||
|
||||
# Deployment using Ghaymah CLI
|
||||
|
||||
The application was deployed to **Ghaymah Cloud** using the official CLI.
|
||||
|
||||
Deployment workflow:
|
||||
|
||||
1. Install Ghaymah CLI
|
||||
2. Authenticate using account credentials
|
||||
3. Select deployment configuration
|
||||
4. Launch the application
|
||||
|
||||
Example commands
|
||||
|
||||
```bash
|
||||
gy auth login
|
||||
|
||||
cp .ghaymah.production.json .ghaymah.json
|
||||
|
||||
gy resource app launch
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Dashboard Preview
|
||||
|
||||
## Local Testing
|
||||
|
||||
The dashboard was first tested locally against the application running on the EC2 instance before deploying to Ghaymah Cloud.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Ghaymah Cloud Deployment
|
||||
|
||||
After deployment, the dashboard successfully monitored the live application hosted on **Ghaymah Cloud**.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Result
|
||||
|
||||
The project successfully demonstrates:
|
||||
|
||||
- Docker containerization
|
||||
- Cloud deployment on Ghaymah
|
||||
- Automated health monitoring
|
||||
- Response time tracking
|
||||
- Monitoring dashboard
|
||||
- Continuous application health verification
|
||||
|
||||
This implementation satisfies all requirements of **Question 1 – Deploy and Monitor an API on Ghaymah Cloud**.
|
||||
ثنائية
q1-deploy-monitor/screenshots/dashboard-ghaymah.png
Normal file
ثنائية
q1-deploy-monitor/screenshots/dashboard-ghaymah.png
Normal file
ملف ثنائي غير معروض.
|
بعد العرض: | الارتفاع: | الحجم: 135 KiB |
ثنائية
q1-deploy-monitor/screenshots/dashboard-local.png
Normal file
ثنائية
q1-deploy-monitor/screenshots/dashboard-local.png
Normal file
ملف ثنائي غير معروض.
|
بعد العرض: | الارتفاع: | الحجم: 134 KiB |
191
q3-cicd/README.md
Normal file
191
q3-cicd/README.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Q3 – CI/CD Pipeline on Ghaymah Cloud
|
||||
|
||||
## Overview
|
||||
|
||||
This task implements a complete CI/CD pipeline using **GitHub Actions** and **Ghaymah Cloud**.
|
||||
|
||||
The pipeline automatically builds the application, performs a smoke test, deploys to the **staging** environment for development changes, and deploys to the **production** environment after merging into the `main` branch with **manual approval** enabled.
|
||||
|
||||
---
|
||||
|
||||
# Pipeline Workflow
|
||||
|
||||
```
|
||||
Developer
|
||||
│
|
||||
│ Push
|
||||
▼
|
||||
GitHub Repository
|
||||
│
|
||||
▼
|
||||
GitHub Actions
|
||||
│
|
||||
├── Build Docker Image
|
||||
├── Smoke Test (/health)
|
||||
│
|
||||
├──────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
develop main
|
||||
│ │
|
||||
Deploy Manual Approval
|
||||
Staging │
|
||||
▼
|
||||
Deploy Production
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# CI Pipeline
|
||||
|
||||
The Continuous Integration stage performs:
|
||||
|
||||
- Checkout repository
|
||||
- Build Docker image
|
||||
- Start container
|
||||
- Execute smoke test
|
||||
- Verify `/health` endpoint
|
||||
- Stop and remove the test container
|
||||
|
||||
This ensures that only healthy builds continue to deployment.
|
||||
|
||||
---
|
||||
|
||||
# CD Pipeline
|
||||
|
||||
## Staging Deployment
|
||||
|
||||
Triggered automatically when code is pushed to the **develop** branch.
|
||||
|
||||
Steps:
|
||||
|
||||
1. Install Ghaymah CLI
|
||||
2. Authenticate using GitHub Secrets
|
||||
3. Load `.ghaymah.staging.json`
|
||||
4. Deploy application to the staging environment
|
||||
|
||||
Purpose:
|
||||
|
||||
- Integration testing
|
||||
- Validation before production
|
||||
- Detect deployment issues early
|
||||
|
||||
---
|
||||
|
||||
## Production Deployment
|
||||
|
||||
Triggered after merging into the **main** branch.
|
||||
|
||||
Deployment requires **manual approval** through the GitHub Environment protection rules before execution.
|
||||
|
||||
Steps:
|
||||
|
||||
1. Install Ghaymah CLI
|
||||
2. Authenticate using GitHub Secrets
|
||||
3. Load `.ghaymah.production.json`
|
||||
4. Deploy application to the production environment
|
||||
|
||||
Purpose:
|
||||
|
||||
- Stable release
|
||||
- Manual verification before deployment
|
||||
- Reduce production risks
|
||||
|
||||
---
|
||||
|
||||
# Staging vs Production
|
||||
|
||||
| Staging | Production |
|
||||
|----------|------------|
|
||||
| Testing environment | Live environment |
|
||||
| Used by developers | Used by end users |
|
||||
| Automatic deployment | Protected deployment |
|
||||
| Safe for validation | High availability |
|
||||
| Can be updated frequently | Only verified releases |
|
||||
|
||||
---
|
||||
|
||||
# Manual Approval
|
||||
|
||||
Production deployments are protected using **GitHub Environments**.
|
||||
|
||||
The workflow pauses before deploying to production until manual approval is granted.
|
||||
|
||||
Benefits:
|
||||
|
||||
- Prevent accidental deployments
|
||||
- Final verification before release
|
||||
- Safer production deployments
|
||||
|
||||
---
|
||||
|
||||
# Ghaymah CLI Integration
|
||||
|
||||
Deployment is performed using the official **Ghaymah CLI**.
|
||||
|
||||
Installation:
|
||||
|
||||
```bash
|
||||
curl -sSL https://cli.ghaymah.systems/install.sh | bash
|
||||
```
|
||||
|
||||
Authentication:
|
||||
|
||||
```bash
|
||||
gy auth login \
|
||||
--email "<EMAIL>" \
|
||||
--password "<PASSWORD>"
|
||||
```
|
||||
|
||||
Deployment:
|
||||
|
||||
```bash
|
||||
cp .ghaymah.production.json .ghaymah.json
|
||||
gy resource app launch
|
||||
```
|
||||
|
||||
For the staging environment, the workflow uses:
|
||||
|
||||
```bash
|
||||
cp .ghaymah.staging.json .ghaymah.json
|
||||
```
|
||||
|
||||
before deployment.
|
||||
|
||||
---
|
||||
|
||||
# GitHub Secrets
|
||||
|
||||
Sensitive credentials are stored securely as GitHub Secrets.
|
||||
|
||||
Secrets used:
|
||||
|
||||
- GHAYMAH_EMAIL
|
||||
- GHAYMAH_PW
|
||||
|
||||
No credentials are stored inside the repository.
|
||||
|
||||
---
|
||||
|
||||
# Technologies Used
|
||||
|
||||
- GitHub Actions
|
||||
- Docker
|
||||
- Ghaymah Cloud
|
||||
- Ghaymah CLI
|
||||
- GitHub Environments
|
||||
- GitHub Secrets
|
||||
|
||||
---
|
||||
|
||||
# Result
|
||||
|
||||
The pipeline successfully performs:
|
||||
|
||||
- Automated Docker image build
|
||||
- Smoke testing
|
||||
- Automatic deployment to Staging
|
||||
- Manual approval before Production
|
||||
- Production deployment using Ghaymah CLI
|
||||
|
||||
This implementation provides a reliable and production-ready CI/CD workflow.
|
||||
|
قبل العرض: | الارتفاع: | الحجم: 1.2 MiB بعد العرض: | الارتفاع: | الحجم: 1.2 MiB |
6
q5-mithal-monitor/.dockerignore
Normal file
6
q5-mithal-monitor/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
||||
.venv
|
||||
__pycache__
|
||||
*.pyc
|
||||
.git
|
||||
monitor-data.json
|
||||
README.md
|
||||
17
q5-mithal-monitor/.ghaymah.json
Normal file
17
q5-mithal-monitor/.ghaymah.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "bbf0b1b5-2214-479e-b5e7-00be81bd0e7d",
|
||||
"name": "mithal-monitor",
|
||||
"projectId": "6d1bb957-8a2c-4a88-86f3-6a937b7971f0",
|
||||
"ports": [
|
||||
{
|
||||
"expose": true,
|
||||
"number": 5000
|
||||
}
|
||||
],
|
||||
"publicAccess": {
|
||||
"enabled": true,
|
||||
"domain": "auto"
|
||||
},
|
||||
"resourceTier": "t1",
|
||||
"dockerFileName": "Dockerfile"
|
||||
}
|
||||
13
q5-mithal-monitor/Dockerfile
Normal file
13
q5-mithal-monitor/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
|
||||
161
q5-mithal-monitor/README.md
Normal file
161
q5-mithal-monitor/README.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Mithal Monitor
|
||||
|
||||
A Flask-based monitoring application that periodically collects website metrics and exposes them through REST APIs. The application is containerized with Docker and deployed on Ghaymah.
|
||||
|
||||
## Features
|
||||
|
||||
- Monitor website availability
|
||||
- Measure DNS lookup time
|
||||
- Measure HTTP latency
|
||||
- Measure search latency
|
||||
- Check SSL certificate expiration
|
||||
- Store collected metrics
|
||||
- REST API for health checks and metrics
|
||||
- Dockerized deployment
|
||||
- Deployed on Ghaymah
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- Python
|
||||
- Flask
|
||||
- SQLite
|
||||
- Docker
|
||||
- Gunicorn
|
||||
- Ghaymah
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Health Check
|
||||
|
||||
```http
|
||||
GET /health
|
||||
```
|
||||
|
||||
Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "healthy"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Metrics
|
||||
|
||||
```http
|
||||
GET /api/metrics
|
||||
```
|
||||
|
||||
Example Response
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"timestamp": "2026-07-28 12:11:50",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 34.03,
|
||||
"dns_ms": 2.38,
|
||||
"search_latency_ms": 108.42,
|
||||
"ssl_days_left": 49,
|
||||
"ssl_expiry": "2026-09-15"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Running Locally
|
||||
|
||||
Clone the repository
|
||||
|
||||
```bash
|
||||
git clone <repo-url>
|
||||
cd Q5-Mithal-Monitor
|
||||
```
|
||||
|
||||
Install dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Run the application
|
||||
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
The application will be available on
|
||||
|
||||
```
|
||||
http://localhost:5000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Docker
|
||||
|
||||
Build the image
|
||||
|
||||
```bash
|
||||
docker build -t mithal-monitor .
|
||||
```
|
||||
|
||||
Run the container
|
||||
|
||||
```bash
|
||||
docker run -p 80:80 mithal-monitor
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
The application is deployed on Ghaymah.
|
||||
|
||||
Deployment URL:
|
||||
|
||||
```
|
||||
https://mithal-monitor-74639f9fbe38.hosted.ghaymah.systems
|
||||
```
|
||||
|
||||
Health Endpoint
|
||||
|
||||
```
|
||||
https://mithal-monitor-74639f9fbe38.hosted.ghaymah.systems/health
|
||||
```
|
||||
|
||||
Metrics Endpoint
|
||||
|
||||
```
|
||||
https://mithal-monitor-74639f9fbe38.hosted.ghaymah.systems/api/metrics
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
Q5-Mithal-Monitor/
|
||||
│
|
||||
├── app.py
|
||||
├── requirements.txt
|
||||
├── Dockerfile
|
||||
├── monitor.db
|
||||
├── templates/
|
||||
├── static/
|
||||
├── .ghaymah.json
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Author
|
||||
|
||||
Mohammed Hamdy
|
||||
66
q5-mithal-monitor/app.py
Normal file
66
q5-mithal-monitor/app.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from flask import Flask, jsonify, render_template
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
import monitor # our monitor.py — reused directly, not run as a separate process
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
DATA_FILE = "monitor-data.json"
|
||||
|
||||
|
||||
def background_monitor_loop():
|
||||
"""Runs monitor.monitor() every CHECK_INTERVAL seconds inside this
|
||||
same process/container, since the Dockerfile only starts one
|
||||
process (gunicorn running app.py). This replaces running
|
||||
monitor.py as a separate process, which never actually happened
|
||||
after deployment (only app.py ran, monitor.py was never invoked)."""
|
||||
print("Starting background monitor thread...")
|
||||
while True:
|
||||
try:
|
||||
monitor.monitor()
|
||||
except Exception as e:
|
||||
print("Monitor thread error:", e)
|
||||
time.sleep(monitor.CHECK_INTERVAL)
|
||||
|
||||
|
||||
# Start the background thread once, when this module is imported by
|
||||
# gunicorn. Safe because the Dockerfile runs a single gunicorn worker
|
||||
# (no --workers flag => defaults to 1), so this thread won't be
|
||||
# duplicated across multiple worker processes.
|
||||
monitor_thread = threading.Thread(target=background_monitor_loop, daemon=True)
|
||||
monitor_thread.start()
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def home():
|
||||
return render_template("dashboard.html")
|
||||
|
||||
|
||||
@app.route("/dashboard")
|
||||
def dashboard():
|
||||
return render_template("dashboard.html")
|
||||
|
||||
|
||||
@app.route("/api/metrics")
|
||||
def metrics():
|
||||
|
||||
if not os.path.exists(DATA_FILE):
|
||||
return jsonify([])
|
||||
|
||||
with open(DATA_FILE, "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
@app.route("/health")
|
||||
def health():
|
||||
return {
|
||||
"status": "healthy"
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||
692
q5-mithal-monitor/monitor-data.json
Normal file
692
q5-mithal-monitor/monitor-data.json
Normal file
@@ -0,0 +1,692 @@
|
||||
[
|
||||
{
|
||||
"timestamp": "2026-07-28 06:15:26",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 138.66,
|
||||
"dns_ms": 1.88,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 83.64
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:16:27",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 77.9,
|
||||
"dns_ms": 1.0,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 76.89
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:17:27",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 76.17,
|
||||
"dns_ms": 0.99,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 90.49
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:18:27",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 83.58,
|
||||
"dns_ms": 1.11,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 75.83
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:19:27",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 78.07,
|
||||
"dns_ms": 0.82,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 83.3
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:20:27",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 96.93,
|
||||
"dns_ms": 1.13,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 78.55
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:21:28",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 78.4,
|
||||
"dns_ms": 1.37,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 76.19
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:22:28",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 88.03,
|
||||
"dns_ms": 0.89,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 79.74
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:23:28",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 77.57,
|
||||
"dns_ms": 1.22,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 76.56
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:24:28",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 75.44,
|
||||
"dns_ms": 0.98,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 83.31
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:25:28",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 80.72,
|
||||
"dns_ms": 0.9,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 74.22
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:26:29",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 81.2,
|
||||
"dns_ms": 0.87,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 77.07
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:27:29",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 76.89,
|
||||
"dns_ms": 1.5,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 74.69
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:28:29",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 78.55,
|
||||
"dns_ms": 0.84,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 91.66
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:29:29",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 82.33,
|
||||
"dns_ms": 1.07,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 75.03
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:30:30",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 91.92,
|
||||
"dns_ms": 0.87,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 76.92
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:31:30",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 80.75,
|
||||
"dns_ms": 0.9,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 76.38
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:32:30",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 79.62,
|
||||
"dns_ms": 0.86,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 74.62
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:33:30",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 75.97,
|
||||
"dns_ms": 0.82,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 82.48
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:34:30",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 79.56,
|
||||
"dns_ms": 0.88,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 73.19
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:35:31",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 98.91,
|
||||
"dns_ms": 0.86,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 82.02
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:36:31",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 74.78,
|
||||
"dns_ms": 0.92,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 72.29
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:37:31",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 77.6,
|
||||
"dns_ms": 0.89,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 74.45
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:38:31",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 78.87,
|
||||
"dns_ms": 1.08,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 76.15
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:39:31",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 80.61,
|
||||
"dns_ms": 1.34,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 72.64
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:40:32",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 80.76,
|
||||
"dns_ms": 0.85,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 78.7
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:41:32",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 85.68,
|
||||
"dns_ms": 0.83,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 80.57
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:42:39",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 6717.43,
|
||||
"dns_ms": 0.87,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 83.14
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:43:39",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 76.05,
|
||||
"dns_ms": 0.82,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 75.75
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:44:39",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 78.1,
|
||||
"dns_ms": 0.8,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 85.88
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:45:39",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 112.41,
|
||||
"dns_ms": 0.88,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 81.98
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:46:40",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 77.23,
|
||||
"dns_ms": 0.92,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 73.45
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:47:40",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 79.96,
|
||||
"dns_ms": 0.85,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 73.76
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:48:40",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 82.81,
|
||||
"dns_ms": 0.88,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 73.3
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:49:40",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 83.33,
|
||||
"dns_ms": 0.87,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 72.03
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:50:40",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 81.64,
|
||||
"dns_ms": 0.85,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 73.87
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:51:41",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 87.24,
|
||||
"dns_ms": 0.83,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 79.31
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:52:41",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 80.97,
|
||||
"dns_ms": 1.19,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 77.43
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:53:41",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 81.17,
|
||||
"dns_ms": 0.98,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 73.72
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:54:41",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 82.47,
|
||||
"dns_ms": 0.86,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 75.56
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:55:41",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 80.53,
|
||||
"dns_ms": 0.98,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 80.2
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:56:42",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 83.64,
|
||||
"dns_ms": 0.82,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 71.71
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:57:42",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 81.55,
|
||||
"dns_ms": 0.84,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 72.56
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:58:42",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 82.6,
|
||||
"dns_ms": 0.88,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 78.6
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 06:59:42",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 79.31,
|
||||
"dns_ms": 0.86,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 74.71
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:00:43",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 99.38,
|
||||
"dns_ms": 0.88,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 73.06
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:01:43",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 78.82,
|
||||
"dns_ms": 0.86,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 79.59
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:02:43",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 97.32,
|
||||
"dns_ms": 1.02,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 85.14
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:03:43",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 97.17,
|
||||
"dns_ms": 0.88,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 75.99
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:04:43",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 77.36,
|
||||
"dns_ms": 0.94,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 75.38
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:05:44",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 126.34,
|
||||
"dns_ms": 0.94,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 78.33
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:06:44",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 78.6,
|
||||
"dns_ms": 0.83,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 77.94
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:07:44",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 80.89,
|
||||
"dns_ms": 0.84,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 74.3
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:08:44",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 77.8,
|
||||
"dns_ms": 1.0,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 79.14
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:09:45",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 83.49,
|
||||
"dns_ms": 0.86,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 75.71
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:10:45",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 72.7,
|
||||
"dns_ms": 0.86,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 80.3
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:11:45",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 79.11,
|
||||
"dns_ms": 0.86,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 79.6
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:12:45",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 80.55,
|
||||
"dns_ms": 0.88,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 76.45
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:13:45",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 80.62,
|
||||
"dns_ms": 0.85,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 76.94
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:14:46",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 82.55,
|
||||
"dns_ms": 0.85,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 74.5
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:15:47",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 986.76,
|
||||
"dns_ms": 0.86,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 84.61
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:16:47",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 103.86,
|
||||
"dns_ms": 0.98,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 100.18
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:17:48",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 910.26,
|
||||
"dns_ms": 0.88,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 81.43
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:18:49",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 910.72,
|
||||
"dns_ms": 0.89,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 83.03
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:19:49",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 76.95,
|
||||
"dns_ms": 0.86,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 74.19
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:20:50",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 93.67,
|
||||
"dns_ms": 1.39,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 75.06
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:21:50",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 83.26,
|
||||
"dns_ms": 0.87,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 74.3
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:22:50",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 83.7,
|
||||
"dns_ms": 0.84,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 77.12
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-28 07:23:50",
|
||||
"uptime": true,
|
||||
"status_code": 200,
|
||||
"latency_ms": 76.43,
|
||||
"dns_ms": 0.87,
|
||||
"ssl_expiry": "2026-09-15",
|
||||
"ssl_days_left": 49,
|
||||
"search_latency_ms": 75.44
|
||||
}
|
||||
]
|
||||
187
q5-mithal-monitor/monitor.py
Normal file
187
q5-mithal-monitor/monitor.py
Normal file
@@ -0,0 +1,187 @@
|
||||
import requests
|
||||
import socket
|
||||
import ssl
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import dns.resolver
|
||||
|
||||
from datetime import datetime, UTC
|
||||
|
||||
# ===========================================
|
||||
# Configuration
|
||||
# ===========================================
|
||||
|
||||
BASE_URL = "https://mithal.space"
|
||||
SEARCH_URL = "https://mithal.space/search?q=cloud"
|
||||
|
||||
OUTPUT_FILE = "monitor-data.json"
|
||||
|
||||
CHECK_INTERVAL = 60
|
||||
|
||||
# ===========================================
|
||||
# HTTP Latency + Uptime
|
||||
# ===========================================
|
||||
|
||||
def check_http():
|
||||
|
||||
start = time.perf_counter()
|
||||
|
||||
response = requests.get(BASE_URL, timeout=10)
|
||||
|
||||
latency = (time.perf_counter() - start) * 1000
|
||||
|
||||
return response.status_code, round(latency, 2)
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Search Response
|
||||
# ===========================================
|
||||
|
||||
def check_search():
|
||||
|
||||
start = time.perf_counter()
|
||||
|
||||
response = requests.get(SEARCH_URL, timeout=10)
|
||||
|
||||
latency = (time.perf_counter() - start) * 1000
|
||||
|
||||
return round(latency, 2)
|
||||
|
||||
|
||||
# ===========================================
|
||||
# DNS Lookup Time
|
||||
# ===========================================
|
||||
|
||||
def check_dns():
|
||||
|
||||
resolver = dns.resolver.Resolver()
|
||||
|
||||
start = time.perf_counter()
|
||||
|
||||
resolver.resolve("mithal.space")
|
||||
|
||||
latency = (time.perf_counter() - start) * 1000
|
||||
|
||||
return round(latency, 2)
|
||||
|
||||
|
||||
# ===========================================
|
||||
# SSL Certificate
|
||||
# ===========================================
|
||||
|
||||
def check_ssl():
|
||||
|
||||
hostname = "mithal.space"
|
||||
|
||||
context = ssl.create_default_context()
|
||||
|
||||
with socket.create_connection((hostname, 443), timeout=10) as sock:
|
||||
|
||||
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
|
||||
|
||||
cert = ssock.getpeercert()
|
||||
|
||||
expiry = datetime.strptime(
|
||||
cert["notAfter"],
|
||||
"%b %d %H:%M:%S %Y %Z"
|
||||
).replace(tzinfo=UTC)
|
||||
|
||||
days_left = (expiry - datetime.now(UTC)).days
|
||||
|
||||
return expiry.strftime("%Y-%m-%d"), days_left
|
||||
|
||||
|
||||
# ===========================================
|
||||
# JSON
|
||||
# ===========================================
|
||||
|
||||
def load_history():
|
||||
|
||||
if not os.path.exists(OUTPUT_FILE):
|
||||
return []
|
||||
|
||||
try:
|
||||
with open(OUTPUT_FILE, "r") as f:
|
||||
|
||||
content = f.read().strip()
|
||||
|
||||
if not content:
|
||||
return []
|
||||
|
||||
return json.loads(content)
|
||||
|
||||
except (json.JSONDecodeError, FileNotFoundError):
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def save_history(data):
|
||||
|
||||
with open(OUTPUT_FILE, "w") as f:
|
||||
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Monitoring
|
||||
# ===========================================
|
||||
|
||||
def monitor():
|
||||
|
||||
status_code, latency = check_http()
|
||||
|
||||
ssl_expiry, ssl_days = check_ssl()
|
||||
|
||||
entry = {
|
||||
|
||||
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
|
||||
"uptime": status_code == 200,
|
||||
|
||||
"status_code": status_code,
|
||||
|
||||
"latency_ms": latency,
|
||||
|
||||
"dns_ms": check_dns(),
|
||||
|
||||
"ssl_expiry": ssl_expiry,
|
||||
|
||||
"ssl_days_left": ssl_days,
|
||||
|
||||
"search_latency_ms": check_search()
|
||||
|
||||
}
|
||||
|
||||
history = load_history()
|
||||
|
||||
history.append(entry)
|
||||
|
||||
# آخر 24 ساعة (كل دقيقة)
|
||||
history = history[-1440:]
|
||||
|
||||
save_history(history)
|
||||
|
||||
print("=" * 60)
|
||||
print(json.dumps(entry, indent=4))
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Main Loop
|
||||
# ===========================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
print("Starting Mithal Monitoring...")
|
||||
|
||||
while True:
|
||||
|
||||
try:
|
||||
|
||||
monitor()
|
||||
|
||||
except Exception as e:
|
||||
|
||||
print("ERROR:", e)
|
||||
|
||||
time.sleep(CHECK_INTERVAL)
|
||||
4
q5-mithal-monitor/requirements.txt
Normal file
4
q5-mithal-monitor/requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Flask==3.1.1
|
||||
requests==2.32.4
|
||||
dnspython==2.7.0
|
||||
gunicorn==23.0.0
|
||||
280
q5-mithal-monitor/templates/dashboard.html
Normal file
280
q5-mithal-monitor/templates/dashboard.html
Normal file
@@ -0,0 +1,280 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Mithal Monitoring Dashboard</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<style>
|
||||
|
||||
*{
|
||||
margin:0;
|
||||
padding:0;
|
||||
box-sizing:border-box;
|
||||
font-family:Arial,Helvetica,sans-serif;
|
||||
}
|
||||
|
||||
body{
|
||||
background:#0f172a;
|
||||
color:white;
|
||||
padding:30px;
|
||||
}
|
||||
|
||||
.container{
|
||||
max-width:1400px;
|
||||
margin:auto;
|
||||
}
|
||||
|
||||
h1{
|
||||
text-align:center;
|
||||
margin-bottom:30px;
|
||||
}
|
||||
|
||||
.grid{
|
||||
display:grid;
|
||||
grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
|
||||
gap:20px;
|
||||
margin-bottom:25px;
|
||||
}
|
||||
|
||||
.card{
|
||||
background:#1e293b;
|
||||
border-radius:12px;
|
||||
padding:20px;
|
||||
box-shadow:0 0 10px rgba(0,0,0,.3);
|
||||
}
|
||||
|
||||
.card h3{
|
||||
color:#94a3b8;
|
||||
margin-bottom:15px;
|
||||
}
|
||||
|
||||
.value{
|
||||
font-size:32px;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.green{
|
||||
color:#22c55e;
|
||||
}
|
||||
|
||||
.red{
|
||||
color:#ef4444;
|
||||
}
|
||||
|
||||
.orange{
|
||||
color:#f59e0b;
|
||||
}
|
||||
|
||||
.chart{
|
||||
background:#1e293b;
|
||||
padding:20px;
|
||||
border-radius:12px;
|
||||
margin-bottom:25px;
|
||||
}
|
||||
|
||||
.table-card{
|
||||
background:#1e293b;
|
||||
border-radius:12px;
|
||||
padding:20px;
|
||||
}
|
||||
|
||||
table{
|
||||
width:100%;
|
||||
border-collapse:collapse;
|
||||
}
|
||||
|
||||
th{
|
||||
background:#334155;
|
||||
padding:12px;
|
||||
}
|
||||
|
||||
td{
|
||||
text-align:center;
|
||||
padding:10px;
|
||||
border-bottom:1px solid #334155;
|
||||
}
|
||||
|
||||
footer{
|
||||
text-align:center;
|
||||
margin-top:25px;
|
||||
color:#94a3b8;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<h1>🚀 Mithal.space Monitoring Dashboard</h1>
|
||||
|
||||
<div class="grid">
|
||||
|
||||
<div class="card">
|
||||
<h3>Status</h3>
|
||||
<div id="status" class="value green">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>HTTP Latency</h3>
|
||||
<div id="latency" class="value">--</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>DNS Lookup</h3>
|
||||
<div id="dns" class="value">--</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Search Response</h3>
|
||||
<div id="search" class="value">--</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>SSL Days Left</h3>
|
||||
<div id="ssl" class="value">--</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>24h Uptime</h3>
|
||||
<div id="uptime" class="value">--</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="chart">
|
||||
<canvas id="latencyChart"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="table-card">
|
||||
<h2 style="margin-bottom:20px;">Last 10 Checks</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Status</th>
|
||||
<th>Latency</th>
|
||||
<th>DNS</th>
|
||||
<th>Search</th>
|
||||
<th>SSL Days</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="history"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<footer>Updates every 60 seconds</footer>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
let chart;
|
||||
|
||||
async function loadData(){
|
||||
|
||||
const response = await fetch("/api/metrics");
|
||||
const data = await response.json();
|
||||
|
||||
if(data.length === 0){
|
||||
return;
|
||||
}
|
||||
|
||||
const latest = data[data.length - 1];
|
||||
|
||||
document.getElementById("status").innerHTML =
|
||||
latest.uptime
|
||||
? '<span class="green">● ONLINE</span>'
|
||||
: '<span class="red">● OFFLINE</span>';
|
||||
|
||||
document.getElementById("latency").innerHTML = latest.latency_ms + " ms";
|
||||
document.getElementById("dns").innerHTML = latest.dns_ms + " ms";
|
||||
document.getElementById("search").innerHTML = latest.search_latency_ms + " ms";
|
||||
document.getElementById("ssl").innerHTML = latest.ssl_days_left + " days";
|
||||
|
||||
const uptimePercent = (
|
||||
data.filter(x => x.uptime).length / data.length * 100
|
||||
).toFixed(2);
|
||||
|
||||
document.getElementById("uptime").innerHTML = uptimePercent + "%";
|
||||
|
||||
const labels = data.slice(-60).map(x => x.timestamp.split(" ")[1]);
|
||||
const latency = data.slice(-60).map(x => x.latency_ms);
|
||||
|
||||
if(chart){
|
||||
chart.destroy();
|
||||
}
|
||||
|
||||
chart = new Chart(
|
||||
document.getElementById("latencyChart"),
|
||||
{
|
||||
type: "line",
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: "Latency (ms)",
|
||||
data: latency,
|
||||
borderWidth: 3,
|
||||
fill: false,
|
||||
tension: .3,
|
||||
borderColor: "#38bdf8"
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
labels: {
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: {
|
||||
color: "white"
|
||||
}
|
||||
},
|
||||
y: {
|
||||
ticks: {
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const tbody = document.getElementById("history");
|
||||
tbody.innerHTML = "";
|
||||
|
||||
data.slice(-10).reverse().forEach(row => {
|
||||
tbody.innerHTML += `
|
||||
<tr>
|
||||
<td>${row.timestamp}</td>
|
||||
<td>
|
||||
${row.uptime
|
||||
? '<span class="green">Online</span>'
|
||||
: '<span class="red">Offline</span>'}
|
||||
</td>
|
||||
<td>${row.latency_ms} ms</td>
|
||||
<td>${row.dns_ms} ms</td>
|
||||
<td>${row.search_latency_ms} ms</td>
|
||||
<td>${row.ssl_days_left} days</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
loadData();
|
||||
setInterval(loadData, 60000);
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم