fix: resolve server from DB for SSH when client sends no ssh_key_path (fixes #466)

- Add resolveServerForSSH() to load full server (including ssh_key_path) from DB
  when WebSocket server has id but key auth without valid ssh_key_path
- Call resolver in handleMessage for all start flows (clone, backup, update,
  shell, script) so Shell and Update over SSH work with key auth
- Extend ServerInfo typedef with auth_type, ssh_key_path for TypeScript
This commit is contained in:
Michel Rögl-Brunner
2026-01-29 15:59:58 +01:00
parent 20dbcae42a
commit e1d270d52c

View File

@@ -3,6 +3,7 @@ import { parse } from 'url';
import next from 'next'; import next from 'next';
import { WebSocketServer } from 'ws'; import { WebSocketServer } from 'ws';
import { spawn } from 'child_process'; import { spawn } from 'child_process';
import { existsSync } from 'fs';
import { join, resolve } from 'path'; import { join, resolve } from 'path';
import stripAnsi from 'strip-ansi'; import stripAnsi from 'strip-ansi';
import { spawn as ptySpawn } from 'node-pty'; import { spawn as ptySpawn } from 'node-pty';
@@ -56,6 +57,8 @@ const handle = app.getRequestHandler();
* @property {string} user * @property {string} user
* @property {string} password * @property {string} password
* @property {number} [id] * @property {number} [id]
* @property {string} [auth_type]
* @property {string} [ssh_key_path]
*/ */
/** /**
@@ -295,6 +298,20 @@ class ScriptExecutionHandler {
}); });
} }
/**
* Resolve full server from DB when client sends server with id but no ssh_key_path (e.g. for Shell/Update over SSH).
* @param {ServerInfo|null} server - Server from WebSocket message
* @returns {Promise<ServerInfo|null>} Same server or full server from DB
*/
async resolveServerForSSH(server) {
if (!server?.id) return server;
if (server.auth_type === 'key' && (!server.ssh_key_path || !existsSync(server.ssh_key_path))) {
const full = await this.db.getServerById(server.id);
return /** @type {ServerInfo|null} */ (full ?? server);
}
return server;
}
/** /**
* @param {ExtendedWebSocket} ws * @param {ExtendedWebSocket} ws
* @param {WebSocketMessage} message * @param {WebSocketMessage} message
@@ -305,16 +322,21 @@ class ScriptExecutionHandler {
switch (action) { switch (action) {
case 'start': case 'start':
if (scriptPath && executionId) { if (scriptPath && executionId) {
let serverToUse = server;
if (serverToUse?.id) {
serverToUse = await this.resolveServerForSSH(serverToUse) ?? serverToUse;
}
const resolved = serverToUse ?? server;
if (isClone && containerId && storage && server && cloneCount && hostnames && containerType) { if (isClone && containerId && storage && server && cloneCount && hostnames && containerType) {
await this.startSSHCloneExecution(ws, containerId, executionId, storage, server, containerType, cloneCount, hostnames); await this.startSSHCloneExecution(ws, containerId, executionId, storage, /** @type {ServerInfo} */ (resolved), containerType, cloneCount, hostnames);
} else if (isBackup && containerId && storage) { } else if (isBackup && containerId && storage) {
await this.startBackupExecution(ws, containerId, executionId, storage, mode, server); await this.startBackupExecution(ws, containerId, executionId, storage, mode, resolved);
} else if (isUpdate && containerId) { } else if (isUpdate && containerId) {
await this.startUpdateExecution(ws, containerId, executionId, mode, server, backupStorage); await this.startUpdateExecution(ws, containerId, executionId, mode, resolved, backupStorage);
} else if (isShell && containerId) { } else if (isShell && containerId) {
await this.startShellExecution(ws, containerId, executionId, mode, server, containerType); await this.startShellExecution(ws, containerId, executionId, mode, resolved, containerType);
} else { } else {
await this.startScriptExecution(ws, scriptPath, executionId, mode, server, envVars); await this.startScriptExecution(ws, scriptPath, executionId, mode, resolved, envVars);
} }
} else { } else {
this.sendMessage(ws, { this.sendMessage(ws, {