enforce subprotocol/tableKey to alphanumeric and underscore, refine UI

This commit is contained in:
Iris Lightshard 2023-07-05 22:27:38 -06:00
parent 0d3b4fb5ae
commit 43d85c2abc
Signed by: Iris Lightshard
GPG key ID: 3B7FBC22144E6398
7 changed files with 132 additions and 78 deletions

View file

@ -16,6 +16,7 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
) )
func apiGetTableList(next http.Handler, udb auth.UserStore) http.Handler { func apiGetTableList(next http.Handler, udb auth.UserStore) http.Handler {
@ -80,6 +81,13 @@ func apiCreateTable(next http.Handler, udb auth.UserStore, dbAdapter mongodb.DbA
return return
} }
r := regexp.MustCompile("^[a-zA-Z0-9_]+$")
if !r.MatchString(tableKey.Name) || !r.MatchString(tableKey.Passcode) {
w.WriteHeader(422)
next.ServeHTTP(w, req)
return
}
// table name is primary key so w edon't need to check // table name is primary key so w edon't need to check
err = dbAdapter.CreateTable(tableKey) err = dbAdapter.CreateTable(tableKey)

View file

@ -127,7 +127,9 @@ async function destroyTable() {
}); });
if (res.ok) { if (res.ok) {
conn.close(1000); conn.close(1000);
initializeMap("");
getTables(); getTables();
} else { } else {
setErr(await res.json()); setErr(await res.json());
} }

View file

@ -25,10 +25,8 @@
</form> </form>
</details><br/> </details><br/>
<div id="tabletop" style="display:none;"> <div id="tabletop" style="display:none;">
<details class="ui_win"><summary>dice</summary> <details id="dice_win" class="ui_win"><summary>dice</summary>
<select id="num_dice"> <select id="num_dice">
<option>1</option> <option>1</option>
<option>2</option> <option>2</option>

View file

@ -14,6 +14,10 @@ function initializeMap(mapImgUrl) {
mapImg.addTo(map); mapImg.addTo(map);
map.setMaxBounds([[-180,180],[180,-180]]); map.setMaxBounds([[-180,180],[180,-180]]);
map.setView([0,0], 2); map.setView([0,0], 2);
while (tokens.some(t=>t)) {
tokens[0].m.removeFrom(map);
tokens.shift();
}
} }
// this works but assumes the map is square (reasonable limitation I think) // this works but assumes the map is square (reasonable limitation I think)

View file

