diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..7052132 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,451 @@ +import command from "../config.json" assert { type: "json" }; +import { HELP } from "./commands/help"; +import { BANNER } from "./commands/banner"; +import { ABOUT } from "./commands/about"; +import { DEFAULT } from "./commands/default"; +import { createWhoami } from "./commands/whoami"; +import config from "../config.json"; + +let mutWriteLines = document.getElementById("write-lines"); +let historyIdx = 0; +let tempInput = ""; +let userInput: string; +let isRoot = false; +let isPasswordInput = false; +let passwordCounter = 0; +let bareMode = false; + +const WRITELINESCOPY = mutWriteLines; +const TERMINAL = document.getElementById("terminal"); +const USERINPUT = document.getElementById("user-input") as HTMLInputElement; +const INPUT_HIDDEN = document.getElementById("input-hidden"); +const PASSWORD = document.getElementById("password-input"); +const PASSWORD_INPUT = document.getElementById( + "password-field" +) as HTMLInputElement; +const PRE_HOST = document.getElementById("pre-host"); +const PRE_USER = document.getElementById("pre-user"); +const HOST = document.getElementById("host"); +const USER = document.getElementById("user"); +const PROMPT = document.getElementById("prompt"); +const COMMANDS = [ + "help", + "about", + "whoami", + "banner", + "clear", + "key", + "linkedin", + "youtube", + "nagini", + "email", +]; +const HISTORY: string[] = []; +const ROOT_PASSWORD = command.password; + +const scrollToBottom = () => { + const MAIN = document.getElementById("main"); + if (!MAIN) return; + + MAIN.scrollTop = MAIN.scrollHeight; +}; + +function userInputHandler(e: KeyboardEvent) { + const key = e.key; + + switch (key) { + case "Enter": + e.preventDefault(); + if (!isPasswordInput) { + enterKey(); + } else { + passwordHandler(); + } + + scrollToBottom(); + break; + case "Escape": + USERINPUT.value = ""; + break; + case "ArrowUp": + arrowKeys(key); + e.preventDefault(); + break; + case "ArrowDown": + arrowKeys(key); + break; + case "Tab": + tabKey(); + e.preventDefault(); + break; + } +} + +function enterKey() { + if (!mutWriteLines || !PROMPT) return; + const resetInput = ""; + let newUserInput; + userInput = USERINPUT.value; + + if (bareMode) { + newUserInput = userInput; + } else { + newUserInput = `${userInput}`; + } + + HISTORY.push(userInput); + historyIdx = HISTORY.length; + + if (userInput === "clear") { + commandHandler(userInput.toLowerCase().trim()); + USERINPUT.value = resetInput; + userInput = resetInput; + return; + } + + const div = document.createElement("div"); + div.innerHTML = `${PROMPT.innerHTML} ${newUserInput}`; + + if (mutWriteLines.parentNode) { + mutWriteLines.parentNode.insertBefore(div, mutWriteLines); + } + + if (userInput.trim().length !== 0) { + commandHandler(userInput.toLowerCase().trim()); + } + + USERINPUT.value = resetInput; + userInput = resetInput; +} + +function tabKey() { + let currInput = USERINPUT.value; + + for (const ele of COMMANDS) { + if (ele.startsWith(currInput)) { + USERINPUT.value = ele; + return; + } + } +} + +function arrowKeys(e: string) { + switch (e) { + case "ArrowDown": + if (historyIdx !== HISTORY.length) { + historyIdx += 1; + USERINPUT.value = HISTORY[historyIdx]; + if (historyIdx === HISTORY.length) USERINPUT.value = tempInput; + } + break; + case "ArrowUp": + if (historyIdx === HISTORY.length) tempInput = USERINPUT.value; + if (historyIdx !== 0) { + historyIdx -= 1; + USERINPUT.value = HISTORY[historyIdx]; + } + break; + } +} + +function commandHandler(input: string) { + if (input.startsWith("rm -rf") && input.trim() !== "rm -rf") { + if (isRoot) { + if (input === "rm -rf /" && !bareMode) { + bareMode = true; + + setTimeout(() => { + if (!TERMINAL || !WRITELINESCOPY) return; + TERMINAL.innerHTML = ""; + TERMINAL.appendChild(WRITELINESCOPY); + mutWriteLines = WRITELINESCOPY; + }); + + easterEggStyles(); + setTimeout(() => { + writeLines(["What made you think that was a good idea?", "
"]); + }, 200); + + setTimeout(() => { + writeLines(["Now everything is ruined.", "
"]); + }, 1200); + } else if (input === "rm -rf /" && bareMode) { + writeLines(["there's no more / folder.", "
"]); + } else { + if (bareMode) { + writeLines(["What else are you trying to delete?", "
"]); + } else { + writeLines([ + "
", + "Directory not found.", + "type 'ls' for a list of directories.", + "
", + ]); + } + } + } else { + writeLines(["Permission not granted.", "
"]); + } + return; + } + + switch (input) { + case "clear": + setTimeout(() => { + if (!TERMINAL || !WRITELINESCOPY) return; + TERMINAL.innerHTML = ""; + TERMINAL.appendChild(WRITELINESCOPY); + mutWriteLines = WRITELINESCOPY; + }); + break; + + case "pwd": + writeLines(["/home/guest", "
"]); + break; + + case "banner": + writeLines(bareMode ? ["Joshua SystemV", "
"] : BANNER); + break; + + case "help": + writeLines( + bareMode + ? ["maybe restarting your browser will fix this.", "
"] + : HELP + ); + break; + + case "whoami": + writeLines(bareMode ? [`${command.username}`, "
"] : createWhoami()); + break; + + case "about": + writeLines(bareMode ? ["Nothing to see here.", "
"] : ABOUT); + break; + + case "key": + writeLines([`Downloading to key/${config.social["pgp-key"]}...`, "
"]); + setTimeout(() => { + window.open( + `https://src.panahifar.ir/pgp/C86437BFC43196C6.asc`, + "_blank" + ); + }, 500); + break; + + case "linkedin": + const linkedinUrl = `https://linkedin.com/in/${config.social.linkedin}`; + writeLines([ + `Redirecting to Linkedin/${config.social.linkedin}...`, + "
", + ]); + setTimeout(() => { + window.open(linkedinUrl, "_blank"); + }, 500); + break; + + case "youtube": + const youtubeUrl = `https://www.youtube.com/@${config.social.youtube}`; + writeLines([ + `Redirecting to Linkedin/${config.social.linkedin}...`, + "
", + ]); + setTimeout(() => { + window.open(youtubeUrl, "_blank"); + }, 500); + break; + + case "nagini": + writeLines([`Redirecting to negini/${config.social.git}...`, "
"]); + setTimeout(() => { + window.open( + `https://src.panahifar.ir/${config.social.git}`, + "_blank" + ); + }, 500); + break; + + case "email": + writeLines([`Redirecting to ${config.social.email}...`, "
"]); + setTimeout(() => { + window.open(`mailto:${config.social.email}`, "_blank"); + }, 500); + break; + + case "rm -rf": + if (bareMode) { + writeLines(["don't try again.", "
"]); + break; + } + if (isRoot) { + writeLines([ + "Usage: 'rm -rf <dir>'", + "
", + ]); + } else { + writeLines(["Permission not granted.", "
"]); + } + break; + case "su": + if (bareMode) { + writeLines(["no.", "
"]); + break; + } + if (!PASSWORD) return; + isPasswordInput = true; + USERINPUT.disabled = true; + + if (INPUT_HIDDEN) INPUT_HIDDEN.style.display = "none"; + PASSWORD.style.display = "block"; + setTimeout(() => { + PASSWORD_INPUT.focus(); + }, 100); + + break; + case "ls": + if (bareMode) { + writeLines(["there's nothing to see here.", "
"]); + break; + } + + if (isRoot) { + writeLines(["/", "
"]); + } else { + writeLines(["Permission not granted.", "
"]); + } + break; + default: + if (bareMode) { + writeLines(["type 'help'", "
"]); + break; + } + + writeLines(DEFAULT); + break; + } +} + +function writeLines(message: string[]) { + message.forEach((item, idx) => { + displayText(item, idx); + }); +} + +function displayText(item: string, idx: number) { + setTimeout(() => { + if (!mutWriteLines) return; + const p = document.createElement("p"); + p.innerHTML = item; + mutWriteLines.parentNode!.insertBefore(p, mutWriteLines); + scrollToBottom(); + }, 40 * idx); +} + +function revertPasswordChanges() { + if (!INPUT_HIDDEN || !PASSWORD) return; + PASSWORD_INPUT.value = ""; + USERINPUT.disabled = false; + INPUT_HIDDEN.style.display = "block"; + PASSWORD.style.display = "none"; + isPasswordInput = false; + + setTimeout(() => { + USERINPUT.focus(); + }, 200); +} + +function passwordHandler() { + if (passwordCounter === 2) { + if (!INPUT_HIDDEN || !mutWriteLines || !PASSWORD) return; + writeLines([ + "
", + "INCORRECT PASSWORD.", + "PERMISSION NOT GRANTED.", + "
", + ]); + revertPasswordChanges(); + passwordCounter = 0; + return; + } + + if (PASSWORD_INPUT.value === ROOT_PASSWORD) { + if (!mutWriteLines || !mutWriteLines.parentNode) return; + writeLines([ + "
", + "PERMISSION GRANTED.", + "Try 'rm -rf'", + "
", + ]); + revertPasswordChanges(); + isRoot = true; + return; + } else { + PASSWORD_INPUT.value = ""; + passwordCounter++; + } +} + +function easterEggStyles() { + const bars = document.getElementById("bars"); + const body = document.body; + const main = document.getElementById("main"); + const span = document.getElementsByTagName("span"); + + if (!bars) return; + bars.innerHTML = ""; + bars.remove(); + + if (main) main.style.border = "none"; + + body.style.backgroundColor = "black"; + body.style.fontFamily = "VT323, monospace"; + body.style.fontSize = "20px"; + body.style.color = "white"; + + for (let i = 0; i < span.length; i++) { + span[i].style.color = "white"; + } + + USERINPUT.style.backgroundColor = "black"; + USERINPUT.style.color = "white"; + USERINPUT.style.fontFamily = "VT323, monospace"; + USERINPUT.style.fontSize = "20px"; + if (PROMPT) PROMPT.style.color = "white"; +} + +const initEventListeners = () => { + if (HOST) { + HOST.innerText = command.hostname; + } + + if (USER) { + USER.innerText = command.username; + } + + if (PRE_HOST) { + PRE_HOST.innerText = command.hostname; + } + + if (PRE_USER) { + PRE_USER.innerText = command.username; + } + + window.addEventListener("load", () => { + writeLines(BANNER); + }); + + USERINPUT.addEventListener("keypress", userInputHandler); + USERINPUT.addEventListener("keydown", userInputHandler); + PASSWORD_INPUT.addEventListener("keypress", userInputHandler); + + window.addEventListener("click", () => { + USERINPUT.focus(); + }); + + console.log( + `%cPassword: ${command.password}`, + "color: red; font-size: 20px;" + ); +}; + +initEventListeners();