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
84 lines
4.1 KiB
HTML
84 lines
4.1 KiB
HTML
{% extends "admin/base.html" %}
|
|
|
|
{% block title %}{{ 'Modifica' if project else 'Nuovo' }} Progetto{% endblock %}
|
|
{% block page_title %}{{ 'Modifica' if project else 'Nuovo' }} Progetto{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<form method="POST">
|
|
<div class="mb-3">
|
|
<label for="title" class="form-label">Titolo *</label>
|
|
<input type="text" class="form-control" id="title" name="title"
|
|
value="{{ project.title if project else '' }}" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="description" class="form-label">Descrizione *</label>
|
|
<textarea class="form-control" id="description" name="description" rows="4" required>{{ project.description if project else '' }}</textarea>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label for="image_url" class="form-label">URL Immagine</label>
|
|
<input type="text" class="form-control" id="image_url" name="image_url"
|
|
value="{{ project.image_url if project else '' }}" placeholder="img/project.webp">
|
|
<small class="text-muted">Percorso relativo alla cartella static/</small>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
<label for="github_url" class="form-label">URL GitHub</label>
|
|
<input type="url" class="form-control" id="github_url" name="github_url"
|
|
value="{{ project.github_url if project else '' }}">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label for="demo_url" class="form-label">URL Demo</label>
|
|
<input type="url" class="form-control" id="demo_url" name="demo_url"
|
|
value="{{ project.demo_url if project else '' }}">
|
|
</div>
|
|
|
|
<div class="col-md-3 mb-3">
|
|
<label for="display_order" class="form-label">Ordine Visualizzazione</label>
|
|
<input type="number" class="form-control" id="display_order" name="display_order"
|
|
value="{{ project.display_order if project else 0 }}">
|
|
</div>
|
|
|
|
<div class="col-md-3 mb-3">
|
|
<label for="animation_delay" class="form-label">Delay Animazione</label>
|
|
<input type="text" class="form-control" id="animation_delay" name="animation_delay"
|
|
value="{{ project.animation_delay if project else '0s' }}" placeholder="0.2s">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="tags" class="form-label">Tags (separati da virgola)</label>
|
|
<input type="text" class="form-control" id="tags" name="tags"
|
|
value="{% if project %}{% for tag in project.tags|sort(attribute='display_order') %}{{ tag.name }}:{{ tag.color_class }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}"
|
|
placeholder="Python:bg-primary, Flask:bg-info, Docker:bg-success">
|
|
<small class="text-muted">Formato: Nome:colore, Nome:colore (es: Python:bg-primary, Flask:bg-info)</small>
|
|
</div>
|
|
|
|
<div class="mb-3 form-check">
|
|
<input type="checkbox" class="form-check-input" id="is_published" name="is_published"
|
|
{% if not project or project.is_published %}checked{% endif %}>
|
|
<label class="form-check-label" for="is_published">
|
|
Pubblica il progetto
|
|
</label>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button type="submit" class="btn btn-gradient">
|
|
<i class="fas fa-save me-2"></i>Salva
|
|
</button>
|
|
<a href="{{ url_for('admin.projects_manage') }}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-times me-2"></i>Annulla
|
|
</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|