all repos — felt @ d64fb026d9c9d92caeec2b258a523af06394b315

virtual tabletop for dungeons and dragons (and similar) using Go, MongoDB, and websockets

create and list tables in admin frontend
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAmPUzo8ACgkQO3+8IhRO
Y5gJPA//erHIs4ObmUEZNFPX0fhWCkV2WuzSI3vjbuVZHbdH7nkKnwge2Hn1Z33N
H3iVMA1C/iJBKcjwS4Z0cb5NUxiYRBVwxHui3nuuWaMOzuZTZCiv9p2NOK6U3D6t
bQDsiD3b/z2I3krFVxWSp0IhI8MxTsqdq26eVh6pebw5IB84WnRi+tUc4/M2/n/q
vlk2PBtM96Jsv6zbSUk4RxmDI/l78n4/uhxPqLSZeWYx41Udcl+rk3IlmhaIbno6
NHrfHEklcIRc/Oal7fvjM8nwBGk3kRh3+w7SUIgbGlh5+zurkIjbZM2Uao5flvmZ
mFU8r1M6hVpgFfBdSX8soFGkkPVwMrPWf+mYjnZrkIcn7gHvjFE1YSau0cwJ6NeU
Wq0PRnX7RtK4ShyjKlPJIZez+CbG1XXgD4OVr5GJPXcRW01DtD/HbWgQGbrXZwrb
yPBo359EdWz5bBS5gPMtLcXZuLOn9sE15jd/kNul/dwY/0hi2+xvbgeRrk9WYtVV
HONiCqCKFXiMRPgUe3yig5W269Qn7oFzYp0aGCK3QYKD4KuyElXvHGOTYYQTJLB0
s0/LDT93sRxIJvWgWceYa1VSgZOsoZBQpt1v+UzOnlLkj5NlvM3J8yYiR8OpCjtl
ffL7cwVr4EWA3P9KF3UpWufDMDYPeZqdLsG3FFKmFekW0R9v7SQ=
=/Vii
-----END PGP SIGNATURE-----
commit

d64fb026d9c9d92caeec2b258a523af06394b315

parent

b89711bbf7e30a18571f3d4f9909ab4d4be881f3

7 files changed, 111 insertions(+), 26 deletions(-)

jump to
M admin/admin.goadmin/admin.go

@@ -1,6 +1,7 @@

package admin import ( + "encoding/json" "html/template" "net/http" "nilfm.cc/git/felt/admin/util"

@@ -66,16 +67,16 @@ }

