from flask import Blueprint, render_template, request, flash, redirect, url_for, current_app from flask_mail import Message from itsdangerous import URLSafeTimedSerializer, SignatureExpired, BadSignature from app.extensions import db, mail from app.models import Participant bp = Blueprint('main', __name__) def get_confirmed_count(): return Participant.query.filter_by(confirmed=True).count() def send_verification_email(user_email): s = URLSafeTimedSerializer(current_app.config['SECRET_KEY']) token = s.dumps(user_email, salt='email-confirm-h4tum-ctf-2025') link = url_for('main.verify_email', token=token, _external=True) #msg = Message('Verify your CTF Registration', recipients=["fdohq9ipugtc@mockmail.io"]) #msg.body = f'Click here to confirm your spot: {link}\nThanks for registering {user_email}' print(link) @bp.route('/', methods=['GET', 'POST']) def index(): confirmed_count = get_confirmed_count() max_spots = current_app.config['MAX_SPOTS'] spots_left = max_spots - confirmed_count if request.method == 'POST': name = request.form['name'] email = request.form['email'].lower().strip() looking_for_team = True if request.form.get('looking_for_team') else False tum_domain = current_app.config['TUM_DOMAIN'] if not email.endswith(f"@{tum_domain}"): flash(f"Error: You must use a @{tum_domain} email address.", "error") return redirect(url_for('main.index')) if confirmed_count >= max_spots: flash("Sorry, the event is full!", "error") return redirect(url_for('main.index')) existing_user = Participant.query.filter_by(email=email).first() if existing_user: if existing_user.confirmed: flash("You are already registered and confirmed!", "info") else: flash("Confirmation email resent. Please check your inbox.", "info") send_verification_email(email) return redirect(url_for('main.index')) new_user = Participant(name=name, email=email, looking_for_team=looking_for_team) db.session.add(new_user) db.session.commit() send_verification_email(email) flash("Registration started! Check your email to confirm your spot.", "success") return redirect(url_for('main.index')) return render_template('index.html', spots_left=spots_left) @bp.route('/verify/') def verify_email(token): s = URLSafeTimedSerializer(current_app.config['SECRET_KEY']) try: email = s.loads(token, salt='email-confirm-h4tum-ctf-2025', max_age=3600) except SignatureExpired: return '

Token expired. Please register again.

' except BadSignature: return '

Invalid token.

' user = Participant.query.filter_by(email=email).first_or_404() if get_confirmed_count() >= current_app.config['MAX_SPOTS']: return '

Sorry! The event filled up while you were verifying.

' user.confirmed = True db.session.commit() discord_link = current_app.config['DISCORD_LINK'] discord_token = user.discord_token msg = "You are confirmed!" if user.looking_for_team: msg += " Since you need a team, please join the #looking-for-group channel." return render_template('success.html', message=msg, discord_link=discord_link, discord_token=discord_token) @bp.route('/dsgvo') def dsgvo(): return render_template('dsgvo.html') @bp.route('/impressum') def impressum(): return render_template('impressum.html') @bp.route('/rules') def rules(): return render_template('rules.html')