@ -14,10 +14,19 @@ function showTableModal(show) {
} }
} }
function fmtLeading(n) {
return n < 10 ? "0" + n : String(n);
}
function formatDice(r) { function formatDice(r) {
const date = new Date(r.timestamp) const date = new Date(r.timestamp)
const p = document.createElement("p"); const p = document.createElement("p");
p.innerHTML = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()} ${r.player} rolled ${r.roll.length}d${r.faces} ${(r.note ? "(" + r.note + ")" : "")}<br>[${r.roll}] (total ${r.roll.reduce((a,c)=>a+c,0)})`; const month = date.getMonth() + 1;
const day = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
p.innerHTML = `${date.getFullYear()}-${fmtLeading(month)}-${fmtLeading(day)} ${fmtLeading(hours)}:${fmtLeading(minutes)}:${fmtLeading(seconds)} ${r.player} rolled ${r.roll.length}d${r.faces} ${(r.note ? "(" + r.note + ")" : "")}<br>[${r.roll}] (total ${r.roll.reduce((a,c)=>a+c,0)})`;
return p; return p;
} }
@ -67,58 +76,72 @@ function setMapImg(url) {
initializeMap(url); initializeMap(url);
} }
function isTableKeyValid(name, passcode) {
const r = /^[a-zA-Z0-9_]+$/;
return r.test(name) && r.test(passcode);
}
function dial() { function dial() {
// get tableKey from UI // get tableKey from UI
const tblNameInput = document.getElementById("input_table_name"); const tblNameInput = document.getElementById("input_table_name");
const tblPassInput = document.getElementById("input_table_pass"); const tblPassInput = document.getElementById("input_table_pass");
if (tblNameInput && tblPassInput && tblNameInput.value && tblPassInput.value) { if (tblNameInput && tblPassInput && tblNameInput.value && tblPassInput.value) {
tableKey.name = tblNameInput.value; if (isTableKeyValid(tblNameInput.value, tblPassInput.value)) {
tableKey.passcode = tblPassInput.value; tableKey.name = tblNameInput.value;
tableKey.passcode = tblPassInput.value;
if (conn) { if (conn) {
conn.close(1000); conn.close(1000);
} }
conn = new WebSocket(`ws://${location.host}/subscribe`, `${tableKey.name}.${tableKey.passcode}`); conn = new WebSocket(`ws://${location.host}/subscribe`, `${tableKey.name}.${tableKey.passcode}`);
conn.addEventListener("close", e => { conn.addEventListener("close", e => {
if (e.code > 1001) { if (e.code == 1006) {
// TODO: add message to let user know they are reconnecting setErr("Table not found - check the name and passcode are correct");
setTimeout(dial, 1000) } else if (e.code > 1001) {
} else { // TODO: add message to let user know they are reconnecting
setErr("Websocket error: trying to reconnect");
setTimeout(dial, 1000)
} else {
tabletop = document.getElementById("tabletop");
if (tabletop) {
tabletop.style.display = "none";
}
table = null;
}
});
conn.addEventListener("open", e => {
// TODO: add message to let user know they are at the table
console.info("socket connected");
tabletop = document.getElementById("tabletop"); tabletop = document.getElementById("tabletop");
if (tabletop) { if (tabletop) {
tabletop.style.display = "none"; tabletop.style.display = "block";
} }
table = null; });
} conn.addEventListener("error", e => {
}); setErr(`${e.name}: ${e.message}`);
conn.addEventListener("open", e => { conn.close(3000);
// TODO: add message to let user know they are at the table })
console.info("socket connected");
tabletop = document.getElementById("tabletop");
if (tabletop) {
tabletop.style.display = "block";
}
});
conn.addEventListener("error", e => {
setErr(`${e.name}: ${e.message}`);
conn.close(3000);
})
conn.addEventListener("message", e => { conn.addEventListener("message", e => {
const data = JSON.parse(e.data); const data = JSON.parse(e.data);
if (table == null) { if (table == null) {
// first fetch comes from mongo, so the rolls array in each diceRoll is a byte array and needs to be decoded // first fetch comes from mongo, so the rolls array in each diceRoll is a byte array and needs to be decoded
data.diceRolls.forEach(r=>{ data.diceRolls.forEach(r=>{
r.roll = Uint8Array.from(atob(r.roll), c => c.charCodeAt(0)) r.roll = Uint8Array.from(atob(r.roll), c => c.charCodeAt(0))
}) })
table = data; table = data;
makeUpToDate(table); makeUpToDate(table);
} else { } else {
makeUpToDate(data); makeUpToDate(data);
} }
console.log(data); console.log(data);
}); });
} else {
setErr("Table name and passcode can only be alphanumeric and underscores");
}
}else {
setErr("Table name and passcode required");
} }
} }

View file

