This commit is contained in:
Lera Elvoé 2025-06-29 23:54:29 +03:00
parent fb2a96e94d
commit bd56310067
Signed by: yagich
SSH Key Fingerprint: SHA256:6xjGb6uA7lAVcULa7byPEN//rQ0wPoG+UzYVMfZnbvc
4 changed files with 81 additions and 11 deletions

View File

@ -1,7 +1,7 @@
from flask import Blueprint from flask import Blueprint, redirect, url_for
bp = Blueprint("app", __name__, url_prefix = "/") bp = Blueprint("app", __name__, url_prefix = "/")
@bp.route("/") @bp.route("/")
def hello_world(): def index():
return f"<img src='static/avatars/default.webp'></img>" return redirect(url_for("topics.all_topics"))

View File

@ -2,10 +2,11 @@ from flask import (
Blueprint, render_template, request, redirect, url_for, flash, session Blueprint, render_template, request, redirect, url_for, flash, session
) )
from ..models import Users, Sessions from ..models import Users, Sessions
from ..constants import InfoboxKind from ..constants import InfoboxKind, PermissionLevel
from ..auth import digest, verify from ..auth import digest, verify
import secrets import secrets
import time import time
import re
bp = Blueprint("users", __name__, url_prefix = "/users/") bp = Blueprint("users", __name__, url_prefix = "/users/")
@ -23,6 +24,24 @@ def get_active_user():
return Users.find({"id": sess.user_id}) return Users.find({"id": sess.user_id})
def create_session(user_id):
return Sessions.create({
"key": secrets.token_hex(16),
"user_id": user_id,
"expires_at": int(time.time()) + 30 * 24 * 60 * 60,
})
def validate_password(password):
pattern = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_])(?!.*\s).{10,255}$'
return bool(re.fullmatch(pattern, password))
def validate_username(username):
pattern = r'^[a-zA-Z0-9_-]{3,20}$'
return bool(re.fullmatch(pattern, username))
@bp.get("/log_in") @bp.get("/log_in")
def log_in(): def log_in():
return render_template("users/log_in.html") return render_template("users/log_in.html")
@ -41,11 +60,7 @@ def log_in_post():
flash("Incorrect username or password.", InfoboxKind.ERROR) flash("Incorrect username or password.", InfoboxKind.ERROR)
return redirect(url_for("users.log_in")) return redirect(url_for("users.log_in"))
session_obj = Sessions.create({ session_obj = create_session(target_user.id)
"key": secrets.token_hex(16),
"user_id": target_user.id,
"expires_at": int(time.time()) + 30 * 24 * 60 * 60,
})
session['pyrom_session_key'] = session_obj.key session['pyrom_session_key'] = session_obj.key
flash("Logged in!", InfoboxKind.INFO) flash("Logged in!", InfoboxKind.INFO)
@ -54,7 +69,45 @@ def log_in_post():
@bp.get("/sign_up") @bp.get("/sign_up")
def sign_up(): def sign_up():
return "not yet" return render_template("users/sign_up.html")
@bp.post("/sign_up")
def sign_up_post():
username = request.form['username']
password = request.form['password']
password_confirm = request.form['password-confirm']
if not validate_username(username):
flash("Invalid username.", InfoboxKind.ERROR)
return redirect(url_for("users.sign_up"))
user_exists = Users.count({"username": username}) > 0
if user_exists:
flash(f"Username '{username}' is already taken.", InfoboxKind.ERROR)
return redirect(url_for("users.sign_up"))
if not validate_password(password):
flash("Invalid password.", InfoboxKind.ERROR)
return redirect(url_for("users.sign_up"))
if password != password_confirm:
flash("Passwords do not match.", InfoboxKind.ERROR)
return redirect(url_for("users.sign_up"))
hashed = digest(password)
new_user = Users.create({
"username": username,
"password_hash": hashed,
"permission": PermissionLevel.GUEST.value,
})
session_obj = create_session(new_user.id)
session['pyrom_session_key'] = session_obj.key
flash("Signed up successfully!", InfoboxKind.INFO)
return redirect(url_for("users.sign_up"))
@bp.get("/<username>") @bp.get("/<username>")

View File

@ -2,7 +2,7 @@
{% block title %}Log in{% endblock %} {% block title %}Log in{% endblock %}
{% block content %} {% block content %}
<div class="darkbg login-container"> <div class="darkbg login-container">
<h1>Log In</h1> <h1>Log in</h1>
<form method="post"> <form method="post">
<label for="username">Username</label><br> <label for="username">Username</label><br>
<input type="text" id="username" name="username" required autocomplete="username"><br> <input type="text" id="username" name="username" required autocomplete="username"><br>

View File

@ -0,0 +1,17 @@
{% extends 'base.html' %}
{% block title %}Sign up{% endblock %}
{% block content %}
<div class="darkbg login-container">
<h1>Sign up</h1>
<form method="post">
<label for="username">Username</label><br>
<input type="text" id="username" name="username" pattern="[a-zA-Z0-9_-]{3,20}" title="3-20 characters. Only upper and lowercase letters, digits, hyphens, and underscores" required autocomplete="username"><br>
<label for="password">Password</label>
<input type="password" id="password" name="password" pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_])(?!.*\s).{10,255}" title="10+ chars with: 1 uppercase, 1 lowercase, 1 number, 1 special char, and no spaces" required autocomplete="new-password"><br>
<label for="password-confirm">Confirm Password</label>
<input type="password" id="password-confirm" name="password-confirm" pattern="(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_])(?!.*\s).{10,255}" title="10+ chars with: 1 uppercase, 1 lowercase, 1 number, 1 special char, and no spaces" required autocomplete="new-password"><br>
<input type="submit" value="Sign up">
</form>
<span>After you sign up, a moderator will need to confirm your account before you will be allowed to post.</span>
</div>
{% endblock %}