# Fichier: python_cheats/cheatsheets/heroku.txt
# Cheatsheet Heroku - Guide Ultra-Détaillé pour Grands Débutants


[OK] CONCEPTS FONDAMENTAUX (EXPLICATIONS TRÈS DÉTAILLÉES)

# === QU'EST-CE QUE HEROKU ? ===

# Imagine que tu as créé une super app Python sur ton ordinateur
# Elle fonctionne parfaitement quand tu la lances localement (python app.py)
# Mais comment faire pour que des gens sur internet y accèdent?

# Solution classique (compliquée):
# 1. Louer un serveur sur internet
# 2. Installer Linux, Python, etc. sur ce serveur
# 3. Configurer un firewall, un reverse proxy, SSL, etc.
# 4. Gérer les backups, les mises à jour du serveur...
# = BEAUCOUP DE TRAVAIL! C'est très technique et cher.

# HEROKU = Plateforme qui fait tout ça pour toi!
# Tu ne fais que:
# 1. Pousser ton code via git
# 2. BOOM! Ton app est en ligne
# 3. Des gens du monde entier peuvent y accéder via une URL

# Heroku s'appelle une "PaaS" (Platform as a Service)
# = Plateforme où tu déploies facilement sans gérer l'infrastructure

# === VOCABULAIRE HEROKU (TRÈS IMPORTANT!) ===

# APP (Application)
# = C'est ton projet déployé sur Heroku
# = A une URL unique: myproject.herokuapp.com
# = C'est comme un conteneur qui contient tout ton code
# Exemple: Si tu as un bot Discord, une app Heroku = le bot en ligne

# DYNO (Conteneur léger)
# = Un petit serveur Linux qui exécute ton app
# = Heroku gère tout (OS, updates, maintenance)
# Analogie: Si Heroku est un immeuble, un dyno est un appartement
# Types de dynos:
#   - Web Dyno: exécute ton app web (Flask, Django, etc)
#   - Worker Dyno: exécute des tâches en arrière-plan
# = Tu paies selon le nombre de dynos et leur taille

# DYNO HOURS (Crédit gratuit)
# = Chaque dyno consomme des "dyno hours"
# = Compte gratuit = 550 dyno-hours par mois gratuit
# Calcul simple:
#   - 1 dyno toujours actif 24h = 1 dyno × 24h × 30j = 720 dyno-hours
#   - 720 > 550 = tu dépasses ton quota gratuit!
# Solutions:
#   - Payant: $7/mois minimum par dyno (stay-awake)
#   - Gratuit: Ton app s'endort après 30 min d'inactivité (Free dyno)

# GIT REPOSITORY (Déploiement)
# = Tu pousse ton code vers Heroku avec git (comme GitHub)
# = Heroku détecte les changements et redéploie automatiquement
# = Aucune FTP, aucun upload manuel!

# CONFIG / ENVIRONMENT VARIABLES (Configuration)
# = Des variables qui stockent tes secrets (clés API, mots de passe)
# = Jamais commités dans git (sécurité!)
# = Accessibles à ton app via os.environ.get("VAR_NAME")
# Exemple: Clé API pour une API externe, URL de base de données

# ADD-ON (Service supplémentaire)
# = Services tiers intégrés (base de données, cache, email, etc)
# Exemples:
#   - heroku-postgresql: Base de données PostgreSQL
#   - heroku-redis: Cache/file d'attente Redis
#   - SendGrid: Service d'envoi d'emails
# = Certains sont gratuits, d'autres payants

# PROCFILE (Instructions de démarrage)
# = Fichier qui dit à Heroku comment lancer ton app
# = Sans Procfile, Heroku ne sait pas quoi faire!
# Exemple contenu:
#   web: gunicorn app:app
# = Signifie: "Lance Gunicorn avec le fichier app.py"

# === COMMENT ÇA MARCHE? (FLUX COMPLET) ===

# 1. Tu as un dossier Python sur ton ordinateur
#    myapp/
#    ├── app.py (ton app Flask/Django)
#    ├── requirements.txt (liste des packages)
#    ├── Procfile (instructions Heroku)
#    └── .git (dépôt git)

# 2. Tu fais un commit git:
#    git add .
#    git commit -m "Version 1.0"

# 3. Tu pousses vers Heroku:
#    git push heroku main
#    (Au lieu de pousser vers GitHub)

# 4. Heroku reçoit ton code et:
#    a. Crée un conteneur (dyno)
#    b. Lit Procfile pour savoir comment démarrer
#    c. Installe Python et tes packages (requirements.txt)
#    d. Lance ton app avec gunicorn (ou autre)
#    e. Assign une URL: myapp.herokuapp.com

# 5. Quelqu'un ouvre: https://myapp.herokuapp.com
#    Heroku route la requête vers ton dyno
#    Ton app répond
#    La personne voit le résultat!

# 6. Si tu modifies ton code:
#    git add .
#    git commit -m "Fix bug"
#    git push heroku main
#    Heroku répète les étapes 4a-4e (redéploiement automatique)


[OK] INSTALLATION SUPER DÉTAILLÉE

# === ÉTAPE 1: CRÉER UN COMPTE HEROKU ===

# Pourquoi? Tu dois avoir un compte pour pouvoir déployer des apps

# Instructions:
# 1. Ouvre un navigateur (Chrome, Firefox, Safari...)
# 2. Va sur: https://www.heroku.com/
# 3. Clique sur le bouton "Sign Up" (haut à droite)
# 4. Remplis le formulaire:
#    - Email: ton adresse email
#    - Password: mot de passe sécurisé (>= 8 caractères)
#    - Prénom/Nom: ton nom
#    - Choisis une région: Europe ou USA (ne change rien pour toi)
# 5. Vérifie ton email: Heroku t'envoie un lien de confirmation
# 6. Clique le lien de confirmation
# 7. YAY! Ton compte est créé!

# Note: C'est 100% gratuit pour commencer!


# === ÉTAPE 2: INSTALLER HEROKU CLI ===

# Pourquoi? CLI = Command Line Interface = Programme pour lancer des commandes
# Heroku CLI te permet de contrôler Heroku depuis ton terminal

# === INSTALLATION SELON TON OS ===

# ========== macOS (avec Homebrew) ==========
# Si tu n'as pas Homebrew: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

brew tap heroku/brew && brew install heroku

# Après l'installation, ferme et réouvre le terminal
# Puis vérifie:
heroku --version
# Doit afficher: heroku/7.X.X

# ========== Windows (Installer exe) ==========
# 1. Ouvre un navigateur
# 2. Va sur: https://cli-assets.heroku.com/heroku-x64.exe
# 3. Le fichier télécharge (heroku-x64.exe)
# 4. Double-clique sur le fichier
# 5. Clique "Suivant" plusieurs fois
# 6. À la fin, redémarre ton ordinateur
# 7. Ouvre PowerShell ou cmd
# 8. Tape: heroku --version
# Doit afficher: heroku/7.X.X

# ========== Linux (Ubuntu/Debian) ==========
# Ouvre un terminal et tape:

curl https://cli-assets.heroku.com/install.sh | sh

# Puis vérifie:
heroku --version


# === ÉTAPE 3: SE CONNECTER À HEROKU ===

# Pourquoi? Pour que la CLI sache que c'est TOI qui vas déployer

# Commande:
heroku login

# Qu'il se passe:
# 1. Un navigateur s'ouvre automatiquement
# 2. Heroku te demande de confirmer (clique sur "Log In")
# 3. Ton terminal affiche: "Logged in as email@example.com"
# 4. Tes credentials sont sauvegardés localement

# Alternative (sans ouvrir le navigateur):
heroku login -i
# Demande: Email
# Demande: Password
# Tape ton email et mot de passe directement

# Pour vérifier que tu es connecté:
heroku auth:whoami
# Affiche: email@example.com


[OK] STRUCTURE DE PROJET (EXPLICATIONS TRÈS DÉTAILLÉES)

# === POURQUOI CETTE STRUCTURE? ===

# Heroku ne sait pas automatiquement comment lancer ton app!
# Tu dois lui donner des instructions très précises
# Sinon Heroku va essayer de deviner et va échouer

# === FICHIERS OBLIGATOIRES ===

# === FILE #1: Procfile (TRÈS IMPORTANT!) ===

# Qu'est-ce que c'est?
# = Un fichier de configuration pour Heroku
# = Pas d'extension (.txt, .py... rien!)
# = Contient des instructions pour démarrer ton app

# Pourquoi?
# Sur ton ordinateur tu fais: python app.py
# Sur Heroku tu ne peux pas faire ça manuellement!
# Procfile dit: "Heroku, voici comment démarrer: ..."

# Comment le créer?

# === Sur Linux/macOS ===
# Ouvre terminal dans ton dossier de projet
cd /chemin/vers/myproject

# Crée le fichier:
echo "web: gunicorn app:app" > Procfile

# Vérifie (affiche le contenu):
cat Procfile

# === Sur Windows ===
# Ouvre PowerShell dans ton dossier de projet
cd C:\chemin\vers\myproject

# Crée le fichier:
"web: gunicorn app:app" | Out-File Procfile -Encoding UTF8

# Ou crée simplement avec un éditeur:
# Clique droit -> Créer fichier texte -> Renomme en "Procfile"


# === Contenu du Procfile ===

# Format général:
# process-name: command-to-run

# Explications des éléments:

# "web" = Type de processus
# = Heroku va créer un "web dyno" pour ça
# = C'est l'app web que les gens visitent

# "gunicorn app:app" = La commande à exécuter
# = gunicorn = Serveur web pour Python (plus puissant que Flask dev server)
# = app = Le nom du fichier (app.py)
# = app:app = "fichier.py:variable_app"
# = Dit à gunicorn: "Dans app.py, utilise la variable nommée 'app'"

# Exemples selon ton app:

# Pour Flask (classe nommée app):
web: gunicorn app:app

# Pour Flask (classe nommée application):
web: gunicorn app:application

# Pour Django:
web: gunicorn myproject.wsgi
# (myproject = nom de ton projet Django)

# Pour Streamlit:
web: streamlit run app.py --server.port=$PORT --server.headless true

# Pour FastAPI:
web: gunicorn main:app --worker-class uvicorn.workers.UvicornWorker

# Pour tâche en arrière-plan (worker):
worker: python worker.py

# Pour plusieurs processus:
web: gunicorn app:app
worker: celery -A app.celery worker


# === FILE #2: runtime.txt (RECOMMANDÉ) ===

# Qu'est-ce que c'est?
# = Fichier qui dit à Heroku: "Utilise Python version X.Y.Z"

# Pourquoi?
# Sans ça, Heroku choisit une version par défaut
# Exemple: Heroku pourrait choisir Python 3.8 (très vieux!)
# Alors que tu développes en Python 3.11
# = Version différente = bugs potentiels!

# Comment le créer?

# Linux/macOS:
echo "python-3.11.0" > runtime.txt

# Windows PowerShell:
"python-3.11.0" | Out-File runtime.txt -Encoding UTF8

# Vérifier:
cat runtime.txt

# Versions disponibles sur Heroku:
# https://devcenter.heroku.com/articles/python-runtimes
# Exemples:
python-3.11.8
python-3.10.12
python-3.9.18


# === FILE #3: requirements.txt (OBLIGATOIRE!) ===

# Qu'est-ce que c'est?
# = Liste de tous les packages Python dont tu as besoin
# = Heroku lit ce fichier et installe tous les packages automatiquement

# Pourquoi?
# Ton app a besoin de Flask, Requests, etc.
# Sur ton ordinateur tu les as installés dans ton venv
# Mais Heroku n'a pas ton venv!
# requirements.txt lui dit: "Installe ces packages svp"

# Comment le créer?

# Si tu as un venv activé:
source venv/bin/activate  # Linux/macOS
venv\Scripts\activate     # Windows

# Puis:
pip freeze > requirements.txt

# Cela crée un fichier avec TOUTES tes dépendances
# Exemple contenu:
# Flask==2.3.0
# Requests==2.31.0
# gunicorn==20.1.0

# IMPORTANT: Ajoute gunicorn si tu l'utilises!
pip install gunicorn
pip freeze > requirements.txt

# Vérifier:
cat requirements.txt


# === FILE #4: .gitignore (IMPORTANT POUR LA SÉCURITÉ!) ===

# Qu'est-ce que c'est?
# = Fichier qui dit à git: "N'ajoute PAS ces fichiers au dépôt"

# Pourquoi?
# Ton dossier contient:
#   - .env (fichier avec tes secrets)
#   - venv/ (dossier environnement virtuel - 100MB+!)
#   - __pycache__/ (cache Python)
# Si tu les commites, tout le monde sur GitHub les voit!
# DANGER! (tes clés API, mots de passe visibles!)

# Comment le créer?

# Linux/macOS:
cat > .gitignore << 'EOF'
venv/
.env
.env.local
*.pyc
__pycache__/
.venv/
ENV/
instance/
.DS_Store
.pytest_cache/
.mypy_cache/
*.egg-info/
EOF

