diff --git a/striker-ui-api/src/lib/consts/ENV.ts b/striker-ui-api/src/lib/consts/ENV.ts index 43f12c91..2bf7036f 100644 --- a/striker-ui-api/src/lib/consts/ENV.ts +++ b/striker-ui-api/src/lib/consts/ENV.ts @@ -33,13 +33,3 @@ export const PUID = resolveUid(process.env.PUID ?? 'striker-ui-api'); * @default PUID */ export const PGID = resolveGid(process.env.PGID ?? PUID); - -/** - * Get server screenshot job timeout in milliseconds. The job will be - * forced to progress 100 if it doesn't start within this time limit. - * - * @default 30000 - */ -export const GET_SERVER_SCREENSHOT_TIMEOUT = Number.parseInt( - process.env.GET_SERVER_SCREENSHOT_TIMEOUT ?? '30000', -); diff --git a/striker-ui-api/src/lib/request_handlers/server/getServerDetail.ts b/striker-ui-api/src/lib/request_handlers/server/getServerDetail.ts index 0a86ca75..790777b7 100644 --- a/striker-ui-api/src/lib/request_handlers/server/getServerDetail.ts +++ b/striker-ui-api/src/lib/request_handlers/server/getServerDetail.ts @@ -1,25 +1,12 @@ import assert from 'assert'; import { RequestHandler } from 'express'; -import { ReadStream, createReadStream, writeFileSync } from 'fs'; +import { existsSync, readFileSync } from 'fs'; import path from 'path'; -import { - GET_SERVER_SCREENSHOT_TIMEOUT, - REP_UUID, - SERVER_PATHS, -} from '../../consts'; +import { REP_UUID, SERVER_PATHS } from '../../consts'; -import { getLocalHostUUID, job, query, write } from '../../accessModule'; import { sanitize } from '../../sanitize'; -import { mkfifo, rm, stderr, stdout } from '../../shell'; - -const rmfifo = (path: string) => { - try { - rm(path); - } catch (rmfifoError) { - stderr(`Failed to clean up FIFO; CAUSE: ${rmfifoError}`); - } -}; +import { stderr, stdout, stdoutVar } from '../../shell'; export const getServerDetail: RequestHandler< ServerDetailParamsDictionary, @@ -28,21 +15,18 @@ export const getServerDetail: RequestHandler< ServerDetailParsedQs > = async (request, response) => { const { - params: { serverUUID }, - query: { ss, resize }, + params: { serverUUID: serverUuid }, + query: { ss }, } = request; - const epoch = Date.now(); const isScreenshot = sanitize(ss, 'boolean'); - stdout( - `serverUUID=[${serverUUID}],epoch=[${epoch}],isScreenshot=[${isScreenshot}]`, - ); + stdout(`serverUUID=[${serverUuid}],isScreenshot=[${isScreenshot}]`); try { assert( - REP_UUID.test(serverUUID), - `Server UUID must be a valid UUID; got [${serverUUID}]`, + REP_UUID.test(serverUuid), + `Server UUID must be a valid UUID; got [${serverUuid}]`, ); } catch (assertError) { stderr( @@ -53,161 +37,32 @@ export const getServerDetail: RequestHandler< } if (isScreenshot) { - let requestHostUUID: string, serverHostUUID: string; - - try { - requestHostUUID = getLocalHostUUID(); - } catch (subError) { - stderr(String(subError)); - - return response.status(500).send(); - } - - stdout(`requestHostUUID=[${requestHostUUID}]`); - - try { - [[serverHostUUID]] = await query(` - SELECT server_host_uuid - FROM servers - WHERE server_uuid = '${serverUUID}';`); - } catch (queryError) { - stderr(`Failed to get server host UUID; CAUSE: ${queryError}`); - - return response.status(500).send(); - } - - stdout(`serverHostUUID=[${serverHostUUID}]`); - - const imageFifoName = `${serverUUID}_screenshot_${epoch}`; - const imageFifoPath = path.join(SERVER_PATHS.tmp.self, imageFifoName); - - let fifoReadStream: ReadStream; - - try { - mkfifo(imageFifoPath); - - fifoReadStream = createReadStream(imageFifoPath, { - autoClose: true, - emitClose: true, - encoding: 'utf-8', - }); - - let imageData = ''; - - fifoReadStream.once('error', (readError) => { - stderr(`Failed to read from FIFO; CAUSE: ${readError}`); - }); - - fifoReadStream.once('close', () => { - stdout(`On close; removing FIFO at ${imageFifoPath}.`); - - rmfifo(imageFifoPath); + const imageFileName = `${serverUuid}_screenshot`; + const imageFilePath = path.join(SERVER_PATHS.tmp.self, imageFileName); - return response.status(200).send({ screenshot: imageData }); - }); + stdoutVar( + { imageFileName, imageFilePath }, + `Server ${serverUuid} image file: `, + ); - fifoReadStream.on('data', (data) => { - const imageChunk = data.toString().trim(); - const peekLength = 10; + const rsbody = { screenshot: '' }; - stdout( - `${serverUUID} image chunk: ${ - imageChunk.length > 0 - ? `${imageChunk.substring( - 0, - peekLength, - )}...${imageChunk.substring( - imageChunk.length - peekLength - 1, - )}` - : 'empty' - }`, + if (existsSync(imageFilePath)) { + try { + rsbody.screenshot = readFileSync(imageFilePath, { encoding: 'utf-8' }); + } catch (error) { + stderr( + `Failed to read image file at ${imageFilePath}; CAUSE: ${error}`, ); - imageData += imageChunk; - }); - } catch (prepPipeError) { - stderr( - `Failed to prepare FIFO and/or receive image data; CAUSE: ${prepPipeError}`, - ); - - rmfifo(imageFifoPath); - - return response.status(500).send(); - } - - let resizeArgs = sanitize(resize, 'string'); - - if (!/^\d+x\d+$/.test(resizeArgs)) { - resizeArgs = ''; - } - - let jobUuid: string; - - try { - jobUuid = await job({ - file: __filename, - job_command: SERVER_PATHS.usr.sbin['anvil-get-server-screenshot'].self, - job_data: `server-uuid=${serverUUID} -request-host-uuid=${requestHostUUID} -resize=${resizeArgs} -out-file-id=${epoch}`, - job_name: `get_server_screenshot::${serverUUID}::${epoch}`, - job_title: 'job_0356', - job_description: 'job_0357', - job_host_uuid: serverHostUUID, - }); - } catch (subError) { - stderr(`Failed to queue fetch server screenshot job; CAUSE: ${subError}`); - - return response.status(500).send(); + return response.status(500).send(); + } } - const timeoutId: NodeJS.Timeout = setTimeout<[string, string]>( - async (uuid, fpath) => { - const [[isNotInProgress]]: [[number]] = await query( - `SELECT - CASE - WHEN job_progress IN (0, 100) - THEN CAST(1 AS BOOLEAN) - ELSE CAST(0 AS BOOLEAN) - END AS is_job_started - FROM jobs - WHERE job_uuid = '${uuid}';`, - ); - - if (isNotInProgress) { - stdout( - `Discard job ${uuid} because it's not-in-progress after timeout`, - ); - - try { - const wcode = await write( - `UPDATE jobs SET job_progress = 100 WHERE job_uuid = '${uuid}';`, - ); - - assert(wcode === 0, `Write exited with code ${wcode}`); - - writeFileSync(fpath, ''); - } catch (error) { - stderr(`Failed to discard job ${uuid} on timeout; CAUSE: ${error}`); - - return response.status(500).send(); - } - } - }, - GET_SERVER_SCREENSHOT_TIMEOUT, - jobUuid, - imageFifoPath, - ); - - fifoReadStream.once('data', () => { - stdout(`Receiving server screenshot image data; cancel timeout`); - - clearTimeout(timeoutId); - }); + return response.send(rsbody); } else { // For getting sever detail data. - response.status(200).send(); + response.send(); } };