@ -1,3 +1,10 @@
:root {
--bg_color: rgba(0,0,0,0.7);
--fg_color: #ccc;
--main_color: #1f9b92;
--sub_color: #002b36;
}
* { * {
box-sizing: border-box; box-sizing: border-box;
padding: 0; padding: 0;
@ -6,12 +13,12 @@
outline: none; outline: none;
} }
* { scrollbar-color:#1f9b92 #000;} * { scrollbar-color:var(--main_color) var(--sub_color); }
*::-webkit-scrollbar { width:6px;height:6px; } *::-webkit-scrollbar { width:6px;height:6px; }
*::-webkit-scrollbar-track { background:#000; } *::-webkit-scrollbar-track { background: var(--sub_color);}
*::-webkit-scrollbar-thumb { background-color:#1f9b92;border-radius:0;border:none; } *::-webkit-scrollbar-thumb { background:var(--main_color);border-radius:0;border:none; }
*::-webkit-scrollbar-corner { background:#000; } *::-webkit-scrollbar-corner { background:var(--sub_color); }
*::selection { background-color:#1f9b92;color:#000;text-decoration:none;text-shadow:none; } *::selection { background-color:var(--main_color);color:var(--bg_color);text-decoration:none;text-shadow:none; }
body { body {
background: url('./bg.png'); background: url('./bg.png');
@ -22,15 +29,18 @@ label {
font-size: 80%; font-size: 80%;
} }
input, select { input, select, textarea {
background: #002b36; background: var(--sub_color);
color: #93a1a1; color: var(--fg_color);
border: solid 1px gray; border: solid 1px transparent;
}
input , select {
margin-right: 1ch; margin-right: 1ch;
} }
input:active, input:focus, select:active, select:focus { input:active, input:focus, select:active, select:focus, textarea:focus {
border: solid 1px cyan; border: solid 1px var(--main_color);
} }
@ -41,15 +51,15 @@ ul {
button { button {
padding: 0.5ch; padding: 0.5ch;
background: #000; background: transparent;
color: #fff; color: var(--fg_color);
border: solid 2px lightseagreen; border: solid 2px var(--main_color);
margin-right: 1ch; margin-right: 1ch;
} }
button:hover { button:hover {
color: #000; color: var(--bg_color);
background: lightseagreen; background: var(--main_color);
} }
#errWrapper { #errWrapper {
@ -72,8 +82,8 @@ button:hover {
} }
#dice_log { #dice_log {
background: #002b36; background: var(--sub_color);
color: #93a1a1; color: var(--fg_color);
height: 10em; height: 10em;
max-height: 10em; max-height: 10em;
display: block; display: block;
@ -85,7 +95,7 @@ button:hover {
} }
#dice_log p:not(:last-child) { #dice_log p:not(:last-child) {
border-bottom: solid 1px gray; border-bottom: solid 1px var(--fg_color);
} }
#aux { #aux {
@ -94,14 +104,12 @@ button:hover {
pre { pre {
font-size: 125%; font-size: 125%;
background: #002b36; background: var(--sub_color);
color: #93a1a1; color: var(--fg_color);
} }
#auxMsgZone { #auxMsgZone {
width: 100%; width: 100%;
color: #93a1a1;
background: #002b36;
padding:0.2em; padding:0.2em;
} }
@ -112,18 +120,18 @@ pre {
text-align: left; text-align: left;
position: relative; position: relative;
margin: 2em; margin: 2em;
background: rgba(0,0,0,0.7); background: var(--bg_color);
color: #eee; color: var(--fg_color);
display: inline; display: inline;
height: min-content; height: min-content;
z-index:1; z-index:1;
padding: 0.25em; padding: 0.25em;
border: 2px solid dimgray;; border: 2px solid transparent;
max-width: 80ch; max-width: min(60ch, 80vw);
} }
.ui_win:hover, .ui_win:active { .ui_win:focus-within {
border: 2px solid #1f9b92; border: 2px solid var(--main_color);
} }
.ui_win * { .ui_win * {
@ -135,7 +143,7 @@ pre {
} }
.ui_win a:hover, ui_win a:active { .ui_win a:hover, ui_win a:active {
color: #ff; color: var(--fg_color);
} }
#admin_section { #admin_section {

View file

@ -38,4 +38,15 @@ function loadName() {
} }
} }
function setupDiceAutoScroll() {
const diceWin = document.getElementById("dice_win");
diceWin.addEventListener("toggle", e => {
if (diceWin.open) {
const diceLog = document.getElementById("dice_log");
diceLog.children[diceLog.children.length - 1].scrollIntoView();
}
});
}
setupDiceAutoScroll();
loadName(); loadName();