refactor: flatten monorepo structure to backend/ frontend/ devops/

Rename subdirectories for a cleaner single-repo layout:
- website-monitoring-backend/  → backend/
- website-monitoring-frontend/ → frontend/
- website-monitoring-devops/   → devops/

Update all references in package.json scripts, CI workflows,
docker-compose, pre-commit hooks, and documentation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Dennis
2026-03-07 00:25:29 +01:00
parent 4607af8def
commit 50e25e3ee8
253 changed files with 54 additions and 51 deletions
+174
View File
@@ -0,0 +1,174 @@
import express, { Request, Response } from "express";
import { v4 as uuidv4 } from "uuid";
const router = express.Router();
const progressClients = new Map<string, Response>();
/** Send SSE progress data to the browser */
function sendProgress(clientId: string, data: any) {
const client = progressClients.get(clientId);
if (client) {
client.write(`data: ${JSON.stringify(data)}\n\n`);
}
}
// SSE endpoint for progress
router.get("/status/:id", (req: Request, res: Response) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
res.setHeader("Access-Control-Allow-Origin", "*");
const clientId = req.params.id;
progressClients.set(clientId, res);
// Send an initial event
sendProgress(clientId, {
status: "Connected",
progress: 0,
stage: "setup",
});
req.on("close", () => {
progressClients.delete(clientId);
});
});
router.post("/", async (req: Request, res: Response) => {
const { url } = req.body;
if (!url) {
return res.status(400).json({ error: "Missing URL" });
}
const clientId = uuidv4(); // Generate a UUID
console.log(`New client ID: ${clientId}`);
let currentProgress = 0;
let auditCount = 0;
let totalAudits = 0;
try {
// Immediately tell the frontend were starting (0%)
sendProgress(clientId, {
status: "Starting analysis...",
progress: 0,
stage: "setup",
});
// Return clientId so the frontend can open the SSE channel
res.status(200).json({ clientId });
// Dynamically import chrome-launcher and lighthouse
const { launch } = await import("chrome-launcher");
const lighthouse = (await import("lighthouse")).default;
const log = (await import("lighthouse-logger")).default;
// Launch Chrome
const chrome = await launch({
chromeFlags: ["--headless", "--no-sandbox", "--disable-dev-shm-usage"],
});
// Update progress to 10% after launching Chrome
currentProgress = 10;
sendProgress(clientId, {
status: "Chrome launched",
progress: currentProgress,
stage: "setup",
});
// Turn on Lighthouse logs at "info" level
log.setLevel("info");
// Listen for Lighthouse status events
log.events.addListener("status", (lhStatus) => {
let msg = lhStatus[1].toLowerCase();
let newProgress = currentProgress;
let newStage = "analyzing";
if (msg.includes("initialize config")) {
newProgress = Math.max(newProgress, 15);
newStage = "setup";
} else if (msg.includes("resolve artifact definitions")) {
newProgress = Math.max(newProgress, 20);
newStage = "setup";
} else if (msg.includes("gather phase")) {
newProgress = Math.max(newProgress, 25);
newStage = "analyzing";
} else if (msg.includes("connecting to browser")) {
newProgress = Math.max(newProgress, 28);
newStage = "analyzing";
} else if (msg.includes("navigating to")) {
newProgress = Math.max(newProgress, 30);
newStage = "analyzing";
} else if (msg.includes("benchmarking machine")) {
newProgress = Math.max(newProgress, 35);
newStage = "analyzing";
} else if (msg.includes("getting artifact")) {
newProgress = Math.max(newProgress, 40);
newStage = "analyzing";
} else if (msg.includes("computing artifact")) {
newProgress = Math.max(newProgress, 50);
newStage = "analyzing";
} else if (msg.includes("audit phase")) {
newProgress = Math.max(newProgress, 60);
newStage = "auditing";
} else if (msg.includes("auditing:")) {
auditCount++;
totalAudits = Math.max(totalAudits, auditCount);
newProgress = Math.max(
newProgress,
60 + (auditCount / totalAudits) * 30,
);
newStage = "auditing";
} else if (msg.includes("generating results")) {
newProgress = Math.max(newProgress, 95);
newStage = "finishing";
}
// Add some randomness to the progress increments
newProgress += Math.random() * 2;
// Only send progress if it actually increased
if (newProgress > currentProgress) {
currentProgress = newProgress;
sendProgress(clientId, {
status: lhStatus.message || "Processing...",
progress: currentProgress,
stage: newStage,
});
}
});
// Run Lighthouse
const runnerResult = await lighthouse(url, {
port: chrome.port,
output: "json",
logLevel: "info",
onlyCategories: ["performance", "seo", "accessibility", "best-practices"],
});
if (!runnerResult) {
throw new Error("Lighthouse returned no result");
}
// Finish progress
sendProgress(clientId, {
status: "Analysis complete",
progress: 100,
stage: "complete",
report: runnerResult.lhr,
});
await chrome.kill();
} catch (error) {
console.error("Lighthouse error:", error);
sendProgress(clientId, {
status: "Error occurred",
progress: 0,
stage: "error",
error:
error instanceof Error ? error.message : "An unknown error occurred",
});
}
});
export default router;