# Windows PowerShell:
@"
venv/
.env
.env.local
*.pyc
__pycache__/
.venv/
ENV/
instance/
.DS_Store
.pytest_cache/
.mypy_cache/
*.egg-info/
"@ | Out-File .gitignore -Encoding UTF8

# Ou créer manuellement:
# 1. Crée un fichier ".gitignore" (point au début!)
# 2. Copie les lignes ci-dessus


# === Structure complète d'un projet ===

# myproject/
# │
# ├── .git/                  # Dossier git (créé par "git init")
# ├── .gitignore             # Fichier: dit à git quoi ignorer
# ├── venv/                  # Dossier: environnement virtuel (IGNORÉ par git)
# │
# ├── app.py                 # Fichier: ton app Flask/Django/autre
# ├── Procfile               # Fichier: instructions Heroku (OBLIGATOIRE!)
# ├── runtime.txt            # Fichier: version Python
# ├── requirements.txt       # Fichier: liste des packages
# │
# ├── README.md              # Fichier: documentation
# └── .env                   # Fichier: variables secrètes (IGNORÉ!)


[OK] PRÉPARATION DÉTAILLÉE AVANT DÉPLOIEMENT

# === ÉTAPE 1: Initialiser git ===

# Pourquoi?
# Heroku déploie VIA git
# Sans git, tu ne peux pas déployer!

# Vérifier si git est déjà initialisé:
ls -la | grep ".git"  # Linux/macOS
dir | findstr ".git"  # Windows

# Si rien n'apparaît: git n'est pas initialisé
# Initialiser git:
git init

# Vérifier:
git config --list
# Doit afficher plein de config git

# Note: Si tu as cloné un repo GitHub, git est déjà là!


# === ÉTAPE 2: Créer ton app ===

# Example app.py minimal Flask:

# app.py
from flask import Flask
import os

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World! Mon app fonctionne!"

if __name__ == "__main__":
    # IMPORTANT: utiliser PORT de Heroku!
    port = int(os.environ.get("PORT", 5000))
    app.run(host="0.0.0.0", port=port, debug=False)

# Pourquoi ces détails?
# - os.environ.get("PORT", 5000) = Utilise port d'Heroku ou 5000 par défaut
# - host="0.0.0.0" = Accessible de partout (pas seulement localhost)
# - debug=False = Ne pas activer debug mode en production!


# === ÉTAPE 3: Installer dépendances et créer requirements.txt ===

# Si tu utilises un venv (recommandé!):
python -m venv venv
source venv/bin/activate  # Linux/macOS
venv\Scripts\activate     # Windows

# Installer tes packages:
pip install flask
pip install requests
pip install gunicorn      # TRÈS IMPORTANT pour Heroku!

# Créer requirements.txt:
pip freeze > requirements.txt

# Vérifier le contenu:
cat requirements.txt
# Doit afficher:
# Flask==2.3.0
# Werkzeug==2.3.0
# gunicorn==20.1.0
# etc...


# === ÉTAPE 4: Tester localement ===

# Avant de déployer sur Heroku, tester sur ton ordinateur!

# Lancer ton app localement:
python app.py
# Doit afficher: "Running on http://127.0.0.1:5000/"

# Ouvre un navigateur: http://localhost:5000
# Tu vois: "Hello World! Mon app fonctionne!"

# Si erreur? Regarde le message d'erreur et corrige!

# Pour arrêter: Ctrl+C


# === ÉTAPE 5: Créer Procfile et runtime.txt ===

# Procfile:
echo "web: gunicorn app:app" > Procfile

# runtime.txt:
echo "python-3.11.0" > runtime.txt


# === ÉTAPE 6: Tester le Procfile localement ===

# Heroku fournit un outil: heroku local
# Il simule l'environnement Heroku sur ton ordinateur

# Installer gunicorn (si pas fait):
pip install gunicorn

# Tester:
heroku local web

# Affiche quelque chose comme:
# [OKAY] Loaded ENV .env File as KEY=VALUE Output
# [OKAY] Queued 'web: gunicorn app:app' for immediate execution
# [web.1] Listening on 0.0.0.0:5000

# Ouvre http://localhost:5000 dans le navigateur
# Tu vois ton app comme si tu étais sur Heroku!

# Si erreur? Le problème sera aussi sur Heroku. Corrige avant de pousser!

# Pour arrêter: Ctrl+C


# === ÉTAPE 7: Commiter ton code ===

# Vérifier les fichiers à commiter:
git status
# Affiche les fichiers modifiés

# Ajouter tous les fichiers:
git add .

# Vérifier ce qui sera commité:
git status
# Doit afficher tout en vert

# Faire le commit:
git commit -m "Initial commit - app Flask prête"

# Vérifier le commit:
git log
# Affiche l'historique


# === ÉTAPE 8: Créer l'app sur Heroku ===

# Tu es prêt à créer ton app sur Heroku!

# Assurez-toi que tu es dans le dossier du projet:
pwd  # Linux/macOS (affiche le chemin actuel)
cd   # Windows (affiche le chemin actuel)

# Créer l'app Heroku:
heroku create
# Affiche:
# Creating app... done, [BLACK_HEXAGON] random-name-12345
# https://random-name-12345.herokuapp.com/ | https://git.heroku.com/random-name-12345.git

# Heroku a généré un nom aléatoire (random-name-12345)
# Tu peux utiliser ce nom ou en choisir un spécifique

# Avec un nom personnalisé:
heroku create myapp-name
# IMPORTANT: Le nom doit être unique (pas déjà pris!)

# Vérifier que le remote git a été ajouté:
git remote -v
# Affiche:
# heroku  https://git.heroku.com/myapp-name.git


[OK] DÉPLOIEMENT (LE MOMENT DE VÉRITÉ!)

# === ÉTAPE 1: Pousser le code ===

# C'est LA commande qui déploie ton app sur Heroku!

git push heroku main

# Si tu as une branche "master" au lieu de "main":
git push heroku master

# Qu'il se passe:
# 1. Heroku reçoit ton code
# 2. Lit le Procfile
# 3. Installe Python et packages (requirements.txt)
# 4. Lance gunicorn (ou autre serveur)
# 5. Assigne une URL

# Affiche plein d'info (verbose):
# Receiving objects: 100% (X/X), done.
# remote: Compressing source files... done.
# remote: Building application from source...
# ...
# remote: -----> Python Support Installed
# remote: -----> Installing requirements with pip
# remote: Collecting Flask==2.3.0
# ...
# remote: -----> Launching...
# remote: Released v1
# remote: https://myapp-name.herokuapp.com/ deployed to Heroku


# === ÉTAPE 2: Vérifier le déploiement ===

# Ouvrir l'app dans le navigateur automatiquement:
heroku open
# Ouvre: https://myapp-name.herokuapp.com/

# Tu devrais voir: "Hello World! Mon app fonctionne!"

# Si tu vois une erreur "Application Error"?
# Regarde les logs:
heroku logs --tail
# Affiche les erreurs en temps réel


[OK] LOGS & DÉBOGAGE (QUAND ÇA VA MAL)

# === COMPRENDRE LES LOGS ===

# Les logs = Messages que ton app affiche pendant son exécution
# Très utile pour trouver les bugs!

# Voir les logs en temps réel:
heroku logs --tail
# Affiche les logs continuellement
# Pour arrêter: Ctrl+C

# Voir les 50 dernières lignes:
heroku logs --num 50

# Voir les logs des 24 dernières heures:
heroku logs --num 1000

# Voir seulement les logs d'erreur:
heroku logs --tail | grep -i error

# Logs de processus spécifiques:
heroku logs --source web    # Logs du dyno web
heroku logs --source app    # Logs de ton app
heroku logs --source heroku # Logs système Heroku


# === EXEMPLE: Application crashed ===

# Tu vois dans le navigateur: "Application Error"
# Regarde les logs:
heroku logs --tail

# Exemple erreur dans les logs:
# ModuleNotFoundError: No module named 'flask'

# Signifie: Flask n'a pas été installé
# Solution: Ajoute flask à requirements.txt et redéploie


# === RELANCER L'APP ===

# Parfois l'app bugge, tu veux juste la relancer:
heroku restart
# Tue et redémarre ton dyno


[OK] VARIABLES D'ENVIRONNEMENT EXPLIQUÉES TRÈS SIMPLEMENT

# === PROBLÈME: Où stocker les secrets? ===

# Imagine tu as une clé API pour une service externe:
API_KEY = "secret_key_12345abcde"

# Problème: Si tu la mets dans le code (app.py):
# 1. Tu la commites dans git
# 2. Elle est visible pour tout le monde sur GitHub
# 3. N'IMPORTE QUI peut l'utiliser pour faire de mauvaises choses!

# Solution: Les variables d'environnement!
# = Tu les stockes sur Heroku (pas dans le code)
# = Ton code les récupère pendant l'exécution
# = Personne ne voit les secrets


# === COMMENT ÇA MARCHE? ===

# Étape 1: Ajouter la variable sur Heroku
heroku config:set API_KEY="secret_key_12345abcde"

# Étape 2: Heroku stocke ça sur ses serveurs (sécurisé)

# Étape 3: Ton code récupère la valeur:
import os
api_key = os.environ.get("API_KEY")
# api_key = "secret_key_12345abcde"

# Personne n'a vu le secret!


# === EXEMPLE CONCRET: API DATABASE ===

# Imagine tu utilises une base de données PostgreSQL sur Heroku

# Heroku crée automatiquement une variable DATABASE_URL
# Contenant: postgresql://user:pass@host:port/dbname

# Ton code Python:
import os
database_url = os.environ.get("DATABASE_URL")
print(f"Connecté à: {database_url}")

# Si quelqu'un regarde ton code sur GitHub:
# Il ne voit QUE: os.environ.get("DATABASE_URL")
# Il ne voit PAS: user:pass:host:port
# Sécurisé!


# === AJOUTER PLUSIEURS VARIABLES ===

# Une par une:
heroku config:set VAR1=value1
heroku config:set VAR2=value2

# Ou toutes d'un coup:
heroku config:set VAR1=value1 VAR2=value2 VAR3=value3


# === VOIR LES VARIABLES ===

# Afficher toutes les variables:
heroku config
# Affiche:
# === myapp Config Vars
# API_KEY:       secret_key_12345abcde
# DATABASE_URL:  postgresql://...
# DEBUG:         False

# Voir une variable spécifique:
heroku config:get DATABASE_URL
# Affiche: postgresql://...


# === SUPPRIMER UNE VARIABLE ===

heroku config:unset API_KEY
# L'enlève complètement


# === DÉVELOPPEMENT LOCAL: Fichier .env ===

# Sur Heroku, tu utilises heroku config:set
# Mais localement (sur ton ordinateur), tu utilises un fichier .env

# Créer .env à la racine du projet:
# .env
DEBUG=True
API_KEY=test_key_local_12345
DATABASE_URL=postgresql://localhost/mydb

# N'OUBLIE PAS: Ajouter .env au .gitignore!
echo ".env" >> .gitignore

# Installer python-dotenv:
pip install python-dotenv

# Charger .env dans ton app:
# app.py
from dotenv import load_dotenv
import os

load_dotenv()  # Charge le fichier .env

debug = os.environ.get("DEBUG", "False")
api_key = os.environ.get("API_KEY")
database_url = os.environ.get("DATABASE_URL")

# Maintenant tu peux tester localement:
python app.py


[OK] BASE DE DONNÉES (PostgreSQL)

# === POURQUOI UNE BASE DE DONNÉES? ===

# Ton app a besoin de persister les données
# Exemples:
# - Les utilisateurs qui créent un compte
# - Les posts qu'ils créent
# - Les messages qu'ils envoient

# Si tu relances ton app, les données doivent rester!
# Fichier SQLite: Marche localement, PAS bon en production
# PostgreSQL sur Heroku: Professionnel, sécurisé, redondant


# === AJOUTER PostgreSQL ===

# Sur le dashboard Heroku ou avec CLI:

heroku addons:create heroku-postgresql:hobby-dev

# "heroku-postgresql" = Service PostgreSQL
# "hobby-dev" = Tier gratuit (0.5GB limité)
# = C'est gratuit!

# Cela crée automatiquement une base de données
# Et ajoute la variable d'environnement DATABASE_URL!

# Vérifier:
heroku config:get DATABASE_URL
# Affiche: postgresql://user:pass@ec2-1.2.3.4.compute.amazonaws.com:5432/dbname


# === UTILISER PostgreSQL AVEC Django ===

# Installer le driver PostgreSQL:
pip install psycopg2-binary
pip freeze > requirements.txt

# Dans Django settings.py:
import os
import dj_database_url

DATABASES = {
    'default': dj_database_url.config(
        default=os.environ.get("DATABASE_URL"),
        conn_max_age=600
    )
}

