diff --git a/.auth.py.kak.FOf4Es b/.auth.py.kak.FOf4Es new file mode 100644 index 0000000..0e1df90 --- /dev/null +++ b/.auth.py.kak.FOf4Es @@ -0,0 +1,223 @@ +from flask import render_template, request, make_response, redirect +import database, re, bcrypt, secrets, time + +from app import app + +def get_session(request): + sessionid = request.cookies.get('session') + + if not sessionid: + return False + + sessionid = str(sessionid) + sessioninfo = database.fetch("""SELECT uid, created_at, expires, description + FROM session + WHERE value=?;""", + [sessionid]) + + if len(sessioninfo) == 0: + return False + + if len(sessioninfo) > 1: + # there is a problem + return False + + return sessioninfo[0] + (sessionid,) + +def check_pw(uid, password): + userquery = database.fetch("SELECT uid, password FROM user WHERE username=?", [username]) + + # STOP TIMING ATTACKS + time.sleep(secrets.randbelow(100)/50) + + if len(userquery) == 0: + return render_template("auth/login.html", + response=f"Der Nutzername >{username} oder das Passwort ist falsch.", + username=username) + + uidquery = userquery[0][0] + passquery = userquery[0][1] + + passform = str(request.form['pass']) + passform = passform.encode("utf8") + passcorrect = bcrypt.checkpw(passform, passquery) + + if not passcorrect: + return render_template("auth/login.html", + response=f"Der Nutzername {username} oder das >Passwort ist falsch.", + username=username) + + +@app.route("/auth/headerbar") +def headerbar(): + sessioninfo = get_session(request) + + if not sessioninfo: + return render_template("auth/headerbar.html", loggedin=False) + + return render_template("auth/headerbar.html", loggedin=True) + +@app.route("/auth/sessioninfo", methods=['GET']) +def sessioninfo(): + time.sleep(secrets.randbelow(100)/50) # Rate limiting + + sessioninfo = get_session(request) + + if not sessioninfo: + return "Invalid session" + + (uid, created_at, expires, description) = sessioninfo + # uid = sessioninfo[0][0] + # created_at = sessioninfo[0][1] + # expires = sessioninfo[0][2] + # description = sessioninfo[0][3] + return render_template("/auth/sessioninfo.html", + sessionid=sessionid, + uid=uid, + created_at=created_at, + expires=expires, + description=description) + +@app.route("/auth/logout", methods=['GET']) +def logout(): + sessioninfo = get_session(request) + + response = redirect("/") + response.set_cookie('session', value='', expires=0) + + if not sessioninfo: + return response + + database.execute("DELETE FROM session WHERE value = ?", [sessioninfo[4]]) + + return response + +@app.route("/auth/login", methods=['GET', 'POST']) +def login(): + sessioninfo = get_session(request) + + if sessioninfo: + uid = sessioninfo[0] + username = database.fetch("SELECT username FROM user WHERE uid = ?", [uid]) + username = username[0][0] + return render_template("auth/loginsuccess.html", username=username) + + + if request.method == 'GET': + return render_template("auth/login.html", + response="", + username="") + + username = _sanitizeUsername(request.form['user']) + if len(username) < 3: + return render_template("auth/login.html", + response=f"Der Nutzername {username} ist zu kurz.", + username=username) + + userquery = database.fetch("SELECT uid, password FROM user WHERE username=?", [username]) + + # STOP TIMING ATTACKS + time.sleep(secrets.randbelow(100)/50) + + if len(userquery) == 0: + return render_template("auth/login.html", + response=f"Der Nutzername >{username} oder das Passwort ist falsch.", + username=username) + + uidquery = userquery[0][0] + passquery = userquery[0][1] + + passform = str(request.form['pass']) + passform = passform.encode("utf8") + passcorrect = bcrypt.checkpw(passform, passquery) + + if not passcorrect: + return render_template("auth/login.html", + response=f"Der Nutzername {username} oder das >Passwort ist falsch.", + username=username) + + now = int(time.time()) + + timeout = int(request.form['timeout']) + timeout = now + timeout + + session = secrets.randbelow(999999999) + session = str(session) + str(now) + + database.execute("DELETE FROM session WHERE expires < ?", [str(now)]) + database.execute("""INSERT INTO session + (uid, created_at, expires, description, value) + VALUES (?, ?, ?, ?, ?);""", + (uidquery, int(time.time()), timeout, "", session) + ) + + response = make_response("") + response.set_cookie('session', value=session, expires=timeout) + return response + +@app.route("/auth/newuser", methods=['GET', 'POST']) +def newuser(): + if request.method == 'GET': + return render_template("auth/register.html", + responses=[], + username="") + + success = True + responses = [] + + username = _sanitizeUsername(request.form['user']) + if len(username) < 3: + success = False + responses.append(f"Der Nutzername {username} ist zu kurz.") + usernamequery = database.fetch("SELECT id FROM user WHERE username=?", [username]) + if len(usernamequery) != 0: + success = False + responses.append(f"Der Nutzername {username} wird bereits verwendet.") + + pass1 = str(request.form['pass1']) + pass2 = str(request.form['pass2']) + if pass1 != pass2: + success = False + responses.append(f"Passwort und Passwortbestätigung sind ungleich.") + + if len(pass1) < 10: + success = False + responses.append(f"Das Passwort ist kürzer als 10 Zeichen.") + + if not success: + return render_template("auth/register.html", + responses=responses, + username=username) + + uid = secrets.randbelow(99999999) + created_at = int(time.time()) + # emailcode = secrets.randbelow(999999) + passsalt = bcrypt.gensalt() + passhash = bcrypt.hashpw(pass1.encode("utf8"), passsalt) + + database.execute("""INSERT INTO user + (uid, created_at, username, password) VALUES + (?, ?, ?, ?)""", + (uid, created_at, username, passhash)) + + return render_template("auth/registersuccess.html", username=username) + +def _sanitizeUsername(username): + username = str(username) + username = username.strip() + username = username[:32] + + # Remove all non-word characters (everything except numbers and letters) + username = re.sub(r"[^._\w\s-]", '', username) + + # Replace all runs of whitespace with a single dash + username = re.sub(r"\s+", '_', username) + + return username + +def _verifyEmail(email): + pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' + if re.match(pattern, email): + return True + else: + return False diff --git a/entry.py b/entry.py index 9f4058b..809d139 100644 --- a/entry.py +++ b/entry.py @@ -5,6 +5,37 @@ import secrets, time, os, database from auth import get_session from app import app + +@app.route("/entry//interactions/", methods=['GET']) +def interactions(pid, mode): + # session = get_session(request) + + # empty = (mode!='left' and mode!='right') + # selected = 'none' + + # if session and not empty: + # uid = session[0] + # previous = database.fetch("SELECT mode FROM vote WHERE uid=? AND pid=?", [uid, pid]) + # if len(previous) == 0: + # selected=mode + # database.execute("INSERT INTO vote (uid, pid, mode) VALUES (?,?,?)", [uid, pid, mode]) + # else: + + # previous = previous[0][0] + # previous = 'none' + + # if previous < 1: + # elif previous[0][0]==mode: + # database.execute("DELETE FROM vote WHERE uid=? AND pid=?", [uid, pid]) + # else: + # database.execute("UPDATE vote SET mode=? WHERE uid=? AND pid=?", [mode, uid, pid]) + + # left = database.fetch("SELECT COUNT(id) FROM vote WHERE pid=? and mode='left';", [pid])[0][0] + # right = database.fetch("SELECT COUNT(id) FROM vote WHERE pid=? and mode='right';", [pid])[0][0] + + # return render_template("entry/interactions.html", pid=pid, stats=f"{left} {right} {mode}") + return "--" + @app.route("/entry/feed/", methods=['GET']) def feed(page): page = int(page, 0) diff --git a/static/css/main.css b/static/css/main.css index 503e37c..244c60f 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -20,6 +20,9 @@ h1 { background-color: white; display: inline-block; } +p { + line-height: 1.5; } + span { background-color: white; } @@ -36,6 +39,24 @@ hr { border-bottom: none; margin: 0px; } +#contentnotice { + background-color: white; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 10pt; } + #contentnotice h1, #contentnotice h3 { + margin: 6pt; } + #contentnotice div { + width: 100%; + max-width: 500px; } + .entry .meta { color: gray; margin: 2pt 4pt; @@ -48,6 +69,10 @@ hr { .entry img { max-width: 100%; } +.entry .interactions { + display: flex; + justify-content: space-around; } + #loading { background-color: #fff9; position: absolute; diff --git a/static/css/main.scss b/static/css/main.scss index 08083cb..bf51f33 100644 --- a/static/css/main.scss +++ b/static/css/main.scss @@ -30,6 +30,10 @@ h1 { display: inline-block; } +p { + line-height: 1.5; +} + span { background-color: white; } @@ -49,6 +53,29 @@ hr { margin: 0px; } +#contentnotice { + background-color: white; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 10pt; + + h1, h3 { + margin: 6pt; + } + + div { + width: 100%; + max-width: 500px; + } +} + .entry { .meta { color: gray; @@ -64,6 +91,11 @@ hr { img { max-width: 100%; } + + .interactions { + display: flex; + justify-content: space-around; + } } #loading { diff --git a/static/js/main.js b/static/js/main.js index 88b1ab4..bacc36c 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -1,7 +1,6 @@ $(() => { // $('#auth-visible').hide() - // Startdatum festlegen (Beispiel: 1. Januar 2025, 00:00:00 Uhr) const startDate = new Date('2025-12-13T12:44:00').getTime(); @@ -23,4 +22,15 @@ $(() => { updateCounter(); setInterval(updateCounter, 1000); + let agreedToWarning = document.cookie.includes("agreedToWarning=True") + if(agreedToWarning) { + $("#contentnotice").hide() + } }) + +function setCookie(cname, cvalue, exdays) { + const d = new Date(); + d.setTime(d.getTime() + (exdays*24*60*60*1000)); + let expires = "expires="+ d.toUTCString(); + document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; +} diff --git a/templates/baseof.html b/templates/baseof.html index 4f8b9b8..eb08f33 100644 --- a/templates/baseof.html +++ b/templates/baseof.html @@ -27,10 +27,29 @@ crossorigin="anonymous" > - - +
+

