added basic functionality

This commit is contained in:
2025-04-29 18:57:59 +02:00
parent daa64e62ac
commit 7f5d64b45b
3 changed files with 89 additions and 8 deletions

View File

@@ -30,14 +30,29 @@ from .utils import (
get_token, get_token,
get_unlocked_decks, get_unlocked_decks,
is_deck_unlocked, is_deck_unlocked,
retrieve_token,
serialize_token, serialize_token,
update_token, update_token,
) )
main = Blueprint('main', __name__) main = Blueprint('main', __name__)
slidev_deck_pattern = re.compile(r"^\/slides\/[^\/]+\/\d*(?:\?.*)?$") slidev_deck_pattern = re.compile(r"^\/slides\/[^\/]+\/\d*(?:\?.*)?$")
slidev_all_pattern = re.compile(r"^\/slides\/[^\/]+(?:\/presenter)?\/\d+(?:\?.*)?$") slidev_all_pattern = re.compile(r"^\/slides\/[^\/]+(?:\/presenter)?\/\d+(?:\?.*)?$")
@main.before_request
def ensure_cookie():
token = get_token()
if token:
return
else:
token = generate_token()
resp = make_response(redirect(request.url))
resp.set_cookie('access_token', serialize_token(token), httponly=True)
return resp
@main.after_request @main.after_request
def inject_confused_button(response): def inject_confused_button(response):
if True: if True:
@@ -81,15 +96,15 @@ def inject_confused_button(response):
response.set_data(body) response.set_data(body)
return response return response
@main.route('/') @main.route('/')
def index(): def index():
token = get_token() token = get_token()
if not token: if not token:
token = generate_token() abort(500)
resp = make_response(render_template('index.html', active_banner=get_active_banner(), decks=get_unlocked_decks(token), user_name=token.name)) else:
resp.set_cookie('access_token', serialize_token(token), httponly=True) return render_template('index.html', active_banner=get_active_banner(), decks=get_unlocked_decks(token), user_name=token.name)
return resp
@main.route('/login', methods=['POST', 'GET']) @main.route('/login', methods=['POST', 'GET'])
def login(): def login():
@@ -127,12 +142,34 @@ def access():
return redirect('/') return redirect('/')
return render_template('index.html', error="Invalid password.", decks=get_unlocked_decks(token)) return render_template('index.html', error="Invalid password.", decks=get_unlocked_decks(token))
@main.route('/confused', methods=['POST'])
@main.route('/access/<access_code>', methods=['GET'])
def access_link(access_code: str):
password = db.session.execute(db.select(Password).where(Password.value == access_code)).scalar()
assert isinstance(password, Password|None)
token = get_token()
if password is not None and not password.is_expired():
update_token(password.decks)
return redirect('/')
return render_template('index.html', error="Invalid password.", decks=get_unlocked_decks(token))
@main.route('/confused', methods=['POST'])
def confused(): def confused():
resp = jsonify(success=True) resp = jsonify(success=True)
return resp return resp
@main.route('/retrieve-token', methods=['POST'])
def set_username_token():
new_username : str = request.form.get("user_name", "").strip()
resp = make_response(redirect('/'))
if new_username:
token = retrieve_token(new_username)
if token:
resp.set_cookie('access_token', serialize_token(token), httponly=True)
return resp
@main.route('/slides/<deck>/') @main.route('/slides/<deck>/')
@main.route('/slides/<deck>/<path:path>') @main.route('/slides/<deck>/<path:path>')

View File

@@ -36,7 +36,32 @@
<!-- Header --> <!-- Header -->
<header class="bg-surface border-b border-border shadow-sm"> <header class="bg-surface border-b border-border shadow-sm">
<div class="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between"> <div class="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between">
<div class="text-sm text-muted font-medium">{{ user_name }}</div> <!-- Editable Username Form -->
<form method="POST" action="/retrieve-token" class="flex items-center space-x-2">
<input
type="text"
name="user_name"
value="{{ user_name }}"
class="bg-transparent text-muted text-sm font-medium px-2 py-1 rounded-md border border-transparent focus:border-primary focus:outline-none focus:bg-background"
readonly
id="usernameInput"
/>
<button
type="button"
onclick="enableUsernameEdit()"
class="text-sm text-primary hover:underline"
id="editBtn"
>
Edit
</button>
<button
type="submit"
class="hidden text-sm text-white bg-primary px-3 py-1 rounded-md hover:bg-blue-700"
id="saveBtn"
>
Save
</button>
</form>
<div class="text-lg font-semibold">GRNVS Tutorium</div> <div class="text-lg font-semibold">GRNVS Tutorium</div>
<a href="/admin"> <a href="/admin">
<button class="bg-primary hover:bg-blue-700 text-white px-4 py-2 rounded-md transition"> <button class="bg-primary hover:bg-blue-700 text-white px-4 py-2 rounded-md transition">
@@ -104,5 +129,19 @@
</main> </main>
<script src="https://cdn.jsdelivr.net/npm/flowbite@3.1.2/dist/flowbite.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/flowbite@3.1.2/dist/flowbite.min.js"></script>
<script>
function enableUsernameEdit() {
const input = document.getElementById('usernameInput');
const editBtn = document.getElementById('editBtn');
const saveBtn = document.getElementById('saveBtn');
input.removeAttribute('readonly');
input.classList.add('border-border');
input.focus();
editBtn.classList.add('hidden');
saveBtn.classList.remove('hidden');
}
</script>
</body> </body>
</html> </html>

View File

@@ -1,6 +1,6 @@
import os import os
from flask import Flask, request from flask import Flask, request, session
from typing import cast from typing import cast
from itsdangerous import BadSignature, URLSafeSerializer from itsdangerous import BadSignature, URLSafeSerializer
from xkcdpass import xkcd_password from xkcdpass import xkcd_password
@@ -28,6 +28,7 @@ def get_available_decks(app: Flask):
def generate_username() -> str: def generate_username() -> str:
return cast(str, xkcd_password.generate_xkcdpassword(wordlist=words, numwords=3, delimiter="-")) return cast(str, xkcd_password.generate_xkcdpassword(wordlist=words, numwords=3, delimiter="-"))
def generate_password() -> str: def generate_password() -> str:
return cast(str, xkcd_password.generate_xkcdpassword(wordlist=words, numwords=5, delimiter="-")) return cast(str, xkcd_password.generate_xkcdpassword(wordlist=words, numwords=5, delimiter="-"))
@@ -60,6 +61,10 @@ def get_token() -> (Token | None):
except BadSignature: except BadSignature:
return None return None
def retrieve_token(username : str):
return db.session.execute(db.select(Token).filter_by(name=username)).scalar_one_or_none()
def get_unlocked_decks(token: (Token|None)): def get_unlocked_decks(token: (Token|None)):
if token is None: if token is None:
return [] return []