# Faire les migrations:
heroku run python manage.py migrate

# Créer un superuser:
heroku run python manage.py createsuperuser


# === ACCÉDER À LA BASE DE DONNÉES ===

# Ouvrir un shell PostgreSQL:
heroku pg:psql
# Te connecte directement à la DB!

# Exemples de commandes SQL:
\dt                    # Liste les tables
SELECT * FROM users;   # Voir les utilisateurs
\q                     # Quitter


[OK] DÉPLOYER DES MISES À JOUR (C'EST FACILE!)

# Après modifier ton code:

# === Étape 1: Tester localement ===
python app.py
# Ouvre http://localhost:5000
# Vérifie que ça marche!

# === Étape 2: Commiter ===
git add .
git commit -m "Fix bug dans la page d'accueil"

# === Étape 3: Pousser vers Heroku ===
git push heroku main
# C'EST TOUT!

# Heroku redéploie automatiquement!
# L'app est mis à jour en moins de 1 minute


# === Vérifier le déploiement ===

heroku logs --tail
# Vérifie que tout s'est bien passé

heroku open
# Ouvre ta nouvelle version


[OK] EXEMPLE COMPLET: FLASK POUR DÉBUTANTS

# === ÉTAPE 1: Créer le dossier du projet ===

# macOS/Linux:
mkdir myflaskapp && cd myflaskapp

# Windows:
mkdir myflaskapp
cd myflaskapp


# === ÉTAPE 2: Initialiser git ===

git init


# === ÉTAPE 3: Créer un environnement virtuel ===

# macOS/Linux:
python3 -m venv venv
source venv/bin/activate

# Windows:
python -m venv venv
venv\Scripts\activate

# Doit afficher: (venv) $ ou (venv) C:\...


# === ÉTAPE 4: Installer les packages ===

pip install flask gunicorn python-dotenv

# Explications:
# - flask: Le framework web
# - gunicorn: Serveur web pour production (Heroku)
# - python-dotenv: Pour charger les variables .env


# === ÉTAPE 5: Créer app.py ===

# Ouvre un éditeur (VSCode, Sublime Text, Notepad++...)
# Crée un fichier "app.py" avec ce contenu:

from flask import Flask, render_template_string
import os

app = Flask(__name__)

@app.route("/")
def home():
    return """
    <html>
        <body style="font-family: Arial">
            <h1>Bienvenue sur ma première app Heroku!</h1>
            <p>Cette app fonctionne sur le cloud!</p>
        </body>
    </html>
    """

@app.route("/api/info")
def api_info():
    return {
        "message": "API fonctionne!",
        "status": "ok"
    }

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(host="0.0.0.0", port=port, debug=False)

# Explications ligne par ligne:
# from flask import Flask: Importe le framework Flask
# import os: Pour lire les variables d'environnement
# app = Flask(__name__): Crée l'application Flask
# @app.route("/"): Définit une route pour l'URL racine (/)
# return "...": Retourne du HTML
# port = int(os.environ.get("PORT", 5000)): Utilise le port de Heroku
# app.run(host="0.0.0.0", ...): Démarre le serveur


# === ÉTAPE 6: Créer requirements.txt ===

# Toujours dans le terminal (avec venv activé):

pip freeze > requirements.txt

# Vérifie le contenu:
cat requirements.txt
# Doit afficher:
# Flask==...
# Werkzeug==...
# gunicorn==...


# === ÉTAPE 7: Créer Procfile ===

# macOS/Linux:
echo "web: gunicorn app:app" > Procfile

# Windows PowerShell:
"web: gunicorn app:app" | Out-File Procfile -Encoding UTF8

# Ou crée manuellement un fichier "Procfile" (pas d'extension!)


# === ÉTAPE 8: Créer runtime.txt ===

# macOS/Linux:
echo "python-3.11.0" > runtime.txt

# Windows PowerShell:
"python-3.11.0" | Out-File runtime.txt -Encoding UTF8


# === ÉTAPE 9: Créer .gitignore ===

# macOS/Linux:
cat > .gitignore << 'EOF'
venv/
.env
*.pyc
__pycache__/
EOF

# Windows PowerShell:
@"
venv/
.env
*.pyc
__pycache__/
"@ | Out-File .gitignore -Encoding UTF8

# Ou crée manuellement avec un éditeur


# === ÉTAPE 10: Tester localement ===

# Avant de déployer, teste sur ton ordinateur!

# Toujours avec venv activé:
python app.py

# Affiche:
# WARNING: This is a development server. Do not use it in a production deployment.
#  * Running on http://127.0.0.1:5000

# Ouvre navigateur: http://localhost:5000
# Tu vois: "Bienvenue sur ma première app Heroku!"

# Essaie aussi: http://localhost:5000/api/info
# Tu vois du JSON: {"message": "API fonctionne!", "status": "ok"}

# Pour arrêter: Ctrl+C


# === ÉTAPE 11: Tester avec heroku local ===

# Pour simule EXACTEMENT ce qui se passe sur Heroku:

heroku local web

# Affiche:
# 7:10 PM web.1  |  [2024-01-15 19:10:45 +0000] [12345] [INFO] Starting gunicorn 20.1.0
# 7:10 PM web.1  |  [2024-01-15 19:10:45 +0000] [12345] [INFO] Listening at: 0.0.0.0:5000

# Ouvre: http://localhost:5000
# Doit marcher exactement comme avant!

# Arrête: Ctrl+C


# === ÉTAPE 12: Commiter le code ===

git add .
git commit -m "Application Flask initiale prête pour Heroku"

# Vérifie le commit:
git log
# Affiche ton commit


# === ÉTAPE 13: Se connecter à Heroku ===

heroku login

# Ouvre le navigateur et confirme


# === ÉTAPE 14: Créer l'app sur Heroku ===

heroku create myflaskapp

# Remplace "myflaskapp" par un nom unique!
# Affiche:
# Creating app... done, [BLACK_HEXAGON] myflaskapp
# https://myflaskapp.herokuapp.com/ | https://git.heroku.com/myflaskapp.git

# Vérifie le remote:
git remote -v


# === ÉTAPE 15: DÉPLOYER! ===

git push heroku main

# Cela va:
# 1. Envoyer ton code à Heroku
# 2. Installer Python 3.11
# 3. Installer les packages (Flask, gunicorn, etc)
# 4. Lancer gunicorn
# 5. Assign l'URL https://myflaskapp.herokuapp.com

# Affiche plein de logs. Attends que ça finisse!


# === ÉTAPE 16: Vérifier ===

heroku open

# Ouvre automatiquement: https://myflaskapp.herokuapp.com
# Tu vois: "Bienvenue sur ma première app Heroku!"
# BRAVO! C'est en ligne!

# Si erreur: 
heroku logs --tail
# Pour voir les erreurs


[OK] EXEMPLE COMPLET: DJANGO POUR DÉBUTANTS

# === ÉTAPE 1-3: Setup initial ===

mkdir mydjangoapp && cd mydjangoapp
git init
python3 -m venv venv
source venv/bin/activate  # ou venv\Scripts\activate sur Windows


# === ÉTAPE 4: Installer Django ===

pip install django gunicorn psycopg2-binary python-decouple dj-database-url

# Explications:
# - django: Le framework Django
# - gunicorn: Serveur production
# - psycopg2-binary: Driver pour PostgreSQL
# - python-decouple: Lire les variables .env
# - dj-database-url: Parser la DATABASE_URL


# === ÉTAPE 5: Créer un projet Django ===

django-admin startproject myproject .

# Crée une structure:
# myproject/
# ├── settings.py  (configuration)
# ├── urls.py      (routes)
# └── wsgi.py      (interface web)
# manage.py        (commandes Django)


# === ÉTAPE 6: Configurer Django pour Heroku ===

# Ouvre: myproject/settings.py
# Change ces lignes:

# ===== SECURITY (en haut du fichier) =====
import os
import dj_database_url
from decouple import config, Csv

DEBUG = config('DEBUG', default=False, cast=bool)
# Cela lit DEBUG depuis les variables d'environnement
# Si DEBUG n'existe pas, utilise False (production!)

ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost', cast=Csv())
# Permet à l'app d'être accessible depuis l'URL Heroku

SECRET_KEY = config('SECRET_KEY', default='dev-key-change-this')
# Le secret doit être différent en production!
# Tu le configureras sur Heroku

# ===== DATABASE (remplace la section DATABASES) =====

DATABASES = {
    'default': dj_database_url.config(
        default=config('DATABASE_URL', default='sqlite:///db.sqlite3'),
        conn_max_age=600
    )
}
# Cela lit DATABASE_URL depuis les variables d'environnement
# Heroku configure cette variable automatiquement

# ===== STATIC FILES (à la fin du fichier) =====

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# Dossier pour les fichiers statiques (CSS, JS, images)


# === ÉTAPE 7: Créer requirements.txt ===

pip freeze > requirements.txt


# === ÉTAPE 8: Créer Procfile ===

# Le "release" fait les migrations avant démarrage
# C'est très important pour Django!

echo "web: gunicorn myproject.wsgi" > Procfile
# ou sur Windows:
"web: gunicorn myproject.wsgi" | Out-File Procfile -Encoding UTF8


# === ÉTAPE 9: Créer runtime.txt ===

echo "python-3.11.0" > runtime.txt
# ou sur Windows:
"python-3.11.0" | Out-File runtime.txt -Encoding UTF8


# === ÉTAPE 10: Créer .gitignore ===

cat > .gitignore << 'EOF'
venv/
.env
db.sqlite3
*.pyc
__pycache__/
staticfiles/
EOF


# === ÉTAPE 11: Tester localement ===

# Créer les tables Django:
python manage.py migrate
# Affiche:
# Operations to perform:
#   Apply all migrations: admin, auth, contenttypes, sessions
# Running migrations:
#   Applying contenttypes.0001_initial... OK
#   ...

# Créer un utilisateur admin:
python manage.py createsuperuser
# Demande: Username, Email, Password
# Rentre tes infos

# Lancer le serveur de développement:
python manage.py runserver
# Affiche: Starting development server at http://127.0.0.1:8000/

# Ouvre: http://localhost:8000
# Doit afficher la page Django de bienvenue!

# Admin panel: http://localhost:8000/admin
# Connecte-toi avec le superuser que tu viens de créer!

# Arrête: Ctrl+C


# === ÉTAPE 12: Commiter ===

git add .
git commit -m "Application Django prête pour Heroku"


# === ÉTAPE 13: Sur Heroku ===

heroku login
heroku create mydjangoapp

# Ajoute PostgreSQL:
heroku addons:create heroku-postgresql:hobby-dev

# Configure les variables:
heroku config:set DEBUG=False
heroku config:set SECRET_KEY=$(python -c 'import secrets; print(secrets.token_urlsafe(32))')
heroku config:set ALLOWED_HOSTS=mydjangoapp.herokuapp.com


# === ÉTAPE 14: DÉPLOYER ===

git push heroku main


# === ÉTAPE 15: Faire les migrations sur Heroku ===

# Crée les tables dans la DB PostgreSQL:
heroku run python manage.py migrate

# Crée un superuser sur Heroku:
heroku run python manage.py createsuperuser
# (Demande les infos)

# Collecter les fichiers statiques:
heroku run python manage.py collectstatic --noinput


# === ÉTAPE 16: Vérifier ===

heroku open
# Ouvre l'app en ligne!

# Admin panel:
heroku open /admin
# Connecte-toi avec le superuser


[OK] TYPES DE DYNOS EXPLIQUÉS

# === FREE DYNO ===

# Gratuit!
# Limitations:
#   - S'endort après 30 minutes d'inactivité
#   - Quand quelqu'un visite: 30 sec pour "réveiller"
#   - Lent (partagé avec d'autres apps)
#   - 550 dyno-hours par mois

# Parfait pour:
#   - Prototypes
#   - Tests
#   - Petits projets personnels


# === ECO DYNO ===

# Coût: $5/mois
# Améliorations:
#   - S'endort aussi après 30 min d'inactivité (mais moins souvent)
#   - Un peu plus rapide

# Parfait pour:
#   - Petits projets avec utilisation modérée


# === STANDARD-1X ===

# Coût: $25/mois
# Améliorations:
#   - TOUJOURS allumé (ne s'endort jamais!)
#   - 512 MB RAM
#   - Meilleure performance
#   - Logs illimités

# Parfait pour:
#   - Apps en production
#   - Services importants
#   - Utilisateurs réguliers


# === STANDARD-2X ===

# Coût: $50/mois
# Améliorations:
#   - 1 GB RAM (2x plus)
#   - Beaucoup plus rapide

# Parfait pour:
#   - Apps avec beaucoup d'utilisateurs
#   - Calculs lourds


# === COMMENT CHANGER DE DYNO? ===

# Voir le type actuel:
heroku ps
# Affiche: web.1 (Free dyno / 1X) ou autre

# Passer à Standard-1X:
heroku dyno:type standard-1x --app myapp
# ATTENTION: Ça coûte de l'argent!

# Pour revenir à Free (si tes crédits ne sont pas utilisés):
heroku dyno:type free --app myapp


[OK] AJOUTER UNE BASE DE DONNÉES

# === POSTGRESQL (RECOMMANDÉ) ===

# Pourquoi PostgreSQL?
# - Gratuit en tier "hobby-dev"
# - Professionnel et robuste
# - Durable (tes données persisten)
# - Intégré à Heroku

# Ajouter PostgreSQL:
heroku addons:create heroku-postgresql:hobby-dev

# Affiche:
# Creating heroku-postgresql on myapp... done
# The add-on plan provides an app with dedicated storage

# Cela crée:
# 1. Une base de données PostgreSQL
# 2. Une variable DATABASE_URL automatiquement
# 3. Des sauvegardes automatiques

# Voir les add-ons:
heroku addons
# Affiche:
# Add-on                                  Plan      Price
# ─────────────────────────────────────  ────────  ──────
# heroku-postgresql (postgresql-xxx-123) hobby-dev  free

# Voir les détails de la DB:
heroku pg:info
# Affiche infos comme:
#   Status         : available
#   Data Size      : 0 bytes
#   Connections    : 0
#   Backup Status  : 0 total

# Accéder à la DB via terminal:
heroku pg:psql
# Te connecte directement! Dès lors tu peux faire:
# \dt (liste les tables)
# SELECT * FROM users; (voir les données)
# \q (quitter)


# === SUPPLÉMENTAIRE: Redis (cache) ===

# Redis = Cache ultra-rapide
# Utile pour: sessions, cache, file d'attente

# Ajouter Redis:
heroku addons:create heroku-redis:premium-0
# "premium-0" = Gratuit (0 GB limité)

# Cela crée une variable: REDIS_URL
# Accessible depuis ton app:
import os
redis_url = os.environ.get("REDIS_URL")


# === SUPPLÉMENTAIRE: SendGrid (emails) ===

# SendGrid = Service d'envoi d'emails

# Ajouter:
heroku addons:create sendgrid:starter
# Gratuit: 12,000 emails par mois

# Cela crée: SENDGRID_API_KEY, SENDGRID_USERNAME, SENDGRID_PASSWORD


[OK] SCALE: AJOUTER PLUSIEURS INSTANCES

# === POURQUOI? ===

# Imagine 1000 utilisateurs visitent ton app en même temps
# Un seul dyno peut pas tous les gérer!
# Solution: Plus de dynos!
# Heroku distribue les requêtes (load balancing)

# === VOIR LES DYNOS ACTUELS ===

heroku ps
# Affiche:
# Free dyno formation:
# web.1 (free): up 2024-01-15 19:10:22 +0000 (40 minutes ago)


# === AJOUTER UN DYNO WEB ===

# Augmenter à 2 web dynos:
heroku ps:scale web=2

# Affiche:
# Scaling dynos... done, now running web.1,web.2

# Vérifie:
heroku ps
# web.1 (free): up 2024-01-15 19:10:22 +0000
# web.2 (free): up 2024-01-15 19:15:10 +0000


# === AJOUTER UN WORKER DYNO ===

# Pour tâches en arrière-plan (jobs, queues, etc):

# D'abord, ajoute dans Procfile:
# Procfile
web: gunicorn app:app
worker: celery -A app.celery worker

# Puis:
heroku ps:scale worker=1

# Affiche:
# web.1, web.2, worker.1 en cours d'exécution


# === RÉDUIRE LES DYNOS ===

# Réduire à 1 web dyno:
heroku ps:scale web=1

# Supprimer tous les worker dynos:
heroku ps:scale worker=0


# === COÛTS DES DYNOS MULTIPLES ===

# Free Dyno: Gratuit (mais s'endort)
# Si 2 Free Dynos actifs 24h:
#   2 dynos × 24h × 30 jours = 1440 dyno-hours
#   Dépasse 550 dyno-hours gratuits!
#   Coûte cher!

# Solution: Passer à Standard-1X
#   Standard-1X: $25/mois par dyno
#   2 × $25 = $50/mois (coûte moins que les overage!)


[OK] DÉPANNAGE DÉTAILLÉ

# === PROBLÈME: "Application Error" ===

# Ton app est en ligne mais affiche "Application Error"

# Cause possible 1: Erreur Python
# Solution:
heroku logs --tail
# Cherche la ligne avec "Error"
# Exemple erreur:
# ModuleNotFoundError: No module named 'flask'
# Significa: Flask n'est pas dans requirements.txt!
# Fix: Ajoute flask à requirements.txt et redéploie


# Cause possible 2: Port mal configuré
# Ton app écoute sur le mauvais port
# Solution:
# Dans app.py, assure-toi:
port = int(os.environ.get("PORT", 5000))
app.run(host="0.0.0.0", port=port)
# Puis redéploie


# Cause possible 3: Procfile mal formé
# Erreur de syntaxe dans Procfile
# Solution:
cat Procfile
# Doit afficher exactement:
# web: gunicorn app:app
# Pas d'espaces supplémentaires, pas de fautes de frappe


# === PROBLÈME: "H12 Request Timeout" ===

# Erreur: Requête prend trop de temps

# Cause: Ton app est trop lente
# Solutions:
# 1. Optimiser le code (peut être vraiment lent!)
# 2. Ajouter des dynos: heroku ps:scale web=2
# 3. Mettre à jour vers Standard-1X (plus de resources)


# === PROBLÈME: "No such file or directory: 'Procfile'" ===

# Procfile manquant

# Vérifier:
ls -la | grep Procfile  # Linux/macOS
dir | findstr Procfile  # Windows

# Si rien n'apparaît: créer Procfile
echo "web: gunicorn app:app" > Procfile


# === PROBLÈME: BuildFailure - "Could not find version satisfying requirements" ===

# Package version dans requirements.txt n'existe pas

# Exemple:
# Flask==999.0.0 (version n'existe pas!)

# Solution:
pip list
# Voir les versions réelles de tes packages

pip freeze > requirements.txt
# Régénérer requirements.txt avec les vraies versions


# === PROBLÈME: Dyno goes to sleep / H18 ===

# Free Dyno s'endort après 30 min d'inactivité
# Première visite après sommeil = 30 sec lent

# Solutions:
# 1. Payer pour Eco Dyno: heroku ps:type eco
# 2. Utiliser un bot pour "réveiller" l'app
# 3. Accepter le delay (pour prototypes c'est OK)


# === PROBLÈME: Erreur lors du git push ===

# Erreur: "Permission denied (publickey)"

# Cause: Heroku pas encore authentifiée
# Solution:
heroku login
# Réessaie le push


# === PROBLÈME: Variables d'environnement pas chargées ===

# Code: api_key = os.environ.get("API_KEY")
# Résultat: api_key = None (vide!)

# Vérifier que la variable existe:
heroku config:get API_KEY
# Si rien n'apparaît: variable n'existe pas!

# Ajouter la variable:
heroku config:set API_KEY="my_secret_key"

# Puis redéploie l'app:
git push heroku main


[OK] WORKFLOW COMPLET: DU ZÉRO À LA PRODUCTION

# === JOUR 1: Développement ===

# 1. Créer le projet localement
mkdir myproject && cd myproject
git init

# 2. Créer environnement virtuel
python -m venv venv
source venv/bin/activate  # ou venv\Scripts\activate

# 3. Installer packages
pip install flask gunicorn

# 4. Créer app.py (ton app Flask)
# ... code Flask ...

# 5. Tester
python app.py
# Ouvre http://localhost:5000
# Tout fonctionne!

# 6. Créer requirements.txt
pip freeze > requirements.txt

# 7. Créer Procfile, runtime.txt, .gitignore
echo "web: gunicorn app:app" > Procfile
echo "python-3.11.0" > runtime.txt
echo "venv/" > .gitignore

# 8. Tester avec heroku local
heroku local web
# http://localhost:5000 fonctionne!

# 9. Commiter
git add .
git commit -m "Application initiale"


# === JOUR 2: Déploiement ===

# 1. Se connecter à Heroku
heroku login

# 2. Créer l'app
heroku create myproject-app

# 3. Pousser le code
git push heroku main
# Attends 1-2 minutes...

# 4. Vérifier
heroku open
# L'app fonctionne en ligne!

heroku logs --tail
# Pas d'erreurs


# === JOUR 3+: Maintenance ===

# Chaque fois que tu modifies:
git add .
git commit -m "New feature"
git push heroku main

# Surveille les logs:
heroku logs --tail

# Optimise si besoin:
heroku ps  # Voir utilisations ressources
heroku config  # Voir variables


[OK] EXEMPLE ULTRA-DÉTAILLÉ: BLOG FLASK COMPLET AVEC HEROKU

# === INTRODUCTION ===

# Tu vas créer une APPLICATION BLOG complète et LA DÉPLOYER sur Heroku
# Cette app aura:
# - Page d'accueil avec liste des articles
# - Page pour voir un article en détail
# - Page pour créer un nouvel article
# - Stockage dans une base de données PostgreSQL
# - Accessible sur internet via une URL Heroku

# === ÉTAPE 1: CRÉER LE DOSSIER DU PROJET ===

# Ouvre ton terminal

# macOS/Linux:
mkdir flask-blog && cd flask-blog

# Windows (PowerShell):
mkdir flask-blog
cd flask-blog

# Explications:
# mkdir = make directory = crée un dossier
# cd = change directory = entre dans le dossier
# Maintenant tu es DANS le dossier flask-blog


# === ÉTAPE 2: INITIALISER GIT ===

git init

# Affiche:
# Initialized empty Git repository in /path/to/flask-blog/.git

# Explications:
# git init = crée un dépôt git pour versioning
# Heroku utilise git pour déployer
# Sans git, tu ne peux pas déployer!


# === ÉTAPE 3: CRÉER L'ENVIRONNEMENT VIRTUEL ===

# IMPORTANT: L'environnement virtuel isole tes packages Python

# macOS/Linux:
python3 -m venv venv

# Windows:
python -m venv venv

# Affiche:
# (rien, c'est normal)

# Cela crée un dossier "venv/" avec un Python isolé

# === ÉTAPE 3b: ACTIVER L'ENVIRONNEMENT VIRTUEL ===

# TRÈS IMPORTANT: Toujours activer avant d'installer des packages!

# macOS/Linux (bash/zsh):
source venv/bin/activate

# macOS/Linux (fish shell):
source venv/bin/activate.fish

# Windows (PowerShell):
.\venv\Scripts\Activate.ps1

# Windows (CMD):
venv\Scripts\activate.bat

# Affiche:
# (venv) $ (sur macOS/Linux)
# (venv) C:\path> (sur Windows)

# Le "(venv)" au début = environnement virtuel ACTIVÉ!

# Explications:
# Quand venv est actif:
# - pip install XYZ installe DANS le venv (pas globalement)
# - python utilise le Python du venv
# - Tu peux avoir des versions différentes par projet
# RÈGLE D'OR: Toujours vérifier que tu vois (venv) avant de continuer!


# === ÉTAPE 4: INSTALLER LES PACKAGES ===

# Toujours avec (venv) activé!

pip install flask
pip install flask-sqlalchemy
pip install python-dotenv
pip install gunicorn

# Explications des packages:

# flask = Framework web Python
# - Permet de créer des routes (@app.route)
# - Gère les requêtes HTTP
# - Affiche du HTML aux utilisateurs

# flask-sqlalchemy = ORM pour la base de données
# - ORM = Object-Relational Mapping
# - Permet d'utiliser Python au lieu de SQL
# - Gère automatiquement les créations de tables
# - Exemple: au lieu de "INSERT INTO articles..." tu fais "db.session.add(article)"

# python-dotenv = Charge les variables .env
# - Pour stocker les secrets (clés API, URL DB, etc)
# - Hors du code (sécurité!)

# gunicorn = Serveur web pour production
# - Flask a un serveur "development" trop lent
# - Gunicorn est professionnel et rapide
# - Heroku l'utilise par défaut

# Vérifier l'installation:
pip list
# Affiche:
# Flask          2.3.0
# Flask-SQLAlchemy  3.0.0
# gunicorn       20.1.0
# python-dotenv  1.0.0
# ...

# === ÉTAPE 5: CRÉER LE FICHIER requirements.txt ===

# Ce fichier dit à Heroku: "Installe ces packages"

pip freeze > requirements.txt

# Explications:
# pip freeze = Affiche toutes les versions exactes des packages installés
# > requirements.txt = Sauvegarde le résultat dans le fichier

# Vérifier:
cat requirements.txt
# Affiche:
# click==8.1.3
# Flask==2.3.0
# Flask-SQLAlchemy==3.0.0
# gunicorn==20.1.0
# itsdangerous==2.1.2
# Jinja2==3.1.2
# MarkupSafe==2.1.1
# python-dotenv==1.0.0
# SQLAlchemy==2.0.0
# Werkzeug==2.3.0


# === ÉTAPE 6: CRÉER LE FICHIER .env (LOCAL) ===

# .env = Fichier avec les secrets (pas commité!)

# macOS/Linux:
cat > .env << 'EOF'
FLASK_ENV=development
FLASK_DEBUG=True
SQLALCHEMY_DATABASE_URI=sqlite:///blog.db
SECRET_KEY=your-secret-key-change-this
EOF

# Windows PowerShell:
@"
FLASK_ENV=development
FLASK_DEBUG=True
SQLALCHEMY_DATABASE_URI=sqlite:///blog.db
SECRET_KEY=your-secret-key-change-this
"@ | Out-File .env -Encoding UTF8

# Ou crée manuellement un fichier ".env" avec un éditeur

# Explications:
# FLASK_ENV=development = Mode développement (reload auto, debug mode)
# FLASK_DEBUG=True = Affiche les erreurs en détail
# SQLALCHEMY_DATABASE_URI=sqlite:///blog.db = Utilise SQLite localement
# SECRET_KEY = Clé secrète pour sessions/cookies (change cette valeur!)


# === ÉTAPE 7: CRÉER LE FICHIER .gitignore ===

# Fichier qui dit à git: "N'ajoute pas ces fichiers"

# macOS/Linux:
cat > .gitignore << 'EOF'
venv/
.env
__pycache__/
*.pyc
*.db
.DS_Store
instance/
EOF

# Windows PowerShell:
@"
venv/
.env
__pycache__/
*.pyc
*.db
.DS_Store
instance/
"@ | Out-File .gitignore -Encoding UTF8

# Explications:
# venv/ = Dossier de l'environnement (trop gros à commiter!)
# .env = Fichier avec les secrets (jamais commiter!)
# *.db = Fichiers SQLite (données locales, pas besoin sur Heroku)
# __pycache__/ = Cache Python (auto-généré)


# === ÉTAPE 8: CRÉER L'APPLICATION FLASK (app.py) ===

# C'est le cœur de ton blog!

# Crée un fichier "app.py" avec ce contenu:

"""
app.py - Application blog Flask
"""

from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import os
from dotenv import load_dotenv

# Charger les variables .env
load_dotenv()

# Créer l'application Flask
app = Flask(__name__)

# === CONFIGURATION ===

# Récupérer la DATABASE_URL de l'environnement
# En développement local: utilise SQLite
# Sur Heroku: utilise PostgreSQL
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
    'DATABASE_URL',
    'sqlite:///blog.db'  # Par défaut: SQLite
)

