Migrate from Quart to Flask and add MariaDB dynamic database

Major Changes:
- Migrated web framework from Quart (async) to Flask (sync)
- Added MariaDB database integration with SQLAlchemy ORM
- Implemented dynamic content management for portfolio

New Features:
- Database models for Profile, Skills, Projects, ProjectTags, and SocialLinks
- RESTful API endpoints for CRUD operations on all entities
- Database initialization script (init_db.py) with sample data
- Docker Compose configuration with MariaDB service

Modified Files:
- app.py: Replaced Quart with Flask, added database initialization
- config.py: Added database configuration with environment variables
- routes/home.py: Converted async to sync, added database queries
- requirements.txt: Replaced Quart/Hypercorn with Flask/Gunicorn, added Flask-SQLAlchemy and PyMySQL
- docker-compose.yml: Added MariaDB service with health checks
- templates/: Updated all templates to use dynamic data from database with Jinja2
- .env.example: Added database configuration variables
- README.md: Complete rewrite with new setup instructions and API documentation

New Files:
- models.py: SQLAlchemy models for all database entities
- init_db.py: Database initialization script
- routes/api.py: REST API endpoints for content management

Benefits:
- Simplified architecture (sync vs async)
- Better ecosystem compatibility
- Dynamic content management via database
- Easy content updates through REST API
- Improved deployment with standard WSGI server (Gunicorn)
This commit is contained in:
Claude
2025-11-13 09:16:24 +00:00
parent 058f6205d7
commit c6425235a2
13 changed files with 1010 additions and 217 deletions

180
init_db.py Normal file
View File

