4 Commits

4 changed files with 616 additions and 40 deletions

172
DASHBOARD_FIX.md Normal file
View File

@@ -0,0 +1,172 @@
# Dashboard Routing Fix
Questo branch (`fix-dashboard-routing`) contiene le correzioni per risolvere i problemi di accesso alla dashboard nel branch `dynamic-site-enhancement`.
## Problemi Risolti
### 1. Template Mancante
- ✅ Aggiunto il template mancante `templates/dashboard/users.html`
- Il template è ora completo con paginazione e gestione degli errori
### 2. Gestione Errori Migliorata
- ✅ Aggiunta gestione degli errori in tutte le route della dashboard
- ✅ Messaggi di errore più dettagliati per il debugging
- ✅ Fallback sicuri quando il database non è accessibile
### 3. Route di Debug
- ✅ Aggiunta route `/dashboard/debug/auth` per controllare lo stato di autenticazione
- ✅ Aggiunta route `/dashboard/debug/access` per testare l'accesso alla dashboard
- ✅ Informazioni dettagliate su sessioni e privilegi utente
### 4. Script di Utilità
- ✅ Creato script `utils/create_admin.py` per creare utenti amministratore
- ✅ Supporto per creare, listare e promuovere utenti
## Come Risolvere i Problemi di Accesso
### Passo 1: Verifica la Configurazione
Assicurati che il file `config.py` abbia la `SECRET_KEY` configurata:
```python
SECRET_KEY = 'your-secret-key-here'
```
### Passo 2: Inizializza il Database
Assicurati che il database sia inizializzato e accessibile:
```bash
# Con Docker Compose
docker-compose up -d db
# Controlla i log per errori
docker-compose logs app
```
### Passo 3: Crea un Utente Amministratore
Usa lo script di utilità per creare un utente admin:
```bash
# Crea utente admin predefinito
python utils/create_admin.py create
# Oppure promuovi un utente esistente
python utils/create_admin.py promote username
# Lista tutti gli utenti
python utils/create_admin.py list
```
### Passo 4: Testa l'Accesso
1. **Controlla lo stato di autenticazione:**
```
GET /dashboard/debug/auth
```
Questo ti dirà se sei loggato e hai i privilegi corretti.
2. **Testa l'accesso alla dashboard:**
```
GET /dashboard/debug/access
```
Questo ti dirà se puoi accedere alla dashboard e perché.
3. **Effettua il login:**
- Vai a `/auth/login`
- Usa le credenziali dell'utente admin
- Dovresti essere reindirizzato automaticamente a `/dashboard/`
## Credenziali Admin Predefinite
Se usi lo script `create_admin.py create`, verranno create queste credenziali:
- **Username:** `admin`
- **Email:** `admin@hersel.it`
- **Password:** `admin123`
⚠️ **IMPORTANTE:** Cambia la password predefinita dopo il primo login!
## Troubleshooting
### Problema: "401 Login required"
**Soluzione:** Non sei loggato. Vai a `/auth/login` e effettua il login.
### Problema: "403 Admin access required"
**Soluzione:** Il tuo utente non ha privilegi di amministratore. Usa:
```bash
python utils/create_admin.py promote il_tuo_username
```
### Problema: "500 Internal Server Error"
**Possibili cause:**
1. Database non accessibile
2. Errore nei template
3. Configurazione mancante
**Debug:**
1. Controlla i log dell'applicazione
2. Usa le route di debug: `/dashboard/debug/auth` e `/dashboard/debug/access`
3. Verifica la configurazione del database
### Problema: Template non trovato
**Soluzione:** Assicurati che tutti i template esistano in `templates/dashboard/`:
- `index.html` ✅
- `projects.html` ✅
- `project_form.html` ✅
- `users.html` ✅ (aggiunto in questo fix)
- `base.html` ✅
## URL della Dashboard
Dopo aver risolto i problemi di autenticazione, puoi accedere a:
- **Dashboard principale:** `/dashboard/`
- **Gestione progetti:** `/dashboard/projects`
- **Nuovo progetto:** `/dashboard/projects/new`
- **Gestione utenti:** `/dashboard/users`
- **Debug autenticazione:** `/dashboard/debug/auth`
- **Test accesso:** `/dashboard/debug/access`
## Modifiche Apportate
1. **`templates/dashboard/users.html`** - Nuovo template per la gestione utenti
2. **`routes/dashboard.py`** - Migliorate gestione errori e aggiunte route di debug
3. **`utils/create_admin.py`** - Nuovo script per gestire utenti amministratore
4. **`DASHBOARD_FIX.md`** - Questa documentazione
## Come Applicare i Fix
1. **Merge di questo branch:**
```bash
git checkout dynamic-site-enhancement
git merge fix-dashboard-routing
```
2. **Oppure crea un PR:**
- Crea una pull request da `fix-dashboard-routing` a `dynamic-site-enhancement`
- Rivedi le modifiche e fai il merge
3. **Testa l'applicazione:**
```bash
# Restart dell'applicazione
docker-compose restart app
# Crea utente admin
docker-compose exec app python utils/create_admin.py create
# Testa l'accesso
curl http://localhost:5000/dashboard/debug/access
```
## Supporto
Se hai ancora problemi dopo aver applicato questi fix:
1. Controlla i log dell'applicazione
2. Usa le route di debug per diagnosticare il problema
3. Verifica che il database sia accessibile
4. Assicurati di avere un utente con privilegi di amministratore
Tutti i fix sono backward-compatible e non dovrebbero causare problemi al codice esistente.