# Éviter les avertissements SQLAlchemy
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Clé secrète pour sessions
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-key-change-this')

# Initialiser la base de données
db = SQLAlchemy(app)


# === MODÈLES DE DONNÉES ===

# Un modèle = Une table dans la base de données

class Article(db.Model):
    """
    Modèle pour les articles du blog
    Chaque article a:
    - id: numéro unique
    - titre: titre de l'article
    - contenu: texte de l'article
    - date_creation: quand l'article a été créé
    """
    
    id = db.Column(db.Integer, primary_key=True)
    # primary_key=True = id unique, clé principale
    
    titre = db.Column(db.String(200), nullable=False)
    # String(200) = texte max 200 caractères
    # nullable=False = obligatoire (ne peut pas être vide)
    
    contenu = db.Column(db.Text, nullable=False)
    # Text = texte long sans limite
    
    date_creation = db.Column(db.DateTime, default=datetime.now)
    # DateTime = date et heure
    # default=datetime.now = valeur par défaut: moment création
    
    def __repr__(self):
        """Affichage lisible de l'article"""
        return f'<Article {self.titre}>'


# === CONTEXTE D'APPLICATION ===

# Permet de créer des tables au démarrage
with app.app_context():
    db.create_all()


# === ROUTES (Les pages de ton app) ===

