هذا الالتزام موجود في:
Mohamed Moustafa
2026-07-27 23:27:12 +03:00
التزام c653222887
43 ملفات معدلة مع 3837 إضافات و0 حذوفات

عرض الملف

@@ -0,0 +1,21 @@
From node:22 As builder
WORKDIR /app
COPY package*.json .
RUN npm ci
COPY . .
FROM node:22-alpine as production
WORKDIR /app
COPY --from=builder /app .
EXPOSE 3000
CMD ["npm", "start"]

عرض الملف

@@ -0,0 +1,95 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 500" font-family="Segoe UI, Arial, sans-serif">
<defs>
<linearGradient id="bg" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#0f172a"/>
<stop offset="100%" style="stop-color:#1e293b"/>
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#22c55e"/>
<stop offset="100%" style="stop-color:#16a34a"/>
</linearGradient>
<linearGradient id="blueGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#3b82f6"/>
<stop offset="100%" style="stop-color:#2563eb"/>
</linearGradient>
<linearGradient id="purpleGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#a855f7"/>
<stop offset="100%" style="stop-color:#9333ea"/>
</linearGradient>
<filter id="shadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-opacity="0.3"/>
</filter>
</defs>
<!-- Background -->
<rect width="800" height="500" fill="url(#bg)" rx="12"/>
<!-- Title -->
<text x="400" y="45" text-anchor="middle" fill="#e2e8f0" font-size="20" font-weight="700">Q1: Deploy &amp; Monitor Architecture</text>
<text x="400" y="68" text-anchor="middle" fill="#94a3b8" font-size="12">Node.js API on Ghaymah Cloud</text>
<!-- User -->
<circle cx="100" cy="250" r="35" fill="#334155" stroke="#475569" stroke-width="2" filter="url(#shadow)"/>
<text x="100" y="245" text-anchor="middle" fill="#e2e8f0" font-size="11" font-weight="600">User</text>
<text x="100" y="262" text-anchor="middle" fill="#94a3b8" font-size="9">Browser</text>
<!-- Arrow: User to Load Balancer -->
<line x1="135" y1="250" x2="195" y2="250" stroke="#475569" stroke-width="2" marker-end="url(#arrowhead)"/>
<defs><marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto"><polygon points="0 0, 10 3.5, 0 7" fill="#475569"/></marker></defs>
<!-- Load Balancer -->
<rect x="200" y="215" width="120" height="70" rx="10" fill="url(#blueGrad)" filter="url(#shadow)"/>
<text x="260" y="245" text-anchor="middle" fill="#fff" font-size="11" font-weight="600">Load Balancer</text>
<text x="260" y="263" text-anchor="middle" fill="#bfdbfe" font-size="9">Ghaymah</text>
<!-- Arrow: LB to Container -->
<line x1="320" y1="250" x2="380" y2="250" stroke="#475569" stroke-width="2" marker-end="url(#arrowhead)"/>
<!-- Container -->
<rect x="385" y="170" width="160" height="160" rx="12" fill="#1e293b" stroke="#22c55e" stroke-width="2" filter="url(#shadow)"/>
<text x="465" y="198" text-anchor="middle" fill="#22c55e" font-size="11" font-weight="700">Docker Container</text>
<line x1="400" y1="210" x2="530" y2="210" stroke="#334155" stroke-width="1"/>
<text x="465" y="232" text-anchor="middle" fill="#e2e8f0" font-size="10">Node.js + Express</text>
<text x="465" y="252" text-anchor="middle" fill="#94a3b8" font-size="9">Port 3000</text>
<text x="465" y="272" text-anchor="middle" fill="#94a3b8" font-size="9">/health endpoint</text>
<text x="465" y="292" text-anchor="middle" fill="#94a3b8" font-size="9">Request counter</text>
<text x="465" y="312" text-anchor="middle" fill="#94a3b8" font-size="9">Static dashboard</text>
<!-- Arrow: Container to Block Storage -->
<line x1="465" y1="330" x2="465" y2="380" stroke="#475569" stroke-width="2" marker-end="url(#arrowhead)"/>
<!-- Block Storage -->
<rect x="385" y="385" width="160" height="55" rx="10" fill="url(#purpleGrad)" filter="url(#shadow)"/>
<text x="465" y="408" text-anchor="middle" fill="#fff" font-size="11" font-weight="600">Block Storage</text>
<text x="465" y="425" text-anchor="middle" fill="#ddd6fe" font-size="9">Persistent Data</text>
<!-- Monitoring Section -->
<rect x="590" y="170" width="180" height="160" rx="12" fill="#1e293b" stroke="#eab308" stroke-width="2" filter="url(#shadow)"/>
<text x="680" y="198" text-anchor="middle" fill="#eab308" font-size="11" font-weight="700">Monitoring</text>
<line x1="605" y1="210" x2="755" y2="210" stroke="#334155" stroke-width="1"/>
<text x="680" y="232" text-anchor="middle" fill="#e2e8f0" font-size="10">health-check.sh</text>
<text x="680" y="252" text-anchor="middle" fill="#94a3b8" font-size="9">Every 30 seconds</text>
<text x="680" y="272" text-anchor="middle" fill="#e2e8f0" font-size="10">Dashboard</text>
<text x="680" y="292" text-anchor="middle" fill="#94a3b8" font-size="9">Status / Latency / Requests</text>
<text x="680" y="312" text-anchor="middle" fill="#94a3b8" font-size="9">HTML / CSS / JS</text>
<!-- Arrow: Monitoring to Container -->
<line x1="590" y1="250" x2="545" y2="250" stroke="#eab308" stroke-width="2" stroke-dasharray="6,4" marker-end="url(#arrowYellow)"/>
<defs><marker id="arrowYellow" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto"><polygon points="0 0, 10 3.5, 0 7" fill="#eab308"/></marker></defs>
<!-- Ghaymah Cloud Label -->
<rect x="170" y="130" width="590" height="340" rx="16" fill="none" stroke="#334155" stroke-width="1.5" stroke-dasharray="8,6"/>
<text x="465" y="155" text-anchor="middle" fill="#64748b" font-size="11" font-weight="600">ghaymah.systems</text>
<!-- Legend -->
<rect x="20" y="440" width="760" height="45" rx="8" fill="#0f172a" stroke="#334155" stroke-width="1"/>
<circle cx="50" cy="463" r="6" fill="#3b82f6"/>
<text x="65" y="467" fill="#94a3b8" font-size="10">Infrastructure</text>
<circle cx="180" cy="463" r="6" fill="#22c55e"/>
<text x="195" y="467" fill="#94a3b8" font-size="10">Application</text>
<circle cx="310" cy="463" r="6" fill="#a855f7"/>
<text x="325" y="467" fill="#94a3b8" font-size="10">Storage</text>
<circle cx="420" cy="463" r="6" fill="#eab308"/>
<text x="435" y="467" fill="#94a3b8" font-size="10">Monitoring</text>
<text x="600" y="467" fill="#64748b" font-size="10">Deploy: gy resource app launch</text>
</svg>