func apiCreateTable(next http.Handler, udb auth.UserStore, dbAdapter mongodb.DbAdapter) http.Handler { handlerFunc := func(w http.ResponseWriter, req *http.Request) { - tableName := req.Context().Value("Slug") - tablePass := req.Form["passcode"][0] - - tableKey := models.TableKey{ - Name: tableName.(string), - Passcode: tablePass, - } + tableKey := models.TableKey{} + err := json.NewDecoder(req.Body).Decode(&tableKey) + if err != nil { + w.WriteHeader(400) + next.ServeHTTP(w, req) + return + } // table name is primary key so w edon't need to check - err := dbAdapter.CreateTable(tableKey) + err = dbAdapter.CreateTable(tableKey) if err != nil { AddContextValue(req, "result", err.Error())
M go.modgo.mod

@@ -6,7 +6,7 @@ require (

go.mongodb.org/mongo-driver v1.11.0 golang.org/x/time v0.1.0 nhooyr.io/websocket v1.8.7 - nilfm.cc/git/quartzgun v0.2.2 + nilfm.cc/git/quartzgun v0.2.1 ) require (
M go.sumgo.sum

@@ -105,3 +105,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nilfm.cc/git/quartzgun v0.2.1 h1:R2Mi07c+nzaZL+x0atPXBoPoOXvDiLKqi3lcl81T6BA= +nilfm.cc/git/quartzgun v0.2.1/go.mod h1:/DDvt1DtzNuUf3HHaP29WMei/kkdaRW+ySmEzybvVto=
M models/models.gomodels/models.go

@@ -5,8 +5,8 @@ "time"

) type TableKey struct { - Name string - Passcode string + Name string `json:name` + Passcode string `json:passcode` } type DiceRoll struct {
M static/admin.jsstatic/admin.js

@@ -1,17 +1,31 @@

let adminToken = null; +const adminWrapper = document.getElementById("adminWrapper"); +const adminZone = document.getElementById("adminZone"); +const createTableForm = document.getElementById("createTableForm"); +const newTableName = document.getElementById("newTableName"); +const newTablePass = document.getElementById("newTablePass"); async function getTables() { try { const headers = new Headers(); - self.set('Authorization', 'Bearer ' + adminToken.access_token); + headers.set('Authorization', 'Bearer ' + adminToken.access_token); const res = await fetch('/admin/api/table/', { method: 'GET', headers: headers }); if (res.ok) { + const tableList = await res.json(); + let tableListHTML = "<ul>\n"; + for (const t of tableList) { + tableListHTML += `<li><a href="#">${t.Name}</a></li>\n` + } + tableListHTML += "</ul>" + adminZone.innerHTML = tableListHTML; } else { - console.log(res); - console.log(await res.headers.get("Quartzgun-Error")); + if (res.status == 404) { + return; + } + setErr(await res.headers.get("Quartzgun-Error")); } } catch { }

@@ -20,17 +34,14 @@

async function doLogin() { const adminUsrInput = document.getElementById("input_admin_usr"); const adminPassInput = document.getElementById("input_admin_pass"); - const errDiv = document.getElementById("loginErr"); if (adminUsrInput && adminPassInput) { adminToken = await getAdminToken(adminUsrInput.value, adminPassInput.value); - console.log(adminToken); if (adminToken) { + adminWrapper.style.display="block"; getTables(); } else { - if (errDiv) { - errDiv.innerHTML = "Incorrect credentials"; - } + setErr("Incorrect credentials"); } } }

@@ -52,3 +63,42 @@ } catch (err) {

return null; } } + +function setTableCreateFormVisible(v) { + if (createTableForm) { + createTableForm.style.display = v ? "block" : "none"; + } + if (!v) { + if (newTableName) { + newTableName.value = ""; + } + if (newTablePass) { + newTablePass.value = ""; + } + } +} + +async function createTable() { + const headers = new Headers(); + headers.set('Authorization', 'Bearer ' + adminToken.access_token); + + const formData = new FormData(); + formData.set("name", newTableName.value); + formData.set("passcode", newTablePass.value); + + let bodyStr = "{"; + for (const pair of formData.entries()) { + bodyStr += `"${pair[0]}": "${pair[1]}",`; + } + bodyStr = bodyStr.slice(0, -1); + bodyStr += "}"; + const res = await fetch('/admin/api/table/', { + method: 'POST', + headers: headers, + body: bodyStr, + }); + if (res.ok) { + getTables(); + setTableCreateFormVisible(false); + } +}
M static/index.htmlstatic/index.html

@@ -5,21 +5,20 @@ <meta charset="UTF-8" />

<title>Felt</title> <meta name="viewport" content="width=device-width" /> <link href="./style.css" rel="stylesheet" /> - <script src="./admin.js" type="text/javascript"></script> - <script src="./index.js" type="text/javascript"></script> + </head> <body> + <div id="errWrapper" style='display:none'><button id="closeErr" onclick="closeErr()">x</button><div id="errDiv"></div></div> <nav> <input id="name_entry"> <button id="goto_table">Change Table</button> <button id="admin_login">Admin Login</button> </nav> - <div id="admin_modal"> + <form id="admin_modal" onsubmit="return false"> <label>usr<input id="input_admin_usr"></label> <label>pass<input type="password" id="input_admin_pass"></label> - <button id="admin_login" onclick="doLogin()">login</button> - <div id="loginErr"></div> - </div> + <button type="submit" id="admin_login" onclick="doLogin()">login</button> + </form> <div id="dice_log"></div> <select name="num_dice"> <option>1</option>

@@ -54,4 +53,20 @@ <option>20</option>

</select> <input id="dice_note"><button id="dice_submit">Roll</button> <div id="map"></div> -</body>+ <div id="adminWrapper" style="display:none;"> + <div id="adminCtrl"> + <button onclick="setTableCreateFormVisible(true)">New Table</button> + <form onsubmit="return false" id="createTableForm" style="display:none;"> + <input id="newTableName"/> + <input id="newTablePass" type="password"/> + <button type="submit" onclick="createTable()">Create</button> + <button onclick="setTableCreateFormVisible(false)">Cancel</button> + </form> + </div> + <div id="adminZone"></div> + </div> +</body> + <script src="./util.js" type="text/javascript"></script> + <script src="./admin.js" type="text/javascript"></script> + <script src="./index.js" type="text/javascript"></script> +</html>
A static/util.js

@@ -0,0 +1,17 @@

+const errDiv = document.getElementById("errDiv"); +const errWrapper = document.getElementById("errWrapper"); + +function setErr(x) { + if (errDiv) { + errDiv.innerHTML = x; + } + if (errWrapper) { + errWrapper.style.display = "block"; + } +} + +function closeErr() { + if (errWrapper) { + errWrapper.style.display = "none"; + } +}