Add comprehensive admin panel with user management

Features added:
- Admin authentication system with is_admin field
- Complete admin dashboard with user statistics
- User management (create, edit, delete, toggle admin)
- Protected admin routes with @admin_required decorator
- Security features (prevent self-deletion, last admin protection)
- Responsive admin UI with Bootstrap integration
- Database migration script for admin field
- Admin navigation link for authorized users

Technical improvements:
- Enhanced 3-column dashboard layout (tables | editor | saved queries)
- Removed plus button and made right sidebar more compact
- Admin user (admin/admin123) automatically created with admin privileges
- Full CRUD operations for user management
- Flash messages for user feedback
- Form validation and error handling
This commit is contained in:
DGSoft 2025-10-14 21:33:11 +02:00
parent 44b8667f31
commit f176560c02
12 changed files with 788 additions and 1 deletions

18
.env.example Normal file
View File

@ -0,0 +1,18 @@
# Flask Konfiguration
SECRET_KEY=your-secret-key-here
FLASK_ENV=development
FLASK_DEBUG=True
# Oracle Datenbank Konfiguration
ORACLE_HOST=your-oracle-host.com
ORACLE_PORT=1521
ORACLE_SERVICE_NAME=ORCL
ORACLE_USERNAME=your_username
ORACLE_PASSWORD=your_password
# PostgreSQL Datenbank Konfiguration (optional)
POSTGRES_HOST=your-postgres-host.com
POSTGRES_PORT=5432
POSTGRES_DATABASE=your_database
POSTGRES_USERNAME=your_username
POSTGRES_PASSWORD=your_password

54
.github/copilot-instructions.md vendored Normal file
View File

@ -0,0 +1,54 @@
<!-- Use this file to provide workspace-specific custom instructions to Copilot. For more details, visit https://code.visualstudio.com/docs/copilot/copilot-customization#_use-a-githubcopilotinstructionsmd-file -->
- [x] Verify that the copilot-instructions.md file in the .github directory is created. ✓ Completed
- [x] Clarify Project Requirements - Python Flask web server with authentication, SQL query builder interface with table list on left, query input and results on right, ability to save and retrieve queries via API as JSON/CSV
<!-- Ask for project type, language, and frameworks if not specified. Skip if already provided. -->
- [x] Scaffold the Project - ✓ Flask project structure created with authentication, database models, routes, templates, and static files
<!--
Ensure that the previous step has been marked as completed.
Call project setup tool with projectType parameter.
Run scaffolding command to create project files and folders.
Use '.' as the working directory.
If no appropriate projectType is available, search documentation using available tools.
Otherwise, create the project structure manually using available file creation tools.
-->
- [x] Customize the Project - ✓ Completed with full query builder functionality, authentication, and API endpoints
<!--
Verify that all previous steps have been completed successfully and you have marked the step as completed.
Develop a plan to modify codebase according to user requirements.
Apply modifications using appropriate tools and user-provided references.
Skip this step for "Hello World" projects.
-->
- [x] Install Required Extensions - ✓ No additional extensions needed
<!-- ONLY install extensions provided mentioned in the get_project_setup_info. Skip this step otherwise and mark as completed. -->
- [x] Compile the Project - ✓ Completed - Python environment configured and all dependencies installed successfully
<!--
Verify that all previous steps have been completed.
Install any missing dependencies.
Run diagnostics and resolve any issues.
Check for markdown files in project folder for relevant instructions on how to do this.
-->
- [ ] Create and Run Task
<!--
Verify that all previous steps have been completed.
Check https://code.visualstudio.com/docs/debugtest/tasks to determine if the project needs a task. If so, use the create_and_run_task to create and launch a task based on package.json, README.md, and project structure.
Skip this step otherwise.
-->
- [ ] Launch the Project
<!--
Verify that all previous steps have been completed.
Prompt user for debug mode, launch only if confirmed.
-->
- [ ] Ensure Documentation is Complete
<!--
Verify that all previous steps have been completed.
Verify that README.md and the copilot-instructions.md file in the .github directory exists and contains current project information.
Clean up the copilot-instructions.md file in the .github directory by removing all HTML comments.
-->

63
.gitignore vendored Normal file
View File

@ -0,0 +1,63 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Flask
instance/
.webassets-cache
# Environment variables
.env
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Database
*.db
*.sqlite
*.sqlite3
# Logs
*.log
# Virtual Environment
.venv/
venv/
ENV/
env/
.env
# Temporary files
*.tmp
*.temp

View File

@ -46,7 +46,7 @@ def create_app():
from app.models import User
admin = User.query.filter_by(username='admin').first()
if not admin:
admin = User(username='admin', email='admin@example.com')
admin = User(username='admin', email='admin@example.com', is_admin=True)
admin.set_password('admin123')
db.session.add(admin)
db.session.commit()