بعد

العرض:  |  الارتفاع:  |  الحجم: 5.9 KiB

عرض الملف

@@ -0,0 +1,22 @@
{
"id": "proj_123abc",
"name": "web-app-backend",
"projectId": "b887e332-3ecd-4187-bdfb-17d8a437833e",
"ports": [
{
"expose": true,
"number": 3000
}
],
"publicAccess": {
"enabled": true,
"domain": "auto"
},
"resourceTier": "t1",
"dockerFileName": "Dockerfile",
"registry": {
"type": "ghaymah-internal",
"imageName": "web-app-backend:latest"
}
}

عرض الملف

@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
HEALTH_URL="${HEALTH_URL:-https://teams-ghayama-dc56d4853672.hosted.ghaymah.systems/health}"
INTERVAL="${INTERVAL:-30}"
LOG_FILE="${LOG_FILE:-monitor.log}"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
timestamp() {
date '+%Y-%m-%d %H:%M:%S'
}
check_health() {
local ts
ts="$(timestamp)"
local response
local http_code
local response_time
response=$(curl -s -o /dev/null -w '%{http_code}|%{time_total}' \
--connect-timeout 5 \
--max-time 10 \
"$HEALTH_URL" 2>&1) || true
if [[ $? -ne 0 ]] || [[ -z "$response" ]]; then
printf "[%s]\n" "$ts"
printf "${RED}Status: DOWN${NC}\n"
printf "Error: Connection refused or timeout\n\n"
printf "[%s] DOWN - Connection refused or timeout\n" "$ts" >> "$LOG_FILE"
return
fi
http_code=$(echo "$response" | cut -d'|' -f1)
response_time=$(echo "$response" | cut -d'|' -f2)
local response_ms
response_ms=$(awk "BEGIN {printf \"%.0f\", $response_time * 1000}")
if [[ "$http_code" -eq 200 ]]; then
printf "[%s]\n" "$ts"
printf "${GREEN}Status: UP${NC}\n"
printf "HTTP: %s\n" "$http_code"
printf "Response Time: %s ms\n\n" "$response_ms"
printf "[%s] UP - HTTP %s - %s ms\n" "$ts" "$http_code" "$response_ms" >> "$LOG_FILE"
else
printf "[%s]\n" "$ts"
printf "${RED}Status: DOWN${NC}\n"
printf "HTTP: %s\n" "$http_code"
printf "Response Time: %s ms\n\n" "$response_ms"
printf "[%s] DOWN - HTTP %s - %s ms\n" "$ts" "$http_code" "$response_ms" >> "$LOG_FILE"
fi
}
cleanup() {
printf "\n${YELLOW}Monitoring stopped.${NC}\n"
exit 0
}
trap cleanup SIGINT SIGTERM
printf "${GREEN}Ghaymah Health Monitor${NC}\n"
printf "URL: %s\n" "$HEALTH_URL"
printf "Interval: %ss\n" "$INTERVAL"
printf "Log: %s\n\n" "$LOG_FILE"
while true; do
check_health
sleep "$INTERVAL"
done