@app.route('/')
def index():
    """
    Page d'accueil: affiche tous les articles
    Route: /
    """
    
    # Récupérer tous les articles de la base de données
    articles = Article.query.all()
    # .query.all() = "Donne-moi tous les articles"
    
    # Retourner le template HTML
    return render_template('index.html', articles=articles)


@app.route('/article/<int:id>')
def voir_article(id):
    """
    Page pour voir un article en détail
    Route: /article/1, /article/2, etc
    id = numéro de l'article
    """
    
    # Récupérer l'article par son id
    article = Article.query.get_or_404(id)
    # .query.get_or_404(id) = "Donne-moi l'article avec cet id ou affiche 404"
    
    return render_template('article.html', article=article)


@app.route('/creer', methods=['GET', 'POST'])
def creer_article():
    """
    Page pour créer un nouvel article
    Route: /creer
    methods=['GET', 'POST']:
    - GET = afficher le formulaire
    - POST = traiter l'envoi du formulaire
    """
    
    if request.method == 'POST':
        # Récupérer les données du formulaire
        titre = request.form['titre']
        contenu = request.form['contenu']
        
        # Vérifier que les champs ne sont pas vides
        if not titre or not contenu:
            return redirect(url_for('creer_article'))
        
        # Créer un nouvel objet Article
        nouvel_article = Article(titre=titre, contenu=contenu)
        
        # Ajouter à la base de données
        db.session.add(nouvel_article)
        # db.session = "queue" de modifications
        
        # Valider et sauvegarder
        db.session.commit()
        # db.session.commit() = "Valide tous les changements"
        
        # Rediriger vers la page d'accueil
        return redirect(url_for('index'))
    
    # Si GET: afficher le formulaire
    return render_template('creer.html')


@app.route('/supprimer/<int:id>', methods=['POST'])
def supprimer_article(id):
    """
    Supprimer un article
    Route: /supprimer/1, /supprimer/2, etc
    """
    
    # Récupérer l'article
    article = Article.query.get_or_404(id)
    
    # Supprimer
    db.session.delete(article)
    db.session.commit()
    
    # Rediriger
    return redirect(url_for('index'))


# === DÉMARRAGE DE L'APP ===

if __name__ == '__main__':
    # Obtenir le port de Heroku ou utiliser 5000 par défaut
    port = int(os.environ.get('PORT', 5000))
    
    # Lancer l'app
    app.run(
        host='0.0.0.0',  # Accessible de partout
        port=port,        # Port spécifié
        debug=False       # debug=False en production!
    )


# === ÉTAPE 9: CRÉER LES TEMPLATES HTML ===

# Les templates = Pages HTML affichées aux utilisateurs
# Flask les stocke dans le dossier "templates/"

# Créer le dossier templates:
mkdir templates

# === TEMPLATE #1: templates/base.html ===

# Crée le fichier "templates/base.html"

"""
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Mon Blog{% endblock %}</title>
    <style>
        /* Styles CSS simples */
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f5f5f5;
        }
        header {
            background-color: #333;
            color: white;
            padding: 20px;
            text-align: center;
            margin-bottom: 30px;
            border-radius: 5px;
        }
        header h1 {
            margin: 0;
        }
        nav {
            margin: 20px 0;
        }
        nav a {
            background-color: #007bff;
            color: white;
            padding: 10px 15px;
            text-decoration: none;
            border-radius: 3px;
            margin-right: 10px;
        }
        nav a:hover {
            background-color: #0056b3;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
            background-color: white;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        .article-item {
            border-bottom: 1px solid #ddd;
            padding: 15px 0;
        }
        .article-item:last-child {
            border-bottom: none;
        }
        .article-title {
            font-size: 20px;
            font-weight: bold;
            margin: 0;
        }
        .article-date {
            color: #666;
            font-size: 14px;
        }
        .article-link {
            color: #007bff;
            text-decoration: none;
        }
        .article-link:hover {
            text-decoration: underline;
        }
        form {
            display: flex;
            flex-direction: column;
        }
        input, textarea {
            margin: 10px 0;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 3px;
            font-family: Arial, sans-serif;
        }
        textarea {
            resize: vertical;
            min-height: 200px;
        }
        button {
            background-color: #28a745;
            color: white;
            padding: 10px;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            font-size: 16px;
        }
        button:hover {
            background-color: #218838;
        }
        .delete-btn {
            background-color: #dc3545;
            padding: 5px 10px;
            font-size: 14px;
            margin-top: 10px;
        }
        .delete-btn:hover {
            background-color: #c82333;
        }
    </style>
</head>
<body>
    <header>
        <h1>[DOCS] Mon Blog Flask</h1>
        <p>Bienvenue sur mon blog avec Heroku!</p>
    </header>

    <div class="container">
        <nav>
            <a href="{% url_for('index') %}">Accueil</a>
            <a href="{% url_for('creer_article') %}">Créer un article</a>
        </nav>

        {% block content %}
        {% endblock %}
    </div>
</body>
</html>
"""


# === TEMPLATE #2: templates/index.html ===

# Crée le fichier "templates/index.html"

"""
{% extends "base.html" %}

{% block title %}Accueil - Mon Blog{% endblock %}

{% block content %}
    <h2>Articles récents</h2>
    
    {% if articles %}
        {% for article in articles %}
            <div class="article-item">
                <h3 class="article-title">
                    <a href="{{ url_for('voir_article', id=article.id) }}" class="article-link">
                        {{ article.titre }}
                    </a>
                </h3>
                <p class="article-date">
                    Publié le {{ article.date_creation.strftime('%d/%m/%Y à %H:%M') }}
                </p>
                <p>{{ article.contenu[:100] }}...</p>
                <a href="{{ url_for('voir_article', id=article.id) }}" class="article-link">
                    Lire la suite ->
                </a>
            </div>
        {% endfor %}
    {% else %}
        <p>Aucun article pour le moment. <a href="{{ url_for('creer_article') }}">Créer le premier!</a></p>
    {% endif %}
{% endblock %}
"""


# === TEMPLATE #3: templates/article.html ===

# Crée le fichier "templates/article.html"

"""
{% extends "base.html" %}

{% block title %}{{ article.titre }} - Mon Blog{% endblock %}

{% block content %}
    <article>
        <h2>{{ article.titre }}</h2>
        <p class="article-date">
            Publié le {{ article.date_creation.strftime('%d/%m/%Y à %H:%M') }}
        </p>
        
        <div>
            {{ article.contenu }}
        </div>
        
        <form action="{{ url_for('supprimer_article', id=article.id) }}" method="POST">
            <button type="submit" class="delete-btn" onclick="return confirm('Êtes-vous sûr?')">
                Supprimer cet article
            </button>
        </form>
        
        <p>
            <a href="{{ url_for('index') }}" class="article-link"><- Retour à la liste</a>
        </p>
    </article>
{% endblock %}
"""


# === TEMPLATE #4: templates/creer.html ===

# Crée le fichier "templates/creer.html"

"""
{% extends "base.html" %}

{% block title %}Créer un article - Mon Blog{% endblock %}

{% block content %}
    <h2>Créer un nouvel article</h2>
    
    <form method="POST">
        <label for="titre">Titre:</label>
        <input type="text" id="titre" name="titre" placeholder="Titre de l'article" required>
        
        <label for="contenu">Contenu:</label>
        <textarea id="contenu" name="contenu" placeholder="Écris ton article ici..." required></textarea>
        
        <button type="submit">Publier l'article</button>
    </form>
{% endblock %}
"""


# === ÉTAPE 10: TESTER LOCALEMENT ===

# Vérifier qu'on a bien (venv) activé
# Affiche: (venv) $ ou (venv) C:\...

# Lancer l'app:
python app.py

# Affiche:
# WARNING: This is a development server. Do not use it in a production deployment.
#  * Running on http://127.0.0.1:5000

# Ouvre le navigateur: http://localhost:5000
# Tu vois: Page d'accueil du blog!

# Teste:
# 1. Clique "Créer un article"
# 2. Écris un titre et du contenu
# 3. Clique "Publier l'article"
# 4. Vois l'article sur la page d'accueil
# 5. Clique sur l'article pour le voir entièrement
# 6. Clique "Supprimer cet article" pour le supprimer

# Arrête l'app: Ctrl+C


# === ÉTAPE 11: CRÉER LES FICHIERS HEROKU ===

# === Fichier: Procfile ===

# Crée un fichier "Procfile" (SANS extension):

"""
web: gunicorn app:app
release: python -c "from app import app, db; app.app_context().push(); db.create_all()"
"""

# Explications:
# web: gunicorn app:app = Lancer gunicorn avec le fichier app.py
# release: = Commande qui s'exécute AVANT chaque déploiement
# db.create_all() = Créer les tables dans la base de données

# macOS/Linux:
cat > Procfile << 'EOF'
web: gunicorn app:app
release: python -c "from app import app, db; app.app_context().push(); db.create_all()"
EOF

# Windows PowerShell:
@"
web: gunicorn app:app
release: python -c "from app import app, db; app.app_context().push(); db.create_all()"
"@ | Out-File Procfile -Encoding UTF8