View File

@ -0,0 +1,88 @@
{% extends "base.html" %}
{% block title %}Neuen Benutzer erstellen{% endblock %}
{% block content %}
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2><i class="fas fa-user-plus"></i> Neuen Benutzer erstellen</h2>
<a href="{{ url_for('admin.users') }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> Zurück
</a>
</div>
<div class="card">
<div class="card-body">
<form method="POST">
<div class="mb-3">
<label for="username" class="form-label">
<i class="fas fa-user"></i> Benutzername *
</label>
<input type="text" class="form-control" id="username" name="username"
required maxlength="80" placeholder="z.B. john.doe">
<div class="form-text">Der Benutzername muss eindeutig sein</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">
<i class="fas fa-envelope"></i> E-Mail-Adresse *
</label>
<input type="email" class="form-control" id="email" name="email"
required maxlength="120" placeholder="john.doe@example.com">
<div class="form-text">Die E-Mail-Adresse muss eindeutig sein</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">
<i class="fas fa-lock"></i> Passwort *
</label>
<input type="password" class="form-control" id="password" name="password"
required minlength="6" placeholder="Mindestens 6 Zeichen">
<div class="form-text">Mindestens 6 Zeichen erforderlich</div>
</div>
<div class="mb-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="is_admin" name="is_admin">
<label class="form-check-label" for="is_admin">
<i class="fas fa-user-shield text-danger"></i> Administrator-Rechte gewähren
</label>
<div class="form-text">
Administratoren können andere Benutzer verwalten und haben Vollzugriff
</div>
</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-success">
<i class="fas fa-user-plus"></i> Benutzer erstellen
</button>
<a href="{{ url_for('admin.users') }}" class="btn btn-outline-secondary">
<i class="fas fa-times"></i> Abbrechen
</a>
</div>
</form>
</div>
</div>
<!-- Hinweise -->
<div class="card mt-4">
<div class="card-header">
<h6><i class="fas fa-info-circle"></i> Wichtige Hinweise</h6>
</div>
<div class="card-body">
<ul class="mb-0">
<li>Alle mit * markierten Felder sind Pflichtfelder</li>
<li>Der Benutzername und die E-Mail-Adresse müssen eindeutig sein</li>
<li>Das Passwort sollte sicher gewählt werden</li>
<li>Administrator-Rechte gewähren Vollzugriff auf alle Funktionen</li>
<li>Neue Benutzer können sich sofort mit ihren Anmeldedaten einloggen</li>
</ul>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,173 @@
{% extends "base.html" %}
{% block title %}Admin Dashboard{% 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">
<h2><i class="fas fa-users-cog"></i> Admin Dashboard</h2>
<a href="{{ url_for('main.dashboard') }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> Zurück zum Dashboard
</a>
</div>
<!-- Statistiken -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card bg-primary text-white">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h4>{{ users|length }}</h4>
<small>Benutzer gesamt</small>
</div>
<i class="fas fa-users fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h4>{{ users|selectattr('is_admin')|list|length }}</h4>
<small>Administratoren</small>
</div>
<i class="fas fa-user-shield fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h4>{{ users|rejectattr('is_admin')|list|length }}</h4>
<small>Standard-Benutzer</small>
</div>
<i class="fas fa-user fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-white">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h4>{{ (users|selectattr('created_at')|map(attribute='created_at')|list|length) }}</h4>
<small>Aktive Benutzer</small>
</div>
<i class="fas fa-chart-line fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Schnellzugriff -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-tachometer-alt"></i> Schnellzugriff</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<a href="{{ url_for('admin.users') }}" class="btn btn-outline-primary btn-lg w-100">
<i class="fas fa-users"></i>
<div class="mt-2">
<strong>Benutzerverwaltung</strong>
<br><small class="text-muted">Benutzer anzeigen, bearbeiten und erstellen</small>
</div>
</a>
</div>
<div class="col-md-6 mb-3">
<a href="{{ url_for('admin.create_user') }}" class="btn btn-outline-success btn-lg w-100">
<i class="fas fa-user-plus"></i>
<div class="mt-2">
<strong>Neuen Benutzer erstellen</strong>
<br><small class="text-muted">Schnell einen neuen Benutzer hinzufügen</small>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Letzte Benutzer -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-clock"></i> Letzte Benutzer</h5>
</div>
<div class="card-body">
{% if users %}
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Benutzername</th>
<th>E-Mail</th>
<th>Rolle</th>
<th>Erstellt am</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for user in users[:5] %}
<tr>
<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>
{% if user.is_admin %}
<span class="badge bg-danger"><i class="fas fa-user-shield"></i> Administrator</span>
{% else %}
<span class="badge bg-secondary"><i class="fas fa-user"></i> Benutzer</span>
{% endif %}
</td>
<td>{{ user.created_at.strftime('%d.%m.%Y %H:%M') }}</td>
<td>
<a href="{{ url_for('admin.edit_user', user_id=user.id) }}"
class="btn btn-sm btn-outline-primary" title="Bearbeiten">
<i class="fas fa-edit"></i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="text-center mt-3">
<a href="{{ url_for('admin.users') }}" class="btn btn-primary">
Alle Benutzer anzeigen <i class="fas fa-arrow-right"></i>
</a>
</div>
{% else %}
<div class="text-center text-muted py-4">
<i class="fas fa-users fa-3x mb-3"></i>
<h5>Keine Benutzer gefunden</h5>
<p>Erstellen Sie den ersten Benutzer über den Button oben.</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,145 @@
{% extends "base.html" %}
{% block title %}Benutzer bearbeiten{% endblock %}
{% block content %}
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2><i class="fas fa-user-edit"></i> Benutzer bearbeiten</h2>
<a href="{{ url_for('admin.users') }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> Zurück
</a>
</div>
<div class="card">
<div class="card-header">
<h5>
{{ user.username }}
{% if user.id == current_user.id %}
<span class="badge bg-primary">Sie</span>
{% endif %}
{% if user.is_admin %}
<span class="badge bg-danger">Administrator</span>
{% endif %}
</h5>
</div>
<div class="card-body">
<form method="POST">
<div class="mb-3">
<label for="username" class="form-label">
<i class="fas fa-user"></i> Benutzername *
</label>
<input type="text" class="form-control" id="username" name="username"
value="{{ user.username }}" required maxlength="80"
placeholder="z.B. john.doe">
<div class="form-text">Der Benutzername muss eindeutig sein</div>
</div>
<div class="mb-3">
<label for="email" class="form-label">
<i class="fas fa-envelope"></i> E-Mail-Adresse *
</label>
<input type="email" class="form-control" id="email" name="email"
value="{{ user.email }}" required maxlength="120"
placeholder="john.doe@example.com">
<div class="form-text">Die E-Mail-Adresse muss eindeutig sein</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">
<i class="fas fa-lock"></i> Neues Passwort
</label>
<input type="password" class="form-control" id="password" name="password"
minlength="6" placeholder="Leer lassen um Passwort beizubehalten">
<div class="form-text">Nur ausfüllen wenn Sie das Passwort ändern möchten (mindestens 6 Zeichen)</div>
</div>
<div class="mb-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="is_admin" name="is_admin"
{% if user.is_admin %}checked{% endif %}
{% if user.id == current_user.id %}disabled{% endif %}>
<label class="form-check-label" for="is_admin">
<i class="fas fa-user-shield text-danger"></i> Administrator-Rechte
</label>
{% if user.id == current_user.id %}
<div class="form-text text-warning">
<i class="fas fa-exclamation-triangle"></i> Sie können Ihre eigenen Admin-Rechte nicht ändern
</div>
{% else %}
<div class="form-text">
Administratoren können andere Benutzer verwalten und haben Vollzugriff
</div>
{% endif %}
</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Änderungen speichern
</button>
<a href="{{ url_for('admin.users') }}" class="btn btn-outline-secondary">
<i class="fas fa-times"></i> Abbrechen
</a>
</div>
</form>
</div>
</div>
<!-- Benutzer-Informationen -->
<div class="card mt-4">
<div class="card-header">
<h6><i class="fas fa-info-circle"></i> Benutzer-Informationen</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-sm-4"><strong>Benutzer-ID:</strong></div>
<div class="col-sm-8">{{ user.id }}</div>
</div>
<div class="row">
<div class="col-sm-4"><strong>Erstellt am:</strong></div>
<div class="col-sm-8">{{ user.created_at.strftime('%d.%m.%Y %H:%M:%S') }}</div>
</div>
<div class="row">
<div class="col-sm-4"><strong>Rolle:</strong></div>
<div class="col-sm-8">
{% if user.is_admin %}
<span class="badge bg-danger">Administrator</span>
{% else %}
<span class="badge bg-secondary">Standard-Benutzer</span>
{% endif %}
</div>
</div>
<div class="row">
<div class="col-sm-4"><strong>Anzahl Queries:</strong></div>
<div class="col-sm-8">{{ user.saved_queries|length }}</div>
</div>
</div>
</div>
<!-- Lösch-Option -->
{% if user.id != current_user.id %}
<div class="card mt-4 border-danger">
<div class="card-header bg-danger text-white">
<h6><i class="fas fa-exclamation-triangle"></i> Gefahrenbereich</h6>
</div>
<div class="card-body">
<p class="text-danger">
<strong>Achtung:</strong> Das Löschen eines Benutzers kann nicht rückgängig gemacht werden.
Alle gespeicherten Queries dieses Benutzers gehen verloren.
</p>
<form method="POST" action="{{ url_for('admin.delete_user', user_id=user.id) }}"
onsubmit="return confirm('Benutzer {{ user.username }} wirklich unwiderruflich löschen?\\n\\nAlle gespeicherten Queries gehen verloren!')">
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash"></i> Benutzer löschen
</button>
</form>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,154 @@
{% 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">
<h2><i class="fas fa-users"></i> Benutzerverwaltung</h2>
<div>
<a href="{{ url_for('admin.create_user') }}" class="btn btn-success">
<i class="fas fa-user-plus"></i> Neuen Benutzer erstellen
</a>
<a href="{{ url_for('admin.admin_dashboard') }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> Admin Dashboard
</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-2">
<i class="fas fa-user-shield"></i> Administrator
</span>
{% else %}
<span class="badge bg-secondary me-2">
<i class="fas fa-user"></i> Benutzer
</span>
{% endif %}
{% if user.id != current_user.id %}
<button class="btn btn-xs btn-outline-info"
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" 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" 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-5">
<i class="fas fa-users fa-3x text-muted mb-3"></i>
<h4>Keine Benutzer gefunden</h4>
<p class="text-muted">Erstellen Sie den ersten Benutzer.</p>
<a href="{{ url_for('admin.create_user') }}" class="btn btn-success">
<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 %}