عرض الملف

@@ -0,0 +1,16 @@
{
"name": "version-1",
"version": "1.0.0",
"main": "script.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"express": "^5.1.0"
}
}

ثنائية
q1-deploy-monitor/public/.DS_Store مباع Normal file

ملف ثنائي غير معروض.

عرض الملف

@@ -0,0 +1,137 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #0f172a;
color: #e2e8f0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.dashboard {
width: 100%;
max-width: 800px;
padding: 40px 20px;
}
.header {
text-align: center;
margin-bottom: 48px;
}
.header h1 {
font-size: 1.75rem;
font-weight: 600;
letter-spacing: -0.025em;
margin-bottom: 8px;
}
.update-time {
font-size: 0.85rem;
color: #64748b;
}
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.card {
background: #1e293b;
border: 1px solid #334155;
border-radius: 16px;
padding: 28px 24px;
text-align: center;
transition: border-color 0.3s, box-shadow 0.3s;
}
.card:hover {
border-color: #475569;
}
.card-label {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #94a3b8;
margin-bottom: 12px;
}
.card-value {
font-size: 2rem;
font-weight: 700;
letter-spacing: -0.025em;
}
.card.status-up {
border-color: #22c55e;
box-shadow: 0 0 20px rgba(34, 197, 94, 0.1);
}
.card.status-up .card-value {
color: #4ade80;
}
.card.status-down {
border-color: #ef4444;
box-shadow: 0 0 20px rgba(239, 68, 68, 0.1);
}
.card.status-down .card-value {
color: #f87171;
}
.card.response .card-value {
color: #38bdf8;
}
.card.requests .card-value {
color: #c084fc;
}
.pulse-container {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin-top: 36px;
}
.pulse {
width: 10px;
height: 10px;
border-radius: 50%;
background: #22c55e;
animation: pulse 2s ease-in-out infinite;
}
.pulse.offline {
background: #ef4444;
}
.pulse-label {
font-size: 0.85rem;
color: #64748b;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.4; transform: scale(0.8); }
}
@media (max-width: 640px) {
.cards {
grid-template-columns: 1fr;
}
.card-value {
font-size: 1.5rem;
}
}

عرض الملف

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ghaymah Monitoring Dashboard</title>
<link rel="stylesheet" href="dashboard.css">
</head>
<body>
<div class="dashboard">
<header class="header">
<h1>Ghaymah Monitoring Dashboard</h1>
<span class="update-time" id="updateTime">Last update: --</span>
</header>
<div class="cards">
<div class="card" id="statusCard">
<div class="card-label">Application Status</div>
<div class="card-value" id="appStatus">--</div>
</div>
<div class="card" id="responseCard">
<div class="card-label">Response Time</div>
<div class="card-value" id="responseTime">-- ms</div>
</div>
<div class="card" id="requestsCard">
<div class="card-label">Total Requests</div>
<div class="card-value" id="requestCount">--</div>
</div>
</div>
<div class="pulse-container" id="pulseContainer">
<div class="pulse"></div>
<span class="pulse-label">Live</span>
</div>
</div>
<script src="dashboard.js"></script>
</body>
</html>

