feat: upgrade Node.js to v22 LTS#241
Conversation
There was a problem hiding this comment.
Pull request overview
Upgrades the project’s Node.js baseline to v22 LTS and refreshes related runtime/tooling dependencies, while also refactoring dashboard websocket utilities and adding/adjusting tests and local test harness scripts.
Changes:
- Upgrade Node installation scripts to use NodeSource
setup_22.x(and update multiple JS/PHP tooling lockfiles). - Refactor websocket dashboard helpers (new
utils/helpers.ts) and restructure vnstat parsing logic with added unit tests. - Replace the old websocket test proxy script with a more configurable
tests/server.js, and update client/test files accordingly.
Reviewed changes
Copilot reviewed 24 out of 31 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| setup/dashboard/ws/tests/utils/vnstat.spec.ts | Adds unit tests for getVnstatData parsing and jsonversion handling. |
| setup/dashboard/ws/tests/utils/helpers.spec.ts | Adds tests for formatSize, formatSpeed, and processExists (sinon stub). |
| setup/dashboard/ws/tests/test-server.js | Removes old hard-coded test proxy script. |
| setup/dashboard/ws/tests/server.js | Adds new configurable PHP+WS proxy runner with env overrides and cleanup logic. |
| setup/dashboard/ws/tests/handler/utils/command.spec.ts | Fixes import path to handler command utils for tests. |
| setup/dashboard/ws/tests/handler/handler.spec.ts | Adds tests for log/i18n handlers’ basic behavior. |
| setup/dashboard/ws/tests/client.js | Makes WS client configurable via env vars; improves connect/ping loop and shutdown. |
| setup/dashboard/ws/src/widgets/service_status.tsx | Switches processExists import to new helpers module. |
| setup/dashboard/ws/src/widgets/ram_stats.tsx | Switches formatSize import to new helpers module. |
| setup/dashboard/ws/src/widgets/load.ts | Formatting-only change (blank line). |
| setup/dashboard/ws/src/widgets/disk_data.tsx | Switches helper imports; adds stable React keys in torrent list rendering. |
| setup/dashboard/ws/src/widgets/bw_tables.tsx | Switches size/speed formatting imports to new helpers module. |
| setup/dashboard/ws/src/utils/vnstat.ts | Refactors vnstat loading/parsing into smaller helpers; adds empty dataset builder. |
| setup/dashboard/ws/src/utils/helpers.ts | Introduces shared helper functions (processExists, formatSize, formatSpeed). |
| setup/dashboard/ws/src/server.tsx | Updates ws integration to use WebSocketServer for socket.io wsEngine. |
| setup/dashboard/ws/package.json | Updates test glob to include .mts; bumps deps/devDeps (adds sinon). |
| setup/dashboard/ws/package-lock.json | Lockfile updates corresponding to ws workspace dependency bumps. |
| setup/dashboard/ws/eslint.config.mjs | Expands ignore patterns to include **/*.cjs. |
| setup/dashboard/ws/dist/server.js.LICENSE.txt | Removes bundled license text file from dist output. |
| setup/dashboard/widgets/service_status.php | Makes callback static in array_filter. |
| setup/dashboard/package.json | Bumps dashboard deps/devDeps (socket.io-client, eslint, globals). |
| setup/dashboard/package-lock.json | Lockfile updates corresponding to dashboard dependency bumps. |
| setup/dashboard/lib/socket.io/socket.io.min.js | Updates vendored Socket.IO client build to v4.8.3. |
| setup/dashboard/inc/panel.app_status.ws.js | Reworks formatsize logic to be more robust for invalid/zero inputs. |
| setup.sh | Updates NodeSource install script from setup_18.x to setup_22.x. |
| packages/system/update/qbUpdateService | Updates Node install/upgrade paths to v22. |
| packages/package/install/installpackage-flood | Updates Node install path to v22 for flood installer. |
| composer.lock | Updates PHP dev tooling versions (php-cs-fixer/phpstan/etc) and plugin API version. |
| composer.json | Bumps PHP dev tool version constraints. |
| .php-cs-fixer.dist.php | Adjusts fixer rules (no_useless_else setting added/changed). |
Files not reviewed (2)
- setup/dashboard/package-lock.json: Language not supported
- setup/dashboard/ws/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const firstDay = new Date(d.date.year, d.date.month as number - 1, 1, 0, 0, 0); | ||
| const firstDay = new Date(d.date.year, (d.date.month as number) - 1, 1, 0, 0, 0); | ||
| const lastDay = new Date(d.date.year, d.date.month as number, 1, 0, 0, 0); | ||
| const fullMonthDiff = lastDay.getTime() - firstDay.getTime(); |
There was a problem hiding this comment.
In buildMonthEntries, fullMonthDiff is computed in milliseconds (lastDay.getTime() - firstDay.getTime()), but diffTime is otherwise in seconds ((Date.now() - firstDay.getTime()) / 1000). Because of the unit mismatch, the cap-to-one-month behavior is ineffective for older months and can inflate *_avg rates. Convert fullMonthDiff to seconds (e.g., divide by 1000) before using it in the Math.min.
| const fullMonthDiff = lastDay.getTime() - firstDay.getTime(); | |
| const fullMonthDiff = (lastDay.getTime() - firstDay.getTime()) / 1000; |
| }; | ||
|
|
||
| const HELP_TEXT = ` | ||
| Usage: node tests/test-server.js [options] |
There was a problem hiding this comment.
The help text says Usage: node tests/test-server.js ..., but this script is tests/server.js (and test-server.js was removed). Update the usage string to point at the correct filename to avoid confusion.
| Usage: node tests/test-server.js [options] | |
| Usage: node tests/server.js [options] |
| const WS_HOST = process.env.TEST_WS_HOST || DEFAULTS.WS_HOST; | ||
| const WS_PORT = parsePort(process.env.TEST_WS_PORT, DEFAULTS.WS_PORT); | ||
|
|
||
| const LISTEN_PORT = parsePort(process.env.TEST_SERVER_PORT ?? process.env.TEST_PROXY_PORT, DEFAULTS.PROXY_PORT); | ||
| const LISTEN_HOST = process.env.TEST_SERVER_HOST || process.env.TEST_PROXY_HOST || DEFAULTS.PROXY_HOST; | ||
|
|
||
| let server; | ||
| let cleaningUp = false; | ||
| let php; | ||
| let ws; | ||
| const finishCleanup = (err, phpResult, wsResult) => { | ||
| console.log('Express', err, 'PHP:', phpResult, 'WS:', wsResult); | ||
| process.exit(0); | ||
| }; | ||
| const cleanup = () => { | ||
| if (cleaningUp) { | ||
| return; | ||
| } | ||
| cleaningUp = true; | ||
| const phpResult = php ? php.kill() : false; | ||
| const wsResult = ws ? ws.kill() : false; | ||
| if (server && server.listening) { | ||
| server.close((err) => finishCleanup(err, phpResult, wsResult)); | ||
| } else { | ||
| finishCleanup(new Error('server not started'), phpResult, wsResult); | ||
| } | ||
| }; | ||
|
|
||
| console.log(`Starting PHP server on http://${PHP_HOST}:${PHP_PORT} with docroot ${PHP_DOCROOT}`); | ||
| php = spawn('php', ['-S', `${PHP_HOST}:${PHP_PORT}`, '-t', PHP_DOCROOT]); | ||
| php.stdout.on('data', (data) => process.stdout.write(`[PHP::OUT::STD] ${data}`)); | ||
| php.stderr.on('data', (data) => process.stderr.write(`[PHP::OUT::ERR] ${data}`)); | ||
| php.on('exit', (code, signal) => { | ||
| console.log(`php exited with code ${code} signal ${signal || 'null'}`); | ||
| if (!cleaningUp) { | ||
| cleanup(); | ||
| } | ||
| }); | ||
| php.on('error', (err) => { | ||
| console.error('PHP process error:', err); | ||
| cleanup(); | ||
| }); | ||
|
|
||
| console.log(`Starting websocket server on http://${WS_HOST}:${WS_PORT}`); | ||
| ws = spawn('bun', [path.join(__dirname, '..', 'src', 'server.tsx')]); | ||
| ws.stdout.on('data', (data) => process.stdout.write(`[NODE::OUT::STD] ${data}`)); |
There was a problem hiding this comment.
WS_HOST/WS_PORT are presented as environment overrides, but they currently only affect the proxy target URL; the spawned websocket server (bun .../src/server.tsx) still listens on hard-coded 127.0.0.1:8575 (see src/server.tsx). Either pass these values through to the spawned process (e.g., via env) and make the server read them, or remove them from the documented overrides to avoid a non-working configuration.
| process.on('exit', cleanup); | ||
| process.on('SIGINT', cleanup); | ||
| process.on('SIGTERM', cleanup); | ||
| process.on('uncaughtException', (err) => { | ||
| console.error('Uncaught Exception:', err); | ||
| cleanup(); | ||
| }); |
There was a problem hiding this comment.
process.on('exit', cleanup) is problematic because the exit event does not allow async work; server.close(...) callbacks won't reliably run, and finishCleanup() calling process.exit(0) from within an exit handler can cause unexpected behavior. Prefer handling only signal events (SIGINT/SIGTERM) for graceful shutdown, and keep the exit handler minimal (or remove it).
| reject(error); | ||
| } | ||
| resolve(stdout? stdout : stderr); |
There was a problem hiding this comment.
execAsync calls reject(error) but then continues and also calls resolve(...). This can lead to a promise that resolves even when exec returns an error (or triggers an unhandled resolution attempt). Add an early return after reject, or use if (error) return reject(error); to ensure only one settle path.
| reject(error); | |
| } | |
| resolve(stdout? stdout : stderr); | |
| return reject(error); | |
| } | |
| resolve(stdout ? stdout : stderr); |
No description provided.