Security hardening: env-based config, CSRF protection, secure code generation, plus presenter management and email communications system

- Move all credentials from hardcoded to .env with python-dotenv (add .env.example template and .gitignore)
- Add CSRF token generation and validation on all state-changing requests
- Replace random.choices with secrets.choice for all secure code generation
- Add session cookie security headers (HttpOnly, Secure, SameSite)
- Add presenter management: assign/remove presenters to breakout sessions, presenter QR scanning
- Add email communications system: templates per event, custom email sending, sent email tracking
- Add staff/organizer profile pages with staff code display
- New DB tables: event_email_templates, sent_emails, user_communications
- New DB columns: staff.reminder_count/reminder_dates, breakout_session_organizers.presenter_code
- Expand English translation strings across all new features

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 20:34:48 +00:00
parent 934848b7f5
commit a3546b4c01
45 changed files with 8296 additions and 3207 deletions
@@ -3,82 +3,6 @@
{% block title %}{{ 'confirm_changes'|t }} - {{ session.name }}{% endblock %}
{% block content %}
<style>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: #fff;
border-radius: 12px;
padding: 30px;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
}
.modal-header {
margin-bottom: 20px;
}
.modal-header h2 {
margin: 0 0 10px 0;
color: #2c3e50;
}
.modal-header p {
margin: 0;
color: #666;
}
.changes-list {
background: #f8f9fa;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
}
.change-item {
padding: 10px 0;
border-bottom: 1px solid #e9ecef;
}
.change-item:last-child {
border-bottom: none;
}
.change-label {
font-weight: 600;
color: #495057;
margin-bottom: 5px;
}
.change-values {
display: flex;
gap: 10px;
align-items: center;
}
.old-value {
color: #dc3545;
text-decoration: line-through;
opacity: 0.7;
}
.arrow {
color: #666;
}
.new-value {
color: #28a745;
font-weight: 500;
}
.modal-actions {
display: flex;
gap: 10px;
justify-content: flex-end;
}
</style>
<div class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
@@ -92,7 +16,7 @@
<div class="change-label">{{ field }}</div>
<div class="change-values">
<span class="old-value">{{ old_val or '(' + 'empty' + ')' }}</span>
<span class="arrow"></span>
<span class="arrow"><i data-lucide="arrow-right"></i></span>
<span class="new-value">{{ new_val or '(' + 'empty' + ')' }}</span>
</div>
</div>
@@ -105,12 +29,16 @@
<div class="modal-actions">
<button type="button" class="btn btn-outline" onclick="skipNotify()">{{ 'skip'|t }}</button>
<button type="button" class="btn btn-primary" onclick="notifyAttendees()">{{ 'notify_attendees_email'|t }}</button>
<button type="button" class="btn btn-primary" onclick="notifyAttendees()"><i data-lucide="mail"></i> {{ 'notify_attendees_email'|t }}</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
lucide.createIcons();
});
function notifyAttendees() {
const changesText = `{% for field, old_val, new_val in changes %}{{ field }}: {{ old_val }} → {{ new_val }}
{% endfor %}`.trim();