عرض الملف

@@ -0,0 +1,54 @@
const STATUS_ENDPOINT = '/health';
const POLL_INTERVAL = 2000;
const statusCard = document.getElementById('statusCard');
const responseCard = document.getElementById('responseCard');
const requestsCard = document.getElementById('requestsCard');
const appStatus = document.getElementById('appStatus');
const responseTime = document.getElementById('responseTime');
const requestCount = document.getElementById('requestCount');
const updateTime = document.getElementById('updateTime');
const pulse = document.querySelector('.pulse');
function setStatusCard(status) {
statusCard.classList.remove('status-up', 'status-down');
statusCard.classList.add(status === 'UP' ? 'status-up' : 'status-down');
appStatus.textContent = status;
}
function formatTimestamp(date) {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
const h = String(date.getHours()).padStart(2, '0');
const min = String(date.getMinutes()).padStart(2, '0');
const s = String(date.getSeconds()).padStart(2, '0');
return `${y}-${m}-${d} ${h}:${min}:${s}`;
}
async function poll() {
try {
const start = performance.now();
const res = await fetch(STATUS_ENDPOINT);
const elapsed = Math.round(performance.now() - start);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
setStatusCard(data.status || 'UP');
responseTime.textContent = `${elapsed} ms`;
requestCount.textContent = data.requestCount ?? '--';
pulse.classList.remove('offline');
} catch {
setStatusCard('DOWN');
responseTime.textContent = '-- ms';
requestCount.textContent = '--';
pulse.classList.add('offline');
}
updateTime.textContent = `Last update: ${formatTimestamp(new Date())}`;
}
poll();
setInterval(poll, POLL_INTERVAL);

عرض الملف

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Team Availability</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<div class="header">
<h2>Team Availability</h2>
<div class="buttons">
<button id="saveBtn">Save</button>
</div>
</div>
<div>Click save after updating each week separately</div>
<table id="scheduleTable">
<thead>
<tr>
<th>Name</th>
<th>Week</th>
<th>Mon</th>
<th>Tue</th>
<th>Wed</th>
<th>Thu</th>
<th>Fri</th>
<th>Sat</th>
<th>Sun</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
<script src="script.js"></script>
</body>
</html>

عرض الملف

