diff --git a/src/backend/db.ts b/src/backend/db.ts index 196e9c7..e573cef 100644 --- a/src/backend/db.ts +++ b/src/backend/db.ts @@ -111,4 +111,25 @@ async function deleteMaps(document: DfContentMap, ADMIN_LIST: string[]) { return deleteResult; } -export { addMaps, checkUser, deleteMaps, getMaps }; +// Update `status` and `build_logs` +async function updateMapStatus(subdomain: string, status: string, logs: string = "") { + const query = { + collection: "content_maps", + database: DATABASE, + dataSource: DATA_SOURCE, + filter: { "subdomain": subdomain }, + update: { + $set: { + "status": status, + "build_logs": logs + }, + }, + }; + + options.body = JSON.stringify(query); + const resp = await fetch(MONGO_URLs.update.toString(), options); + const data = await resp.json(); + return data; +} + +export { addMaps, checkUser, deleteMaps, getMaps, updateMapStatus }; diff --git a/src/backend/main.ts b/src/backend/main.ts index eca17ba..10dbb41 100644 --- a/src/backend/main.ts +++ b/src/backend/main.ts @@ -1,7 +1,7 @@ import { Context, Sentry } from "./dependencies.ts"; import { addScript, deleteScript } from "./scripts.ts"; import { checkJWT } from "./utils/jwt.ts"; -import { addMaps, deleteMaps, getMaps } from "./db.ts"; +import { addMaps, deleteMaps, getMaps, updateMapStatus } from "./db.ts"; const ADMIN_LIST = Deno.env.get("ADMIN_LIST")?.split("|"); @@ -97,4 +97,34 @@ async function deleteSubdomain(ctx: Context) { ctx.response.body = data; } -export { addSubdomain, deleteSubdomain, getSubdomains }; +async function updateBuildStatus(ctx: Context) { + if (!ctx.request.hasBody) { + ctx.throw(415); + } + const body = await ctx.request.body().value; + let payload; + try { + payload = JSON.parse(body); + } catch (e) { + payload = body; + } + + // Expect 'status' in the payload now + const { subdomain, status, logs } = payload; + + if (status === "failed") { + // Log Error to Sentry + Sentry.captureMessage(`Build Failed for ${subdomain}: ${logs}`, "error"); + } else if (status === "success") { + // Log Info to Sentry (Optional but good for tracking) + Sentry.captureMessage(`Build Success for ${subdomain}`, "info"); + } + + // Update DB with whatever status came in ("success" or "failed") + await updateMapStatus(subdomain, status, logs); + + ctx.response.status = 200; + ctx.response.body = { received: true }; +} + +export { addSubdomain, deleteSubdomain, getSubdomains, updateBuildStatus }; diff --git a/src/backend/scripts.ts b/src/backend/scripts.ts index fcdb711..c3a12d0 100644 --- a/src/backend/scripts.ts +++ b/src/backend/scripts.ts @@ -3,6 +3,7 @@ import dockerize from "./utils/container.ts"; import DfContentMap from "./types/maps_interface.ts"; const MEMORY_LIMIT = Deno.env.get("MEMORY_LIMIT"); +const PORT = Deno.env.get("PORT_BACKEND"); async function addScript( document: DfContentMap, @@ -24,7 +25,7 @@ async function addScript( } else if (document.resource_type === "GITHUB" && static_content == "Yes") { Deno.writeTextFile(`/hostpipe/.env`, env_content); await exec( - `bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -s ${document.subdomain} ${document.resource} 80 ${MEMORY_LIMIT}' > /hostpipe/pipe"`, + `bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -s ${document.subdomain} ${document.resource} 80 ${MEMORY_LIMIT} ${PORT}' > /hostpipe/pipe"`, ); } else if (document.resource_type === "GITHUB" && static_content == "No") { if(dockerfile_present === 'No'){ @@ -32,12 +33,12 @@ async function addScript( Deno.writeTextFile(`/hostpipe/Dockerfile`, dockerfile); Deno.writeTextFile(`/hostpipe/.env`, env_content); await exec( - `bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -g ${document.subdomain} ${document.resource} ${port} ${MEMORY_LIMIT}' > /hostpipe/pipe"`, + `bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -g ${document.subdomain} ${document.resource} ${port} ${MEMORY_LIMIT} ${PORT}' > /hostpipe/pipe"`, ); }else if(dockerfile_present === 'Yes'){ await exec( - `bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -d ${document.subdomain} ${document.resource} ${port} ${MEMORY_LIMIT}' > /hostpipe/pipe"`, + `bash -c "echo 'bash ../../src/backend/shell_scripts/container.sh -d ${document.subdomain} ${document.resource} ${port} ${MEMORY_LIMIT} ${PORT}' > /hostpipe/pipe"`, ); } diff --git a/src/backend/server.ts b/src/backend/server.ts index f2c7c12..5f80fb9 100644 --- a/src/backend/server.ts +++ b/src/backend/server.ts @@ -13,7 +13,7 @@ import { gitlabAuth, handleJwtAuthentication, } from "./auth/github.ts"; -import { addSubdomain, deleteSubdomain, getSubdomains } from "./main.ts"; +import { addSubdomain, deleteSubdomain, getSubdomains, updateBuildStatus } from "./main.ts"; import { getContainerHealth, getContainerMetrics, @@ -71,6 +71,7 @@ router .get("/map", (ctx) => getSubdomains(ctx)) .post("/map", (ctx) => addSubdomain(ctx)) .post("/mapdel", (ctx) => deleteSubdomain(ctx)) + .post("/maplogs", (ctx) => updateBuildStatus(ctx)) // Health monitoring routes .get("/health", (ctx) => getContainerHealth(ctx)) .get("/health/summary", (ctx) => getHealthDashboard(ctx)) diff --git a/src/backend/shell_scripts/container.sh b/src/backend/shell_scripts/container.sh index a6d4841..1d31e29 100755 --- a/src/backend/shell_scripts/container.sh +++ b/src/backend/shell_scripts/container.sh @@ -1,3 +1,18 @@ +report_error() { + local msg="$1" + local sub="$2" + curl -X POST -H "Content-Type: application/json" \ + -d "{\"subdomain\": \"$sub\", \"status\": \"failed\", \"logs\": \"$msg\"}" \ + http://localhost:$BACKEND_PORT/maplogs +} + +report_success() { + local sub="$1" + curl -X POST -H "Content-Type: application/json" \ + -d "{\"subdomain\": \"$sub\", \"status\": \"success\", \"logs\": \"Build completed successfully\"}" \ + http://localhost:$BACKEND_PORT/maplogs +} + PORT_MIN=8010 PORT_MAX=8099 flag=$1 @@ -5,6 +20,7 @@ name=$2 resource=$3 exp_port=$4 max_mem=$5 +BACKEND_PORT=$6 available_ports=() @@ -17,7 +33,10 @@ done echo "Available ports: ${available_ports[56]}" AVAILABLE=0 echo "Creating subdomain $name" -git clone $resource $name +if ! git clone $resource $name; then + report_error "Git clone failed for repository $resource" "$name" + exit 1 +fi sudo cp .env $name/ cd $name @@ -30,8 +49,14 @@ elif [ $flag = "-s" ]; then " > Dockerfile fi -sudo docker build -t $name . -sudo docker run --memory=$max_mem --name=$name -d -p ${available_ports[$AVAILABLE]}:$exp_port $2 +if ! sudo docker build -t $name .; then + report_error "Docker build failed. Please check your Dockerfile or dependency configurations." "$name" + exit 1 +fi +if ! sudo docker run --memory=$max_mem --name=$name -d -p ${available_ports[$AVAILABLE]}:$exp_port $name; then + report_error "Docker run failed. Container could not start." "$name" + exit 1 +fi cd .. sudo rm -rf $name sudo rm Dockerfile @@ -56,3 +81,5 @@ sudo echo "# Virtual Host configuration for $2 }" > /etc/nginx/sites-available/$2.conf sudo ln -s /etc/nginx/sites-available/$2.conf /etc/nginx/sites-enabled/$2.conf sudo systemctl reload nginx + +report_success "$name" \ No newline at end of file diff --git a/src/backend/types/maps_interface.ts b/src/backend/types/maps_interface.ts index e460ad3..b2f40d7 100644 --- a/src/backend/types/maps_interface.ts +++ b/src/backend/types/maps_interface.ts @@ -4,6 +4,8 @@ interface DfContentMap { resource: string; author: string; date: string; + status?: "building" | "success" | "failed"; + build_logs?: string; } export default DfContentMap; diff --git a/src/frontend/src/components/Home.vue b/src/frontend/src/components/Home.vue index 2982cf1..0e7f4d6 100644 --- a/src/frontend/src/components/Home.vue +++ b/src/frontend/src/components/Home.vue @@ -1,16 +1,65 @@ @@ -51,14 +100,27 @@ const maps = await getMaps(user); - {{ item[field] }} - - {{ item[field] }} + + + {{ item[field] }} + - + + ⏳ Building... + ✅ Live + + ❌ Failed (View Logs) + + Active + - Delete! + + Delete! + + {{ item[field] }} @@ -70,6 +132,8 @@ const maps = await getMaps(user); + + diff --git a/src/frontend/src/components/LogModal.vue b/src/frontend/src/components/LogModal.vue new file mode 100644 index 0000000..db01b98 --- /dev/null +++ b/src/frontend/src/components/LogModal.vue @@ -0,0 +1,44 @@ + + + + + X + + Build Failure Logs + + {{ logs }} + + Copy Logs + + + + + + + \ No newline at end of file diff --git a/src/frontend/src/utils/create.ts b/src/frontend/src/utils/create.ts index f9b6d9a..c4de5c9 100644 --- a/src/frontend/src/utils/create.ts +++ b/src/frontend/src/utils/create.ts @@ -95,6 +95,7 @@ export async function create( "date": new Date().toLocaleDateString(), "token": localStorage.getItem("JWTUser"), "provider": localStorage.getItem("provider"), + "status": "building", }; const resp = await fetch(rootUrl.toString(), { method: "POST",
{{ logs }}