# === Fichier: runtime.txt ===

# Crée un fichier "runtime.txt":

"""
python-3.11.0
"""

# macOS/Linux:
echo "python-3.11.0" > runtime.txt

# Windows PowerShell:
"python-3.11.0" | Out-File runtime.txt -Encoding UTF8


# === ÉTAPE 12: TESTER AVEC heroku local ===

# Avant de déployer sur Heroku, simule l'environnement Heroku!

# D'abord, arrête l'app locale si elle tourne: Ctrl+C

# Assure-toi que (venv) est toujours activé

# Lancer avec heroku local:
heroku local web

# Affiche:
# forego | starting web.1 on port 5000
# web.1  | [2024-01-15 19:10:45 +0000] [12345] [INFO] Starting gunicorn 20.1.0
# web.1  | [2024-01-15 19:10:45 +0000] [12345] [INFO] Listening at: 0.0.0.0:5000

# Ouvre: http://localhost:5000
# L'app fonctionne comme sur Heroku!

# Teste les fonctionnalités (créer, lire, supprimer articles)

# Arrête: Ctrl+C


# === ÉTAPE 13: AJOUTER requirements.txt AU DÉPÔT ===

# Vérifie que requirements.txt est à jour:

pip freeze > requirements.txt

# Vérifier le contenu:
cat requirements.txt
# Doit inclure:
# Flask==2.3.0
# Flask-SQLAlchemy==3.0.0
# gunicorn==20.1.0
# python-dotenv==1.0.0


# === ÉTAPE 14: COMMITER LE CODE ===

# Ajouter tous les fichiers au dépôt git:
git add .

# Vérifier ce qui sera commité:
git status

# Doit afficher (en vert):
# new file:   .gitignore
# new file:   Procfile
# new file:   app.py
# new file:   requirements.txt
# new file:   runtime.txt
# new file:   templates/article.html
# new file:   templates/base.html
# new file:   templates/creer.html
# new file:   templates/index.html

# IMPORTANT: .env et venv/ NE DOIVENT PAS APPARAÎTRE (seront ignorés par .gitignore)

# Faire le commit:
git commit -m "Blog Flask prêt pour Heroku"

# Affiche:
# [main (root-commit) a1b2c3d] Blog Flask prêt pour Heroku
#  8 files changed, 500 insertions(+)

# Vérifier l'historique:
git log
# Affiche ton commit


# === ÉTAPE 15: SE CONNECTER À HEROKU ===

# Vérifier que la CLI Heroku est installée:
heroku --version
# Affiche: heroku/7.X.X ou plus

# Se connecter:
heroku login

# Un navigateur s'ouvre automatiquement
# Clique sur "Log In" pour confirmer

# Terminal affiche: Logged in as email@example.com


# === ÉTAPE 16: CRÉER L'APP SUR HEROKU ===

# TRÈS IMPORTANT: Tu dois être DANS le dossier du projet!

pwd  # macOS/Linux: affiche le chemin actuel
cd   # Windows PowerShell: affiche le chemin actuel

# Créer l'app Heroku avec un nom personnalisé:
heroku create mon-blog-flask

# IMPORTANT: Le nom doit être unique sur Heroku!
# Si ce nom est pris, change-le!

# Affiche:
# Creating [BLACK_HEXAGON] mon-blog-flask... done
# https://mon-blog-flask.herokuapp.com/ | https://git.heroku.com/mon-blog-flask.git

# Vérifier que le remote git a été ajouté:
git remote -v

# Affiche:
# heroku  https://git.heroku.com/mon-blog-flask.git (fetch)
# heroku  https://git.heroku.com/mon-blog-flask.git (push)


# === ÉTAPE 17: AJOUTER LA BASE DE DONNÉES ===

# Sur Heroku, on utilise PostgreSQL (pas SQLite)

# Ajouter PostgreSQL:
heroku addons:create heroku-postgresql:hobby-dev --app mon-blog-flask

# Affiche:
# Creating heroku-postgresql:hobby-dev on [BLACK_HEXAGON] mon-blog-flask... done
# The add-on plan provides an app with dedicated storage.
# Please run `heroku addons:docs heroku-postgresql` to view full docs.

# Cela crée:
# 1. Une base de données PostgreSQL
# 2. Une variable DATABASE_URL automatiquement!

# Vérifier:
heroku config:get DATABASE_URL --app mon-blog-flask

# Affiche: postgresql://username:password@host:port/dbname


# === ÉTAPE 18: CONFIGURER LES VARIABLES D'ENVIRONNEMENT ===

# Sur Heroku, les variables d'environnement remplacent celles de .env

# Ajouter la clé secrète:
heroku config:set SECRET_KEY="my-super-secret-key-change-this" --app mon-blog-flask

# Générer une vraie clé secrète (Linux/macOS):
heroku config:set SECRET_KEY=$(python -c 'import secrets; print(secrets.token_urlsafe(32))') --app mon-blog-flask

# Vérifier:
heroku config --app mon-blog-flask

# Affiche:
# DATABASE_URL: postgresql://...
# SECRET_KEY:   my-super-secret-key-change-this


# === ÉTAPE 19: DÉPLOYER L'APP! ===

# C'EST LE MOMENT! Tu vas pousser ton code vers Heroku

# Assure-toi que tu es DANS le dossier du projet:
pwd  # Vérifie le chemin

# Pousser vers Heroku (branche main):
git push heroku main

# Si tu as une branche "master" au lieu de "main":
git push heroku master

# Affiche BEAUCOUP de logs:
# Receiving objects: 100% (X/X), done.
# remote: Compressing source files... done.
# remote: Building application from source...
# remote: -----> Python Support Installed
# remote: -----> Python 3.11.0 selected
# remote: -----> Installing requirements with pip
# remote: Collecting Flask==2.3.0
# remote: Downloading Flask-2.3.0-py3-none-any.whl
# ... (plein d'installation)
# remote: -----> Launching...
# remote: Released v1
# remote: https://mon-blog-flask.herokuapp.com/ deployed to Heroku

# BRAVO! L'app est déployée!


# === ÉTAPE 20: VÉRIFIER LE DÉPLOIEMENT ===

# Ouvrir l'app dans le navigateur:
heroku open --app mon-blog-flask

# Cela ouvre automatiquement: https://mon-blog-flask.herokuapp.com
# Tu dois voir: "[DOCS] Mon Blog Flask"
# Tu dois pouvoir: créer/lire/supprimer les articles!

# Bravo! Ton blog est en ligne!


# === ÉTAPE 21: SURVEILLER LES LOGS ===

# Si quelque chose se passe mal, regarde les logs:
heroku logs --tail --app mon-blog-flask

# Affiche les logs en temps réel
# Cherche les lignes avec "Error" ou "Traceback"

# Exemple erreur:
# remote: ModuleNotFoundError: No module named 'flask_sqlalchemy'
# Signifie: Flask-SQLAlchemy n'a pas été installé
# Solution: pip install flask-sqlalchemy && pip freeze > requirements.txt && git push heroku main


# === ÉTAPE 22: METTRE À JOUR L'APP ===

# Après modifier le code:

# 1. Tester localement:
python app.py
# Teste les changements

# 2. Commiter:
git add .
git commit -m "Ajouter la fonctionnalité X"

# 3. Pousser vers Heroku:
git push heroku main

# C'EST TOUT! L'app est mise à jour en ~1 minute

# 4. Vérifier:
heroku logs --tail --app mon-blog-flask


# === ÉTAPE 23: AJOUTER UN DOMAINE PERSONNALISÉ (OPTIONNEL) ===

# Remplacer "mon-blog-flask.herokuapp.com" par "monblog.com"

# D'abord, acheter un domaine chez un registraire (GoDaddy, OVH, etc)

# Ajouter le domaine à Heroku:
heroku domains:add monblog.com --app mon-blog-flask

# Ajouter aussi la variante "www":
heroku domains:add www.monblog.com --app mon-blog-flask

# Configurer le DNS chez ton registraire
# (Instructions différentes selon le registraire)

# Vérifier:
heroku domains --app mon-blog-flask


# === RÉSUMÉ DU WORKFLOW COMPLET ===

# Jour 1 - Création:
# 1. mkdir flask-blog && cd flask-blog
# 2. git init
# 3. python -m venv venv && source venv/bin/activate
# 4. pip install flask flask-sqlalchemy python-dotenv gunicorn
# 5. Créer app.py avec modèle Article
# 6. Créer dossier templates/ avec 4 fichiers HTML
# 7. Créer .env, .gitignore, requirements.txt
# 8. python app.py && tester sur http://localhost:5000

# Jour 2 - Préparation Heroku:
# 9. Créer Procfile et runtime.txt
# 10. heroku local web && tester
# 11. pip freeze > requirements.txt
# 12. git add . && git commit -m "..."

# Jour 3 - Déploiement:
# 13. heroku login
# 14. heroku create mon-blog-flask
# 15. heroku addons:create heroku-postgresql:hobby-dev
# 16. heroku config:set SECRET_KEY="..."
# 17. git push heroku main

# Jour 4 - Vérification:
# 18. heroku open
# 19. Créer/lire/supprimer des articles
# 20. Regarder: heroku logs --tail

# C'EST FINI! Ton blog est en ligne!


# === CHECKLISTE FINALE AVANT DÉPLOIEMENT ===

# [OK] Fichier Procfile créé?
cat Procfile
# Doit afficher: web: gunicorn app:app

# [OK] Fichier runtime.txt créé?
cat runtime.txt
# Doit afficher: python-3.11.0

# [OK] Fichier requirements.txt à jour?
cat requirements.txt
# Doit contenir: Flask, Flask-SQLAlchemy, gunicorn, python-dotenv

# [OK] Fichier .gitignore créé?
cat .gitignore
# Doit contenir: venv/, .env, *.db

# [OK] Code testé localement?
heroku local web
# Doit fonctionner sur http://localhost:5000

# [OK] Code commité?
git log
# Doit afficher les commits

# [OK] Dépôt git distant (Heroku) créé?
git remote -v
# Doit afficher: heroku https://git.heroku.com/mon-blog-flask.git

# Si OUI à tous: TU PEUX DÉPLOYER!
git push heroku main


Parfait ! On va créer un **exemple complet de projet Flask avec CRUD**, prêt pour déploiement **Docker + Heroku**, et je vais détailler **toutes les parties** étape par étape.

---

# 1⃣ Structure du projet

```
flask-crud-app/
│
├─ app.py
├─ models.py
├─ routes.py
├─ requirements.txt
├─ Dockerfile
├─ Procfile
├─ config.py
└─ templates/
    ├─ base.html
    ├─ index.html
    ├─ add.html
    └─ edit.html
```

* `app.py` -> initialisation Flask et registration des routes
* `models.py` -> modèle de données (SQLAlchemy)
* `routes.py` -> CRUD (Create, Read, Update, Delete)
* `config.py` -> configuration de la DB et Flask
* `templates/` -> pages HTML (Jinja2)
* `requirements.txt` -> dépendances
* `Dockerfile` -> Docker pour Heroku
* `Procfile` -> Heroku

---

# 2⃣ `requirements.txt`

```
Flask==2.3.4
Flask-SQLAlchemy==3.0.4
gunicorn==21.2.0
```

---

# 3⃣ `config.py`

```python
import os

class Config:
    SECRET_KEY = os.environ.get("SECRET_KEY", "dev-key")
    SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL", "sqlite:///data.db")
    SQLALCHEMY_TRACK_MODIFICATIONS = False
```

* `SECRET_KEY` -> sécurité sessions
* `SQLALCHEMY_DATABASE_URI` -> URL DB, Heroku fournit `DATABASE_URL`
* `SQLALCHEMY_TRACK_MODIFICATIONS=False` -> évite un warning

---

# 4⃣ `models.py`

```python
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Item(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    description = db.Column(db.String(200))
```

* `Item` est notre table principale pour le CRUD
* `db` est l’instance SQLAlchemy

---

# 5⃣ `routes.py`

```python
from flask import Flask, render_template, request, redirect, url_for
from models import db, Item

def register_routes(app: Flask):
    
    @app.route('/')
    def index():
        items = Item.query.all()
        return render_template('index.html', items=items)
    
    @app.route('/add', methods=['GET', 'POST'])
    def add_item():
        if request.method == 'POST':
            name = request.form['name']
            description = request.form['description']
            item = Item(name=name, description=description)
            db.session.add(item)
            db.session.commit()
            return redirect(url_for('index'))
        return render_template('add.html')
    
    @app.route('/edit/<int:id>', methods=['GET', 'POST'])
    def edit_item(id):
        item = Item.query.get_or_404(id)
        if request.method == 'POST':
            item.name = request.form['name']
            item.description = request.form['description']
            db.session.commit()
            return redirect(url_for('index'))
        return render_template('edit.html', item=item)
    
    @app.route('/delete/<int:id>', methods=['POST'])
    def delete_item(id):
        item = Item.query.get_or_404(id)
        db.session.delete(item)
        db.session.commit()
        return redirect(url_for('index'))
```