@@ -0,0 +1,143 @@
let namesData = [];
let weeksData = [];
let statusesData = [];
let historyData = {};
function createDropdown(options, selectedValue = "") {
const select = document.createElement("select");
options.forEach(opt => {
const option = document.createElement("option");
option.value = opt;
option.textContent = opt;
if (opt === selectedValue) {
option.selected = true;
}
select.appendChild(option);
});
return select;
}
function applyStatusColor(select) {
select.className = 'status-select'; // Reset classes
const selectedStatus = select.value;
select.classList.add(`status-${selectedStatus}`);
}
function renderTable() {
const tableBody = document.getElementById("tableBody");
tableBody.innerHTML = "";
namesData.sort((a, b) => a.name.localeCompare(b.name));
namesData.forEach((emp, index) => {
const row = document.createElement("tr");
row.dataset.empId = emp.id;
row.classList.add(index % 2 === 0 ? "even-row" : "odd-row");
// Name cell
const nameCell = document.createElement("td");
nameCell.textContent = emp.name;
row.appendChild(nameCell);
// Week cell
const weekCell = document.createElement("td");
const defaultWeek = Object.keys(historyData[emp.id] || {})[0] || weeksData[0];
const weekSelect = createDropdown(weeksData, defaultWeek);
weekSelect.classList.add("week-select");
weekCell.appendChild(weekSelect);
row.appendChild(weekCell);
// Render status dropdowns for a selected week
const renderDays = (week) => {
while (row.children.length > 2) {
row.removeChild(row.lastChild);
}
const daysData = historyData[emp.id]?.[week] || {};
["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].forEach(day => {
const cell = document.createElement("td");
const selectedStatus = daysData[day] || "Empty";
const daySelect = createDropdown(statusesData, selectedStatus);
daySelect.classList.add("status-select");
daySelect.dataset.day = day;
applyStatusColor(daySelect);
daySelect.addEventListener("change", () => applyStatusColor(daySelect));
cell.appendChild(daySelect);
row.appendChild(cell);
});
};
renderDays(defaultWeek);
// Update days when week changes
weekSelect.addEventListener("change", () => {
renderDays(weekSelect.value);
});
tableBody.appendChild(row);
});
}
async function loadData() {
const namesRes = await fetch("/input/names.json");
const weeksRes = await fetch("/input/selection.json");
const statusRes = await fetch("/input/status.json");
const historyRes = await fetch("/output/history.json");
namesData = await namesRes.json();
weeksData = await weeksRes.json();
statusesData = await statusRes.json();
try {
historyData = await historyRes.json();
} catch {
historyData = {};
}
// Cleanup invalid entries
for (const empId in historyData) {
if (!namesData.some(n => n.id === empId)) {
delete historyData[empId];
continue;
}
for (const week in historyData[empId]) {
if (!weeksData.includes(week)) {
delete historyData[empId][week];
}
}
}
renderTable();
}
document.addEventListener("DOMContentLoaded", loadData);
document.getElementById("saveBtn").addEventListener("click", async () => {
const rows = document.querySelectorAll("#tableBody tr");
rows.forEach(row => {
const empId = row.dataset.empId;
const week = row.querySelector(".week-select").value;
const days = {};
row.querySelectorAll(".status-select").forEach(sel => {
days[sel.dataset.day] = sel.value;
});
if (!historyData[empId]) {
historyData[empId] = {};
}
historyData[empId][week] = days;
});
const response = await fetch("/save-history", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(historyData, null, 2)
});
if (response.ok) {
alert("History saved successfully.");
} else {
alert("Error saving history.");
}
});

عرض الملف

@@ -0,0 +1,93 @@
/* Basic reset for styling */
body {
font-family: Arial, sans-serif;
padding: 10px;
}
.container {
width: 100%;
margin: 0 auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
border: 1px solid #ccc;
padding: 4px;
text-align: center;
}
/* Right-aligning names in the first column */
td:first-child, th:first-child {
text-align: left;
}
/* Styling the status dropdown */
select {
width: 100%;
padding: 5px;
border-radius: 8px; /* Adding rounded edges to dropdown */
border: 1px solid #ccc;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}
/* Status colors for dropdown items */
.status-Empty {
background-color: #f0f0f0;
}
.status-Office {
background-color: #007bff;
color: white;
}
.status-Remote {
background-color: #28a745;
color: white;
}
.status-Casual {
background-color: #ffc107;
color: white;
}
.status-Annual {
background-color: #dc3545;
color: white;
}
.status-Sick {
background-color: #17a2b8;
color: white;
}
.status-Off {
background-color: #6c757d;
color: white;
}
/* Alternating row background colors */
.even-row {
background-color: #f9f9f9;
}
.odd-row {
background-color: white;
}
/* Optional: Hover effect on rows */
tr:hover {
background-color: #e9e9e9;
}

عرض الملف

@@ -0,0 +1,54 @@
const express = require('express');
const fs = require('fs');
const path = require('path');
const bodyParser = require('body-parser');
const app = express();
const PORT = 3000;
let requestCount = 0;
// Middleware
app.use(bodyParser.json());
app.use((req, res, next) => {
requestCount++;
next();
});
// Serve static frontend
app.use(express.static(path.join(__dirname, 'public')));
// Serve input JSON files
app.use('/input', express.static(path.join(__dirname, 'input')));
// Serve output folder (for history.json)
app.use('/output', express.static(path.join(__dirname, 'output')));
// API to save history data
app.post('/save-history', (req, res) => {
const historyPath = path.join(__dirname, 'output', 'history.json');
const json = JSON.stringify(req.body, null, 2);
fs.writeFile(historyPath, json, 'utf8', (err) => {
if (err) {
console.error('Error saving history.json:', err);
res.status(500).send('Failed to save history.json');
} else {
console.log('History successfully saved.');
res.status(200).send('Saved');
}
});
});
// Health Check Endpoint
app.get('/health', (req, res) => {
res.status(200).json({
status: 'UP',
requestCount: requestCount
});
});
// Start server
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});