This commit is contained in:
maaa 2023-08-19 15:17:23 +02:00
parent a8f721eab6
commit 52cff35cd2
11 changed files with 265 additions and 24 deletions

74
main
View File

@ -76,6 +76,15 @@ def get_session(id):
return "error"
return post
def get_session_from_sessionid(id):
conn = get_db_connection()
post = conn.execute("SELECT * FROM sessions WHERE sessionid = ?",
(id,)).fetchone()
conn.close()
if post is None:
return "error"
return post
def check_username_taken(username):
conn = get_db_connection()
post = conn.execute("SELECT * FROM users WHERE lower(username) = ?",
@ -148,8 +157,8 @@ def apisignup():
randomCharacters = secrets.token_hex(512)
conn = get_db_connection()
conn.execute("INSERT INTO sessions (session, id) VALUES (?, ?)",
(randomCharacters, userID))
conn.execute("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)",
(randomCharacters, userID, request.headers.get("user-agent")))
conn.commit()
conn.close()
@ -178,8 +187,8 @@ def apilogin():
randomCharacters = secrets.token_hex(512)
conn = get_db_connection()
conn.execute("INSERT INTO sessions (session, id) VALUES (?, ?)",
(randomCharacters, userID))
conn.execute("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)",
(randomCharacters, userID, request.headers.get("user-agent")))
conn.commit()
conn.close()
@ -374,6 +383,59 @@ def apideleteaccount():
return {}, 200
@app.route("/api/sessions/list", methods=("GET", "POST"))
def apisessionslist():
if request.method == "POST":
data = request.get_json()
secretKey = data["secretKey"]
userCookie = get_session(secretKey)
user = get_user(userCookie["id"])
conn = get_db_connection()
sessions = conn.execute("SELECT * FROM sessions WHERE id = ? ORDER BY id DESC;", (user["id"],)).fetchall()
conn.close()
datatemplate = []
for x in sessions:
device = x["device"]
thisSession = False
if (x["session"] == secretKey):
thisSession = True
sessiontemplate = {
"id": x["sessionid"],
"thisSession": thisSession,
"device": device
}
datatemplate.append(sessiontemplate)
return datatemplate, 200
@app.route("/api/sessions/remove", methods=("GET", "POST"))
def apisessionsremove():
if request.method == "POST":
data = request.get_json()
secretKey = data["secretKey"]
sessionId = data["sessionId"]
userCookie = get_session(secretKey)
user = get_user(userCookie["id"])
session = get_session_from_sessionid(sessionId)
if (session != "error"):
if (user["id"] == session["id"]):
conn = get_db_connection()
conn.execute("DELETE FROM sessions WHERE sessionid = ?", (session["sessionid"],))
conn.commit()
conn.close()
return {}, 200
else:
return {}, 403
else:
return {}, 422
@app.route("/listusers/<secretkey>", methods=("GET", "POST"))
def listusers(secretkey):
@ -397,6 +459,10 @@ def apilogout():
def burger(e):
return {}, 500
@app.errorhandler(404)
def burger(e):
return render_template("error.html", errorCode=404, errorMessage="Page not found"), 404
# Start server
if __name__ == "__main__":
print("[INFO] Server started")

View File

@ -19,6 +19,8 @@ CREATE TABLE notes (
);
CREATE TABLE sessions (
session TEXT PRIMARY KEY NOT NULL,
id INTEGER NOT NULL
sessionid INTEGER PRIMARY KEY AUTOINCREMENT,
session TEXT NOT NULL,
id INTEGER NOT NULL,
device TEXT NOT NULL DEFAULT "?"
);

View File

@ -273,6 +273,33 @@ body {
margin-bottom: 10px;
}
.sessionDiv div {
background-color: #f4f4f4;
border-radius: 8px;
margin-bottom: 5px;
padding: 10px;
height: 35px;
}
.sessionDiv div p {
display: inline;
position: absolute;
transform: translateY(-7.5px);
}
.sessionDiv div button {
position: absolute;
border-radius: 99px;
right: 15px;
width: 40px;
height: 40px;
font-size: 16px;
transform: translateY(-2px);
}
.sessionDiv img {
display: inline;
filter: none;
height: 100%;
}
/* Sign up/log in div */
.inoutdiv {

View File

@ -11,6 +11,14 @@ if (localStorage.getItem("DONOTSHARE-password") === null) {
function formatBytes(a, b = 2) { if (!+a) return "0 Bytes"; const c = 0 > b ? 0 : b, d = Math.floor(Math.log(a) / Math.log(1000)); return `${parseFloat((a / Math.pow(1000, d)).toFixed(c))} ${["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"][d]}` }
function truncateString(str, num) {
if (str.length > num) {
return str.slice(0, num) + "...";
} else {
return str;
}
}
let secretkey = localStorage.getItem("DONOTSHARE-secretkey")
let password = localStorage.getItem("DONOTSHARE-password")
@ -23,6 +31,10 @@ let closeErrorButton = document.getElementById("closeErrorButton")
let cancelErrorButton = document.getElementById("cancelErrorButton")
let errorInput = document.getElementById("errorInput")
let exitThing = document.getElementById("exitThing")
let exitSessionsThing = document.getElementById("exitSessionsThing")
let sessionManagerButton = document.getElementById("sessionManagerButton")
let sessionManagerDiv = document.getElementById("sessionManagerDiv")
let sessionDiv = document.getElementById("sessionDiv")
let deleteMyAccountButton = document.getElementById("deleteMyAccountButton")
let storageThing = document.getElementById("storageThing")
let storageProgressThing = document.getElementById("storageProgressThing")
@ -45,6 +57,7 @@ if (/Android|iPhone/i.test(navigator.userAgent)) {
notesBar.style.width = "calc(100% - 10px)"
noteBox.readOnly = true
noteBox.style.fontSize = "18px"
noteBox.classList.add("hidden")
notesBar.addEventListener("touchstart", function (event) {
touchstartX = event.changedTouches[0].screenX;
@ -69,19 +82,25 @@ if (/Android|iPhone/i.test(navigator.userAgent)) {
}, false);
function handleGesture() {
if (touchendX > touchstartX) {
notesBar.style.width = "calc(100% - 30px)";
noteBox.style.width = "30px"
noteBox.readOnly = true
if (touchendX > touchstartX + 75) {
notesBar.style.width = "calc(100% - 10px)";
noteBox.style.width = "10px"
if (selectedNote != 0) {
noteBox.readOnly = true
}
notesDiv.classList.remove("hidden")
noteBox.classList.add("hidden")
newNote.classList.remove("hidden")
}
if (touchendX < touchstartX) {
if (touchendX < touchstartX - 75) {
noteBox.style.width = "calc(100% - 30px)";
notesBar.style.width = "30px"
noteBox.readOnly = false
notesBar.style.width = "10px"
if (selectedNote != 0) {
noteBox.readOnly = false
}
notesDiv.classList.add("hidden")
noteBox.classList.remove("hidden")
newNote.classList.add("hidden")
}
}
@ -104,7 +123,6 @@ closeErrorButton.addEventListener("click", (event) => {
optionsCoverDiv.classList.add("hidden")
});
function displayPrompt(message, callback) {
errorMessageThing.innerText = message
errorInput.value = ""
@ -160,13 +178,22 @@ function updateUserInfo() {
.then((response) => response)
.then((response) => {
async function doStuff() {
let responseData = await response.json()
usernameBox.innerText = responseData["username"]
usernameThing.innerText = "logged in as " + responseData["username"]
storageThing.innerText = "you've used " + formatBytes(responseData["storageused"]) + " out of " + formatBytes(responseData["storagemax"])
storageProgressThing.value = responseData["storageused"]
storageProgressThing.max = responseData["storagemax"]
noteCount = responseData["notecount"]
if (response.status == 500) {
displayError("Something went wrong. Signing you out..")
closeErrorButton.classList.add("hidden")
usernameBox.innerText = ""
setTimeout(function() {
window.location.replace("/api/logout")
}, 2500);
} else {
let responseData = await response.json()
usernameBox.innerText = responseData["username"]
usernameThing.innerText = "logged in as " + responseData["username"]
storageThing.innerText = "you've used " + formatBytes(responseData["storageused"]) + " out of " + formatBytes(responseData["storagemax"])
storageProgressThing.value = responseData["storageused"]
storageProgressThing.max = responseData["storagemax"]
noteCount = responseData["notecount"]
}
}
doStuff()
});
@ -177,7 +204,7 @@ usernameBox.addEventListener("click", (event) => {
updateUserInfo()
});
logOutButton.addEventListener("click", (event) => {
window.location.href = "/api/logout"
window.location.replace("/api/logout")
});
exitThing.addEventListener("click", (event) => {
optionsDiv.classList.add("hidden")
@ -204,6 +231,85 @@ deleteMyAccountButton.addEventListener("click", (event) => {
})
}
});
sessionManagerButton.addEventListener("click", (event) => {
optionsDiv.classList.add("hidden")
sessionManagerDiv.classList.remove("hidden")
fetch("/api/sessions/list", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey
}),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then((response) => response)
.then((response) => {
async function doStuff() {
let responseData = await response.json()
document.querySelectorAll(".burgerSession").forEach((el) => el.remove());
for (let i in responseData) {
let sessionElement = document.createElement("div")
let sessionText = document.createElement("p")
let sessionImage = document.createElement("img")
let sessionRemoveButton = document.createElement("button")
sessionText.classList.add("w300")
if (responseData[i]["thisSession"] == true) {
sessionText.innerHTML = "<span style='background-color: #157efb; color: white; padding: 8px; border-radius: 8px; margin-right: 5px;'>current</span>" + truncateString(responseData[i]["device"], 18)
} else {
sessionText.innerHTML = truncateString(responseData[i]["device"], 27)
}
sessionText.title = responseData[i]["device"]
sessionRemoveButton.innerText = "X"
sessionImage.src = "/static/svg/device_other.svg"
ua = responseData[i]["device"]
if (ua.includes("NT") || ua.includes("Linux")) {
sessionImage.src = "/static/svg/device_computer.svg"
}
if (ua.includes("iPhone" || ua.includes("Android"))) {
sessionImage.src = "/static/svg/device_smartphone.svg"
}
sessionRemoveButton.addEventListener("click", (event) => {
fetch("/api/sessions/remove", {
method: "POST",
body: JSON.stringify({
secretKey: secretkey,
sessionId: responseData[i]["id"]
}),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then((response) => response)
.then((response) => {
if (responseData[i]["thisSession"] == true) {
window.location.replace("/api/logout")
}
});
sessionElement.remove()
});
sessionElement.append(sessionImage)
sessionElement.append(sessionText)
sessionElement.append(sessionRemoveButton)
sessionElement.classList.add("burgerSession")
sessionDiv.append(sessionElement)
}
}
doStuff()
});
});
exitSessionsThing.addEventListener("click", (event) => {
optionsDiv.classList.remove("hidden")
sessionManagerDiv.classList.add("hidden")
});
updateUserInfo()
@ -342,7 +448,6 @@ updateNotes()
newNote.addEventListener("click", (event) => {
let noteName = displayPrompt("note name? :3", burgerFunction)
//let noteName = prompt("note name? :3")
function burgerFunction(noteName) {
if (noteName != null) {
let encryptedName = CryptoJS.AES.encrypt(noteName, password).toString();
@ -410,7 +515,6 @@ function exportNotes() {
responseData[i]["content"] = originalContent
}
let jsonString = JSON.parse(JSON.stringify(responseData))
console.log(jsonString)
exportNotesButton.innerText = "export notes"
downloadObjectAsJson(jsonString, "data")

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M70-120q-12.75 0-21.375-8.675Q40-137.351 40-150.175 40-163 48.625-171.5T70-180h820q12.75 0 21.375 8.675 8.625 8.676 8.625 21.5 0 12.825-8.625 21.325T890-120H70Zm70-120q-24 0-42-18t-18-42v-480q0-24 18-42t42-18h680q24 0 42 18t18 42v480q0 24-18 42t-42 18H140Zm0-60h680v-480H140v480Zm0 0v-480 480Z"/></svg>

After

Width:  |  Height:  |  Size: 399 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M140-160q-24 0-42-18t-18-42v-520q0-24 18-42t42-18h680q24 0 42 18t18 42v520q0 24-18 42t-42 18H140Zm0-60h680v-436H140v436Zm160-72-42-42 103-104-104-104 43-42 146 146-146 146Zm190 4v-60h220v60H490Z"/></svg>

After

Width:  |  Height:  |  Size: 300 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M260-40q-24 0-42-18t-18-42v-760q0-24 18-42t42-18h440q24 0 42 18t18 42v760q0 24-18 42t-42 18H260Zm0-90v30h440v-30H260Zm0-60h440v-580H260v580Zm0-640h440v-30H260v30Zm0 0v-30 30Zm0 700v30-30Z"/></svg>

After

Width:  |  Height:  |  Size: 293 B

1
static/svg/list.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M290-620v-60h550v60H290Zm0 170v-60h550v60H290Zm0 170v-60h550v60H290ZM150-620q-12 0-21-9t-9-21.5q0-12.5 9-21t21.5-8.5q12.5 0 21 8.625T180-650q0 12-8.625 21T150-620Zm0 170q-12 0-21-9t-9-21.5q0-12.5 9-21t21.5-8.5q12.5 0 21 8.625T180-480q0 12-8.625 21T150-450Zm0 170q-12 0-21-9t-9-21.5q0-12.5 9-21t21.5-8.5q12.5 0 21 8.625T180-310q0 12-8.625 21T150-280Z"/></svg>

After

Width:  |  Height:  |  Size: 455 B

View File

@ -39,8 +39,17 @@
<p>account managment</p>
<button id="deleteMyAccountButton"><img src="/static/svg/delete_forever.svg">delete my account</button>
<button id="exportNotesButton"><img src="/static/svg/download.svg">export notes</button>
<button id="sessionManagerButton"><img src="/static/svg/list.svg">session manager</button>
<button class="lastButton" id="logOutButton"><img src="/static/svg/logout.svg">log out</button>
</div>
<div id="sessionManagerDiv" class="optionsDiv hidden">
<button class="exit" id="exitSessionsThing">X</button>
<h3 class="w300">session manager</h3>
<p>manage your sessions</p>
<div class="section"></div>
<div class="sessionDiv" id="sessionDiv">
</div>
</div>
<div id="errorDiv" class="optionsDiv hidden">
<p id="errorMessageThing"></p>
<input class="hidden" id="errorInput" type="text" placeholder="e.g. shopping list"><br></input>

23
templates/error.html Normal file
View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>burgernotes</title>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
</head>
<body>
<h2 class="w300">{{ errorMessage }}</h2>
{{ errorCode }} | {{ errorMessage }}
</body>
<style>
body {
margin-left: 15px;
}
</style>
</html>

View File

@ -21,6 +21,12 @@
<ul>
<li>Username, and your password hashed</li>
<li>Date of creating account</li>
<li>Web browser "User Agent"</li>
</ul>
<h2 class="w300">Information collected when logging in</h2>
<p>When logging back in to your account, we collect and store the following information:</p>
<ul>
<li>Web browser "User agent"</li>
</ul>
<h2 class="w300">Information we collect while using our services</h2>
<p>When you create an note, we collect and use this information:</p>