diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml index 820fa18..e38841a 100644 --- a/.github/workflows/desktop-release.yml +++ b/.github/workflows/desktop-release.yml @@ -54,7 +54,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh release create Overlord-Desktop-v${{ env.VERSION }} "dist/Overlord Setup.exe" \ + gh release create Overlord-Desktop-v${{ env.VERSION }} "dist/Overlord Setup.exe" "PLEASE_READ.txt" \ -t "Overlord Desktop v${{ env.VERSION }}" \ -n "Automated build release for Overlord Desktop v${{ env.VERSION }}" \ --draft=false \ diff --git a/Overlord-Desktop/PLEASE_READ.txt b/Overlord-Desktop/PLEASE_READ.txt new file mode 100644 index 0000000..22895af --- /dev/null +++ b/Overlord-Desktop/PLEASE_READ.txt @@ -0,0 +1,32 @@ +================================================================================ + OVERLORD DESKTOP — PLEASE READ BEFORE USING +================================================================================ + +This application is a DESKTOP CLIENT ONLY. It does NOT include the server. + +If you launch Overlord Desktop without a running Overlord server, it will +show a connection error and nothing will work. That is expected. + +-------------------------------------------------------------------------------- + WHAT YOU NEED TO DO +-------------------------------------------------------------------------------- + +1. Set up and start the Overlord server FIRST. + The server is a separate component that you must deploy yourself. + +2. Full setup instructions are in the README: + https://github.com/vxaboveground/Overlord/blob/main/README.md + +3. Once the server is running, open this desktop client and enter the + server's IP address (or hostname) and port on the connection screen. + +-------------------------------------------------------------------------------- + QUICK SUMMARY +-------------------------------------------------------------------------------- + + Overlord Desktop → connects TO a server + Overlord Server → must be running BEFORE you connect + +Without the server, this client is useless. + +================================================================================ diff --git a/Overlord-Desktop/src/connect/renderer.js b/Overlord-Desktop/src/connect/renderer.js index 7fb5485..4dc96b6 100644 --- a/Overlord-Desktop/src/connect/renderer.js +++ b/Overlord-Desktop/src/connect/renderer.js @@ -10,7 +10,8 @@ function setStatus(msg, type = "info") { statusEl.innerHTML = msg; } -// Load saved connection on startup +// Load saved connection on startup and surface any pending error from a +// previous session (e.g. the server dropped mid-session and we bounced back). (async () => { try { const saved = await window.overlord.getSavedConnection(); @@ -22,6 +23,13 @@ function setStatus(msg, type = "info") { } catch { // first run, no saved data } + + try { + const err = await window.overlord.getPendingError(); + if (err) setStatus(err, "error"); + } catch { + // ignore + } })(); form.addEventListener("submit", async (e) => { diff --git a/Overlord-Desktop/src/main.js b/Overlord-Desktop/src/main.js index daaf2ac..0ae616a 100644 --- a/Overlord-Desktop/src/main.js +++ b/Overlord-Desktop/src/main.js @@ -1,6 +1,8 @@ const { app, BrowserWindow, ipcMain } = require("electron"); const path = require("path"); const fs = require("fs"); +const https = require("https"); +const http = require("http"); // ── Persistence ────────────────────────────────────────────── app.setAppUserModelId("com.overlord.desktop"); @@ -34,6 +36,24 @@ function saveConnection(conn) { // ── Window ─────────────────────────────────────────────────── let win; let serverBaseUrl = null; // e.g. "https://1.2.3.4:5173" +let pendingError = null; // error message to show on the connect page after a failed load + +// ── Server probe ───────────────────────────────────────────── +// Checks whether a server is reachable before telling Electron to navigate to it. +function probeServer(url) { + return new Promise((resolve, reject) => { + const mod = url.startsWith("https") ? https : http; + const req = mod.get(url, { rejectUnauthorized: false, timeout: 8000 }, (res) => { + res.resume(); // drain so the socket can be reused + resolve(); + }); + req.on("timeout", () => { + req.destroy(); + reject(new Error("timeout")); + }); + req.on("error", reject); + }); +} function createWindow() { win = new BrowserWindow({ @@ -52,6 +72,21 @@ function createWindow() { // Allow self-signed certs on the target Overlord server win.webContents.session.setCertificateVerifyProc((_req, cb) => cb(0)); + + // If the server URL fails to load mid-session (e.g. server stopped), bounce back + // to the connect screen with a helpful error instead of showing a white page. + win.webContents.on("did-fail-load", (_event, errorCode, _errorDescription, validatedURL) => { + // ERR_ABORTED (-3) fires on normal navigation cancellations – ignore it. + if (errorCode === -3) return; + if (serverBaseUrl && validatedURL && validatedURL.startsWith(serverBaseUrl)) { + pendingError = + "Lost connection to the Overlord server. " + + "Make sure the server is still running, then try connecting again. " + + "See the README if you need help starting the server."; + serverBaseUrl = null; + win.loadFile(path.join(__dirname, "connect", "index.html")); + } + }); } app.whenReady().then(createWindow); @@ -69,7 +104,13 @@ ipcMain.handle("get-saved-connection", () => { return loadSavedConnection(); }); -ipcMain.handle("connect-to-server", (_event, { host, port, useTLS }) => { +ipcMain.handle("get-pending-error", () => { + const err = pendingError; + pendingError = null; + return err; +}); + +ipcMain.handle("connect-to-server", async (_event, { host, port, useTLS }) => { if (!host || host.trim().length === 0) { return { success: false, error: "Host is required" }; } @@ -80,6 +121,19 @@ ipcMain.handle("connect-to-server", (_event, { host, port, useTLS }) => { const protocol = useTLS ? "https" : "http"; const url = `${protocol}://${host.trim()}:${port}`; + // Probe the server before navigating so we never end up on a white screen. + try { + await probeServer(url); + } catch { + return { + success: false, + error: + "Cannot reach the Overlord server at that address. " + + "Make sure the server is running before connecting — " + + "see the README for setup instructions.", + }; + } + saveConnection({ host: host.trim(), port, useTLS }); serverBaseUrl = url; win.loadURL(url); diff --git a/Overlord-Desktop/src/preload.js b/Overlord-Desktop/src/preload.js index eb3a92c..19d9e33 100644 --- a/Overlord-Desktop/src/preload.js +++ b/Overlord-Desktop/src/preload.js @@ -2,6 +2,7 @@ const { contextBridge, ipcRenderer } = require("electron"); contextBridge.exposeInMainWorld("overlord", { getSavedConnection: () => ipcRenderer.invoke("get-saved-connection"), + getPendingError: () => ipcRenderer.invoke("get-pending-error"), connectToServer: (opts) => ipcRenderer.invoke("connect-to-server", opts), goBackToConnect: () => ipcRenderer.invoke("go-back-to-connect"), });