added html email and made site deployment ready | security fixes still needed
This commit is contained in:
@@ -8,14 +8,13 @@ class Config:
|
|||||||
'sqlite:///' + os.path.join(basedir, '../instance/registration.db')
|
'sqlite:///' + os.path.join(basedir, '../instance/registration.db')
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
|
|
||||||
# Email Settings (Update these!)
|
MAIL_SERVER = 'mail.your-server.de'
|
||||||
MAIL_SERVER =
|
MAIL_PORT = 587
|
||||||
MAIL_PORT =
|
|
||||||
MAIL_USE_TLS = True
|
MAIL_USE_TLS = True
|
||||||
MAIL_USERNAME = os.environ.get('EMAIL_USER')
|
MAIL_USERNAME = os.environ.get('EMAIL_USER')
|
||||||
MAIL_PASSWORD = os.environ.get('EMAIL_PASS')
|
MAIL_PASSWORD = os.environ.get('EMAIL_PASS')
|
||||||
MAIL_DEFAULT_SENDER = os.environ.get('EMAIL_USER')
|
MAIL_DEFAULT_SENDER = os.environ.get('EMAIL_USER')
|
||||||
|
|
||||||
MAX_SPOTS =
|
MAX_SPOTS = 84
|
||||||
TUM_DOMAIN = "tum.de"
|
TUM_DOMAIN = "tum.de"
|
||||||
DISCORD_LINK =
|
DISCORD_LINK = os.environ.get('DISCORD_LINK')
|
||||||
|
|||||||
@@ -13,10 +13,11 @@ def send_verification_email(user_email):
|
|||||||
s = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
|
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)
|
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=["fdohq9ipugtc@mockmail.io"])
|
msg = Message('Verify your CTF Registration', recipients=[user_email])
|
||||||
#msg.body = f'Click here to confirm your spot: {link}\nThanks for registering {user_email}'
|
msg.html = render_template("email.html", user_email=user_email, link=link, logo_url=logo)
|
||||||
print(link)
|
mail.send(msg)
|
||||||
|
|
||||||
@bp.route('/', methods=['GET', 'POST'])
|
@bp.route('/', methods=['GET', 'POST'])
|
||||||
def index():
|
def index():
|
||||||
|
|||||||
BIN
app/static/h4tum_white.png
Normal file
BIN
app/static/h4tum_white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
81
app/static/timeline.css
Normal file
81
app/static/timeline.css
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
.timeline-container {
|
||||||
|
position: relative;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The actual horizontal line */
|
||||||
|
.line {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: #dfe6e9;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
width: 120px;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
/* This height ensures the time is above and label is below the line */
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The Dot - centered perfectly on the line */
|
||||||
|
.dot {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%); /* Centers the dot exactly */
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background: #00ff00;
|
||||||
|
border: 2px solid #1a1a1a;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 0 8px #00ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Time - positioned above the dot */
|
||||||
|
.time {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 65%; /* Push above the center line */
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Label - positioned below the dot */
|
||||||
|
.label {
|
||||||
|
position: absolute;
|
||||||
|
top: 65%; /* Push below the center line */
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label span {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #dddddd;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: Stack vertically */
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.line { display: none; }
|
||||||
|
.items-wrapper { flex-direction: column; gap: 10px; align-items: flex-start; }
|
||||||
|
.item { height: auto; text-align: center; width: 100%; }
|
||||||
|
.dot { display: none;}
|
||||||
|
.time, .label { position: static; }
|
||||||
|
}
|
||||||
53
app/templates/email.html
Normal file
53
app/templates/email.html
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<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_email }}</strong><br><br>
|
||||||
|
To secure your spot at the event, please confirm your attendance by clicking the button below.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding-bottom: 30px;">
|
||||||
|
<table border="0" cellspacing="0" cellpadding="0" align="center">
|
||||||
|
<tr>
|
||||||
|
<td align="center" bgcolor="#00ff00" style="padding: 15px 25px;">
|
||||||
|
<a href="{{ link }}" target="_blank" style="font-size: 18px; font-weight: bold; color: #000000; text-decoration: none; display: inline-block;">
|
||||||
|
CONFIRM MY SPOT
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</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>
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='timeline.css') }}">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>> Info</h1>
|
<h1>> Info</h1>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -23,7 +25,44 @@
|
|||||||
<li>Teams will be finalized on site at the competition</li>
|
<li>Teams will be finalized on site at the competition</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr style="border-color: #333; margin: 30px 0;"></hr>
|
<hr style="border-color: #333; margin: 30px 0;"></hr>
|
||||||
<a href="{{ url_for('main.rules') }}">Rules and further information</a>
|
<h1>> Timeline</h1>
|
||||||
|
<div class="timeline-container">
|
||||||
|
<div class="line"></div>
|
||||||
|
|
||||||
|
<div class="items-wrapper">
|
||||||
|
<div class="item">
|
||||||
|
<div class="time">12:00</div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div class="label">Entry<span>Setup & find team</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<div class="time">13:00</div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div class="label">Start of Event</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<div class="time">~18:30</div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div class="label">Pizza Time<span>Bring cash. <a href="https://lapergola.igetnow.com/menu" target="_blank">Menu</a></span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<div class="time">21:00</div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div class="label">End of Event</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item">
|
||||||
|
<div class="time">21:15</div>
|
||||||
|
<div class="dot"></div>
|
||||||
|
<div class="label">Awards</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr style="border-color: #333; margin: 30px 0;"></hr>
|
||||||
|
<a href="{{ url_for('main.rules') }}" style="text-align: center;"><h3>Rules and Code of Conduct</h3></a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|||||||
Reference in New Issue
Block a user