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:
@@ -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 we’re 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;
|
||||
Reference in New Issue
Block a user