* **CRUD complet** : create, read, update, delete
* Chaque route rend un template ou redirige après modification

---

# 6⃣ `app.py`

```python
from flask import Flask
from models import db
from routes import register_routes
from config import Config

app = Flask(__name__)
app.config.from_object(Config)

# Initialiser la DB
db.init_app(app)

# Créer les tables si elles n’existent pas
with app.app_context():
    db.create_all()

# Enregistrer les routes
register_routes(app)

if __name__ == '__main__':
    import os
    port = int(os.environ.get("PORT", 5000))
    app.run(host="0.0.0.0", port=port)
```

---

# 7⃣ Templates HTML (`templates/`)

**base.html** :

```html
<!doctype html>
<html>
<head>
  <title>Flask CRUD</title>
</head>
<body>
  <h1>Flask CRUD App</h1>
  {% block content %}{% endblock %}
</body>
</html>
```

**index.html** :

```html
{% extends "base.html" %}
{% block content %}
<a href="{{ url_for('add_item') }}">Add Item</a>
<ul>
  {% for item in items %}
  <li>{{ item.name }} - {{ item.description }}
    <a href="{{ url_for('edit_item', id=item.id) }}">Edit</a>
    <form action="{{ url_for('delete_item', id=item.id) }}" method="POST" style="display:inline;">
      <button type="submit">Delete</button>
    </form>
  </li>
  {% endfor %}
</ul>
{% endblock %}
```

**add.html** et **edit.html** sont similaires avec un formulaire `<input>` pour `name` et `description`.

---

# 8⃣ Dockerfile pour Heroku

```dockerfile
FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 5000
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:$PORT"]
```

* Multi-stage non nécessaire ici car l’app est simple
* Heroku utilise `$PORT` automatiquement

---

# 9⃣ Procfile

```
web: gunicorn app:app
```

---

# 10⃣ Déployer sur Heroku

```bash
heroku login
heroku container:login
heroku create flask-crud-demo

# Construire et pousser l'image
docker build -t registry.heroku.com/flask-crud-demo/web .
docker push registry.heroku.com/flask-crud-demo/web

# Relâcher l'image
heroku container:release web -a flask-crud-demo

# Ouvrir l'app
heroku open -a flask-crud-demo
```

---

# 11⃣ Gestion des secrets et DB

* Heroku fournit une base Postgres automatiquement si tu ajoutes l’addon :

```bash
heroku addons:create heroku-postgresql:hobby-dev -a flask-crud-demo
```

* La variable `DATABASE_URL` est automatiquement définie et utilisée dans `config.py`.
* Pour les autres secrets :

```bash
heroku config:set SECRET_KEY=mysecret -a flask-crud-demo
```

---


Parfait ! On va ajouter **Docker Compose** pour gérer la production de ton app Flask CRUD sur Heroku ou même sur un serveur classique avec **PostgreSQL**. Je vais détailler chaque partie.

---

# 1⃣ Structure du projet mise à jour

```
flask-crud-app/
│
├─ app.py
├─ models.py
├─ routes.py
├─ config.py
├─ requirements.txt
├─ Dockerfile
├─ docker-compose.yml
├─ Procfile
└─ templates/
    ├─ base.html
    ├─ index.html
    ├─ add.html
    └─ edit.html
```

---

# 2⃣ `docker-compose.yml` pour production

```yaml
version: "3.9"

services:
  web:
    build: .
    image: flask-crud-prod
    restart: unless-stopped
    ports:
      - "5000:5000"           # Port exposé sur le serveur
    environment:
      - FLASK_ENV=production
      - SECRET_KEY=${SECRET_KEY}
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/flaskdb
    depends_on:
      - db

  db:
    image: postgres:15
    restart: unless-stopped
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: flaskdb
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
```

---

## 3⃣ Explications

### 3.1 Service `web`

* `build: .` -> construit l’image à partir du Dockerfile
* `image: flask-crud-prod` -> nom de l’image
* `restart: unless-stopped` -> relance automatique du container
* `ports` -> mappe le port interne du container vers le port du serveur
* `environment` -> variables d’environnement (clé Flask, URL DB)
* `depends_on: db` -> attend que la DB soit prête avant de lancer l’app

### 3.2 Service `db`

* Utilise l’image officielle PostgreSQL
* Variables pour l’utilisateur, mot de passe et base
* `volumes` -> persistance des données

### 3.3 Volumes

* `pgdata` -> stockage persistant pour PostgreSQL afin que les données survivent au redémarrage des containers

---

# 4⃣ Adapter `config.py` pour Docker Compose

```python
import os

class Config:
    SECRET_KEY = os.environ.get("SECRET_KEY", "dev-key")
    SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL", "postgresql://postgres:postgres@db:5432/flaskdb")
    SQLALCHEMY_TRACK_MODIFICATIONS = False
```

* Par défaut, la DB pointe sur le service `db` défini dans Docker Compose

---

# 5⃣ Dockerfile (inchangé)

```dockerfile
FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 5000
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:5000"]
```

* Pas besoin de `$PORT` car Docker Compose gère le mapping

---

# 6⃣ Déploiement en production

### 6.1 Construire les containers

```bash
docker-compose build
```

### 6.2 Lancer les containers

```bash
docker-compose up -d
```

* `-d` -> détaché (background)
* Vérifie avec :

```bash
docker-compose ps
docker-compose logs -f web
```

### 6.3 Accéder à l’application

* Depuis ton navigateur : `http://<IP_SERVEUR>:5000`

---

# 7⃣ Avantages de Docker Compose pour la prod

1. Déploiement **multi-services** simple (app + DB + éventuellement Nginx, Redis…)
2. Configuration centralisée des variables d’environnement
3. Persistance des données avec volumes
4. Redémarrage automatique en cas de crash
5. Facile à migrer vers Heroku, AWS ECS ou un serveur Linux classique

---

# 8⃣ Bonus : variables sensibles

* Crée un fichier `.env` :

```
SECRET_KEY=mysecretkey
```

* Puis Docker Compose utilisera automatiquement :

```yaml
environment:
  - SECRET_KEY=${SECRET_KEY}
```

* Permet de ne pas stocker les secrets dans le YAML ou Dockerfile.

---


Parfait ! Je vais te montrer **comment ajouter Nginx dans Docker Compose** pour la production avec Flask + PostgreSQL, étape par étape et en détail. Cela permet d’avoir :

