v 0.0.1
This commit is contained in:
172
app/routes.py
Normal file
172
app/routes.py
Normal file
@@ -0,0 +1,172 @@
|
||||
from datetime import datetime, timezone
|
||||
import os
|
||||
import re
|
||||
from typing import cast, List
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
abort,
|
||||
current_app,
|
||||
flash,
|
||||
make_response,
|
||||
redirect,
|
||||
render_template,
|
||||
request,
|
||||
send_from_directory,
|
||||
url_for,
|
||||
)
|
||||
import flask_login
|
||||
import pytz
|
||||
from sqlalchemy.sql import expression
|
||||
|
||||
from .auth import check_password, user_loader
|
||||
from .forms import DeckForm, LoginForm, PasswordForm
|
||||
from .models import Deck, Password, db
|
||||
from .utils import (
|
||||
generate_token,
|
||||
get_available_decks,
|
||||
get_token,
|
||||
get_unlocked_decks,
|
||||
serialize_token,
|
||||
is_deck_unlocked,
|
||||
update_token,
|
||||
generate_password,
|
||||
)
|
||||
|
||||
main = Blueprint('main', __name__)
|
||||
slidev_slide_pattern = re.compile(r'\d+$')
|
||||
|
||||
@main.route('/')
|
||||
def index():
|
||||
token = get_token()
|
||||
if not token:
|
||||
token = generate_token()
|
||||
resp = make_response(render_template('index.html', decks=get_unlocked_decks(token), user_name=token.name))
|
||||
resp.set_cookie('access_token', serialize_token(token), httponly=True)
|
||||
return resp
|
||||
|
||||
@main.route('/login', methods=['POST', 'GET'])
|
||||
def login():
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
assert isinstance(form.user_name.data, str)
|
||||
assert isinstance(form.password.data, str)
|
||||
user_name: str = form.user_name.data
|
||||
password: str = form.password.data
|
||||
user = user_loader(user_name)
|
||||
if user and check_password(user, password):
|
||||
flask_login.login_user(user)
|
||||
flash('Login successful!', 'success')
|
||||
|
||||
next = request.args.get('next')
|
||||
if next is None:
|
||||
return redirect(url_for('main.index'))
|
||||
|
||||
if not next.startswith('/admin'):
|
||||
return abort(400)
|
||||
|
||||
return redirect(next)
|
||||
else:
|
||||
flash('Incorrect username or password. Please try again.', 'error')
|
||||
return render_template("login.html", form=form)
|
||||
|
||||
|
||||
@main.route('/access', methods=['POST'])
|
||||
def access():
|
||||
password = db.session.execute(db.select(Password).where(Password.value == request.form.get('password'))).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('/slides/<deck>/')
|
||||
@main.route('/slides/<deck>/<path:path>')
|
||||
def serve_deck(deck, path='index.html'):
|
||||
if not db.session.execute(db.select(Deck).where(Deck.deck==deck)).scalar():
|
||||
abort(404)
|
||||
|
||||
if not is_deck_unlocked(deck):
|
||||
return redirect('/')
|
||||
|
||||
slides_dir = current_app.config['SLIDES_DIR']
|
||||
deck_path = os.path.join(slides_dir, deck)
|
||||
if not os.path.exists(deck_path):
|
||||
return f"Deck '{deck}' not found.", 404
|
||||
|
||||
if slidev_slide_pattern.match(path):
|
||||
return send_from_directory(deck_path, 'index.html')
|
||||
|
||||
requested_file = os.path.join(deck_path, path)
|
||||
if os.path.isfile(requested_file):
|
||||
return send_from_directory(deck_path, path)
|
||||
|
||||
abort(404)
|
||||
|
||||
@main.route('/admin', methods=['GET', 'POST'])
|
||||
@flask_login.login_required
|
||||
def admin():
|
||||
deck_form = DeckForm()
|
||||
password_form = PasswordForm()
|
||||
password_form.value.data = generate_password()
|
||||
deck_form.deck.choices = get_available_decks(current_app)
|
||||
decks = db.session.scalars(db.select(Deck).order_by(Deck.name)).all()
|
||||
passwords = db.session.scalars(db.select(Password)).all()
|
||||
password_form.decks.choices = [(deck.id, deck.name) for deck in decks]
|
||||
|
||||
|
||||
if request.method == 'POST':
|
||||
if "submit_deck" in request.form and deck_form.validate():
|
||||
deck = Deck(
|
||||
name=cast(str,deck_form.name.data),
|
||||
deck=deck_form.deck.data,
|
||||
description=cast(str,deck_form.description.data),
|
||||
)
|
||||
db.session.add(deck)
|
||||
db.session.commit()
|
||||
|
||||
if "submit_password" in request.form:
|
||||
if password_form.validate():
|
||||
value: str = password_form.value.data
|
||||
expires_at: datetime | None = password_form.expires_at.data
|
||||
if expires_at:
|
||||
tz = pytz.timezone('Europe/Berlin')
|
||||
utc = pytz.utc
|
||||
expires_at = tz.localize(expires_at).astimezone(utc)
|
||||
password = Password(
|
||||
value=value,
|
||||
expires_at=expires_at
|
||||
)
|
||||
decks = db.session.execute(db.select(Deck).filter(Deck.id.in_(cast(List[Deck], password_form.decks.data)))).scalars()
|
||||
password.decks.extend(decks)
|
||||
db.session.add(password)
|
||||
db.session.commit()
|
||||
else:
|
||||
# Form was submitted but validation failed
|
||||
for field, errors in password_form.errors.items():
|
||||
for error in errors:
|
||||
print(f"Error in {getattr(password_form, field).label.text}: {error}", flush=True)
|
||||
return redirect(url_for('main.admin'))
|
||||
|
||||
return render_template("admin.html", deck_form=deck_form, password_form=password_form, decks=decks, passwords=passwords)
|
||||
|
||||
|
||||
@main.route('/admin/decks/<int:deck_id>/delete', methods=['POST'])
|
||||
@flask_login.login_required
|
||||
def delete_deck(deck_id: int):
|
||||
deck = Deck.query.get_or_404(deck_id)
|
||||
db.session.delete(deck)
|
||||
db.session.commit()
|
||||
flash(f'Deck "{deck.name}" deleted.', 'success')
|
||||
return redirect(url_for('main.admin'))
|
||||
|
||||
@main.route('/admin/passwords/<int:password_id>/delete', methods=['POST'])
|
||||
@flask_login.login_required
|
||||
def delete_password(password_id: int):
|
||||
password = Password.query.get_or_404(password_id)
|
||||
db.session.delete(password)
|
||||
db.session.commit()
|
||||
flash(f'Password "{password.value}" deleted.', 'success')
|
||||
return redirect(url_for('main.admin'))
|
||||
Reference in New Issue
Block a user