From 11523b50466822283c3f2ed3d13c4976e3b7f369 Mon Sep 17 00:00:00 2001 From: "Felix W. Dekker" Date: Sat, 9 Nov 2019 19:23:33 +0100 Subject: [PATCH] Christen "josh" and add version number Oh and make persistence a bit more manageable. --- Gruntfile.js | 21 ++++++- package-lock.json | Bin 209342 -> 211125 bytes package.json | 3 +- src/main/js/Commands.ts | 14 ++--- src/main/js/Main.ts | 2 +- src/main/js/Persistence.ts | 115 +++++++++++++++++++++++++++++++++++++ src/main/js/Shell.ts | 80 ++++---------------------- 7 files changed, 152 insertions(+), 83 deletions(-) create mode 100644 src/main/js/Persistence.ts diff --git a/Gruntfile.js b/Gruntfile.js index 255b8e7..c93c3b4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -17,6 +17,18 @@ module.exports = grunt => { files: [{expand: true, cwd: "src/main/", src: "**/*.css", dest: "build/"}] } }, + replace: { + default: { + src: "./build/bundle.js", + replacements: [ + { + from: "%%VERSION_NUMBER%%", + to: "<%= pkg.version %>" + } + ], + overwrite: true + } + }, webpack: { options: { entry: "./src/main/js/Main.ts", @@ -49,6 +61,7 @@ module.exports = grunt => { grunt.loadNpmTasks("grunt-contrib-clean"); grunt.loadNpmTasks("grunt-contrib-copy"); + grunt.loadNpmTasks("grunt-text-replace"); grunt.loadNpmTasks("grunt-webpack"); grunt.registerTask("dev", [ @@ -59,7 +72,9 @@ module.exports = grunt => { "copy:html", "copy:css", // Compile - "webpack:dev" + "webpack:dev", + // Post + "replace" ]); grunt.registerTask("deploy", [ // Pre @@ -69,7 +84,9 @@ module.exports = grunt => { "copy:html", "copy:css", // Compile JS - "webpack:deploy" + "webpack:deploy", + // Post + "replace" ]); grunt.registerTask("default", ["dev"]); diff --git a/package-lock.json b/package-lock.json index 1a799ac012db8886a6b73b91d0e1be6c19065805..e0b1d1fb1229a044cfc6173a641ce5510705cff2 100644 GIT binary patch delta 642 zcmdn@m}l!po((>WC+}`jo&M$wlhX9NMNItD_r7A(neKU(NnpB4F_ZA*1)oGGC$O+g z_bFydnymMVe|p0KM&-@Xi+`t1-tdBN`UMU~_3e3_jEqb$uG;iAZpM=76I2;3Ca1Xx zZdc=BbY=ugKHy>0nA{!4J-wHgF&Hi~{Unfa$Li)Pf`-eArs&w?;( zn>AxBj3+gHe+;9RD_F3P?fn`@pQp2jN+36 zboi&w4`F26-jU4696Q;;foGaAquTUMuNckoMCEp1RNl3j{x*V<)cD_?^OrG|WBR;1 Sr0aaZ%JeX2`>A54FM9zS1MIN? delta 412 zcmXYsO(;ZR6oz}hJ93%1zKi5%?#$f$WQ>i3CS=OiC<_!aBP9#NT2`|YB|o>ElB8H! za7%n5#)=}PEHxTojgpkbxXovGI_G(xbI#GH=iPx# diff --git a/package.json b/package.json index de01d3a..4e6ed1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fwdekker.com", - "version": "1.0.0", + "version": "1.6.4", "description": "The source code of [my personal website](https://fwdekker.com/).", "author": "Felix W. Dekker", "repository": { @@ -26,6 +26,7 @@ "grunt-cli": "^1.3.2", "grunt-contrib-clean": "^2.0.0", "grunt-contrib-copy": "^1.0.0", + "grunt-text-replace": "^0.4.0", "grunt-webpack": "^3.1.3", "mocha": "^6.2.2", "ts-loader": "^6.2.1", diff --git a/src/main/js/Commands.ts b/src/main/js/Commands.ts index 6f0fe34..60c60e6 100644 --- a/src/main/js/Commands.ts +++ b/src/main/js/Commands.ts @@ -1,7 +1,7 @@ -import * as Cookies from "js-cookie"; import "./Extensions" import {Environment} from "./Environment"; import {Directory, File, FileSystem, Path} from "./FileSystem" +import {Persistence} from "./Persistence"; import {IllegalArgumentError, IllegalStateError} from "./Shared"; import {InputArgs} from "./Shell"; import {EscapeCharacters} from "./Terminal"; @@ -229,8 +229,7 @@ export class Commands { */ execute(input: InputArgs): string { if (input.command === "factory-reset") { - Cookies.remove("files"); - Cookies.remove("env"); + Persistence.reset(); location.reload(); throw new Error("Goodbye"); } @@ -369,7 +368,7 @@ export class Commands { List of commands ${commandEntries.join("\n")} - Write "help [COMMAND]" or click a command in the list above for more information on a command.\\\ + Write "help [COMMAND]" or click a command in the list above for more information.\\\ `.trimMultiLines(); } } @@ -476,12 +475,9 @@ export class Commands { if (userName === "") throw new IllegalStateError("Cannot execute `poweroff` while not logged in."); - Cookies.set("poweroff", "true", { - "expires": new Date(new Date().setSeconds(new Date().getSeconds() + 30)), - "path": "/" - }); - + Persistence.setPoweroff(true); setTimeout(() => location.reload(), 2000); + return `Shutdown NOW! *** FINAL System shutdown message from ${userName}@fwdekker.com *** diff --git a/src/main/js/Main.ts b/src/main/js/Main.ts index 8910a16..95d4944 100644 --- a/src/main/js/Main.ts +++ b/src/main/js/Main.ts @@ -29,5 +29,5 @@ addOnLoad(() => { // @ts-ignore: Ugly hack to execute it anyway if (window.terminal.shell.environment.get("user") !== "") - window.terminal.processInput("ls"); + window.execute("ls"); }); diff --git a/src/main/js/Persistence.ts b/src/main/js/Persistence.ts new file mode 100644 index 0000000..bfb3e93 --- /dev/null +++ b/src/main/js/Persistence.ts @@ -0,0 +1,115 @@ +import * as Cookies from "js-cookie"; +import {Environment} from "./Environment"; +import {Directory, FileSystem, Node, Path} from "./FileSystem"; +import {UserList} from "./UserList"; + + +/** + * Manages persistence of state. + */ +export class Persistence { + /** + * Deserializes a file system from persistent storage, or returns the default file system if the deserialization + * failed. + */ + static getFileSystem(): FileSystem { + const filesString = Cookies.get("files"); + + let files: Directory | undefined = undefined; + if (filesString !== undefined) { + try { + const parsedFiles = Node.deserialize(filesString); + if (parsedFiles instanceof Directory) + files = parsedFiles; + else + console.warn("`files` cookie contains non-directory."); + } catch (error) { + console.warn("Failed to deserialize `files` cookie.", error); + } + } + + return new FileSystem(files); + } + + /** + * Persists the given file system. + * + * @param fileSystem the file system to persist + */ + static setFileSystem(fileSystem: FileSystem) { + Cookies.set("files", fileSystem.root, { + "expires": new Date(new Date().setFullYear(new Date().getFullYear() + 25)), + "path": "/" + }); + } + + /** + * Deserializes an environment from persistent storage, or returns the default environment if the deserialization + * failed. + * + * @param fileSystem the file system used to validate the `cwd` environment variable + * @param userList the list of users used to validate the `user` environment variable + */ + static getEnvironment(fileSystem: FileSystem, userList: UserList): Environment { + const environmentString = Cookies.get("env") ?? "{}"; + + let environment: Environment; + try { + environment = new Environment(["cwd", "home", "user"], JSON.parse(environmentString)); + } catch (error) { + console.warn("Failed to set environment from cookie."); + environment = new Environment(["cwd", "home", "user"]); + } + + // Check user in environment + if (!environment.has("user")) { + environment.set("user", "felix"); + } else if (environment.get("user") !== "" && !userList.has(environment.get("user"))) { + console.warn(`Invalid user '${environment.get("user")}' in environment.`); + environment.set("user", "felix"); + } + + // Set home directory + environment.set("home", userList.get(environment.get("user"))?.home ?? "/"); + + // Check cwd in environment + if (!environment.has("cwd")) { + environment.set("cwd", environment.get("home")); + } else if (!fileSystem.has(new Path(environment.get("cwd")))) { + console.warn(`Invalid cwd '${environment.get("cwd")}' in environment.`); + environment.set("cwd", environment.get("home")); + } + + return environment; + } + + /** + * Persists the given environment. + * + * @param environment the environment to persist + */ + static setEnvironment(environment: Environment) { + Cookies.set("env", environment.variables, {"path": "/"}); + } + + /** + * Persists the "power off" setting. + * + * @param value the value to persist for the "power off" setting + */ + static setPoweroff(value: boolean) { + Cookies.set("poweroff", `${value}`, { + "expires": new Date(new Date().setSeconds(new Date().getSeconds() + 30)), + "path": "/" + }); + } + + /** + * Removes all persistent storage. + */ + static reset() { + Cookies.remove("files"); + Cookies.remove("env"); + Cookies.remove("poweroff"); + } +} diff --git a/src/main/js/Shell.ts b/src/main/js/Shell.ts index 399f1c5..0b50d33 100644 --- a/src/main/js/Shell.ts +++ b/src/main/js/Shell.ts @@ -1,8 +1,8 @@ -import * as Cookies from "js-cookie"; import {Commands} from "./Commands"; import {Environment} from "./Environment"; -import {Directory, File, FileSystem, Node, Path} from "./FileSystem"; +import {Directory, File, FileSystem, Path} from "./FileSystem"; import {InputParser} from "./InputParser"; +import {Persistence} from "./Persistence"; import {asciiHeaderHtml, IllegalStateError, stripHtmlTags} from "./Shared"; import {EscapeCharacters, InputHistory} from "./Terminal"; import {UserList} from "./UserList"; @@ -48,8 +48,8 @@ export class Shell { constructor(inputHistory: InputHistory) { this.inputHistory = inputHistory; this.userList = new UserList(); - this.fileSystem = Shell.loadFileSystem(); - this.environment = Shell.loadEnvironment(this.fileSystem, this.userList); + this.fileSystem = Persistence.getFileSystem(); + this.environment = Persistence.getEnvironment(this.fileSystem, this.userList); this.commands = new Commands(this.environment, this.userList, this.fileSystem); this.saveState(); @@ -71,6 +71,7 @@ export class Shell { Type "help" for help. + Welcome to josh v%%VERSION_NUMBER%%, the javascript online shell. `.trimLines(); } @@ -205,74 +206,13 @@ export class Shell { /** - * Saves the shell's state in cookies. + * Persists the shell's state. + * + * @see Persistence */ private saveState() { - Cookies.set("files", this.fileSystem.root, { - "expires": new Date(new Date().setFullYear(new Date().getFullYear() + 25)), - "path": "/" - }); - Cookies.set("env", this.environment.variables, {"path": "/"}); - } - - /** - * Returns the file system loaded from a cookie, or the default file system if no cookie is present or the cookie - * is invalid. - */ - private static loadFileSystem(): FileSystem { - let files: Directory | undefined = undefined; - const filesString = Cookies.get("files"); - if (filesString !== undefined) { - try { - const parsedFiles = Node.deserialize(filesString); - if (parsedFiles instanceof Directory) - files = parsedFiles; - else - console.warn("`files` cookie contains non-directory."); - } catch (error) { - console.warn("Failed to deserialize `files` cookie.", error); - } - } - return new FileSystem(files); - } - - /** - * Returns the environment loaded from a cookie, or the default environment if no cookie is present or the cookie - * is invalid. - * - * @param fileSystem the file system used to validate the `cwd` environment variable - * @param userList the list of users used to validate the `user` environment variable - */ - private static loadEnvironment(fileSystem: FileSystem, userList: UserList): Environment { - const environmentString = Cookies.get("env") ?? "{}"; - let environment: Environment; - try { - environment = new Environment(["cwd", "home", "user"], JSON.parse(environmentString)); - } catch (error) { - console.warn("Failed to set environment from cookie."); - environment = new Environment(["cwd", "home", "user"]); - } - - // Check user in environment - if (!environment.has("user")) { - environment.set("user", "felix"); - } else if (environment.get("user") !== "" && !userList.has(environment.get("user"))) { - console.warn(`Invalid user '${environment.get("user")}' in environment.`); - environment.set("user", "felix"); - } - - // Set home directory - environment.set("home", userList.get(environment.get("user"))?.home ?? "/"); - - // Check cwd in environment - if (!environment.has("cwd")) { - environment.set("cwd", environment.get("home")); - } else if (!fileSystem.has(new Path(environment.get("cwd")))) { - console.warn(`Invalid cwd '${environment.get("cwd")}' in environment.`); - environment.set("cwd", environment.get("home")); - } - - return environment; + Persistence.setFileSystem(this.fileSystem); + Persistence.setEnvironment(this.environment); } }