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:
140
templates/admin/base.html
Normal file
140
templates/admin/base.html
Normal 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>
|
||||
Reference in New Issue
Block a user