215 lines
6.1 KiB
Python
215 lines
6.1 KiB
Python
from datetime import datetime
|
|
|
|
import flask_login
|
|
from flask_socketio import join_room
|
|
|
|
from flask import (
|
|
Blueprint,
|
|
current_app, # Used for logging
|
|
flash,
|
|
make_response,
|
|
redirect,
|
|
render_template,
|
|
request,
|
|
url_for,
|
|
)
|
|
|
|
from . import socketio
|
|
|
|
from .auth import user_loader
|
|
from .models import Password, db
|
|
from .utils import (
|
|
create_token,
|
|
get_active_banner,
|
|
get_all_courses,
|
|
get_token,
|
|
get_unlocked_decks,
|
|
retrieve_token,
|
|
update_token,
|
|
get_course,
|
|
token_required,
|
|
set_cookie,
|
|
)
|
|
|
|
|
|
main = Blueprint("main", __name__)
|
|
|
|
|
|
@socketio.on("connect")
|
|
def on_connect():
|
|
if flask_login.current_user.is_authenticated:
|
|
join_room("instructor")
|
|
current_app.logger.info(
|
|
f"Instructor connected to socket: {flask_login.current_user.id}"
|
|
)
|
|
|
|
|
|
@main.route("/")
|
|
@token_required
|
|
def index(token):
|
|
current_app.logger.debug(
|
|
f"User {token.name} is viewing index with {len(token.decks)} decks unlocked."
|
|
)
|
|
return render_template(
|
|
"index.html",
|
|
active_banner=get_active_banner(),
|
|
decks=get_unlocked_decks(token),
|
|
user_name=token.name,
|
|
active_course_name=token.active_course.name,
|
|
courses=get_all_courses(),
|
|
)
|
|
|
|
|
|
@main.route("/set_token", methods=["POST", "GET"])
|
|
def set_token():
|
|
"""
|
|
Handles.
|
|
GET: Renders the registration form.
|
|
POST: Processes the form submission.
|
|
"""
|
|
if request.method == "POST":
|
|
# Get form data
|
|
username = request.form.get("username")
|
|
|
|
# Simple validation
|
|
if not username:
|
|
flash("Username is required.", "error")
|
|
return render_template("set_token.html")
|
|
|
|
if user_loader(username):
|
|
current_app.logger.info(
|
|
f"Admin username '{username}' entered in set_token. Redirecting to admin login."
|
|
)
|
|
return redirect(url_for("admin.admin_login"))
|
|
|
|
token = retrieve_token(username)
|
|
|
|
if not token:
|
|
flash(f'The username "{username}" does not exist.', "error")
|
|
current_app.logger.warning(
|
|
f"Login failed: Username '{username}' not found."
|
|
)
|
|
return render_template("set_token.html")
|
|
|
|
current_app.logger.info(f"User logged in: {username}")
|
|
return set_cookie(token)
|
|
|
|
return render_template("set_token.html")
|
|
|
|
|
|
@main.route("/register", methods=["POST", "GET"])
|
|
def register():
|
|
"""
|
|
Handles user registration.
|
|
GET: Renders the registration form.
|
|
POST: Processes the form submission.
|
|
"""
|
|
if request.method == "POST":
|
|
# Get form data
|
|
user_name = request.form.get("username")
|
|
acknowledge = request.form.get("acknowledge")
|
|
|
|
# Simple validation
|
|
if not user_name:
|
|
flash("Username is required.", "error")
|
|
current_app.logger.debug("Username is required for registering")
|
|
return render_template("register.html")
|
|
|
|
if retrieve_token(user_name):
|
|
flash(f'The username "{user_name}" is already taken.', "error")
|
|
current_app.logger.debug(f"Username {user_name} is already registered")
|
|
return render_template("register.html")
|
|
|
|
if not acknowledge:
|
|
flash("You must acknowledge the important information to proceed.", "error")
|
|
current_app.logger.debug("User did not acknowledge register information")
|
|
return render_template("register.html")
|
|
|
|
# If all validation passes, "register" the user
|
|
token = create_token(user_name)
|
|
current_app.logger.info(f"New user registered: {user_name}")
|
|
|
|
return set_cookie(token)
|
|
|
|
# If GET request, render the form
|
|
return render_template("register.html")
|
|
|
|
|
|
@main.route("/logout", methods=["POST"])
|
|
def logout():
|
|
token = get_token()
|
|
user_name = token.name if token else "Unknown"
|
|
|
|
if flask_login.current_user.is_authenticated:
|
|
current_app.logger.info(f"Admin logout: {flask_login.current_user.id}")
|
|
flask_login.logout_user()
|
|
|
|
current_app.logger.info(f"User logout: {user_name}")
|
|
|
|
resp = make_response(redirect(url_for("main.index")))
|
|
resp.set_cookie("access_token", "", expires=0)
|
|
return resp
|
|
|
|
|
|
@main.route("/switch_course/<int:new_course>", methods=["POST"])
|
|
@token_required
|
|
def switch_course(new_course: int, token):
|
|
switched = get_course(new_course)
|
|
|
|
if switched:
|
|
token.active_course = switched
|
|
db.session.commit()
|
|
current_app.logger.info(
|
|
f"User {token.name} switched to course: {switched.name}"
|
|
)
|
|
else:
|
|
current_app.logger.warning(
|
|
f"User {token.name} attempted to switch to invalid course ID: {new_course}"
|
|
)
|
|
|
|
return redirect(url_for("main.index"))
|
|
|
|
|
|
def process_access_code(access_code: str, token):
|
|
password = db.session.execute(
|
|
db.select(Password).where(Password.value == access_code)
|
|
).scalar_one_or_none()
|
|
|
|
if password is not None:
|
|
if not password.is_expired():
|
|
update_token(password.decks)
|
|
current_app.logger.info(f"User {token.name} used valid access code.")
|
|
else:
|
|
current_app.logger.info(f"User {token.name} used expired access code.")
|
|
flash("Password has expired. Please obtain a new one.", "expired")
|
|
else:
|
|
current_app.logger.warning(
|
|
f"User {token.name} used invalid access code: {access_code}"
|
|
)
|
|
flash("Password is not valid. Please check the code and try again.", "error")
|
|
return redirect(url_for("main.index"))
|
|
|
|
|
|
@main.route("/access", methods=["POST"])
|
|
@token_required
|
|
def access_post(token):
|
|
access_code = request.form.get("password")
|
|
|
|
if not access_code:
|
|
return redirect(url_for("main.index"))
|
|
|
|
return process_access_code(access_code, token)
|
|
|
|
|
|
# Route for direct link/QR code access (GET)
|
|
@main.route("/access/<access_code>", methods=["GET"])
|
|
@token_required
|
|
def access_get(access_code: str, token):
|
|
return process_access_code(access_code, token)
|
|
|
|
|
|
@main.route("/confused", methods=["POST"])
|
|
def confused():
|
|
socketio.emit("confused", {"time": datetime.utcnow().isoformat()}, to="instructor")
|
|
return ("", 204)
|