forked from tools/josh
1
0
Fork 0

Christen "josh" and add version number

Oh and make persistence a bit more manageable.
main
Florine W. Dekker 2019-11-09 19:23:33 +01:00
parent f5713fdf31
commit 11523b5046
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
7 changed files with 218 additions and 100 deletions

View File

@ -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"]);

83
package-lock.json generated
View File

@ -1055,6 +1055,14 @@
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"crypto-browserify": {
@ -1761,7 +1769,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@ -1782,12 +1791,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -1802,17 +1813,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -1929,7 +1943,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -1941,6 +1956,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -1955,6 +1971,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -1962,12 +1979,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -1986,6 +2005,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -2066,7 +2086,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -2078,6 +2099,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -2163,7 +2185,8 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@ -2199,6 +2222,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -2218,6 +2242,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -2261,12 +2286,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
}
}
},
@ -2546,6 +2573,12 @@
"which": "~1.3.0"
}
},
"grunt-text-replace": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/grunt-text-replace/-/grunt-text-replace-0.4.0.tgz",
"integrity": "sha1-252c5Z4v5J2id+nbwZXD4Rz7FsI=",
"dev": true
},
"grunt-webpack": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/grunt-webpack/-/grunt-webpack-3.1.3.tgz",
@ -3197,6 +3230,12 @@
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
@ -3611,6 +3650,14 @@
"requires": {
"object.getownpropertydescriptors": "^2.0.3",
"semver": "^5.7.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"node-libs-browser": {
@ -3671,6 +3718,14 @@
"resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"normalize-path": {
@ -4440,12 +4495,6 @@
"ajv-keywords": "^3.1.0"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"serialize-javascript": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",

View File

@ -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",

View File

@ -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 {
<b>List of commands</b>
${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 ***

View File

@ -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");
});

115
src/main/js/Persistence.ts Normal file
View File

@ -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");
}
}

View File

@ -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 {
</span>
Type "<a href="#" onclick="execute('help');">help</a>" 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);
}
}