From 4a3ee95b63789d426d94cd939e8c31e75e541138 Mon Sep 17 00:00:00 2001 From: Xi-Lin Yeh Date: Thu, 20 Jan 2022 00:03:07 -0800 Subject: [PATCH 1/5] Enhanced API Server interface - Used restful patterns for handling server calls (GET/PUT/DELETE) - Utilized Server interface for saves rather than reimplementing code - Exposed operations for getting codebase of scripts on the home server and deleting files. - Added a common response structure --- electron/api-server.js | 74 +++++++++++++++++++++++++++++++++++++----- src/Electron.tsx | 74 +++++++++++++++++++++++++++++++----------- 2 files changed, 120 insertions(+), 28 deletions(-) diff --git a/electron/api-server.js b/electron/api-server.js index 3a9f06bca..eadda17a4 100644 --- a/electron/api-server.js +++ b/electron/api-server.js @@ -12,10 +12,12 @@ async function initialize(win) { window = win; server = http.createServer(async function (req, res) { let body = ""; + res.setHeader('Content-Type', 'application/json'); req.on("data", (chunk) => { body += chunk.toString(); // convert Buffer to string }); + req.on("end", () => { const providedToken = req.headers?.authorization?.replace('Bearer ', '') ?? ''; const isValid = providedToken === getAuthenticationToken(); @@ -24,8 +26,11 @@ async function initialize(win) { } else { log.log('Invalid authentication token'); res.writeHead(401); - res.write('Invalid authentication token'); - res.end(); + + res.end(JSON.stringify({ + success: false, + msg: 'Invalid authentication token' + })); return; } @@ -35,17 +40,68 @@ async function initialize(win) { } catch (error) { log.warn(`Invalid body data`); res.writeHead(400); - res.write('Invalid body data'); - res.end(); + res.end(JSON.stringify({ + success: false, + msg: 'Invalid body data' + })); + return; } - if (data) { - window.webContents.executeJavaScript(`document.saveFile("${data.filename}", "${data.code}")`).then((result) => { - res.write(result); - res.end(); - }); + + switch(req.method) { + // Request files + case "GET": + window.webContents.executeJavaScript(`document.getFiles("${data.filename}", "${data.code}")`).then((result) => { + res.end(JSON.stringify({ + success: result.res, + msg: result.msg, + data: result.data + })); + }); + break; + + // Create or update files + // Support POST for VScode implementation + case "POST": + case "PUT": + if (!data) { + log.warn(`Invalid script update request - No data`); + res.writeHead(400); + res.end(JSON.stringify({ + success: false, + msg: 'Invalid script update request - No data' + })); + } + + window.webContents.executeJavaScript(`document.saveFile("${data.filename}", "${data.code}")`).then((result) => { + res.write(result); + + if (!result.res) { + //We've encountered an error + res.writeHead(JSON.stringify({ + success: result.res, + msg: result.msg, + data: result.data + })); + } + + res.end(); + }); + break; + + // Delete files + case "DELETE": + window.webContents.executeJavaScript(`document.deleteFiles("${data.filename}", "${data.code}")`).then((result) => { + res.end(JSON.stringify({ + success: result.res, + msg: result.msg, + data: result.data + })); + }); + break; } + }); }); diff --git a/src/Electron.tsx b/src/Electron.tsx index b4f4ba684..ea7fb10d4 100644 --- a/src/Electron.tsx +++ b/src/Electron.tsx @@ -1,10 +1,8 @@ import { Player } from "./Player"; -import { isScriptFilename } from "./Script/isScriptFilename"; -import { Script } from "./Script/Script"; import { removeLeadingSlash } from "./Terminal/DirectoryHelpers"; import { Terminal } from "./Terminal"; import { SnackbarEvents } from "./ui/React/Snackbar"; -import { IMap } from "./types"; +import { IMap, IReturnStatus } from "./types"; import { GetServer } from "./Server/AllServers"; export function initElectron(): void { @@ -18,32 +16,70 @@ export function initElectron(): void { } function initWebserver(): void { - (document as any).saveFile = function (filename: string, code: string): string { + interface IReturnWebStatus extends IReturnStatus { + data?: { + [propName: string]: any; + }; + } + function normalizeFileName(filename: string): string { filename = filename.replace(/\/\/+/g, "/"); filename = removeLeadingSlash(filename); if (filename.includes("/")) { filename = "/" + removeLeadingSlash(filename); } + return filename; + } + + (document as any).getFiles = function (): IReturnWebStatus { + const home = GetServer("home"); + if (home === null) { + return { + res: false, + msg: "Home server does not exist." + } + } + return { + res: true, + data: { + files: home.scripts.map((script) => ({ + filename: script.filename, + code: script.code + })) + } + } + }; + + (document as any).deleteFile = function (filename: string): IReturnWebStatus { + filename = normalizeFileName(filename); + const home = GetServer("home"); + if (home === null) { + return { + res: false, + msg: "Home server does not exist." + } + } + return home.removeFile(filename); + }; + + (document as any).saveFile = function (filename: string, code: string): IReturnWebStatus { + filename = normalizeFileName(filename); + code = Buffer.from(code, "base64").toString(); const home = GetServer("home"); - if (home === null) return "'home' server not found."; - if (isScriptFilename(filename)) { - //If the current script already exists on the server, overwrite it - for (let i = 0; i < home.scripts.length; i++) { - if (filename == home.scripts[i].filename) { - home.scripts[i].saveScript(Player, filename, code, "home", home.scripts); - return "written"; - } + if (home === null) { + return { + res: false, + msg: "Home server does not exist." } - - //If the current script does NOT exist, create a new one - const script = new Script(); - script.saveScript(Player, filename, code, "home", home.scripts); - home.scripts.push(script); - return "written"; } + const result = home.writeToScriptFile(Player, filename, code); - return "not a script file"; + return { + res: result.success, + data: { + overwritten: result.overwritten + } + }; }; } From 5386f8e02747b3840fff21cf613a5b988aed23e4 Mon Sep 17 00:00:00 2001 From: Xi-Lin Yeh Date: Thu, 20 Jan 2022 00:14:42 -0800 Subject: [PATCH 2/5] Commonalize Api-server response handling - Fixed issue with error headers - Aggregated response handling --- electron/api-server.js | 48 ++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/electron/api-server.js b/electron/api-server.js index eadda17a4..8fdfe3f9c 100644 --- a/electron/api-server.js +++ b/electron/api-server.js @@ -18,7 +18,7 @@ async function initialize(win) { body += chunk.toString(); // convert Buffer to string }); - req.on("end", () => { + req.on("end", async () => { const providedToken = req.headers?.authorization?.replace('Bearer ', '') ?? ''; const isValid = providedToken === getAuthenticationToken(); if (isValid) { @@ -48,17 +48,11 @@ async function initialize(win) { return; } - + let result; switch(req.method) { // Request files case "GET": - window.webContents.executeJavaScript(`document.getFiles("${data.filename}", "${data.code}")`).then((result) => { - res.end(JSON.stringify({ - success: result.res, - msg: result.msg, - data: result.data - })); - }); + result = await window.webContents.executeJavaScript(`document.getFiles("${data.filename}", "${data.code}")`); break; // Create or update files @@ -72,36 +66,30 @@ async function initialize(win) { success: false, msg: 'Invalid script update request - No data' })); + return; } - window.webContents.executeJavaScript(`document.saveFile("${data.filename}", "${data.code}")`).then((result) => { - res.write(result); - - if (!result.res) { - //We've encountered an error - res.writeHead(JSON.stringify({ - success: result.res, - msg: result.msg, - data: result.data - })); - } - - res.end(); - }); + result = await window.webContents.executeJavaScript(`document.saveFile("${data.filename}", "${data.code}")`); break; // Delete files case "DELETE": - window.webContents.executeJavaScript(`document.deleteFiles("${data.filename}", "${data.code}")`).then((result) => { - res.end(JSON.stringify({ - success: result.res, - msg: result.msg, - data: result.data - })); - }); + result = await window.webContents.executeJavaScript(`document.deleteFiles("${data.filename}", "${data.code}")`); break; } + if (!result.res) { + //We've encountered an error + res.writeHead(400); + log.warn(`Api Server Error`, result.msg); + } + + res.end(JSON.stringify({ + success: result.res, + msg: result.msg, + data: result.data + })); + }); }); From 861fcf70c405bca86412e6f5d875c07cfd96af38 Mon Sep 17 00:00:00 2001 From: Xi-Lin Yeh Date: Thu, 20 Jan 2022 09:39:50 -0800 Subject: [PATCH 3/5] Clean invocation of get/delete --- electron/api-server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/electron/api-server.js b/electron/api-server.js index 8fdfe3f9c..22cd347b7 100644 --- a/electron/api-server.js +++ b/electron/api-server.js @@ -52,7 +52,7 @@ async function initialize(win) { switch(req.method) { // Request files case "GET": - result = await window.webContents.executeJavaScript(`document.getFiles("${data.filename}", "${data.code}")`); + result = await window.webContents.executeJavaScript(`document.getFiles()`); break; // Create or update files @@ -74,7 +74,7 @@ async function initialize(win) { // Delete files case "DELETE": - result = await window.webContents.executeJavaScript(`document.deleteFiles("${data.filename}", "${data.code}")`); + result = await window.webContents.executeJavaScript(`document.deleteFiles("${data.filename}")`); break; } From 935f4718e05038084773ebf487427e2f7821bd1a Mon Sep 17 00:00:00 2001 From: Xi-Lin Yeh Date: Fri, 21 Jan 2022 08:52:53 -0800 Subject: [PATCH 4/5] Fix type in delete function --- electron/api-server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electron/api-server.js b/electron/api-server.js index 22cd347b7..971922b71 100644 --- a/electron/api-server.js +++ b/electron/api-server.js @@ -74,7 +74,7 @@ async function initialize(win) { // Delete files case "DELETE": - result = await window.webContents.executeJavaScript(`document.deleteFiles("${data.filename}")`); + result = await window.webContents.executeJavaScript(`document.deleteFile("${data.filename}")`); break; } From cf8908fbc812889600cb3b269359276463e5dc34 Mon Sep 17 00:00:00 2001 From: Xi-Lin Yeh Date: Fri, 21 Jan 2022 20:49:12 -0800 Subject: [PATCH 5/5] Add metadata (hash, ramUsage) to be returns Allows for better dirty checking and integrations --- src/Electron.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Electron.tsx b/src/Electron.tsx index ea7fb10d4..50811ded7 100644 --- a/src/Electron.tsx +++ b/src/Electron.tsx @@ -4,6 +4,7 @@ import { Terminal } from "./Terminal"; import { SnackbarEvents } from "./ui/React/Snackbar"; import { IMap, IReturnStatus } from "./types"; import { GetServer } from "./Server/AllServers"; +import { resolve } from "cypress/types/bluebird"; export function initElectron(): void { const userAgent = navigator.userAgent.toLowerCase(); @@ -43,7 +44,9 @@ function initWebserver(): void { data: { files: home.scripts.map((script) => ({ filename: script.filename, - code: script.code + code: script.code, + hash: script.hash(), + ramUsage: script.ramUsage })) } } @@ -72,12 +75,17 @@ function initWebserver(): void { msg: "Home server does not exist." } } - const result = home.writeToScriptFile(Player, filename, code); - + const {success, overwritten} = home.writeToScriptFile(Player, filename, code); + let script; + if (success) { + script = home.getScript(filename); + } return { - res: result.success, + res: success, data: { - overwritten: result.overwritten + overwritten, + hash: script?.hash() || undefined, + ramUsage: script?.ramUsage } }; };