finished dev version

This commit is contained in:
2026-01-12 14:01:18 +01:00
parent 79c011a217
commit 462f8a688b
8 changed files with 111 additions and 41 deletions

View File

@@ -5,8 +5,10 @@ class Participant(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
num_emails_sent = db.Column(db.Integer, default=0)
looking_for_team = db.Column(db.Boolean, default=False)
confirmed = db.Column(db.Boolean, default=False)
on_waitlist = db.Column(db.Boolean, default=False)
discord_token = db.Column(db.String(16), unique=True)
discord_user_id = db.Column(db.String(50))

View File

@@ -9,14 +9,31 @@ bp = Blueprint('main', __name__)
def get_confirmed_count():
return Participant.query.filter_by(confirmed=True).count()
def send_verification_email(user_email):
def add_user(name, email, looking_for_team, on_waitlist = False):
new_user = Participant(name=name, email=email, looking_for_team=looking_for_team)
if on_waitlist:
new_user.on_waitlist = on_waitlist
db.session.add(new_user)
db.session.commit()
return new_user
def send_verification_email(user):
s = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
token = s.dumps(user_email, salt='email-confirm-h4tum-ctf-2025')
token = s.dumps(user.email, salt='email-confirm-h4tum-ctf-2025')
link = url_for('main.verify_email', token=token, _external=True)
logo = url_for('static', filename='h4tum_white.png', _external=True)
msg = Message('Verify your CTF Registration', recipients=[user_email])
msg.html = render_template("email.html", user_email=user_email, link=link, logo_url=logo)
msg = Message('Verify your CTF Registration', recipients=[user.email])
msg.html = render_template("email.html", user_name=user.name, link=link, logo_url=logo)
mail.send(msg)
user.num_emails_sent += 1
db.session.commit()
return True
def send_waitlist_email(user):
msg = Message('h4tum CTF 2026 Registration', recipients=[user.email])
logo = url_for('static', filename='h4tum_white.png', _external=True)
msg.html = render_template("waitlist_email.html", logo_url=logo, user_name=user.name)
mail.send(msg)
@bp.route('/', methods=['GET', 'POST'])
@@ -35,28 +52,34 @@ def index():
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")
elif existing_user.on_waitlist:
flash("You are already on the waitlist", "info")
else:
if existing_user.num_emails_sent < 5:
flash("Confirmation email resent. Please check your inbox.", "info")
send_verification_email(email)
send_verification_email(existing_user)
else:
flash("Email limit reached", "error")
return redirect(url_for('main.index'))
else:
new_user = add_user(name, email, looking_for_team)
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)
if confirmed_count < max_spots:
send_verification_email(new_user)
flash("Registration started! Check your email to confirm your spot.", "success")
else:
new_user.on_waitlist = True
db.session.commit()
send_waitlist_email(new_user)
flash("The event is already full!\nYou have been placed on the waitlist", "info")
return redirect(url_for('main.index'))
return render_template('index.html', spots_left=spots_left)
return render_template('index.html', spots_left=spots_left, max_spots=max_spots)
@bp.route('/verify/<token>')
def verify_email(token):

View File

@@ -45,6 +45,10 @@ footer {
margin: 0;
}
.status {
text-align: center;
}
h1 { font-size: 1.5rem; word-wrap: break-word; }

View File

@@ -20,7 +20,7 @@
<tr>
<td align="center" style="padding-bottom: 30px; font-size: 16px; line-height: 1.5; text-align: center;">
Thanks for registering <br>
<strong style="color: #00ff00;">{{ user_email }}</strong><br><br>
<strong style="color: #00ff00;">{{ user_name }}</strong><br><br>
To secure your spot at the event, please confirm your attendance by clicking the button below.
</td>
</tr>

View File

@@ -13,9 +13,9 @@
<hr style="border-color: #333; margin: 30px 0;"></hr>
<h1>> Prizes</h1>
<ul>
<li>1st Place: 100€</li>
<li>2nd Place: 75</li>
<li>3rd Place: 50</li>
<li>1st Place: 150€</li>
<li>2nd Place: 110</li>
<li>3rd Place: 75€</li>
</ul>
<hr style="border-color: #333; margin: 30px 0;"></hr>
<h1>> Format</h1>
@@ -76,10 +76,14 @@
{% endwith %}
<div class="status">
<h2>Spots Remaining: {{ spots_left }} / 84</h2>
{% if spots_left > 0 %}
<h2>Spots Remaining: {{ spots_left }} / {{max_spots}}</h2>
{% else %}
<h2>Register for Waitlist</h2>
<p>This event is currently full. Join the queue to be notified of openings.</p>
{%endif %}
</div>
{% if spots_left > 0 %}
<form method="POST">
<label>Name</label>
<input type="text" name="name" required>
@@ -94,8 +98,5 @@
<button type="submit">INITIALIZE_REGISTRATION</button>
</form>
{% else %}
<h2 style="color:red">EVENT FULL</h2>
{% endif %}
</div>
{% endblock %}

View File

@@ -15,6 +15,7 @@
<li>If something feels unintentionally easy, please report it.</li>
<li>Do not share flags or solutions with other teams. We will not tolerate flag sharing.</li>
<li>Each team may register only once, and each person may belong to only one team.</li>
<li>A team has at maximum four members</li>
<li>Only TUM Students are permitted to participate in the CTF.</li>
<li>Attacking personnel, including participants and non-participants, or the competition system itself is forbidden.</li>
<li>Every participant has to follow the Code of Conduct.</li>

View File

@@ -0,0 +1,39 @@
<html>
<body style="margin: 0; padding: 0; background-color: #1a1a1a; font-family: 'Courier New', Courier, monospace; color: #ffffff;">
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #1a1a1a; padding: 20px;">
<tr>
<td align="center">
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="max-width: 600px; background-color: #222222; border: 1px solid #00ff00; padding: 30px;">
<tr>
<td align="center" style="padding-bottom: 20px;">
<img src="{{ logo_url }}" alt="Club Logo" width="150" style="display: block; border: 0;">
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 20px;">
<h1 style="color: #00ff00; font-size: 24px; margin: 0; text-transform: uppercase; text-align: center;">Registration Received</h1>
</td>
</tr>
<tr>
<td align="center" style="padding-bottom: 30px; font-size: 16px; line-height: 1.5; text-align: center;">
Thanks for registering <br>
<strong style="color: #00ff00;">{{ user_name }}</strong><br><br>
At this time, the event has reached maximum capacity. Your name has been placed on a waiting list; we will contact you in the event of any cancellations.
</td>
</tr>
<tr>
<td align="center" style="padding-top: 20px; border-top: 1px solid #333333; font-size: 12px; color: #888888; text-align: center;">
If you didn't request this, you can safely ignore this email.
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

2
run.py
View File

@@ -3,4 +3,4 @@ from app import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
app.run()