Add authentication system and admin dashboard

Security Features:
- Added User model with bcrypt password hashing
- Implemented Flask-Login for session management
- Protected all API write operations with @login_required decorator
- Added authentication routes (login/logout)

Admin Dashboard:
- Created comprehensive admin dashboard with statistics
- Profile management interface
- Skills management (add/edit/delete)
- Projects management with full CRUD operations
- Social links management
- Modern responsive UI with Bootstrap 5

New Files:
- models.py: Added User model with bcrypt
- routes/auth.py: Login/logout functionality
- routes/admin.py: Complete admin dashboard with CRUD operations
- templates/auth/login.html: Login page
- templates/admin/base.html: Admin base template
- templates/admin/dashboard.html: Main dashboard
- templates/admin/profile.html: Profile editor
- templates/admin/skills.html: Skills manager
- templates/admin/projects.html: Projects list
- templates/admin/project_form.html: Project editor
- templates/admin/social_links.html: Social links manager

Modified Files:
- app.py: Integrated Flask-Login and bcrypt, registered new blueprints
- requirements.txt: Added Flask-Login, Flask-Bcrypt, bcrypt
- init_db.py: Creates default admin user (admin/admin123)
- routes/api.py: Protected all write operations with authentication

Default Credentials:
- Username: admin
- Password: admin123
- ⚠️ MUST be changed after first login!

Benefits:
- Secure API access with session-based authentication
- User-friendly admin interface for content management
- No need to edit code or database directly
- Bcrypt password hashing for security
- Protected against unauthorized access
This commit is contained in:
Claude
2025-11-13 13:49:36 +00:00
parent c6425235a2
commit aa2c704bfb
15 changed files with 1159 additions and 4 deletions

140
templates/admin/base.html Normal file
View File

@@ -0,0 +1,140 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Admin Dashboard{% endblock %} - Portfolio</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--sidebar-width: 250px;
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.sidebar {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: var(--sidebar-width);
background: var(--primary-gradient);
color: white;
padding: 0;
overflow-y: auto;
}
.sidebar-header {
padding: 1.5rem;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.sidebar-menu {
padding: 1rem 0;
}
.sidebar-menu a {
color: rgba(255,255,255,0.8);
text-decoration: none;
padding: 0.75rem 1.5rem;
display: block;
transition: all 0.3s;
}
.sidebar-menu a:hover,
.sidebar-menu a.active {
background: rgba(255,255,255,0.1);
color: white;
}
.main-content {
margin-left: var(--sidebar-width);
padding: 2rem;
}
.top-bar {
background: white;
padding: 1rem 2rem;
margin: -2rem -2rem 2rem -2rem;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}
.stat-card {
background: white;
border-radius: 10px;
padding: 1.5rem;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
transition: transform 0.3s;
}
.stat-card:hover {
transform: translateY(-5px);
}
.btn-gradient {
background: var(--primary-gradient);
border: none;
color: white;
}
.btn-gradient:hover {
opacity: 0.9;
color: white;
}
</style>
{% block extra_css %}{% endblock %}
</head>
<body>
<!-- Sidebar -->
<div class="sidebar">
<div class="sidebar-header">
<h4 class="mb-0"><i class="fas fa-briefcase me-2"></i>Portfolio Admin</h4>
<small class="text-white-50">Ciao, {{ current_user.username }}</small>
</div>
<nav class="sidebar-menu">
<a href="{{ url_for('admin.dashboard') }}" class="{% if request.endpoint == 'admin.dashboard' %}active{% endif %}">
<i class="fas fa-th-large me-2"></i>Dashboard
</a>
<a href="{{ url_for('admin.profile_manage') }}" class="{% if 'profile' in request.endpoint %}active{% endif %}">
<i class="fas fa-user me-2"></i>Profilo
</a>
<a href="{{ url_for('admin.skills_manage') }}" class="{% if 'skills' in request.endpoint %}active{% endif %}">
<i class="fas fa-code me-2"></i>Competenze
</a>
<a href="{{ url_for('admin.projects_manage') }}" class="{% if 'projects' in request.endpoint %}active{% endif %}">
<i class="fas fa-folder-open me-2"></i>Progetti
</a>
<a href="{{ url_for('admin.social_links_manage') }}" class="{% if 'social' in request.endpoint %}active{% endif %}">
<i class="fas fa-share-alt me-2"></i>Link Social
</a>
<hr class="border-white my-3 mx-3">
<a href="{{ url_for('route_home.home') }}" target="_blank">
<i class="fas fa-external-link-alt me-2"></i>Visualizza Sito
</a>
<a href="{{ url_for('auth.logout') }}">
<i class="fas fa-sign-out-alt me-2"></i>Logout
</a>
</nav>
</div>
<!-- Main Content -->
<div class="main-content">
<div class="top-bar">
<div class="d-flex justify-content-between align-items-center">
<h3 class="mb-0">{% block page_title %}Dashboard{% endblock %}</h3>
<div>
<span class="badge bg-success"><i class="fas fa-check-circle me-1"></i>Online</span>
</div>
</div>
</div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% block extra_js %}{% endblock %}
</body>
</html>