Fixed live editing, corrected many small errors, added more general best practices

This commit is contained in:
Tracker-Friendly 2024-04-29 00:15:40 +01:00
parent e178bbee57
commit 503b26403c
7 changed files with 78 additions and 53 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
database.db
config.ini
config.ini.example
.idea

View File

@ -14,7 +14,7 @@ To prevent the server from knowing the encryption key, the password you provide
If you wish to change the user's password, set "passwordchange" to "yes" and "newpass" to the new hash.
Some users use the legacy argon2id mode (by which i mean about 8, so only implement if you feel like it), and to implement argon2id functionality, you hash like this:
Some users use the legacy argon2 id mode (by which I mean about 8, so only implement if you feel like it), and to implement argon2 id functionality, you hash like this:
```
Parallelism should be 1
@ -29,7 +29,7 @@ The output should be in the encoded format, not the hashed format
Salt should be the SHA512 of the password
```
(Yes i know this is really bad practice, guess why we are replacing it)
(Yes I know these are really awful practice, guess why we are replacing it)
To test if SHA-3 or argon2 is used, just try the SHA-3 and if 422 gets returned try argon2.

View File

@ -2,4 +2,5 @@
- Switch to WebSockets for updating notes + live updating of note list and more, this involves redoing some APIs
- Compress notes to reduce bandwidth and storage
- Use OAuth for authentication
- Dedicated domain (not just a subdomain, if anyone can donate a domain let Arzumify know!)

View File

@ -2,19 +2,19 @@
import sqlite3
import os
def generatedb():
connection = sqlite3.connect("database.db")
with open("schema.sql") as f:
connection.executescript(f.read())
cur = connection.cursor()
connection.commit()
connection.close()
print("[INFO] Generated database")
if not os.path.exists("database.db"):
generatedb()
else:
@ -26,4 +26,4 @@ else:
elif ":3" in answer:
print(":3")
else:
print("Stopped")
print("Stopped")

106
main
View File