@@ -0,0 +1,180 @@
"""
Database initialization script
Populates the database with initial portfolio data
"""
from app import app
from models import db, Profile, Skill, Project, ProjectTag, SocialLink
def init_database():
"""Initialize database with portfolio data"""
with app.app_context():
# Drop all tables and recreate
print("Dropping all tables...")
db.drop_all()
print("Creating all tables...")
db.create_all()
# Create profile information
print("Adding profile information...")
profile = Profile(
title="Il ponte tra sistemi e sviluppo web",
lead_text="Con oltre 7 Anni di esperienza nello sviluppo di applicazioni web con Python Flask, offro soluzioni complete end-to-end.",
description_1="La mia doppia specializzazione mi permette di comprendere a fondo l'intero ciclo di vita delle applicazioni, dall'architettura del server fino all'implementazione e al deployment.",
description_2="Mi piace risolvere problemi complessi e creare soluzioni che siano robuste, scalabili e facili da mantenere.",
years_experience=7
)
db.session.add(profile)
# Create skills
print("Adding skills...")
skills_data = [
{"name": "Linux", "icon_class": "fab fa-linux", "category": "OS", "display_order": 1},
{"name": "Windows", "icon_class": "fab fa-windows", "category": "OS", "display_order": 2},
{"name": "Python", "icon_class": "fab fa-python", "category": "Language", "display_order": 3},
{"name": "Flask", "icon_class": "fas fa-flask", "category": "Framework", "display_order": 4},
{"name": "Database", "icon_class": "fas fa-database", "category": "Tool", "display_order": 5},
{"name": "Docker", "icon_class": "fab fa-docker", "category": "Tool", "display_order": 6},
{"name": "Server", "icon_class": "fas fa-server", "category": "Infrastructure", "display_order": 7},
{"name": "Networking", "icon_class": "fas fa-network-wired", "category": "Infrastructure", "display_order": 8},
]
for skill_data in skills_data:
skill = Skill(**skill_data)
db.session.add(skill)
# Create projects
print("Adding projects...")
# Project 1: Database Backup Script
project1 = Project(
title="Script di Backup Database (MariaDB/MySQL)",
description="Script in Bash per sistemi Linux che permette l'automazione dei backup database",
image_url="img/bash.webp",
github_url="https://github.com/BluLupo/server-script/tree/main/db_bash_backup-main",
display_order=1,
animation_delay="0s"
)
db.session.add(project1)
db.session.flush() # Get project1.id
# Project 1 tags
project1_tags = [
ProjectTag(project_id=project1.id, name="Bash", color_class="bg-primary", display_order=1),
ProjectTag(project_id=project1.id, name="Linux", color_class="bg-info", display_order=2),
]
db.session.add_all(project1_tags)
# Project 2: ByteStash
project2 = Project(
title="Personal ByteStash",
description="Ho realizzato un repository personale di snippet sfruttando Bytestash, ottimizzando la gestione del codice riutilizzabile e migliorando la produttività nello sviluppo di progetti software.",
image_url="img/byte.webp",
demo_url="https://bytestash.gwserver.it/public/snippets",
display_order=2,
animation_delay="0.2s"
)
db.session.add(project2)
db.session.flush()
# Project 2 tags
project2_tags = [
ProjectTag(project_id=project2.id, name="LXC", color_class="bg-warning text-dark", display_order=1),
ProjectTag(project_id=project2.id, name="Proxmox", color_class="bg-dark", display_order=2),
ProjectTag(project_id=project2.id, name="Nginx", color_class="bg-info", display_order=3),
ProjectTag(project_id=project2.id, name="Reverse Proxy", color_class="bg-secondary", display_order=4),
ProjectTag(project_id=project2.id, name="Linux", color_class="bg-primary", display_order=5),
ProjectTag(project_id=project2.id, name="Self-hosted", color_class="bg-primary", display_order=6),
]
db.session.add_all(project2_tags)
# Project 3: Nextcloud
project3 = Project(
title="Nextcloud Personale",
description="Installazione di Nextcloud su container LXC con database PostgreSQL e caching Redis, integrato in una rete privata con gestione IP tramite server DHCP.",
image_url="img/next.webp",
demo_url="https://cloud.gwserver.it",
display_order=3,
animation_delay="0.4s"
)
db.session.add(project3)
db.session.flush()
# Project 3 tags
project3_tags = [
ProjectTag(project_id=project3.id, name="Nextcloud", color_class="bg-primary", display_order=1),
ProjectTag(project_id=project3.id, name="PostgreSQL", color_class="bg-secondary", display_order=2),
ProjectTag(project_id=project3.id, name="Redis", color_class="bg-info", display_order=3),
ProjectTag(project_id=project3.id, name="LXC", color_class="bg-warning text-dark", display_order=4),
ProjectTag(project_id=project3.id, name="Proxmox", color_class="bg-dark", display_order=5),
ProjectTag(project_id=project3.id, name="Rete Privata", color_class="bg-success", display_order=6),
ProjectTag(project_id=project3.id, name="DHCP Server", color_class="bg-secondary", display_order=7),
]
db.session.add_all(project3_tags)
# Create social links
print("Adding social links...")
social_links_data = [
{
"platform_name": "LinkedIn",
"url": "https://linkedin.com/in/hersel",
"icon_class": "fab fa-linkedin",
"display_order": 1,
"animation_delay": "0.1s"
},
{
"platform_name": "GitHub",
"url": "https://github.com/blulupo",
"icon_class": "fab fa-github",
"display_order": 2,
"animation_delay": "0.2s"
},
{
"platform_name": "Stack Overflow",
"url": "https://stackoverflow.com/users/11765177/hersel-giannella",
"icon_class": "fab fa-stack-overflow",
"display_order": 3,
"animation_delay": "0.3s"
},
{
"platform_name": "CodeWars",
"url": "https://www.codewars.com/users/BluLupo",
"icon_class": "fas fa-code",
"display_order": 4,
"animation_delay": "0.4s"
},
{
"platform_name": "Blog",
"url": "https://blog.hersel.it",
"icon_class": "fas fa-blog",
"display_order": 5,
"animation_delay": "0.5s"
},
{
"platform_name": "Email",
"url": "mailto:info@hersel.it",
"icon_class": "fas fa-envelope",
"display_order": 6,
"animation_delay": "0.6s"
},
]
for link_data in social_links_data:
social_link = SocialLink(**link_data)
db.session.add(social_link)
# Commit all changes
print("Committing changes to database...")
db.session.commit()
print("\n✅ Database initialized successfully!")
print(f" - Profile: 1 record")
print(f" - Skills: {len(skills_data)} records")
print(f" - Projects: 3 records")
print(f" - Project Tags: {len(project1_tags) + len(project2_tags) + len(project3_tags)} records")
print(f" - Social Links: {len(social_links_data)} records")
if __name__ == '__main__':
init_database()