summaryrefslogtreecommitdiff
path: root/app/public
diff options
context:
space:
mode:
Diffstat (limited to 'app/public')
-rw-r--r--app/public/index.html21
-rw-r--r--app/public/main.js175
2 files changed, 195 insertions, 1 deletions
diff --git a/app/public/index.html b/app/public/index.html
index 3c435f5..d5932a1 100644
--- a/app/public/index.html
+++ b/app/public/index.html
@@ -52,12 +52,31 @@
<meta name="author" content="Alexis Hovorka">
<meta name="description" content="Zettelkasten-inspired note taking web app">
- <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400&display=swap">
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,400;0,700;1,400;1,700&family=Roboto:ital,wght@0,400;0,500;0,700;1,400;1,700&display=swap">
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Notes</h1>
+ <form>
+ <input type="text" id="username" name="username">
+ <input type="password" id="password" name="password">
+ <input type="checkbox" id="keep-session" name="keepSession">
+ <button id="login">Log In</button>
+ </form>
+ <br>
+ <input type="password" id="new-password" name="newPassword">
+ <button id="change-password">Change Password</button>
+ <br>
+ <input type="text" id="new-username" name="newUsername">
+ <button id="change-username">Change Username</button>
+ <br>
+ <input type="text" id="uid" name="uid">
+ <button id="logout">Log Out</button>
+ <button id="logout-everywhere">Log Out Everywhere</button>
+ <div id="notes"></div>
+ <button id="new-note">New Note</button>
+
<!--script src="sock.js"></script-->
<script src="main.js"></script>
</body>
diff --git a/app/public/main.js b/app/public/main.js
index ff960c2..5dc9531 100644
--- a/app/public/main.js
+++ b/app/public/main.js
@@ -5,6 +5,17 @@ const $ = (s,c) => (c||document).querySelector(s);
function $$(x,y,z,a){a=(z||document).querySelectorAll(x);if(typeof y=="function")[].forEach.call(a,y);return a}
function m(a,b,c){c=document;b=c.createElement(b||"p");b.innerHTML=a.trim();for(a=c.createDocumentFragment();c=b.firstChild;)a.appendChild(c);return a.firstChild}
+function debounce(fn, delay) {
+ let timeout = null;
+ return (...args) => {
+ if (timeout) clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ timeout = null;
+ fn.apply(null, args);
+ }, delay||500);
+ }
+}
+
//sock.init(`ws${secure?"s":""}://${location.host}/ws`);
//sock.on("hello", e => {
// console.log("hello", e);
@@ -15,4 +26,168 @@ function m(a,b,c){c=document;b=c.createElement(b||"p");b.innerHTML=a.trim();for(
// navigator.serviceWorker.register("sw.js")
// .then(() => console.log("Service worker registered"));
//}
+
+function drawNote(note) {
+ const el = m(`<div>
+ <h3>${note.id}</h3>
+ <textarea>${note.content}</textarea>
+ </div>`);
+
+ $("textarea", el).addEventListener("input",
+ debounce(() => { saveNote(note.id, $("textarea", el).value); }, 500));
+
+ $("#notes").appendChild(el);
+}
+
+async function getNote(id) {
+ const res = await fetch(`${id}`);
+ const note = await res.json();
+ console.log(note);
+ drawNote(note);
+}
+
+async function getList() {
+ const res = await fetch("/list");
+ const list = await res.json();
+ console.log(list);
+
+ for (let i=0;i<list.length;i++) {
+ await getNote(list[i]);
+ }
+}
+
+async function saveNote(id, content) {
+ const res = await fetch("/"+id, {
+ method: "POST",
+ body: JSON.stringify({id, content}),
+ });
+
+ const note = await res.json();
+ console.log(note);
+}
+
+async function newNote() {
+ const res = await fetch("/new", {
+ method: "POST",
+ });
+
+ const note = await res.json();
+ console.log(note);
+
+ drawNote(note);
+}
+
+$("#new-note").addEventListener("click", newNote);
+
+async function getUserData() {
+ const res = await fetch(`/user`);
+ const user = await res.json();
+ console.log(user);
+ if (user.uid) $("#uid").value = user.uid;
+}
+
+async function getUserSessions() {
+ const res = await fetch(`/session-list`);
+ const sessions = await res.json();
+ console.log(sessions);
+}
+
+$("#login").addEventListener("click", async () => {
+ const res = await fetch("/login", {
+ method: "POST",
+ headers: {"Content-Type": "application/json"},
+ body: JSON.stringify({
+ username: $("#username").value,
+ password: $("#password").value,
+ keepSession: $("#keep-session").checked,
+ })
+ });
+
+ // TODO check return code
+
+ const json = await res.json();
+ console.log(json);
+
+ // TODO "Must Change Password" flow
+
+ getUserData();
+ getList();
+});
+
+$("#change-password").addEventListener("click", async () => {
+ const res = await fetch("/change-password", {
+ method: "POST",
+ headers: {"Content-Type": "application/json"},
+ body: JSON.stringify({
+ username: $("#username").value,
+ password: $("#password").value,
+ newPassword: $("#new-password").value,
+ keepSession: $("#keep-session").checked,
+ })
+ });
+
+ const json = await res.json();
+ console.log(json);
+
+ getUserData();
+});
+
+$("#change-username").addEventListener("click", async () => {
+ const res = await fetch("/change-username", {
+ method: "POST",
+ headers: {"Content-Type": "application/json"},
+ body: JSON.stringify({
+ newUsername: $("#new-username").value,
+ password: $("#password").value,
+ })
+ });
+
+ const json = await res.json();
+ console.log(json);
+
+ getUserData();
+});
+
+$("#logout").addEventListener("click", async () => {
+ const res = await fetch("/logout", {
+ method: "POST",
+ });
+
+ //const json = await res.json();
+ //console.log(json);
+ console.log("Logged out");
+ $("#uid").value = "";
+});
+
+$("#logout-everywhere").addEventListener("click", async () => {
+ const res = await fetch("/deauth-all", {
+ method: "POST",
+ });
+
+ console.log("Logged out everywhere");
+ $("#uid").value = "";
+});
+
+try { getUserData(); getList(); getUserSessions(); } catch(e) {}
+
+// TODO make sure to save unsynced delta even if token has expired, then sync when logged in
+// TODO Cookie consent on signup; "only necessary for maintaining user session and cached data"
+// TODO "Review sessions" popup client-side when relogin required on a token which shouldn't have expired yet
+
+// TODO prevent data: and javascript: links?
+// Also embedding of external content?
+// encodeURI *AND* encode for attribute (quote marks etc)
+// https://github.com/cure53/DOMPurify
+//
+// elem.textContent = dangerVariable; !!!!!!!!!
+// elem.insertAdjacentText(dangerVariable);
+// elem.className = dangerVariable;
+// elem.setAttribute(safeName, dangerVariable);
+// formfield.value = dangerVariable;
+// document.createTextNode(dangerVariable);
+// document.createElement(dangerVariable);
+// elem.innerHTML = DOMPurify.sanitize(dangerVar);
+// https://github.com/cure53/DOMPurify/blob/main/src/attrs.js
+// https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
+
});