Alten Flask/Python-Code und Jinja-Templates entfernt

Go-Reimplementierung hat Feature-Paritaet (inkl. URL-Linkify im
JS-Frontend via linkify()). static/ bleibt (Go liefert /static/* aus
und legt Uploads unter static/media/ ab); alte Flask-Assets dort
unberuehrt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 11:37:47 +02:00
parent e210df3e2e
commit 2ab08ebf9a
26 changed files with 0 additions and 888 deletions
-3
View File
@@ -1,3 +0,0 @@
from flask import Flask
app = Flask(__name__)
-215
View File
@@ -1,215 +0,0 @@
from flask import render_template, request, make_response, redirect
import database, re, bcrypt, secrets, time
import user_controller
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("<script>location.href = '/';</script>")
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.")
if user_controller.id_from_username(username) != 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("Passwort und Passwortbestätigung sind ungleich.")
if len(pass1) < 10:
success = False
responses.append("Das Passwort ist kürzer als 10 Zeichen.")
if not success:
return render_template("auth/register.html",
responses=responses,
username=username)
user_controller.new(username, pass1)
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
-25
View File
@@ -1,25 +0,0 @@
import sqlite3
def execute(query, data):
connection = sqlite3.connect("kver.db")
cursor = connection.cursor()
cursor.execute(query, data)
connection.commit()
connection.close()
def fetch(query, data, many=True):
connection = sqlite3.connect("kver.db")
cursor = connection.cursor()
result = cursor.execute(query, data)
if many:
result = result.fetchall()
else:
result = result.fetchone()
connection.close()
return result
-150
View File
@@ -1,150 +0,0 @@
from flask import render_template, request
from PIL import Image
# import database, re, bcrypt, secrets, time
import secrets, time, os, database
from auth import get_session
from app import app
@app.route("/entry/<pid>/interactions/<mode>", 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/<page>", methods=['GET'])
def feed(page):
page = int(page, 0)
page = max(0,page)
page = min(100,page)
count = 20
offset = page*count
posts = database.fetch("SELECT * FROM entry ORDER BY created_at DESC LIMIT ? OFFSET ?", [count, offset])
if len(posts) <= 0:
return """<div id="infinite-scroll-indicator">
<h2>YOU REACHED THE END!</h2>
</div>"""
posts_with_usernames = []
for post in posts:
username = database.fetch("SELECT username FROM user WHERE uid = ?", [post[2]])
posts_with_usernames.append(post + username[0])
return render_template("entry/feed.html", posts=posts_with_usernames, page=page)
@app.route("/entry/create", methods=['GET', 'POST'])
def newentry():
session = get_session(request)
if not session:
return "Melde dich an, um einen Beitrag zu erstellen!"
uid = session[0]
if request.method == 'GET':
return render_template('entry/create.html')
content = str(request.form["content"])
content = content.strip()
content = content[:1000]
fileattached = False
if 'file' in request.files:
file = request.files['file']
fileattached=True
if fileattached:
fileattached = _storeimage(file)
if not fileattached and content == '':
return render_template('entry/create.html', responses=["Leerer Beitrag!"])
if not fileattached:
fileattached = "none"
pid = secrets.randbelow(999999999999)
now = int(time.time())
database.execute("""INSERT INTO entry
(pid, uid, created_at, content, filepath)
VALUES (?, ?, ?, ?, ?)""",
(pid, uid, now, content, fileattached))
return render_template('entry/create.html', responses=[content, str(fileattached)])
def _storeimage(file):
maxsize = 1024
tmpfile = secrets.randbelow(999999)
tmppath = f"tmp/{tmpfile}"
import resizegif
try:
file.save(tmppath)
i = Image.open(tmppath)
if i.format == 'GIF':
filetype = 'gif'
else:
i = i.convert("RGB")
filetype = 'jpg'
newpath = f"static/media/{int(time.time())}-{tmpfile}.{filetype}"
(width, height) = i.size;
aspect = width/height
if width < maxsize and height < maxsize:
if filetype == 'gif':
resizegif.resize_gif(i, newpath, (width, height))
else:
i.save(newpath)
os.remove(tmppath)
return newpath
if width > height:
width = maxsize
height = maxsize/aspect
else:
height = maxsize
width = aspect*maxsize
if filetype == 'gif':
resizegif.resize_gif(i, newpath, (width, height))
else:
i.thumbnail((width, height), Image.Resampling.LANCZOS)
i.save(newpath)
os.remove(tmppath)
return newpath
except Exception as e:
print(e)
os.remove(tmppath)
return False
-55
View File
@@ -1,55 +0,0 @@
import os
from flask import Flask, request, render_template
from datetime import datetime
from werkzeug.exceptions import HTTPException
from app import app
app.config['UPLOAD_FOLDER'] = "uploads/"
@app.errorhandler(400)
def handle_bad_request(e):
print(f"400 BAD REQUEST: {e}")
return '400<br>BAD REQUEST'
@app.template_filter('timestamp')
def timestamp_filter(value):
try:
dt = datetime.fromtimestamp(value)
return dt.strftime("%d.%m.%Y %H:%M")
except:
return value
import html, re
@app.template_filter('content')
def content_filter(value):
value = value.strip()
value = html.escape(value)
# Regulärer Ausdruck, um URLs zu finden
url_pattern = re.compile(
r'https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:;%_\+.~#?&//=]*)'
)
# Ersetze jede gefundene URL durch einen <a>-Tag
def replace_url(match):
url = match.group(0)
if not url.startswith(('http://', 'https://')):
url = 'http://' + url
return f'<a href="{url}" target="_blank">{match.group(0)}</a>'
value = url_pattern.sub(replace_url, value)
value = value.replace('\n', '<br>')
return value
import auth
import entry
import user
@app.route("/")
def template():
return render_template("index.html", name="Hmmm")
if __name__ == "__main__":
app.run(debug=True)
-44
View File
@@ -1,44 +0,0 @@
# Source - https://stackoverflow.com/a
# Posted by brvoisin
# Retrieved 2025-12-13, License - CC BY-SA 4.0
"""
# Resize an animated GIF
Inspired from https://gist.github.com/skywodd/8b68bd9c7af048afcedcea3fb1807966
Useful links:
* https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#saving
* https://stackoverflow.com/a/69850807
Example:
```
python resize_gif.py input.gif output.gif 400,300
```
"""
import sys
from PIL import Image
from PIL import ImageSequence
def resize_gif(input_image, output_path, max_size):
frames = list(_thumbnail_frames(input_image, max_size))
output_image = frames[0]
output_image.save(
output_path,
save_all=True,
append_images=frames[1:],
disposal=input_image.disposal_method,
**input_image.info,
)
def _thumbnail_frames(image, max_size):
for frame in ImageSequence.Iterator(image):
new_frame = frame.copy()
new_frame.thumbnail(max_size, Image.Resampling.LANCZOS)
yield new_frame
if __name__ == "__main__":
max_size = [int(px) for px in sys.argv[3].split(",")] # "150,100" -> (150, 100)
resize_gif(sys.argv[1], sys.argv[2], max_size)
-41
View File
@@ -1,41 +0,0 @@
import smtplib, ssl, database, time
def send(subject, message, recipient, type):
hour_ago = int(time.time()) - 3600
count = database.fetch("SELECT COUNT(*) FROM email WHERE time > ?", [hour_ago])
if count[0][0] > 5:
return False
database.execute("INSERT INTO email (recipient, time, type) VALUES (?, ?, ?)", [
recipient,
int(time.time()),
type
])
smtp_server = "webspace29.do.de"
port = 587 # For starttls
sender_email = "system@bnd.wtf"
password = ""
message = f"""From: system@bnd.wtf\nSubject: {subject}
{message}"""
# Create a secure SSL context
context = ssl.create_default_context()
# Try to log in to server and send email
try:
server = smtplib.SMTP(smtp_server,port)
server.ehlo() # Can be omitted
server.starttls(context=context) # Secure the connection
server.ehlo() # Can be omitted
server.login(sender_email, password)
server.sendmail(sender_email, recipient, message)
# TODO: Send email here
except Exception as e:
# Print any error messages to stdout
print(e)
finally:
server.quit()
return True
-17
View File
@@ -1,17 +0,0 @@
<div id="headerbar">
<div class="headerbar-left">
{% if loggedin %}
<!--button hx-get="/auth/sessioninfo" hx-target="#main-frame">Username</button-->
<button onclick="if(confirm('Abmelden?')){location.href = '/auth/logout';}">Abmelden</button>
{% else %}
<button hx-get="/auth/login" hx-target="#main-frame">Anmelden</button>
<button hx-get="/auth/newuser" hx-target="#main-frame">Neuen Account erstellen</button>
{% endif %}
</div>
<div class="headerbar-right">
<a href="/static/docs/impressum.html">Impressum</a>
<a href="/static/docs/beitragsordnung.html">Beitragsordnung</a>
<a href="/static/docs/datenschutz.html">Datenschutz</a>
<a href="/static/docs/kontakt.html">Kontakt</a>
</div>
</div>
-17
View File
@@ -1,17 +0,0 @@
<h2>Einloggen</h2>
<form hx-post="/auth/login" hx-target="#main-frame">
<input type="text" name="user" placeholder="Nutzername" value="{{ username }}"><br>
<input type="password" name="pass" placeholder="Passphrase"><br>
<label>Automatisch abmelden in:</label>
<select name="timeout">
<option value="1200">20 Minuten</option>
<option value="3600" selected="selected">1 Stunde</option>
<option value="18000">5 Stunden</option>
<option value="86400">24 Stunden</option>
<option value="31536000">1 Jahr</option>
</select>
<button type="submit">Anmelden</button>
<div>
{{response}}
</div>
</form>
-3
View File
@@ -1,3 +0,0 @@
<h1>Willkommen, {{username}}!</h1>
<button hx-get="/auth/logout" hx-swap="outerHTML">Abmelden...</button>
-15
View File
@@ -1,15 +0,0 @@
<h2>Neuen Account registrieren</h2>
<form hx-post="/auth/newuser" hx-target="#main-frame" method="post">
<input type="text" name="user" placeholder="Nutzername" value="{{ username }}"><br>
<input type="password" name="pass1" placeholder="Passphrase"><br>
<input type="password" name="pass2" placeholder="Passphrase wiederholen"><br>
<label><input type="checkbox" name="pass2"> Ich bin mit der <a href="">Datenschutzerklärung</a> einverstanden!</label>
<button type="submit">Account registrieren!</button>
<div>
{% for r in responses %}
{{ r }} <br>
{% endfor %}
</div>
</form>
-4
View File
@@ -1,4 +0,0 @@
<h2>Willkommen im Kontrollverlust!</h2>
<p>Du kannst dich nun als {{username}} <button hx-get="/auth/login" hx-target="#main-frame">Anmelden</button>
!</p>
-5
View File
@@ -1,5 +0,0 @@
Sessionid: {{ sessionid }} <br>
UID: {{ uid }} <br>
Created: {{ created_at | timestamp }} <br>
Expires: {{ expires | timestamp }} <br>
Description: {{ description }} <br>
-67
View File
@@ -1,67 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>{% block title %}{% endblock %}</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"
integrity="sha512-NhSC1YmyruXifcj/KFRWoC561YpHpc5Jtzgvbuzx5VozKpWvQ+4nXhPdFgmx8xqexRcpAglTj9sIBWINXa8x5w=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<link rel="stylesheet" href="/static/css/main.css" />
<script
src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"
integrity="sha384-/TgkGk7p307TH7EXJDuUlgG3Ce1UVolAOFopFekQkkXihi5u/6OCvVKyz1W+idaz"
crossorigin="anonymous"
></script>
<script
src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
crossorigin="anonymous"
></script>
<script src="/static/js/main.js"></script>
</head>
<body>
<div id="contentnotice">
<h1>Achtung!</h1>
<h3>Kontrollverlust</h3>
<div>
<p>Dieses Forum richtet sich ausschließlich an <strong>erwachsene Besucher:innen</strong>.</p>
<p>
Die Inhalte könnten <strong>explizite Sprache, anstößige Themen, gewalthaltige Äußerungen, sowie pornografische Darstellungen in Text-, Zeichnungs- oder angedeuteter Form</strong> enthalten.
</p>
<p>
<strong>Fotografische Nacktheit, reale Gewalt, Gore oder NSFL-Inhalte sind verboten</strong> dennoch können Diskussionen und Beiträge für sensible Personen belastend sein.
</p>
<p>
Wenn du mit solchen Inhalten nicht konfrontiert werden möchtest, <a href="https://optical.toys/">verlasse die Seite jetzt</a>. Durch das Fortsetzen erklärst du dich damit einverstanden, dass du die Inhalte auf eigene Verantwortung nutzt.
</p>
</div>
<button onclick="location.href='https://optical.toys/'" >Ich möchte lieber etwas anderes sehen!</button>
<button onclick="setCookie('agreedToWarning', 'True', '1'); $('#contentnotice').hide()" >Ich bin erwachsen und verstehe die Warnung weiter</button>
<br style="margin: 30px;">
</div>
<div hx-get="/auth/headerbar" hx-trigger="load"></div>
<div class="htmx-indicator" id="loading"></div>
<h1>Kontrollverlust</h1>
<br />
<span> Verbuggte scheise hier. Komm hack mich doch, trau dich! </span>
<br />
<span id="uptime-counter"></span>
<br />
<br />
<div id="main-frame" hx-indicator="#loading">
{% block content %}{% endblock %}
</div>
</body>
</html>
-15
View File
@@ -1,15 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Database</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
{% for tablename in tables %}
<a href="/database/{{ tablename[0] }}">{{ tablename[0] }}</a><br>
{% endfor %}
</body>
</html>
-9
View File
@@ -1,9 +0,0 @@
<table>
{% for row in data %}
<tr>
{% for column in row %}
<td>{{ column }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
-11
View File
@@ -1,11 +0,0 @@
<form hx-post="/entry/create" hx-target="#entry-rendertarget" method="post" enctype="multipart/form-data">
<textarea name="content"></textarea>
<input type="file" name="file">
<button type="submit">Beitrag erstellen!</button>
<div>
{% for r in responses %}
{{ r }} <br>
{% endfor %}
</div>
</form>
-31
View File
@@ -1,31 +0,0 @@
{% for p in posts %}
<div class="entry">
<div class="meta">{{ p[6] }} - {{ p[3] | timestamp }}<br /></div>
{% if p[4] != '' %}
<div class="content">
<p>{{ p[4] | content | safe}}</p>
</div>
{% endif %}
{% if p[5] != 'none' %}
<img src="/{{ p[5] }}" />
{% endif %}
<div
class="interactions"
id="int-{{p[1]}}"
hx-get="/entry/{{p[1]}}/interactions/none"
hx-trigger="load"
></div>
</div>
<hr />
{% endfor %}
<div
id="infinite-scroll-indicator"
hx-get="/entry/feed/{{page+1}}"
hx-trigger="revealed"
hx-swap="outerHTML"
>
<h2>Loading more...</h2>
</div>
-3
View File
@@ -1,3 +0,0 @@
<button hx-get="/entry/{{pid}}/interactions/left" hx-target="#int-{{pid}}">Links</button>
<span>{{stats}}</span>
<button hx-get="/entry/{{pid}}/interactions/right" hx-target="#int-{{pid}}">Rechts</button>
-6
View File
@@ -1,6 +0,0 @@
<button onclick="$('#auth-visible').show()" hx-get="/auth/login" hx-target="#auth-rendertarget">Anmelden</button>
<button onclick="$('#auth-visible').show()" hx-get="/auth/newuser" hx-target="#auth-rendertarget">Account erstellen</button>
<div id="auth-visible">
<div id="auth-rendertarget"></div>
<button onclick="$('#auth-visible').hide()">Abbrechen</button>
</div>
-17
View File
@@ -1,17 +0,0 @@
{% extends "baseof.html" %}
{% block title %}Kontrollverlust{% endblock %}
{% block content %}
<div id="auth-rendertarget"></div>
<hr>
<div hx-get="/entry/create" hx-trigger="load" id="entry-rendertarget"></div>
<hr>
<div hx-get="/entry/feed/0" hx-trigger="load" hx-indicator="none"></div>
{% endblock %}
-22
View File
@@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My new Site!</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Upload new File</h1>
<span>{{ state }}</span>
<form method="post" enctype="multipart/form-data">
<input type=file name=file>
<input type=submit value=Upload>
</form>
{% for id in imagedata %}
{{ id }}
{% endfor %}
</body>
</html>
-13
View File
@@ -1,13 +0,0 @@
<div id="deleteuser-rendertarget">
<h2>Account dauerhaft löschen</h2>
<form hx-post="/user/delete" hx-target="#deleteuser-rendertarget" method="post">
<input type="password" name="pass1" placeholder="Passphrase"><br>
<button type="submit">Account Löschen!</button>
<div>
{% for r in responses %}
{{ r }} <br>
{% endfor %}
</div>
</form>
</div>
-10
View File
@@ -1,10 +0,0 @@
{% extends "baseof.html" %}
{% block content %}
Username: {{ username }} <br>
ID: {{ uid }} <br>
Profil erstellt: {{ created_at | timestamp }} <br>
<div hx-get="/user/delete" hx-trigger="load"></div>
{% endblock %}
-27
View File
@@ -1,27 +0,0 @@
from flask import render_template, request, make_response, redirect
import database, re, bcrypt, secrets, time
from auth import get_session
from app import app
@app.route("/u/<username>")
def userpage(username):
return "Page of " + username
@app.route("/user/info")
def userinfo():
sessioninfo = get_session(request)
if not sessioninfo:
return "Melde dich an, um Einstellungen zu ändern!"
uid = sessioninfo[0]
userdata = database.fetch("SELECT created_at, last_login, username FROM user WHERE uid=?", [uid])[0]
return render_template("user/userinfo.html", username=userdata[2], uid=uid, created_at=userdata[0])
@app.route("/user/delete")
def userdelete():
return render_template("user/delete.html")
-63
View File
@@ -1,63 +0,0 @@
import database, re, bcrypt, secrets, time
def new(username, password):
uid = secrets.randbelow(99999999)
created_at = int(time.time())
passsalt = bcrypt.gensalt()
passhash = bcrypt.hashpw(password.encode("utf8"), passsalt)
database.execute("""INSERT INTO user
(uid, created_at, username, password) VALUES
(?, ?, ?, ?)""",
(uid, created_at, username, passhash))
return
def from_uid():
pass
def id_from_username(username):
query = database.fetch("SELECT id FROM user WHERE username=?", [username], False)
if query:
return query[0]
else:
return 0
def id_from_session(session):
sessioninfo = database.fetch("""SELECT uid, expires
FROM session
WHERE value=?;""",
[sessionid], False)
if not sessioninfo:
return 0
# if int(sessioninfo[2] > int(time.time())):
# return 0 # session expired
pass
def exists():
# count from database
pass
def authenticate(password):
# verify password with stored hash
pass
def get_info() -> dict:
pass
def set_username(newname):
# update username
pass
def set_password(newpass):
# update password
pass
def delete():
# delete database entry
pass