* **HTTPS** avec certificat SSL (via Let's Encrypt ou auto-signé)
* **Reverse proxy** : Nginx reçoit les requêtes et les redirige vers Flask
* Séparation nette entre **app** et **proxy**

---

# 1⃣ Structure du projet mise à jour

```
flask-crud-app/
│
├─ app.py
├─ models.py
├─ routes.py
├─ config.py
├─ requirements.txt
├─ Dockerfile
├─ docker-compose.yml
├─ nginx/
│   ├─ nginx.conf
│   └─ certs/
│       ├─ server.crt
│       └─ server.key
├─ Procfile
└─ templates/
    ├─ base.html
    ├─ index.html
    ├─ add.html
    └─ edit.html
```

* `nginx/` -> configuration Nginx + certificats SSL
* `nginx.conf` -> fichier de config pour le reverse proxy

---

# 2⃣ Docker Compose pour production avec Nginx

```yaml
version: "3.9"

services:
  web:
    build: .
    image: flask-crud-prod
    restart: unless-stopped
    environment:
      - FLASK_ENV=production
      - SECRET_KEY=${SECRET_KEY}
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/flaskdb
    depends_on:
      - db

  db:
    image: postgres:15
    restart: unless-stopped
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: flaskdb
    volumes:
      - pgdata:/var/lib/postgresql/data

  nginx:
    image: nginx:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/certs:/etc/nginx/certs:ro
    depends_on:
      - web

volumes:
  pgdata:
```

---

# 3⃣ Dockerfile (inchangé)

```dockerfile
FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 5000
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:5000"]
```

* Flask écoute sur le port **5000** à l’intérieur du container
* Nginx fait le reverse proxy sur les ports **80/443** vers Flask

---

# 4⃣ Configuration Nginx (`nginx/nginx.conf`)

```nginx
worker_processes 1;

events { worker_connections 1024; }

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout 65;

    upstream flask_app {
        server web:5000;  # Nom du service Docker Compose + port interne Flask
    }

    server {
        listen 80;
        server_name _;

        # Redirection HTTP -> HTTPS
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name _;

        ssl_certificate /etc/nginx/certs/server.crt;
        ssl_certificate_key /etc/nginx/certs/server.key;

        location / {
            proxy_pass http://flask_app;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}
```

* `upstream flask_app` -> pointe vers le container `web:5000`
* Redirection HTTP -> HTTPS pour sécuriser
* Les headers `X-Forwarded-*` permettent à Flask de connaître le vrai IP et le protocole

---

# 5⃣ Certificat SSL

* Pour un test local, tu peux générer un certificat auto-signé :

```bash
mkdir -p nginx/certs
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout nginx/certs/server.key \
  -out nginx/certs/server.crt \
  -subj "/C=FR/ST=Paris/L=Paris/O=FlaskApp/OU=Dev/CN=localhost"
```

* Pour prod réel, utiliser **Let’s Encrypt** ou un certificat officiel

---

# 6⃣ Adapter `config.py` pour HTTPS (optionnel)

Si tu veux détecter HTTPS dans Flask :

```python
class Config:
    SECRET_KEY = os.environ.get("SECRET_KEY", "dev-key")
    SQLALCHEMY_DATABASE_URI = os.environ.get(
        "DATABASE_URL",
        "postgresql://postgres:postgres@db:5432/flaskdb"
    )
    PREFERRED_URL_SCHEME = "https"
    SQLALCHEMY_TRACK_MODIFICATIONS = False
```

---

# 7⃣ Déploiement

### 7.1 Construire les containers

```bash
docker-compose build
```

### 7.2 Lancer en prod

```bash
docker-compose up -d
```

* Flask écoute sur **5000**
* Nginx sur **80/443**
* Redirection automatique HTTP -> HTTPS
* PostgreSQL persistant via volume

### 7.3 Vérification

* Logs Flask : `docker-compose logs -f web`
* Logs Nginx : `docker-compose logs -f nginx`
* Accès depuis navigateur : `https://<IP_SERVEUR>`

---

# 8⃣ Avantages

1. **Sécurité** : HTTPS via Nginx
2. **Reverse proxy** : simplifie la gestion des ports et headers
3. **Multi-service** : Flask + Postgres + Nginx isolés
4. **Scalabilité** : tu peux ajouter plusieurs containers Flask derrière Nginx
5. **Facilité de déploiement** : tout est décrit dans Docker Compose

---


Parfait ! Voici un **schéma détaillé et expliqué** de l’architecture complète de ton projet Flask CRUD en production avec **Docker Compose et Nginx**.

---

# 1⃣ Vue d’ensemble de l’architecture

```
                    ┌───────────────┐
                    │   Internet    │
                    └───────┬───────┘
                            │
                        Ports 80/443
                            │
                    ┌───────[BLACK_DOWN-POINTING_TRIANGLE]───────┐
                    │     Nginx     │
                    │  (Reverse     │
                    │   Proxy/SSL)  │
                    └───────┬───────┘
                            │
                   Proxy vers HTTP 5000
                            │
                    ┌───────[BLACK_DOWN-POINTING_TRIANGLE]───────┐
                    │     Flask     │
                    │   CRUD App    │
                    │(Gunicorn WSGI)│
                    └───────┬───────┘
                            │
                   ORM SQLAlchemy
                            │
                    ┌───────[BLACK_DOWN-POINTING_TRIANGLE]───────┐
                    │   PostgreSQL  │
                    │     Database  │
                    └───────────────┘
```

---

# 2⃣ Explications détaillées

### 2.1 Nginx (Reverse Proxy + HTTPS)

* Sert de **point d’entrée unique** pour toutes les requêtes web.
* Écoute sur **ports 80 et 443**.

  * HTTP (80) -> redirigé vers HTTPS (443)
* SSL/TLS avec certificats dans `nginx/certs/`.
* Transfère les requêtes à Flask (`web:5000`) via `proxy_pass`.
* Ajoute les headers nécessaires (`X-Forwarded-For`, `X-Forwarded-Proto`) pour que Flask connaisse l’IP réelle et le protocole.

---

### 2.2 Flask CRUD App

* Écoute sur **port interne 5000** (non exposé à Internet directement).
* Serveur WSGI **Gunicorn** pour production.
* Contient :

  * `app.py` -> initialisation Flask + routes
  * `models.py` -> tables SQLAlchemy
  * `routes.py` -> CRUD complet (Create, Read, Update, Delete)
  * `templates/` -> pages HTML avec Jinja2
  * `config.py` -> variables de configuration (DB, secrets)

---

### 2.3 PostgreSQL

* Conteneur dédié pour **la persistance des données**.
* Volume Docker `pgdata` pour stocker les données sur le disque du serveur.
* Accessible uniquement depuis le conteneur Flask via Docker network.
* Configuré avec :

  * `POSTGRES_USER=postgres`
  * `POSTGRES_PASSWORD=postgres`
  * `POSTGRES_DB=flaskdb`

---

### 2.4 Docker Compose

* Définit **tous les services** et leur configuration dans un seul fichier `docker-compose.yml`.
* Gère :

  * Dépendances entre services (`depends_on`)
  * Variables d’environnement (`SECRET_KEY`, `DATABASE_URL`)
  * Ports exposés pour Nginx seulement
  * Volumes pour PostgreSQL
* Permet un **déploiement simple** avec une seule commande :

```bash
docker-compose up -d
```

---

# 3⃣ Flux d’une requête utilisateur

1. L’utilisateur tape `https://monapp.com`.
2. La requête arrive sur **Nginx** (443).
3. Nginx :

   * Décrypte SSL
   * Redirige la requête vers `web:5000` (Flask)
   * Ajoute les headers nécessaires
4. Flask reçoit la requête :

   * Utilise SQLAlchemy pour interroger PostgreSQL si nécessaire
   * Retourne HTML ou JSON
5. Nginx renvoie la réponse au client via HTTPS.

---

# 4⃣ Points clés pour la production

* **Sécurité**

  * HTTPS obligatoire
  * Variables d’environnement pour secrets
* **Isolation**

  * Chaque service tourne dans son propre container
  * Base de données non exposée directement à Internet
* **Scalabilité**

  * Ajouter plusieurs containers Flask derrière Nginx possible
* **Persistance**

  * Volume Docker pour PostgreSQL (`pgdata`)
* **Maintenance**

  * Logs faciles à suivre avec `docker-compose logs -f <service>`
  * Redémarrage automatique en cas de crash (`restart: unless-stopped`)

---

# 5⃣ Bonus : Vue schématique des conteneurs Docker

```
Docker Network: flask_crud_app_default
┌───────────────┐          ┌───────────────┐
│   Container   │          │   Container   │
│     Nginx     │ <------> │     Flask     │
│ Ports 80/443  │          │ Port 5000     │
└───────────────┘          └───────────────┘
                                 │
                                 │
                         ┌───────────────┐
                         │ PostgreSQL    │
                         │ Port 5432     │
                         └───────────────┘
```

* Nginx communique avec Flask via le **Docker network interne**
* Flask communique avec PostgreSQL via le **Docker network interne**
* Les ports externes exposés au serveur : **80 et 443 seulement**

---


Parfait ! Voici un **guide complet étape par étape pour déployer ta stack Flask CRUD + PostgreSQL + Nginx avec HTTPS sur un serveur Linux réel**, prêt pour la production, en utilisant **Docker Compose et Let's Encrypt**.

---

# 1⃣ Prérequis sur le serveur

1. Serveur Linux (Ubuntu 22.04 recommandé) avec accès root ou sudo.
2. Nom de domaine pointant vers le serveur (ex : `monapp.com`).
3. Installer **Docker** et **Docker Compose** :

```bash
# Docker
sudo apt update
sudo apt install -y docker.io
sudo systemctl enable --now docker

# Docker Compose
sudo apt install -y docker-compose
docker-compose --version
```

4. Installer **certbot** pour générer des certificats SSL :

```bash
sudo apt install -y certbot
```

---

# 2⃣ Préparer les fichiers du projet

Structure complète :

```
flask-crud-app/
│
├─ app.py
├─ models.py
├─ routes.py
├─ config.py
├─ requirements.txt
├─ Dockerfile
├─ docker-compose.yml
├─ nginx/
│   ├─ nginx.conf
│   └─ certs/  # sert à stocker les certificats SSL générés par certbot
└─ templates/
```

---

# 3⃣ Docker Compose pour prod avec Nginx + SSL automatique

```yaml
version: "3.9"

services:
  web:
    build: .
    container_name: flask_web
    restart: unless-stopped
    environment:
      - FLASK_ENV=production
      - SECRET_KEY=${SECRET_KEY}
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/flaskdb
    depends_on:
      - db

  db:
    image: postgres:15
    container_name: flask_db
    restart: unless-stopped
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: flaskdb
    volumes:
      - pgdata:/var/lib/postgresql/data

  nginx:
    image: nginx:latest
    container_name: flask_nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/certs:/etc/nginx/certs:ro
    depends_on:
      - web

volumes:
  pgdata:
```

* Tous les services sont sur le **même réseau Docker Compose**.
* PostgreSQL est **isolé**, accessible uniquement par Flask.
* Nginx expose les ports 80 et 443 et fait le reverse proxy vers Flask.

---

# 4⃣ Configurer Nginx pour HTTPS automatique

**nginx/nginx.conf** :

```nginx
worker_processes 1;

events { worker_connections 1024; }

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout 65;

    upstream flask_app {
        server web:5000;
    }

    server {
        listen 80;
        server_name monapp.com www.monapp.com;

        location /.well-known/acme-challenge/ {
            root /etc/nginx/certs;
        }

        # Redirection HTTP -> HTTPS
        location / {
            return 301 https://$host$request_uri;
        }
    }

    server {
        listen 443 ssl;
        server_name monapp.com www.monapp.com;

        ssl_certificate /etc/nginx/certs/fullchain.pem;
        ssl_certificate_key /etc/nginx/certs/privkey.pem;

        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;

        location / {
            proxy_pass http://flask_app;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}
```

* `/.well-known/acme-challenge/` est utilisé par **certbot** pour valider le domaine.
* `ssl_certificate` et `ssl_certificate_key` pointent vers les fichiers générés par certbot.

---

# 5⃣ Générer les certificats SSL Let’s Encrypt

1. Arrêter Nginx si un autre service utilise le port 80 :

```bash
docker-compose down
```

2. Lancer certbot pour générer le certificat :

```bash
sudo certbot certonly --standalone -d monapp.com -d www.monapp.com
```

* Les certificats seront créés dans `/etc/letsencrypt/live/monapp.com/`
* Copier les certificats dans le dossier `nginx/certs/` :

```bash
sudo cp /etc/letsencrypt/live/monapp.com/fullchain.pem ./nginx/certs/
sudo cp /etc/letsencrypt/live/monapp.com/privkey.pem ./nginx/certs/
```

---

# 6⃣ Lancer la stack en production

```bash
docker-compose up -d --build
```

* Vérifie les logs :

```bash
docker-compose logs -f web
docker-compose logs -f nginx
```

* Accès depuis navigateur : `https://monapp.com`

* Les données PostgreSQL sont persistantes via le volume `pgdata`.

---

# 7⃣ Renouvellement automatique SSL

* Les certificats Let’s Encrypt expirent tous les 90 jours.
* Ajouter une **tâche cron** sur le serveur pour renouveler automatiquement :

```bash
0 3 * * * docker-compose down && sudo certbot renew && docker-compose up -d
```

* Cela vérifie et renouvelle le certificat chaque jour à 3h du matin.

---

# 8⃣ Avantages de cette configuration

1. **Sécurité maximale**

   * HTTPS obligatoire
   * Nginx en reverse proxy
2. **Isolation des services**

   * App Flask, Nginx et PostgreSQL dans des containers distincts
3. **Persistance**

   * Données PostgreSQL stockées dans un volume Docker
4. **Redondance et maintenance**

   * Redémarrage automatique (`restart: unless-stopped`)
   * Logs centralisés via Docker
5. **Scalabilité**

   * Possibilité d’ajouter plusieurs containers Flask derrière Nginx

---


Parfait ! Voici un **schéma visuel détaillé et expliqué** de l’architecture finale pour ton application Flask CRUD avec **Docker Compose + PostgreSQL + Nginx + HTTPS**.

---

# 1⃣ Schéma général de l’architecture

```
                  ┌───────────────┐
                  │   Internet    │
                  └───────┬───────┘
                          │
                  Ports 80 (HTTP) / 443 (HTTPS)
                          │
                  ┌───────[BLACK_DOWN-POINTING_TRIANGLE]───────┐
                  │     Nginx     │
                  │  Reverse Proxy│
                  │  + SSL/TLS    │
                  └───────┬───────┘
                          │
                  Proxy HTTP -> Flask
                          │
                  ┌───────[BLACK_DOWN-POINTING_TRIANGLE]───────┐
                  │     Flask     │
                  │  CRUD App     │
                  │(Gunicorn WSGI)│
                  └───────┬───────┘
                          │
                  ORM SQLAlchemy
                          │
                  ┌───────[BLACK_DOWN-POINTING_TRIANGLE]───────┐
                  │   PostgreSQL  │
                  │   Database    │
                  └───────────────┘
```

---

# 2⃣ Détails des flux

### 2.1 Flux HTTP/HTTPS

1. L’utilisateur accède à `https://monapp.com`.
2. La requête arrive sur le **container Nginx** sur le port 443.
3. Nginx décrypte le SSL et ajoute les headers nécessaires (`X-Forwarded-For`, `X-Forwarded-Proto`).
4. Nginx transmet la requête à Flask via le port interne 5000.
5. Flask traite la requête, effectue éventuellement des opérations CRUD via SQLAlchemy.
6. La réponse Flask retourne vers Nginx.
7. Nginx renvoie la réponse sécurisée HTTPS à l’utilisateur.

---

### 2.2 Flux CRUD interne

* Flask utilise **SQLAlchemy** pour communiquer avec **PostgreSQL**.
* PostgreSQL est **isolé**, accessible uniquement depuis Flask sur le réseau interne Docker Compose.
* Données persistantes grâce au volume Docker `pgdata`.

---

### 2.3 Gestion des certificats SSL

* Les fichiers `fullchain.pem` et `privkey.pem` sont montés dans Nginx via `docker-compose.yml`.
* Certbot gère le renouvellement automatique via un cron job.
* HTTP (port 80) redirige automatiquement vers HTTPS (port 443).

---

# 3⃣ Vue conteneur Docker détaillée

```
Docker Network: flask_crud_app_default
┌───────────────────┐
│   Container Nginx  │
│ Ports: 80/443      │
│ Reverse Proxy/SSL  │
│ Volume certs/      │
└─────────┬─────────┘
          │ proxy_pass HTTP 5000
┌─────────[BLACK_DOWN-POINTING_TRIANGLE]─────────┐
│   Container Flask │
│ Port: 5000        │
│ Gunicorn WSGI     │
│ Env: SECRET_KEY   │
│ Env: DATABASE_URL │
└─────────┬─────────┘
          │ SQLAlchemy
┌─────────[BLACK_DOWN-POINTING_TRIANGLE]─────────┐
│  Container DB     │
│ PostgreSQL 5432   │
│ Volume: pgdata    │
└───────────────────┘
```

* Tous les containers communiquent via le **réseau Docker interne**.
* Seuls Nginx et le port 443 sont exposés au monde extérieur.
* Flask et PostgreSQL ne sont pas directement exposés au réseau public.

---

# 4⃣ Avantages de cette architecture

| Composant        | Avantage                                                                   |
| ---------------- | -------------------------------------------------------------------------- |
| Nginx            | Reverse proxy, HTTPS, redirection HTTP->HTTPS, gestion des headers          |
| Flask (Gunicorn) | Serveur WSGI robuste, exécute l’application CRUD                           |
| PostgreSQL       | Base de données persistante, isolée, scalable                              |
| Docker Compose   | Déploiement multi-services simple, redémarrage automatique, réseaux isolés |
| Volumes Docker   | Persistance des données et des certificats                                 |
| Certbot          | SSL gratuit et renouvellement automatique                                  |

---

# 5⃣ Résumé des flux

1. **Internet -> Nginx (443 HTTPS)**
2. **Nginx -> Flask (5000 HTTP interne)**
3. **Flask -> PostgreSQL (via SQLAlchemy)**
4. **Réponse** : PostgreSQL -> Flask -> Nginx -> Internet

* Le **client ne voit jamais Flask ou PostgreSQL directement**.
* Nginx protège et sécurise l’accès avec HTTPS.

---



[OK] RESSOURCES & LIENS

# Documentation officielle Heroku:
# https://devcenter.heroku.com/

# Guides spécifiques:
# Python: https://devcenter.heroku.com/articles/getting-started-with-python
# Django: https://devcenter.heroku.com/articles/deploying-python-django-app
# Flask: https://devcenter.heroku.com/articles/getting-started-with-python-introduction

# Heroku CLI Reference:
# https://devcenter.heroku.com/articles/heroku-cli-commands

# Add-ons disponibles:
# https://elements.heroku.com/addons

# Pricing:
# https://www.heroku.com/pricing

# Blog et actualités:
# https://blog.heroku.com/

# Forum d'aide:
# https://stackoverflow.com/questions/tagged/heroku