diff --git a/common-mortakaz/integration-1.md b/common-mortakaz/integration-1.md new file mode 100644 index 0000000..60f3990 --- /dev/null +++ b/common-mortakaz/integration-1.md @@ -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. diff --git a/common-mortakaz/integration-2.md b/common-mortakaz/integration-2.md new file mode 100644 index 0000000..e876191 --- /dev/null +++ b/common-mortakaz/integration-2.md @@ -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. diff --git a/common-qabilah/qabilah-profile.txt b/common-qabilah/qabilah-profile.txt new file mode 100644 index 0000000..20a0045 --- /dev/null +++ b/common-qabilah/qabilah-profile.txt @@ -0,0 +1,4 @@ +Name: Mohammed Hamdy Mahmoud + +Qabilah Profile: +https://qabilah.com/profile/mohammedhamdy102003/posts diff --git a/Dockerfile b/q1-deploy-monitor/Dockerfile similarity index 100% rename from Dockerfile rename to q1-deploy-monitor/Dockerfile diff --git a/q1-deploy-monitor/README.md b/q1-deploy-monitor/README.md new file mode 100644 index 0000000..8403311 --- /dev/null +++ b/q1-deploy-monitor/README.md @@ -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. + +![Local Dashboard](screenshots/dashboard-local.png) + +--- + +## Ghaymah Cloud Deployment + +After deployment, the dashboard successfully monitored the live application hosted on **Ghaymah Cloud**. + +![Ghaymah Dashboard](screenshots/dashboard-ghaymah.png) + +--- + +# 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**. diff --git a/app.py b/q1-deploy-monitor/app.py similarity index 100% rename from app.py rename to q1-deploy-monitor/app.py diff --git a/dashboard.html b/q1-deploy-monitor/dashboard.html similarity index 100% rename from dashboard.html rename to q1-deploy-monitor/dashboard.html diff --git a/health-check.py b/q1-deploy-monitor/health-check.py similarity index 100% rename from health-check.py rename to q1-deploy-monitor/health-check.py diff --git a/install-docker.sh b/q1-deploy-monitor/install-docker.sh similarity index 100% rename from install-docker.sh rename to q1-deploy-monitor/install-docker.sh diff --git a/monitor-log.csv b/q1-deploy-monitor/monitor-log.csv similarity index 100% rename from monitor-log.csv rename to q1-deploy-monitor/monitor-log.csv diff --git a/requirements.txt b/q1-deploy-monitor/requirements.txt similarity index 100% rename from requirements.txt rename to q1-deploy-monitor/requirements.txt diff --git a/q1-deploy-monitor/screenshots/dashboard-ghaymah.png b/q1-deploy-monitor/screenshots/dashboard-ghaymah.png new file mode 100644 index 0000000..d8588cd Binary files /dev/null and b/q1-deploy-monitor/screenshots/dashboard-ghaymah.png differ diff --git a/q1-deploy-monitor/screenshots/dashboard-local.png b/q1-deploy-monitor/screenshots/dashboard-local.png new file mode 100644 index 0000000..621c300 Binary files /dev/null and b/q1-deploy-monitor/screenshots/dashboard-local.png differ diff --git a/postmortem-report.md b/q2-postmortem/README.md similarity index 100% rename from postmortem-report.md rename to q2-postmortem/README.md diff --git a/q3-cicd/README.md b/q3-cicd/README.md new file mode 100644 index 0000000..1d79892 --- /dev/null +++ b/q3-cicd/README.md @@ -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 "" \ + --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. diff --git a/Q4-Scalability/README.md b/q4-scalability/README.md similarity index 100% rename from Q4-Scalability/README.md rename to q4-scalability/README.md diff --git a/Q4-Scalability/calculations.md b/q4-scalability/calculations.md similarity index 100% rename from Q4-Scalability/calculations.md rename to q4-scalability/calculations.md diff --git a/Q4-Scalability/ghaymah_full_production_architecture.png b/q4-scalability/ghaymah_full_production_architecture.png similarity index 100% rename from Q4-Scalability/ghaymah_full_production_architecture.png rename to q4-scalability/ghaymah_full_production_architecture.png diff --git a/q5-mithal-monitor/.dockerignore b/q5-mithal-monitor/.dockerignore new file mode 100644 index 0000000..854779e --- /dev/null +++ b/q5-mithal-monitor/.dockerignore @@ -0,0 +1,6 @@ +.venv +__pycache__ +*.pyc +.git +monitor-data.json +README.md diff --git a/q5-mithal-monitor/.ghaymah.json b/q5-mithal-monitor/.ghaymah.json new file mode 100644 index 0000000..0413168 --- /dev/null +++ b/q5-mithal-monitor/.ghaymah.json @@ -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" +} diff --git a/q5-mithal-monitor/Dockerfile b/q5-mithal-monitor/Dockerfile new file mode 100644 index 0000000..81a1b7f --- /dev/null +++ b/q5-mithal-monitor/Dockerfile @@ -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"] diff --git a/q5-mithal-monitor/README.md b/q5-mithal-monitor/README.md new file mode 100644 index 0000000..3fde7aa --- /dev/null +++ b/q5-mithal-monitor/README.md @@ -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 +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 diff --git a/q5-mithal-monitor/app.py b/q5-mithal-monitor/app.py new file mode 100644 index 0000000..a16ec03 --- /dev/null +++ b/q5-mithal-monitor/app.py @@ -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) diff --git a/q5-mithal-monitor/monitor-data.json b/q5-mithal-monitor/monitor-data.json new file mode 100644 index 0000000..e02b55f --- /dev/null +++ b/q5-mithal-monitor/monitor-data.json @@ -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 + } +] \ 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..15cc5eb --- /dev/null +++ b/q5-mithal-monitor/monitor.py @@ -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) diff --git a/q5-mithal-monitor/requirements.txt b/q5-mithal-monitor/requirements.txt new file mode 100644 index 0000000..83b6eac --- /dev/null +++ b/q5-mithal-monitor/requirements.txt @@ -0,0 +1,4 @@ +Flask==3.1.1 +requests==2.32.4 +dnspython==2.7.0 +gunicorn==23.0.0 diff --git a/q5-mithal-monitor/templates/dashboard.html b/q5-mithal-monitor/templates/dashboard.html new file mode 100644 index 0000000..3e51d72 --- /dev/null +++ b/q5-mithal-monitor/templates/dashboard.html @@ -0,0 +1,280 @@ + + + + + + + +Mithal Monitoring Dashboard + + + + + +
+ +

🚀 Mithal.space Monitoring Dashboard

+ +
+ +
+

Status

+
Loading...
+
+ +
+

HTTP Latency

+
--
+
+ +
+

DNS Lookup

+
--
+
+ +
+

Search Response

+ +
+ +
+

SSL Days Left

+
--
+
+ +
+

24h Uptime

+
--
+
+ +
+ +
+ +
+ +
+

Last 10 Checks

+ + + + + + + + + + + + +
TimeStatusLatencyDNSSearchSSL Days
+
+ +
Updates every 60 seconds
+ +
+ + + + + +