Achtung!

+

Kontrollverlust

+
+

Dieses Forum richtet sich ausschließlich an erwachsene Besucher:innen.

+ +

+ Die Inhalte könnten explizite Sprache, anstößige Themen, gewalthaltige Äußerungen, sowie pornografische Darstellungen in Text-, Zeichnungs- oder angedeuteter Form enthalten. +

+

+ Fotografische Nacktheit, reale Gewalt, Gore oder NSFL-Inhalte sind verboten – dennoch können Diskussionen und Beiträge für sensible Personen belastend sein. +

+

+ Wenn du mit solchen Inhalten nicht konfrontiert werden möchtest, verlasse die Seite jetzt. Durch das Fortsetzen erklärst du dich damit einverstanden, dass du die Inhalte auf eigene Verantwortung nutzt. +

+
+ + +
+
+

Kontrollverlust

diff --git a/templates/entry/feed.html b/templates/entry/feed.html index 397b261..e15e8b8 100644 --- a/templates/entry/feed.html +++ b/templates/entry/feed.html @@ -1,23 +1,29 @@ {% for p in posts %}
-
- {{ p[6] }} - {{ p[3] | timestamp }}
-
+
{{ p[6] }} - {{ p[3] | timestamp }} {{ p[1] }}

{{ p[4] }}

{% if p[5] != 'none' %} - + {% endif %} +
-
+
{% endfor %} -
+ hx-swap="outerHTML" +>

Loading more...

diff --git a/templates/entry/interactions.html b/templates/entry/interactions.html new file mode 100644 index 0000000..cdc927a --- /dev/null +++ b/templates/entry/interactions.html @@ -0,0 +1,3 @@ + +{{stats}} +