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:
@@ -25,11 +25,11 @@
|
||||
{% if req.profile_picture %}
|
||||
<img src="{{ url_for('uploaded_file', filename=req.profile_picture) }}" alt="{{ req.first_name }}" class="profile-photo">
|
||||
{% else %}
|
||||
<div class="profile-photo-placeholder">{{ req.first_name[0] }}{{ req.last_name[0] }}</div>
|
||||
<div class="profile-photo-placeholder"><span class="first-name">{{ req.first_name[0] }}</span><span class="last-name">{{ req.last_name[0] }}</span></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="request-info">
|
||||
<h3>{{ req.first_name }} {{ req.last_name }}</h3>
|
||||
<h3><span class="first-name">{{ req.first_name }}</span> <span class="last-name">{{ req.last_name }}</span></h3>
|
||||
<p class="request-email">{{ req.email }}</p>
|
||||
<p class="request-org">{{ req.organisation if req.organisation else '' }}{% if req.role %} - {{ req.role }}{% endif %}</p>
|
||||
{% if req.introduction %}
|
||||
@@ -38,8 +38,8 @@
|
||||
<p class="request-time">{{ 'requested'|t }} {{ req.created_at|localized_date if req.created_at else 'recently' }}</p>
|
||||
</div>
|
||||
<div class="request-actions">
|
||||
<button class="btn btn-success respond-btn" data-connection-id="{{ req.id }}" data-action="approve">{{ 'approve'|t }}</button>
|
||||
<button class="btn btn-danger respond-btn" data-connection-id="{{ req.id }}" data-action="reject">{{ 'reject'|t }}</button>
|
||||
<button class="btn btn-success btn-sm respond-btn" data-connection-id="{{ req.id }}" data-action="approve">{{ 'approve'|t }}</button>
|
||||
<button class="btn btn-danger btn-sm respond-btn" data-connection-id="{{ req.id }}" data-action="reject">{{ 'reject'|t }}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -70,85 +70,12 @@ document.querySelectorAll('.respond-btn').forEach(btn => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.error || 'Error processing request');
|
||||
showToast(data.error || 'Error processing request', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Error processing request');
|
||||
showToast('Error processing request', 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.request-card {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 15px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.request-profile {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.profile-photo {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.profile-photo-placeholder {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: #e2e8f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.request-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.request-info h3 {
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.request-email {
|
||||
color: #64748b;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.request-org {
|
||||
color: #475569;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.request-intro {
|
||||
font-style: italic;
|
||||
color: #64748b;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.request-time {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.request-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user