@ -8,7 +8,7 @@ import asyncio
from hypercorn.config import Config
from hypercorn.asyncio import serve
from werkzeug.security import generate_password_hash, check_password_hash
from quart import Quart, request, url_for, flash, redirect, session, make_response, send_from_directory, stream_with_context, Response, request
from quart import Quart, request
# Parse configuration file, and check if anything is wrong with it
if not os.path.exists("config.ini"):
@ -31,33 +31,38 @@ if SECRET_KEY == "supersecretkey" or SECRET_KEY == "placeholder":
app = Quart(__name__)
app.config["SECRET_KEY"] = SECRET_KEY
# Database functions
def get_db_connection():
conn = sqlite3.connect("database.db")
conn.row_factory = sqlite3.Row
return conn
def get_user(id):
def get_user(identifier):
conn = get_db_connection()
post = conn.execute("SELECT * FROM users WHERE id = ?",
(id,)).fetchone()
(identifier,)).fetchone()
conn.close()
if post is None:
return None
return post
def get_note(id):
def get_note(identifier):
conn = get_db_connection()
post = conn.execute("SELECT * FROM notes WHERE id = ?",
(id,)).fetchone()
(identifier,)).fetchone()
conn.close()
if post is None:
return None
return post
def get_space(id):
def get_space(identifier):
conn = get_db_connection()
notes = conn.execute("SELECT content, title FROM notes WHERE creator = ? ORDER BY id DESC;", (id,)).fetchall()
notes = conn.execute("SELECT content, title FROM notes WHERE creator = ? ORDER BY id DESC;",
(identifier,)).fetchall()
conn.close()
spacetaken = 0
for x in notes:
@ -65,33 +70,35 @@ def get_space(id):
spacetaken = spacetaken + len(x["title"].encode("utf-8"))
return spacetaken
def get_note_count(id):
conn = get_db_connection()
notes = conn.execute("SELECT content, title FROM notes WHERE creator = ? ORDER BY id DESC;", (id,)).fetchall()
conn.close()
notecount = 0
for x in notes:
notecount = notecount + 1
return notecount
def get_session(id):
def get_note_count(identifier):
conn = get_db_connection()
notes = conn.execute("SELECT content, title FROM notes WHERE creator = ? ORDER BY id DESC;",
(identifier,)).fetchall()
conn.close()
return len(notes)
def get_session(identifier):
conn = get_db_connection()
post = conn.execute("SELECT * FROM sessions WHERE session = ?",
(id,)).fetchone()
(identifier,)).fetchone()
conn.close()
if post is None:
return None
return post
def get_session_from_sessionid(id):
def get_session_from_sessionid(identifier):
conn = get_db_connection()
post = conn.execute("SELECT * FROM sessions WHERE sessionid = ?",
(id,)).fetchone()
(identifier,)).fetchone()
conn.close()
if post is None:
return None
return post
def check_username_taken(username):
conn = get_db_connection()
post = conn.execute("SELECT * FROM users WHERE lower(username) = ?",
@ -101,6 +108,7 @@ def check_username_taken(username):
return None
return post["id"]
# Disable CORS
@app.after_request
async def add_cors_headers(response):
@ -109,13 +117,16 @@ async def add_cors_headers(response):
response.headers.add("Access-Control-Allow-Methods", "*")
return response
# Live editing store
messages = {}
@app.route("/api/version", methods=("GET", "POST"))
async def apiversion():
return "Burgernotes Version 1.2"
@app.route("/api/signup", methods=("GET", "POST"))
async def apisignup():
if request.method == "POST":
@ -138,7 +149,7 @@ async def apisignup():
if len(password) < 14:
return {}, 422
if not check_username_taken(username) == None:
if not check_username_taken(username) is None:
return {}, 409
hashedpassword = generate_password_hash(password)
@ -148,7 +159,6 @@ async def apisignup():
(username, hashedpassword, str(time.time())))
userID = check_username_taken(username)
user = get_user(userID)
randomCharacters = secrets.token_hex(512)
@ -161,6 +171,7 @@ async def apisignup():
"key": randomCharacters
}, 200
@app.route("/api/login", methods=("GET", "POST"))
async def apilogin():
if request.method == "POST":
@ -172,13 +183,13 @@ async def apilogin():
check_username_thing = check_username_taken(username)
if check_username_thing == None:
if check_username_thing is None:
return {}, 401
userID = check_username_taken(username)
user = get_user(userID)
if not check_password_hash(user["password"], (password)):
if not check_password_hash(user["password"], password):
return {}, 401
randomCharacters = secrets.token_hex(512)
@ -217,6 +228,7 @@ async def apiuserinfo():
}
return datatemplate
@app.route("/api/listnotes", methods=("GET", "POST"))
async def apilistnotes():
if request.method == "POST":
@ -241,6 +253,7 @@ async def apilistnotes():
return datatemplate, 200
@app.route("/api/exportnotes", methods=("GET", "POST"))
async def apiexportnotes():
if request.method == "POST":
@ -268,6 +281,7 @@ async def apiexportnotes():
return datatemplate, 200
@app.route("/api/newnote", methods=("GET", "POST"))
async def apinewnote():
if request.method == "POST":
@ -280,12 +294,13 @@ async def apinewnote():
conn = get_db_connection()
conn.execute("INSERT INTO notes (title, content, creator, created, edited) VALUES (?, ?, ?, ?, ?)",
(noteName, "", user["id"], str(time.time()), str(time.time())))
(noteName, "", user["id"], str(time.time()), str(time.time())))
conn.commit()
conn.close()
return {}, 200
@app.route("/api/readnote", methods=("GET", "POST"))
async def apireadnote():
if request.method == "POST":
@ -298,8 +313,8 @@ async def apireadnote():
note = get_note(noteId)
if (note != None):
if (user["id"] == note["creator"]):
if note is not None:
if user["id"] == note["creator"]:
contenttemplate = {
"content": note["content"]
}
@ -310,6 +325,7 @@ async def apireadnote():
else:
return {}, 422
@app.route("/api/waitforedit", methods=("GET", "POST"))
async def waitforedit():
if request.method == "POST":
@ -317,26 +333,27 @@ async def waitforedit():
secretKey = data["secretKey"]
userCookie = get_session(secretKey)
user = get_user(userCookie["id"])
complete = true
complete = True
start_time = time.time()
while user["id"] not in messages or not messages[user["id"]]:
await asyncio.sleep(0)
elapsed_time = time.time() - start_time
if elapsed_time >= 20:
complete = False
break
complete = false
message = messages[user["id"]].pop(0)
del messages[user["id"]]
if complete == true:
if complete:
return {
"note": message
}, 200
else:
return 400
@app.route("/api/editnote", methods=("GET", "POST"))
async def apieditnote():
if request.method == "POST":
@ -354,10 +371,11 @@ async def apieditnote():
if get_space(user["id"]) + len(content.encode("utf-8")) > int(MAX_STORAGE):
return {}, 418
if (note != None):
if (user["id"] == note["creator"]):
if note is not None:
if user["id"] == note["creator"]:
conn = get_db_connection()
conn.execute("UPDATE notes SET content = ?, title = ?, edited = ? WHERE id = ?", (content, title, str(time.time()), noteId))
conn.execute("UPDATE notes SET content = ?, title = ?, edited = ? WHERE id = ?",
(content, title, str(time.time()), noteId))
conn.commit()
conn.close()
@ -388,8 +406,8 @@ async def apieditnotetitle():
if get_space(user["id"]) + len(content.encode("utf-8")) > int(MAX_STORAGE):
return {}, 418
if (note != None):
if (user["id"] == note["creator"]):
if note is not None:
if user["id"] == note["creator"]:
conn = get_db_connection()
conn.execute("UPDATE notes SET title = ?, edited = ? WHERE id = ?", (content, str(time.time()), noteId))
conn.commit()
@ -414,8 +432,8 @@ async def apiremovenote():
note = get_note(noteId)
if (note != None):
if (user["id"] == note["creator"]):
if note is not None:
if user["id"] == note["creator"]:
conn = get_db_connection()
conn.execute("DELETE FROM notes WHERE id = ?", (noteId,))
conn.commit()
@ -435,7 +453,6 @@ async def apideleteaccount():
secretKey = data["secretKey"]
userCookie = get_session(secretKey)
user = get_user(userCookie["id"])
conn = get_db_connection()
conn.execute("DELETE FROM notes WHERE creator = ?", (userCookie["id"],))
@ -445,6 +462,7 @@ async def apideleteaccount():
return {}, 200
@app.route("/api/sessions/list", methods=("GET", "POST"))
async def apisessionslist():
if request.method == "POST":
@ -463,7 +481,7 @@ async def apisessionslist():
for x in sessions:
device = x["device"]
thisSession = False
if (x["session"] == secretKey):
if x["session"] == secretKey:
thisSession = True
sessiontemplate = {
"id": x["sessionid"],
@ -474,6 +492,7 @@ async def apisessionslist():
return datatemplate, 200
@app.route("/api/sessions/remove", methods=("GET", "POST"))
async def apisessionsremove():
if request.method == "POST":
@ -486,8 +505,8 @@ async def apisessionsremove():
session = get_session_from_sessionid(sessionId)
if (session != None):
if (user["id"] == session["id"]):
if session is not None:
if user["id"] == session["id"]:
conn = get_db_connection()
conn.execute("DELETE FROM sessions WHERE sessionid = ?", (session["sessionid"],))
conn.commit()
@ -521,14 +540,17 @@ async def listusers():
else:
return {}, 401
@app.errorhandler(500)
async def burger(e):
async def burger():
return {}, 500
@app.errorhandler(404)
async def burger(e):
async def burger():
return {}, 404
# Start server
hypercornconfig = Config()
hypercornconfig.bind = (HOST + ":" + PORT)

View File

@ -5,13 +5,14 @@ import sys
print("type n to cancel")
answer = input("delete user, what is user id?")
if (answer == "n"):
if answer == "n":
sys.exit()
def get_db_connection():
conn = sqlite3.connect("database.db")
conn.row_factory = sqlite3.Row
return conn
connection = sqlite3.connect("database.db")
connection.row_factory = sqlite3.Row
return connection
print("deleting notes")
@ -28,4 +29,4 @@ conn.execute("DELETE FROM users WHERE id = ?", (int(answer),))
conn.commit()
conn.close()
print("success")
print("success")

View File

@ -22,5 +22,5 @@ CREATE TABLE sessions (
sessionid INTEGER PRIMARY KEY AUTOINCREMENT,
session TEXT NOT NULL,
id INTEGER NOT NULL,
device TEXT NOT NULL DEFAULT "?"
device TEXT NOT NULL DEFAULT '?'
);