Migration v1 (Flask) -> v2 (FastAPI + Vue.js) complétée
✨ Changements majeurs: - Suppression complète du code Flask legacy - Migration backend FastAPI vers racine /backend - Migration frontend Vue.js vers racine /frontend - Suppression de notytex-v2/ (code monté à la racine) ✅ Validations: - Backend démarre correctement (port 8000) - API /api/v2/health répond healthy - 99/99 tests unitaires passent - Frontend configuré avec proxy Vite 📝 Documentation: - README.md réécrit pour v2 - Instructions de démarrage mises à jour - .gitignore adapté pour backend/frontend/ 🎯 Architecture finale: notytex/ ├── backend/ # FastAPI + SQLAlchemy + Pydantic ├── frontend/ # Vue 3 + Vite + TailwindCSS ├── docs/ # Documentation └── school_management.db # Base de données (inchangée) Jalon 6 complété: Application v2 prête pour utilisation!
This commit is contained in:
0
backend/core/__init__.py
Normal file
0
backend/core/__init__.py
Normal file
117
backend/core/config.py
Normal file
117
backend/core/config.py
Normal file
@@ -0,0 +1,117 @@
|
||||
"""
|
||||
Configuration de l'application avec pydantic-settings.
|
||||
"""
|
||||
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""Configuration de l'application."""
|
||||
|
||||
# Application
|
||||
app_name: str = "Notytex API"
|
||||
app_version: str = "2.0.0"
|
||||
debug: bool = False
|
||||
|
||||
# Database - deux modes de configuration:
|
||||
# 1. database_path: chemin simple vers le fichier SQLite (recommandé)
|
||||
# 2. database_url: URL complète SQLAlchemy (rétrocompatibilité)
|
||||
# Si database_url est défini, il a la priorité sur database_path
|
||||
database_path: Optional[str] = None
|
||||
database_url: Optional[str] = None
|
||||
|
||||
def _resolve_database_path(self) -> Path:
|
||||
"""Résout le chemin de la base de données."""
|
||||
if self.database_url:
|
||||
# Extraire le chemin depuis l'URL SQLAlchemy
|
||||
# Format: sqlite+aiosqlite:///path ou sqlite+aiosqlite:////absolute/path
|
||||
url = self.database_url
|
||||
# Retirer le préfixe
|
||||
for prefix in ["sqlite+aiosqlite://", "sqlite://"]:
|
||||
if url.startswith(prefix):
|
||||
url = url[len(prefix):]
|
||||
break
|
||||
# Le chemin commence par / ou // pour absolu, ou rien pour relatif
|
||||
return Path(url).resolve()
|
||||
elif self.database_path:
|
||||
path = Path(self.database_path)
|
||||
if not path.is_absolute():
|
||||
# Par défaut, relatif au répertoire du projet
|
||||
path = Path(__file__).parent.parent / self.database_path
|
||||
return path.resolve()
|
||||
else:
|
||||
# Valeur par défaut
|
||||
return (Path(__file__).parent.parent / "school_management.db").resolve()
|
||||
|
||||
def get_database_url(self) -> str:
|
||||
"""Retourne l'URL de connexion async pour SQLAlchemy."""
|
||||
if self.database_url is not None and self.database_url.startswith("sqlite"):
|
||||
return self.database_url
|
||||
# Construire depuis le chemin
|
||||
path = self._resolve_database_path()
|
||||
return f"sqlite+aiosqlite:///{path}"
|
||||
|
||||
@property
|
||||
def sync_database_url(self) -> str:
|
||||
"""Retourne l'URL de base de données synchrone."""
|
||||
return self.get_database_url().replace("sqlite+aiosqlite://", "sqlite://")
|
||||
|
||||
@property
|
||||
def database_path_resolved(self) -> Path:
|
||||
"""Retourne le chemin absolu résolu de la base de données."""
|
||||
return self._resolve_database_path()
|
||||
|
||||
@property
|
||||
def database_path_configured(self) -> str:
|
||||
"""Retourne le chemin tel que configuré (pour affichage)."""
|
||||
if self.database_url:
|
||||
# Extraire depuis l'URL
|
||||
return str(self._resolve_database_path())
|
||||
return self.database_path or "school_management.db"
|
||||
|
||||
@property
|
||||
def database_exists(self) -> bool:
|
||||
"""Vérifie si le fichier de base de données existe."""
|
||||
return self.database_path_resolved.exists()
|
||||
|
||||
# API
|
||||
api_v2_prefix: str = "/api/v2"
|
||||
|
||||
# CORS
|
||||
cors_origins: list[str] = ["http://localhost:3000", "http://localhost:5173"]
|
||||
|
||||
# Logging
|
||||
log_level: str = "INFO"
|
||||
log_format: str = "json"
|
||||
|
||||
# Email (repris de v1)
|
||||
smtp_server: Optional[str] = None
|
||||
smtp_port: int = 587
|
||||
smtp_username: Optional[str] = None
|
||||
smtp_password: Optional[str] = None
|
||||
smtp_use_tls: bool = True
|
||||
email_from: Optional[str] = None
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
case_sensitive=False,
|
||||
extra="ignore",
|
||||
)
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def get_settings() -> Settings:
|
||||
"""
|
||||
Récupère les settings (cached).
|
||||
Utilise lru_cache pour éviter de relire le fichier .env à chaque appel.
|
||||
"""
|
||||
return Settings()
|
||||
|
||||
|
||||
# Export pour faciliter l'import
|
||||
settings = get_settings()
|
||||
Reference in New Issue
Block a user