diff --git a/dist/index.html b/dist/index.html index 48922ba..3bb3f63 100644 --- a/dist/index.html +++ b/dist/index.html @@ -45,6 +45,13 @@ +
+ + + + + +
diff --git a/dist/style.css b/dist/style.css index e5a85ef..7bf4bd2 100644 --- a/dist/style.css +++ b/dist/style.css @@ -66,3 +66,10 @@ -moz-box-shadow: 0 0 9px 0 rgba(242,255,63,.75); -webkit-box-shadow: 0 0 9px 0 rgba(242,255,63,.75) } + +#staffbtns { + display: none; +} +#staffbtns > button { + display: none; +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index dd9b822..4aa53af 100644 --- a/src/index.js +++ b/src/index.js @@ -2,11 +2,13 @@ import { guacutils } from "./protocol"; import { config } from "./common"; import { GetKeysym } from "./keyboard"; import { createNanoEvents } from "nanoevents"; +import { makeperms } from "./permissions"; // None = -1 // Has turn = 0 // In queue = var turn = -1; -var perms = 0; +var perms = makeperms(0); +var rank = 0; var connected = false; const vms = []; const users = []; @@ -14,7 +16,13 @@ const buttons = { takeTurn: window.document.getElementById("takeTurnBtn"), changeUsername: window.document.getElementById("changeUsernameBtn"), voteReset: window.document.getElementById("voteResetButton"), - screenshot: window.document.getElementById("screenshotButton") + screenshot: window.document.getElementById("screenshotButton"), + // Staff + restore: window.document.getElementById("restoreBtn"), + reboot: window.document.getElementById("rebootBtn"), + clearQueue: window.document.getElementById("clearQueueBtn"), + bypassTurn: window.document.getElementById("bypassTurnBtn"), + endTurn: window.document.getElementById("endTurnBtn"), } var hasTurn = false; var vm; @@ -38,12 +46,14 @@ const votenobtn = document.getElementById("voteNoBtn"); const voteyeslabel = document.getElementById("voteYesLabel"); const votenolabel = document.getElementById("voteNoLabel"); const votetime = document.getElementById("votetime"); +const staffbtns = document.getElementById("staffbtns"); // needed to scroll to bottom const chatListDiv = document.querySelector(".chat-table"); class CollabVMClient { eventemitter = createNanoEvents(); socket; + node; #url; constructor(url) { this.#url = url; @@ -65,6 +75,7 @@ class CollabVMClient { } connectToVM(node) { return new Promise((res, rej) => { + this.node = node; var savedUsername = window.localStorage.getItem("username"); if (savedUsername === null) this.socket.send(guacutils.encode(["rename"])); @@ -78,6 +89,9 @@ class CollabVMClient { rej(); }); this.socket.send(guacutils.encode(["connect", node])); + var pass = window.localStorage.getItem("password_"+this.#url); + if (pass) + this.admin.login(pass); }); } async #onMessage(event) { @@ -202,7 +216,7 @@ class CollabVMClient { turn = 0; turnstatus.innerText = "You have the turn."; display.className = "focused"; - } + } // Highlight all waiting users and set their status if (queuedUsers > 1) { for (var i = 1; i < queuedUsers; i++) { @@ -255,6 +269,50 @@ class CollabVMClient { break; } break; + case "admin": + switch (msgArr[1]) { + case "0": + // Login + switch (msgArr[2]) { + case "0": + this.eventemitter.emit('login', {error: 'badpassword'}); + return; + break; + case "1": + perms = makeperms(65535); + rank = 2; + break; + case "3": + rank = 3; + perms = makeperms(parseInt(msgArr[3])) + } + this.eventemitter.emit('login', {perms: perms, rank: rank}); + usernameSpan.classList.remove("text-light"); + switch (rank) { + case 2: + usernameSpan.classList.add("text-danger"); + break; + case 3: + usernameSpan.classList.add("text-success"); + break; + } + // Disabled for now until we figure out the issue of uservm + //window.localStorage.setItem("password_"+this.#url, password); + staffbtns.style.display = "block"; + if (perms.restore) buttons.restore.style.display = "inline-block"; + if (perms.reboot) buttons.reboot.style.display = "inline-block"; + if (perms.bypassturn) { + buttons.bypassTurn.style.display = "inline-block"; + buttons.clearQueue.style.display = "inline-block"; + buttons.endTurn.style.display = "inline-block"; + } + break; + + } + break; + default: + window.cvmEvents.emit(msgArr[0], msgArr.slice(1)); + break; } } reloadUsers() { @@ -316,6 +374,28 @@ class CollabVMClient { voteReset(reset) { this.socket.send(guacutils.encode(["vote", reset ? "1" : "0"])); } + admin = { + login: (password) => { + return new Promise((res, rej) => { + var unbind = this.eventemitter.on('login', (args) => { + unbind(); + if (args.error) rej(error); + res(args); + }) + this.socket.send(guacutils.encode(["admin", "2", password])); + }); + }, + adminInstruction: (...args) => { // Compatibility + args.unshift("admin"); + console.log(args); + this.socket.send(guacutils.encode(args)); + }, + restore: () => this.socket.send(guacutils.encode(["admin", "8", this.node])), + reboot: () => this.socket.send(guacutils.encode(["admin", "10", this.node])), + clearQueue: () => this.socket.send(guacutils.encode(["admin", "17", this.node])), + bypassTurn: () => this.socket.send(guacutils.encode(["admin", "20"])), + endTurn: () => this.socket.send(guacutils.encode(["admin", "16", users[0].username])), + } } function multicollab(url) { return new Promise(async (res, rej) => { @@ -371,15 +451,15 @@ async function openVM(url, node) { await vm.connectToVM(node); vmlist.style.display = "none"; vmview.style.display = "block"; - display.addEventListener('mousemove', (e) => vm.mouseevent(e)) - display.addEventListener('mousedown', (e) => vm.mouseevent(e)); - display.addEventListener('mouseup', (e) => vm.mouseevent(e)); + display.addEventListener('mousemove', (e) => vm.mouseevent(e), {capture: true}) + display.addEventListener('mousedown', (e) => vm.mouseevent(e), {capture: true}); + display.addEventListener('mouseup', (e) => vm.mouseevent(e), {capture: true}); display.addEventListener('contextmenu', (e) => e.preventDefault()); display.addEventListener('click', () => { if (turn === -1) vm.turn(); - }); - display.addEventListener('keydown', (e) => vm.keyevent(e, true)); - display.addEventListener('keyup', (e) => vm.keyevent(e, false)); + }, {capture: true}); + display.addEventListener('keydown', (e) => vm.keyevent(e, true), {capture: true}); + display.addEventListener('keyup', (e) => vm.keyevent(e, false), {capture: true}); } function screenshotVM() { return new Promise((res, rej) => { @@ -413,9 +493,31 @@ buttons.takeTurn.addEventListener('click', () => vm.turn()); buttons.voteReset.addEventListener('click', () => vm.voteReset(true)); voteyesbtn.addEventListener('click', () => vm.voteReset(true)); votenobtn.addEventListener('click', () => vm.voteReset(false)); +// Staff buttons +buttons.restore.addEventListener('click', () => vm.admin.restore()); +buttons.reboot.addEventListener('click', () => vm.admin.reboot()); +buttons.clearQueue.addEventListener('click', () => vm.admin.clearQueue()); +buttons.bypassTurn.addEventListener('click', () => vm.admin.bypassTurn()); +buttons.endTurn.addEventListener('click', () => vm.admin.endTurn()); +// Login +var usernameClick = false; +usernameSpan.addEventListener('click', () => { + if (!usernameClick) { + usernameClick = true; + setInterval(() => {usernameClick = false;}, 1000); + return; + } + var pass = window.prompt("🔑"); + if (!pass) return; + vm.admin.login(pass); +}); // Load all vms config.serverAddresses.forEach(multicollab); // Export some stuff window.screenshotVM = screenshotVM; -window.multicollab = multicollab; \ No newline at end of file +window.multicollab = multicollab; +window.getPerms = () => perms; +window.getRank = () => rank; +window.GetAdmin = () => vm.admin; +window.cvmEvents = createNanoEvents(); \ No newline at end of file diff --git a/src/permissions.js b/src/permissions.js new file mode 100644 index 0000000..87dca86 --- /dev/null +++ b/src/permissions.js @@ -0,0 +1,23 @@ +export function makeperms(mask) { + const perms = { + restore: false, + reboot: false, + ban: false, + forcevote: false, + mute: false, + kick: false, + bypassturn: false, + rename: false, + grabip: false + }; + if ((mask & 1) !== 0) perms.restore = true; + if ((mask & 2) !== 0) perms.reboot = true; + if ((mask & 4) !== 0) perms.ban = true; + if ((mask & 8) !== 0) perms.forcevote = true; + if ((mask & 16) !== 0) perms.mute = true; + if ((mask & 32) !== 0) perms.kick = true; + if ((mask & 64) !== 0) perms.bypassturn = true; + if ((mask & 128) !== 0) perms.rename = true; + if ((mask & 256) !== 0) perms.grabip = true; + return perms; +} \ No newline at end of file