View File

@@ -3,7 +3,7 @@
# Dashboard Routes (Admin)
from quart import Blueprint, request, render_template, redirect, url_for, jsonify
from quart import Blueprint, request, render_template, redirect, url_for, jsonify, session
from models.user import User
from models.project import Project
from models.category import Category
@@ -13,27 +13,115 @@ from utils.validators import validate_project_data
dashboard_bp = Blueprint('dashboard', __name__, url_prefix='/dashboard')
# Debug route to check authentication status
@dashboard_bp.route('/debug/auth')
async def debug_auth():
"""Debug route to check authentication status"""
current_user = await get_current_user()
session_data = dict(session)
debug_info = {
'session_exists': 'user_id' in session,
'user_id_in_session': session.get('user_id'),
'username_in_session': session.get('username'),
'is_admin_in_session': session.get('is_admin'),
'current_user_found': current_user is not None,
'current_user_is_admin': current_user.is_admin if current_user else None,
'current_user_details': {
'id': current_user.id,
'username': current_user.username,
'email': current_user.email,
'role': current_user.role,
'is_admin': current_user.is_admin
} if current_user else None,
'session_data': session_data
}
return jsonify(debug_info)
# Public route to check dashboard access without admin_required decorator
@dashboard_bp.route('/debug/access')
async def debug_access():
"""Debug route to check dashboard access requirements"""
try:
current_user = await get_current_user()
if not current_user:
return jsonify({
'status': 'error',
'message': 'Nessun utente loggato',
'redirect': url_for('auth.login')
}), 401
if not current_user.is_admin:
return jsonify({
'status': 'error',
'message': f'Utente {current_user.username} non ha privilegi di amministratore',
'user_role': current_user.role,
'is_admin': current_user.is_admin,
'redirect': url_for('home.index')
}), 403
return jsonify({
'status': 'success',
'message': f'Accesso consentito per {current_user.username}',
'user': {
'id': current_user.id,
'username': current_user.username,
'role': current_user.role,
'is_admin': current_user.is_admin
},
'dashboard_url': url_for('dashboard.index')
})
except Exception as e:
return jsonify({
'status': 'error',
'message': f'Errore durante il controllo accesso: {str(e)}'
}), 500
@dashboard_bp.route('/')
@admin_required
async def index():
"""Dashboard home"""
current_user = await get_current_user()
# Get statistics
stats = {
'total_users': await User.count(),
'total_projects': await Project.count(published_only=False),
'published_projects': await Project.count(published_only=True),
'featured_projects': len(await Project.get_featured())
}
# Get recent projects
recent_projects = await Project.get_all(published_only=False, limit=5)
return await render_template('dashboard/index.html',
user=current_user,
stats=stats,
recent_projects=recent_projects)
try:
current_user = await get_current_user()
# Get statistics with error handling
stats = {
'total_users': 0,
'total_projects': 0,
'published_projects': 0,
'featured_projects': 0
}
try:
stats['total_users'] = await User.count()
except Exception as e:
print(f"Error getting user count: {e}")
try:
stats['total_projects'] = await Project.count(published_only=False)
stats['published_projects'] = await Project.count(published_only=True)
featured_projects = await Project.get_featured()
stats['featured_projects'] = len(featured_projects) if featured_projects else 0
except Exception as e:
print(f"Error getting project stats: {e}")
# Get recent projects with error handling
recent_projects = []
try:
recent_projects = await Project.get_all(published_only=False, limit=5)
except Exception as e:
print(f"Error getting recent projects: {e}")
return await render_template('dashboard/index.html',
user=current_user,
stats=stats,
recent_projects=recent_projects)
except Exception as e:
flash_message(f'Errore nel caricamento della dashboard: {str(e)}', 'error')
return redirect(url_for('home.index'))
@dashboard_bp.route('/projects')
@admin_required
@@ -42,26 +130,34 @@ async def projects():
page = int(request.args.get('page', 1))
per_page = 10
# Get projects with pagination
projects = await Project.get_all(published_only=False, limit=per_page, offset=(page-1)*per_page)
total_projects = await Project.count(published_only=False)
pagination = calculate_pagination(total_projects, page, per_page)
return await render_template('dashboard/projects.html',
projects=projects,
pagination=pagination)
try:
# Get projects with pagination
projects = await Project.get_all(published_only=False, limit=per_page, offset=(page-1)*per_page)
total_projects = await Project.count(published_only=False)
pagination = calculate_pagination(total_projects, page, per_page)
return await render_template('dashboard/projects.html',
projects=projects,
pagination=pagination)
except Exception as e:
flash_message(f'Errore nel caricamento dei progetti: {str(e)}', 'error')
return redirect(url_for('dashboard.index'))
@dashboard_bp.route('/projects/new', methods=['GET', 'POST'])
@admin_required
async def new_project():
"""Create new project"""
if request.method == 'GET':
categories = await Category.get_all()
return await render_template('dashboard/project_form.html',
project=None,
categories=categories,
action='create')
try:
categories = await Category.get_all()
return await render_template('dashboard/project_form.html',
project=None,
categories=categories,
action='create')
except Exception as e:
flash_message(f'Errore nel caricamento delle categorie: {str(e)}', 'error')
return redirect(url_for('dashboard.projects'))
form_data = await request.form
data = {
@@ -208,11 +304,15 @@ async def users():
page = int(request.args.get('page', 1))
per_page = 10
users = await User.get_all(limit=per_page, offset=(page-1)*per_page)
total_users = await User.count()
pagination = calculate_pagination(total_users, page, per_page)
return await render_template('dashboard/users.html',
users=users,
pagination=pagination)
try:
users = await User.get_all(limit=per_page, offset=(page-1)*per_page)
total_users = await User.count()
pagination = calculate_pagination(total_users, page, per_page)
return await render_template('dashboard/users.html',
users=users,
pagination=pagination)
except Exception as e:
flash_message(f'Errore nel caricamento degli utenti: {str(e)}', 'error')
return redirect(url_for('dashboard.index'))

View File

@@ -0,0 +1,157 @@
{% extends "dashboard/base.html" %}
{% block title %}Gestione Utenti - Dashboard{% endblock %}
{% block dashboard_content %}
<div class="dashboard-header">
<h1 class="dashboard-title">
<i class="fas fa-users"></i>
Gestione Utenti
</h1>
<div class="dashboard-actions">
<span class="badge badge-info">{{ pagination.total_items if pagination else 0 }} utenti totali</span>
</div>
</div>
<div class="dashboard-content">
{% if users %}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Username</th>
<th>Nome Completo</th>
<th>Email</th>
<th>Ruolo</th>
<th>Stato</th>
<th>Registrato</th>
<th>Ultimo Accesso</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>
<strong>{{ user.username }}</strong>
{% if user.is_admin %}
<span class="badge badge-danger ms-1">Admin</span>
{% endif %}
</td>
<td>{{ user.full_name or '-' }}</td>
<td>{{ user.email }}</td>
<td>
<span class="badge {% if user.role == 'admin' %}badge-danger{% else %}badge-secondary{% endif %}">
{{ user.role.title() }}
</span>
</td>
<td>
<span class="badge {% if user.is_active %}badge-success{% else %}badge-warning{% endif %}">
{% if user.is_active %}Attivo{% else %}Inattivo{% endif %}
</span>
</td>
<td>
{% if user.created_at %}
<small>{{ user.created_at.strftime('%d/%m/%Y %H:%M') }}</small>
{% else %}
-
{% endif %}
</td>
<td>
{% if user.last_login %}
<small>{{ user.last_login.strftime('%d/%m/%Y %H:%M') }}</small>
{% else %}
<span class="text-muted">Mai</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if pagination and pagination.total_pages > 1 %}
<nav aria-label="User pagination" class="mt-4">
<ul class="pagination justify-content-center">
{% if pagination.has_prev %}
<li class="page-item">
<a class="page-link" href="{{ url_for('dashboard.users', page=pagination.prev_num) }}">
<i class="fas fa-chevron-left"></i> Precedente
</a>
</li>
{% endif %}
{% for page_num in pagination.iter_pages() %}
{% if page_num %}
{% if page_num != pagination.page %}
<li class="page-item">
<a class="page-link" href="{{ url_for('dashboard.users', page=page_num) }}">{{ page_num }}</a>
</li>
{% else %}
<li class="page-item active">
<span class="page-link">{{ page_num }}</span>
</li>
{% endif %}
{% else %}
<li class="page-item disabled">
<span class="page-link"></span>
</li>
{% endif %}
{% endfor %}
{% if pagination.has_next %}
<li class="page-item">
<a class="page-link" href="{{ url_for('dashboard.users', page=pagination.next_num) }}">
Successivo <i class="fas fa-chevron-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="alert alert-info text-center">
<i class="fas fa-info-circle fa-2x mb-3"></i>
<h4>Nessun utente trovato</h4>
<p class="mb-0">Non ci sono utenti registrati nel sistema.</p>
</div>
{% endif %}
</div>
<style>
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 2px solid #e9ecef;
}
.dashboard-title {
color: #495057;
margin: 0;
font-weight: 600;
}
.dashboard-title i {
color: #6c757d;
margin-right: 0.5rem;
}
.table th {
font-weight: 600;
border-top: none;
}
.badge {
font-size: 0.75em;
}
.alert i {
color: #0dcaf0;
}
</style>
{% endblock %}

