diff --git a/striker-ui-api/src/lib/consts/SERVER_PATHS.ts b/striker-ui-api/src/lib/consts/SERVER_PATHS.ts index 6fad8458..99ee1776 100644 --- a/striker-ui-api/src/lib/consts/SERVER_PATHS.ts +++ b/striker-ui-api/src/lib/consts/SERVER_PATHS.ts @@ -38,6 +38,7 @@ const EMPTY_SERVER_PATHS: ServerPath = { 'striker-initialize-host': {}, 'striker-manage-install-target': {}, 'striker-manage-peers': {}, + 'striker-manage-vnc-pipes': {}, 'striker-parse-os-list': {}, }, }, diff --git a/striker-ui-api/src/lib/request_handlers/command/index.ts b/striker-ui-api/src/lib/request_handlers/command/index.ts index 923d60fd..76f51445 100644 --- a/striker-ui-api/src/lib/request_handlers/command/index.ts +++ b/striker-ui-api/src/lib/request_handlers/command/index.ts @@ -1,6 +1,7 @@ export * from './getHostSSH'; export * from './joinAn'; export * from './leaveAn'; +export * from './manageVncSshTunnel'; export * from './poweroffStriker'; export * from './rebootStriker'; export * from './runManifest'; diff --git a/striker-ui-api/src/lib/request_handlers/command/manageVncSshTunnel.ts b/striker-ui-api/src/lib/request_handlers/command/manageVncSshTunnel.ts new file mode 100644 index 00000000..0a680db4 --- /dev/null +++ b/striker-ui-api/src/lib/request_handlers/command/manageVncSshTunnel.ts @@ -0,0 +1,79 @@ +import assert from 'assert'; +import { RequestHandler } from 'express'; + +import { REP_UUID } from '../../consts'; + +import { sanitize } from '../../sanitize'; +import { stderr, vncpipe } from '../../shell'; + +export const manageVncSshTunnel: RequestHandler< + unknown, + { forwardPort: number; protocol: string }, + { open: boolean; serverUuid: string } +> = (request, response) => { + const { body: { open: rOpen, serverUuid: rServerUuid } = {} } = request; + + const isOpen = sanitize(rOpen, 'boolean'); + const serverUuid = sanitize(rServerUuid, 'string'); + + try { + assert( + REP_UUID.test(serverUuid), + `Server UUID must be a valid UUIDv4; got: [${serverUuid}]`, + ); + } catch (error) { + stderr(`Assert input failed when manage VNC SSH tunnel; CAUSE: ${error}`); + + return response.status(400).send(); + } + + let cstdout = ''; + + try { + cstdout = vncpipe( + '--server-uuid', + serverUuid, + '--component', + 'st', + isOpen ? '--open' : '', + ); + } catch (error) { + stderr( + `Failed to ${ + isOpen ? 'open' : 'close' + } VNC SSH tunnel to server ${serverUuid}; CAUSE: ${error}`, + ); + + return response.status(500).send(); + } + + const coutput = cstdout + .split(/\s*,\s*/) + .reduce>((previous, pair: string) => { + const [key, value] = pair.split(/\s*:\s*/, 2); + + previous[key] = value; + + return previous; + }, {}); + + let forwardPort: number; + let protocol: string; + + try { + assert('forwardPort' in coutput, 'Missing forward port in command output'); + assert('protocol' in coutput, 'Missing protocol in command output'); + + forwardPort = Number.parseInt(coutput.forwardPort); + protocol = coutput.protocol; + } catch (error) { + stderr(`Failed to get VNC SSH tunnel connect info; CAUSE: ${error}`); + + return response.status(500).send(); + } + + return response.json({ + forwardPort, + protocol, + }); +}; diff --git a/striker-ui-api/src/lib/shell.ts b/striker-ui-api/src/lib/shell.ts index ffff8a7c..ab6870e6 100644 --- a/striker-ui-api/src/lib/shell.ts +++ b/striker-ui-api/src/lib/shell.ts @@ -47,6 +47,9 @@ export const rm = (...args: string[]) => export const uuidgen = (...args: string[]) => systemCall(SERVER_PATHS.usr.bin.uuidgen.self, args); +export const vncpipe = (...args: string[]) => + systemCall(SERVER_PATHS.usr.sbin['striker-manage-vnc-pipes'].self, args); + export const resolveId = (id: number | string, database: string) => Number.parseInt(getent(database, String(id)).split(':', 3)[2]); diff --git a/striker-ui-api/src/routes/command.ts b/striker-ui-api/src/routes/command.ts index cff7de3c..df0e1e4f 100644 --- a/striker-ui-api/src/routes/command.ts +++ b/striker-ui-api/src/routes/command.ts @@ -4,6 +4,7 @@ import { getHostSSH, joinAn, leaveAn, + manageVncSshTunnel, poweroffStriker, rebootStriker, runManifest, @@ -21,6 +22,7 @@ router .put('/inquire-host', getHostSSH) .put('/join-an/:uuid', joinAn) .put('/leave-an/:uuid', leaveAn) + .put('/vnc-pipe', manageVncSshTunnel) .put('/poweroff-host', poweroffStriker) .put('/reboot-host', rebootStriker) .put('/run-manifest/:manifestUuid', runManifest)