View File

@ -23,8 +23,16 @@
{% if current_user.is_authenticated %}
<div class="navbar-nav ms-auto">
{% if current_user.is_admin %}
<a class="nav-link me-3" href="{{ url_for('admin.admin_dashboard') }}">
<i class="fas fa-users-cog"></i> Admin
</a>
{% endif %}
<span class="navbar-text me-3">
<i class="fas fa-user"></i> {{ current_user.username }}
{% if current_user.is_admin %}
<span class="badge bg-danger ms-1">Admin</span>
{% endif %}
</span>
<a class="nav-link" href="{{ url_for('auth.logout') }}">
<i class="fas fa-sign-out-alt"></i> Abmelden

Binary file not shown.

84
migrate_admin.py Normal file
View File

@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""
Migrations-Skript um das is_admin Feld zur User-Tabelle hinzuzufügen
"""
import sqlite3
import os
from pathlib import Path
def migrate_database():
"""Fügt das is_admin Feld zur User-Tabelle hinzu"""
# Finde die Datenbankdatei
possible_paths = [
'querybuilder.db',
'instance/querybuilder.db',
Path(__file__).parent / 'querybuilder.db',
Path(__file__).parent / 'instance' / 'querybuilder.db'
]
db_path = None
for path in possible_paths:
if os.path.exists(path):
db_path = path
break
if not db_path:
print("❌ Keine Datenbankdatei gefunden!")
return False
print(f"📁 Verwende Datenbank: {db_path}")
try:
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Prüfe ob Spalte bereits existiert
cursor.execute("PRAGMA table_info(user)")
columns = [row[1] for row in cursor.fetchall()]
if 'is_admin' in columns:
print("✅ Spalte 'is_admin' existiert bereits!")
# Setze den ersten User (admin) als Administrator
cursor.execute("UPDATE user SET is_admin = 1 WHERE username = 'admin'")
if cursor.rowcount > 0:
print("✅ Admin-User wurde als Administrator markiert!")
conn.commit()
conn.close()
return True
# Füge is_admin Spalte hinzu
print(" Füge 'is_admin' Spalte hinzu...")
cursor.execute("ALTER TABLE user ADD COLUMN is_admin BOOLEAN NOT NULL DEFAULT 0")
# Setze den ersten User (admin) als Administrator
cursor.execute("UPDATE user SET is_admin = 1 WHERE username = 'admin'")
admin_updated = cursor.rowcount > 0
conn.commit()
conn.close()
print("✅ Migration erfolgreich abgeschlossen!")
if admin_updated:
print("✅ Admin-User wurde als Administrator markiert!")
else:
print("⚠️ Kein Admin-User gefunden - wird beim nächsten Start erstellt")
return True
except Exception as e:
print(f"❌ Fehler bei der Migration: {e}")
return False
if __name__ == "__main__":
print("🔄 Starte Datenbank-Migration...")
success = migrate_database()
if success:
print("\n🎉 Migration abgeschlossen!")
print("Der Admin-User kann jetzt auf das Admin-Panel zugreifen.")
else:
print("\n💥 Migration fehlgeschlagen!")
print("Bitte überprüfen Sie die Fehlermeldungen oben.")