147
utils/create_admin.py Normal file
View File

@@ -0,0 +1,147 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Utility script to create an admin user
import asyncio
import sys
import os
# Add the parent directory to Python path to import our modules
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from models.database import init_database
from models.user import User
async def create_admin_user():
"""Create an admin user for dashboard access"""
try:
# Initialize database
await init_database()
print("✅ Database connection established")
# Check if admin user already exists
existing_admin = await User.find_by_username('admin')
if existing_admin:
print("⚠️ Admin user already exists!")
print(f" Username: {existing_admin.username}")
print(f" Email: {existing_admin.email}")
print(f" Role: {existing_admin.role}")
print(f" Is Admin: {existing_admin.is_admin}")
return
# Create admin user
admin_user = User(
username='admin',
email='admin@hersel.it',
first_name='Admin',
last_name='User',
role='admin'
)
# Set password (change this to a secure password)
admin_password = 'admin123' # CHANGE THIS IN PRODUCTION!
admin_user.set_password(admin_password)
# Save user
await admin_user.save()
print("🎉 Admin user created successfully!")
print("📝 Login credentials:")
print(f" Username: {admin_user.username}")
print(f" Email: {admin_user.email}")
print(f" Password: {admin_password}")
print(f" Role: {admin_user.role}")
print(f" Is Admin: {admin_user.is_admin}")
print("")
print("🔐 IMPORTANT: Change the default password after first login!")
print("📍 You can now access the dashboard at: /dashboard/")
except Exception as e:
print(f"❌ Error creating admin user: {e}")
import traceback
traceback.print_exc()
async def list_users():
"""List all users in the system"""
try:
await init_database()
users = await User.get_all()
if not users:
print("No users found in the system.")
return
print("\n👥 Users in the system:")
print("-" * 80)
print(f"{'ID':<5} {'Username':<15} {'Email':<25} {'Role':<10} {'Admin':<7} {'Active':<8}")
print("-" * 80)
for user in users:
print(f"{user.id:<5} {user.username:<15} {user.email:<25} {user.role:<10} {user.is_admin:<7} {user.is_active:<8}")
print("-" * 80)
print(f"Total users: {len(users)}")
except Exception as e:
print(f"❌ Error listing users: {e}")
async def promote_user_to_admin(username):
"""Promote an existing user to admin"""
try:
await init_database()
user = await User.find_by_username(username)
if not user:
print(f"❌ User '{username}' not found.")
return
# Update user role
user.role = 'admin'
await user.save()
print(f"✅ User '{username}' promoted to admin successfully!")
print(f" Username: {user.username}")
print(f" Email: {user.email}")
print(f" Role: {user.role}")
print(f" Is Admin: {user.is_admin}")
except Exception as e:
print(f"❌ Error promoting user: {e}")
def print_usage():
"""Print usage instructions"""
print("Usage:")
print(" python utils/create_admin.py create # Create default admin user")
print(" python utils/create_admin.py list # List all users")
print(" python utils/create_admin.py promote <username> # Promote user to admin")
print("")
print("Examples:")
print(" python utils/create_admin.py create")
print(" python utils/create_admin.py promote john_doe")
async def main():
"""Main function"""
if len(sys.argv) < 2:
print_usage()
return
command = sys.argv[1].lower()
if command == 'create':
await create_admin_user()
elif command == 'list':
await list_users()
elif command == 'promote':
if len(sys.argv) < 3:
print("❌ Please provide a username to promote.")
print(" Usage: python utils/create_admin.py promote <username>")
return
username = sys.argv[2]
await promote_user_to_admin(username)
else:
print(f"❌ Unknown command: {command}")
print_usage()
if __name__ == '__main__':
asyncio.run(main())