Security improvements:
- Removed registration link from login page
- Disabled /auth/register route - redirects with error message
- Removed demo credentials from login page
- Added info message: 'New users are created by administrators'
UI improvements:
- Compacted all admin interface buttons (btn-sm)
- Reduced heading sizes (H2 → H4) for less visual dominance
- Shortened badge texts ('Administrator' → 'Admin', 'Benutzer' → 'User')
- Optimized spacing and reduced margins/paddings
- Cleaner, more professional admin interface
Access control:
- Only administrators can create new users via admin panel
- Self-registration completely disabled for security
- Maintains full admin functionality with improved UX
154 lines
7.0 KiB
HTML
154 lines
7.0 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Benutzerverwaltung{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h4><i class="fas fa-users"></i> Benutzerverwaltung</h4>
|
|
<div>
|
|
<a href="{{ url_for('admin.create_user') }}" class="btn btn-success btn-sm">
|
|
<i class="fas fa-user-plus"></i> Erstellen
|
|
</a>
|
|
<a href="{{ url_for('admin.admin_dashboard') }}" class="btn btn-outline-secondary btn-sm">
|
|
<i class="fas fa-arrow-left"></i> Zurück
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
{% if users %}
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Benutzername</th>
|
|
<th>E-Mail</th>
|
|
<th>Rolle</th>
|
|
<th>Erstellt am</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for user in users %}
|
|
<tr id="user-row-{{ user.id }}">
|
|
<td>{{ user.id }}</td>
|
|
<td>
|
|
<strong>{{ user.username }}</strong>
|
|
{% if user.id == current_user.id %}
|
|
<span class="badge bg-primary ms-1">Sie</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ user.email }}</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
{% if user.is_admin %}
|
|
<span class="badge bg-danger me-1">
|
|
<i class="fas fa-user-shield"></i> Admin
|
|
</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary me-1">
|
|
<i class="fas fa-user"></i> User
|
|
</span>
|
|
{% endif %}
|
|
|
|
{% if user.id != current_user.id %}
|
|
<button class="btn btn-xs btn-outline-info btn-sm"
|
|
onclick="toggleAdmin({{ user.id }})"
|
|
title="Rolle wechseln">
|
|
<i class="fas fa-exchange-alt"></i>
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
<td>{{ user.created_at.strftime('%d.%m.%Y %H:%M') }}</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<a href="{{ url_for('admin.edit_user', user_id=user.id) }}"
|
|
class="btn btn-outline-primary btn-sm" title="Bearbeiten">
|
|
<i class="fas fa-edit"></i>
|
|
</a>
|
|
|
|
{% if user.id != current_user.id %}
|
|
<form method="POST" action="{{ url_for('admin.delete_user', user_id=user.id) }}"
|
|
class="d-inline" onsubmit="return confirm('Benutzer {{ user.username }} wirklich löschen?')">
|
|
<button type="submit" class="btn btn-outline-danger btn-sm" title="Löschen">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="card">
|
|
<div class="card-body text-center py-4">
|
|
<i class="fas fa-users fa-2x text-muted mb-2"></i>
|
|
<h6>Keine Benutzer gefunden</h6>
|
|
<p class="text-muted small">Erstellen Sie den ersten Benutzer.</p>
|
|
<a href="{{ url_for('admin.create_user') }}" class="btn btn-success btn-sm">
|
|
<i class="fas fa-user-plus"></i> Ersten Benutzer erstellen
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function toggleAdmin(userId) {
|
|
try {
|
|
const response = await fetch(`/admin/users/${userId}/toggle_admin`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
showAlert(data.message, 'success');
|
|
// Seite neu laden um Änderungen anzuzeigen
|
|
setTimeout(() => window.location.reload(), 1000);
|
|
} else {
|
|
showAlert(data.error, 'error');
|
|
}
|
|
} catch (error) {
|
|
showAlert(`Fehler: ${error.message}`, 'error');
|
|
}
|
|
}
|
|
|
|
function showAlert(message, type = 'info') {
|
|
const alertClass = type === 'error' ? 'alert-danger' :
|
|
type === 'success' ? 'alert-success' : 'alert-info';
|
|
|
|
const alertHtml = `
|
|
<div class="alert ${alertClass} alert-dismissible fade show" role="alert">
|
|
${message}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
`;
|
|
|
|
// Alert am Anfang der Seite einfügen
|
|
const container = document.querySelector('.container-fluid');
|
|
const existingAlert = container.querySelector('.alert');
|
|
if (existingAlert) {
|
|
existingAlert.remove();
|
|
}
|
|
|
|
container.insertAdjacentHTML('afterbegin', alertHtml);
|
|
}
|
|
</script>
|
|
{% endblock %} |