Files
Termix/electron/backend-failure.cjs
T
62ac931468 v26.10.1 (#1599)
* feat: AI translations at release, 7 new languages and regional fallback

* fix: proper plural forms for count strings and full translations

* fix: plural forms for selected, hidden and active counts

* fix: synced plugin updates failing on desktop and server-only plugins shown as stopped

* fix: restore rounded corners on app icons and favicon

* feat: desktop app auto updates with automatic, ask first or never modes

* feat: community and custom plugin registries

* feat: plugin guide for AI tools in the SDK with a lint check

* fix: keep the terminal WebSocket open past nginx's 60s default (#1588)

* chore: update changelog

* feat: show plugin download counts only and stop CI builds inflating them

* feat: hide installed plugins in the browse tab by default

* chore: pin bundled plugins to latest releases

* chore: update translations

* fix: focusing a saved password field and saving no longer erases it

* fix: tell desktop users when their 2.8 data needs 2.9 first

* fix: point windows update info at the x64 installer

* fix: keep desktop client tunnel sockets alive behind proxies

* chore: pin bundled plugins to latest releases

* fix: cover client tunnel keepalive in the c2s runtime tests

* fix: typing after opening a host no longer goes to the host search

* fix: accept tab and = separators in SSH config import (#1598)

* chore: update changelog

* chore: drop plugin versions from core changelog

* chore: update translations

* chore: lint, format, and bump version to 26.10.1

---------

Co-authored-by: Darnel Hunter <68697450+innotelinc@users.noreply.github.com>
Co-authored-by: Iain <Iain1970@protonmail.com>
2026-10-12 11:35:48 -05:00

53 lines
2.4 KiB
JavaScript

// The embedded backend is a forked child process, so the only evidence the
// main process gets when it dies is whatever it wrote to stderr on the way
// out. A port conflict is by far the most common cause (a leftover backend
// from a hard-killed session, a Termix container bound to the same ports,
// or an unrelated service on 30001) and it is also the only one the user
// can actually act on, so it is classified separately from a generic crash
// and reported with the port that was taken.
//
// Every exit reaching here is a failure. Whether the child exited 0, was
// killed by a signal, or crashed makes no difference to the renderer: the
// backend process is gone, nothing restarts it, and retrying can never
// succeed. Only stopBackendServer() knows a shutdown was intentional, and
// it guards this call rather than encoding that verdict here -- an exit
// code cannot distinguish "we asked for this" from "something else killed
// it", and treating a clean-looking exit as benign would leave the
// renderer waiting forever, which is the failure this classification
// exists to prevent.
function parseConflictingPort(stderr) {
// Node's EADDRINUSE error prints the port twice: once inside the message
// ("address already in use :::30001") and once as a numeric `port` field
// on the error object. Either is enough, but stderr arrives in chunks and
// the tail can be cut off, so both are tried.
const fromErrorField = stderr.match(/\bport:\s*(\d{1,5})\b/);
if (fromErrorField) return Number(fromErrorField[1]);
const fromMessage = stderr.match(/address already in use\s+\S*?:(\d{1,5})\b/);
if (fromMessage) return Number(fromMessage[1]);
return null;
}
/**
* Classifies why the embedded backend process ended, from the tail of its
* stderr. Always returns a failure -- see the note above on why every exit
* that reaches this point counts as one.
*/
// Part of PRE_29_DATABASE_ERROR in src/backend/database/db/upgrade-guard.ts.
const OLD_DATABASE_MARKER = "This database is from Termix 2.8 or older";
function classifyBackendFailure(stderr = "") {
if (stderr.includes(OLD_DATABASE_MARKER)) {
return { reason: "old-database", port: null };
}
if (stderr.includes("EADDRINUSE")) {
return { reason: "port-in-use", port: parseConflictingPort(stderr) };
}
return { reason: "crashed", port: null };
}
module.exports = { classifyBackendFailure };