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


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

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

# 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.

# RENDER = Plateforme moderne qui fait tout ça pour toi!
# Tu ne fais que:
# 1. Connecter ton dépôt GitHub
# 2. BOOM! Ton app est en ligne
# 3. Des gens du monde entier peuvent y accéder via une URL

# Render s'appelle une "PaaS" (Platform as a Service)
# = Plateforme où tu déploies facilement sans gérer l'infrastructure
# = Alternative moderne à Heroku (mais souvent moins cher!)


# === POURQUOI RENDER EST POPULAIRE EN 2024 ? ===

# Avantages par rapport à Heroku:
# [OK] Free tier PLUS GÉNÉREUX (pas de sleep après 30 min comme Heroku ancien)
# [OK] MOINS CHER: Web Services à partir de $7/mois vs $25-50 sur Heroku
# [OK] Déploiement automatique depuis GitHub/GitLab
# [OK] SSL/HTTPS gratuit et automatique
# [OK] Protection DDoS incluse gratuitement
# [OK] Plus rapide: infrastructure moderne
# [OK] Interface simple et claire

# Render a été créé en 2018 par d'anciens ingénieurs Heroku
# = Ils ont pris ce qui marchait bien et l'ont amélioré!


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

# WEB SERVICE (Service Web)
# = Ton application Python qui tourne sur un serveur
# = Peut être Flask, Django, FastAPI, etc.
# = A une URL unique: myapp.onrender.com
# = C'est comme un conteneur qui contient tout ton code
# Exemple: Si tu as une app Flask, un Web Service = l'app en ligne

# STATIC SITE (Site Statique)
# = Site web qui ne change pas (HTML, CSS, JS uniquement)
# = Pas de code backend (pas de Python qui tourne)
# = Exemples: portfolio, landing page, documentation
# = GRATUIT sur Render!

# BACKGROUND WORKER (Travailleur en arrière-plan)
# = Programme qui fait des tâches longues sans bloquer l'app
# = Exemples: envoyer des emails, traiter des images, scraping
# = Tourne continuellement en arrière-plan

# CRON JOB (Tâche planifiée)
# = Script qui s'exécute à intervalles réguliers
# = Exemples: backup quotidien, nettoyage hebdomadaire
# = Comme un réveil qui lance ton script

# PRIVATE SERVICE (Service Privé)
# = Service accessible SEULEMENT par tes autres services
# = Pas accessible depuis internet
# = Exemple: API interne, microservice

# INSTANCE TYPE (Type d'instance)
# = La puissance du serveur qui exécute ton app
# = Free: 512 MB RAM, 0.1 CPU (gratuit mais limité)
# = Starter: 512 MB RAM, 0.5 CPU ($7/mois)
# = Standard: 2 GB RAM, 1 CPU ($25/mois)
# = Pro: 4 GB RAM, 2 CPU ($85/mois)
# = Plus c'est puissant, plus c'est rapide!

# ENVIRONMENT VARIABLES (Variables d'environnement)
# = 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

# AUTO-DEPLOY (Déploiement automatique)
# = Chaque fois que tu push sur GitHub, Render redéploie automatiquement
# = Tu n'as rien à faire manuellement!
# = Push git = nouvelle version en ligne

# RENDER DISK (Disque persistant)
# = Espace de stockage qui persiste même quand l'app redémarre
# = Par défaut, les fichiers sont perdus au redémarrage!
# = Avec Render Disk, tu peux sauvegarder des fichiers (uploads, cache, etc)

# BUILD COMMAND (Commande de construction)
# = Commande qui prépare ton app avant démarrage
# = Exemple: pip install -r requirements.txt
# = Render l'exécute automatiquement

# START COMMAND (Commande de démarrage)
# = Commande pour lancer ton app
# = Exemple: gunicorn app:app
# = C'est l'équivalent du "Procfile" sur Heroku


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

# 1. Tu as un dépôt GitHub avec ton app Python
#    myapp/
#    ├── app.py (ton app Flask/Django)
#    ├── requirements.txt (liste des packages)
#    └── .git (dépôt git)

# 2. Tu connectes ton GitHub à Render (une seule fois)

# 3. Tu crées un "Web Service" sur Render
#    Tu choisis: ton dépôt GitHub, la branche (main/master)

# 4. Render détecte automatiquement que c'est Python
#    Il suggère les commandes:
#    Build: pip install -r requirements.txt
#    Start: gunicorn app:app

# 5. Tu cliques "Create Web Service"

# 6. Render fait automatiquement:
#    a. Clone ton code depuis GitHub
#    b. Installe Python et tes packages (requirements.txt)
#    c. Lance ton app avec gunicorn (ou autre)
#    d. Assigne une URL: myapp.onrender.com

# 7. Quelqu'un ouvre: https://myapp.onrender.com
#    Render route la requête vers ton service
#    Ton app répond
#    La personne voit le résultat!

# 8. Si tu modifies ton code:
#    git add .
#    git commit -m "Fix bug"
#    git push origin main
#    Render DÉTECTE le push et redéploie AUTOMATIQUEMENT!
#    = Pas besoin de "git push render" comme Heroku


# === DIFFÉRENCES CLÉS AVEC HEROKU ===

# HEROKU:
# - Nécessite CLI (heroku login, heroku create, etc)
# - Déploiement: git push heroku main
# - Procfile OBLIGATOIRE
# - Dynos (terminologie propre à Heroku)
# - Free tier: sleep après 30 min (ancien modèle)
# - Plus cher: minimum $7/mois

# RENDER:
# - Pas besoin de CLI (tout via Dashboard web)
# - Déploiement: git push origin main (GitHub détecte)
# - Pas de Procfile nécessaire (mais recommandé)
# - Web Services / Workers (terminologie claire)
# - Free tier: peut spin down mais redémarre vite
# - Moins cher: $7/mois pour Starter
# - Interface plus moderne et intuitive


[OK] INSTALLATION ET COMPTE (SUPER SIMPLE!)

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

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

# Instructions:
# 1. Ouvre un navigateur (Chrome, Firefox, Safari...)
# 2. Va sur: https://render.com/
# 3. Clique sur "Get Started" ou "Sign Up" (haut à droite)
# 4. Tu as 3 options de connexion:

#    OPTION A: Avec GitHub (RECOMMANDÉ!)
#    - Clique "Sign Up with GitHub"
#    - Autorise Render à accéder à ton GitHub
#    - BOOM! Compte créé instantanément
#    - Avantage: Déploiement automatique depuis GitHub

#    OPTION B: Avec GitLab
#    - Clique "Sign Up with GitLab"
#    - Autorise Render à accéder à ton GitLab
#    - Compte créé!

#    OPTION C: Avec Email
#    - Clique "Sign Up with Email"
#    - Email: ton adresse email
#    - Password: mot de passe sécurisé (>= 8 caractères)
#    - Nom complet: ton nom
#    - Clique "Sign Up"
#    - Vérifie ton email: Render t'envoie un lien
#    - Clique sur le lien de confirmation

# 5. YAY! Ton compte est créé!

# Note: C'est 100% gratuit pour commencer!
# Pas de carte bancaire requise pour le Free tier


# === ÉTAPE 2: CONNECTER GITHUB (SI PAS FAIT) ===

# Si tu as créé ton compte avec email, tu dois connecter GitHub

# Instructions:
# 1. Connecte-toi à Render: https://dashboard.render.com
# 2. Clique sur ton avatar (haut à droite)
# 3. Clique "Account Settings"
# 4. Dans la section "Connected Accounts"
# 5. Clique "+ Connect GitHub Account"
# 6. Autorise Render sur GitHub
# 7. Choisis les dépôts:
#    - All repositories (tous tes dépôts)
#    - Only select repositories (seulement certains)
# 8. Clique "Install & Authorize"

# Maintenant Render peut voir tes dépôts GitHub!


# === PAS DE CLI NÉCESSAIRE! ===

# Contrairement à Heroku, Render n'a PAS BESOIN de CLI!
# Tout se fait via le Dashboard web

# MAIS il existe une CLI Render (optionnelle):
# https://render.com/docs/cli
# Utile pour: logs, shell, automatisation

# Installation (optionnelle):
# macOS/Linux:
brew install render

# Windows:
# Télécharge depuis: https://github.com/render-com/cli/releases

# Vérifier:
render --version

# Se connecter:
render login
# Ouvre le navigateur pour autoriser


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

# === POURQUOI CETTE STRUCTURE? ===

# Render détecte automatiquement Python
# Mais tu dois lui donner des instructions pour:
# 1. Installer les dépendances (requirements.txt)
# 2. Lancer l'app (commande de démarrage)

# Render est PLUS FLEXIBLE que Heroku:
# - Pas de Procfile obligatoire
# - Configuration via Dashboard OU fichier render.yaml
# - Détection automatique du langage

# === FICHIERS IMPORTANTS ===

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

# Qu'est-ce que c'est?
# = Liste de tous les packages Python dont tu as besoin
# = Render 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 Render 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==3.0.0
# Requests==2.31.0
# gunicorn==21.2.0

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

# Vérifier:
cat requirements.txt


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

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

# Pourquoi?
# Sans ça, Render choisit une version par défaut (Python 3.7)
# Tu développes probablement en Python 3.11 ou 3.12
# = 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:
# python-3.12.0
# python-3.11.0
# python-3.10.0
# python-3.9.0

# Note: Render supporte toutes les versions 3.7+


# === FILE #3: .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/
*.db
*.sqlite3
EOF

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


# === FILE #4: render.yaml (OPTIONNEL - Infrastructure as Code) ===

# Qu'est-ce que c'est?
# = Fichier de configuration pour TOUTE ton infrastructure
# = Alternative au Dashboard: tout est défini en code
# = Très pratique pour projets complexes

# Pourquoi?
# - Versionner ta configuration (dans git)
# - Répliquer facilement l'infrastructure
# - Déployer plusieurs environnements (dev, staging, prod)
# - Automatiser complètement

# Exemple simple:

"""
services:
  # Web Service
  - type: web
    name: myapp
    runtime: python
    plan: starter
    buildCommand: pip install -r requirements.txt
    startCommand: gunicorn app:app
    envVars:
      - key: FLASK_ENV
        value: production
      - key: SECRET_KEY
        generateValue: true

databases:
  # PostgreSQL
  - name: myapp-db
    plan: starter
    databaseName: myapp_db
    user: myapp_user
"""

# Avantage: Tu push ce fichier sur GitHub
# Render le détecte et crée TOUT automatiquement!

# Pour utiliser render.yaml:
# 1. Crée le fichier à la racine du projet
# 2. Push sur GitHub
# 3. Sur Render Dashboard: "New" > "Blueprint"
# 4. Sélectionne ton dépôt
# 5. Render crée tous les services automatiquement!


# === 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
# ├── requirements.txt       # Fichier: liste des packages (OBLIGATOIRE!)
# ├── runtime.txt            # Fichier: version Python (RECOMMANDÉ!)
# ├── render.yaml            # Fichier: config infrastructure (OPTIONNEL)
# │
# ├── templates/             # Dossier: templates HTML (Flask)
# ├── static/                # Dossier: CSS, JS, images
# │
# ├── README.md              # Fichier: documentation
# └── .env                   # Fichier: variables secrètes (IGNORÉ!)


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

# === ÉTAPE 1: Créer ton app localement ===

# Example app.py minimal Flask:

# app.py
from flask import Flask
import os

app = Flask(__name__)

@app.route("/")
def hello():
    return """
    <html>
        <body style="font-family: Arial">
            <h1>Hello from Render!</h1>
            <p>Mon app fonctionne sur Render!</p>
        </body>
    </html>
    """

@app.route("/health")
def health():
    return {"status": "ok"}

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

# Pourquoi ces détails?
# - os.environ.get("PORT", 10000) = Render utilise le port 10000 par défaut
# - host="0.0.0.0" = Accessible de partout (pas seulement localhost)
# - debug=False = Ne pas activer debug mode en production!
# - /health = Endpoint pour vérifier que l'app est vivante


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

# Pourquoi? Pour isoler les packages Python de ton projet

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

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

# Tu dois voir: (venv) $ ou (venv) C:\...


# === ÉTAPE 3: Installer les dépendances ===

# TOUJOURS avec (venv) activé!

pip install flask
pip install gunicorn  # TRÈS IMPORTANT pour Render!

# Pour Django:
pip install django gunicorn

# Pour FastAPI:
pip install fastapi uvicorn gunicorn

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

# Vérifier:
cat requirements.txt
# Doit afficher:
# Flask==3.0.0
# Werkzeug==3.0.0
# gunicorn==21.2.0
# etc...


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

# Spécifier la version Python:
echo "python-3.11.0" > runtime.txt


# === ÉTAPE 5: Tester localement ===

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

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

# Ouvre un navigateur: http://localhost:10000
# Tu vois: "Hello from Render!"

# Teste aussi: http://localhost:10000/health
# Tu vois: {"status": "ok"}

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

# Pour arrêter: Ctrl+C


# === ÉTAPE 6: Initialiser git et push sur GitHub ===

# Vérifier si git est déjà initialisé:
git status

# Si erreur "not a git repository":
git init

# Créer .gitignore:
cat > .gitignore << 'EOF'
venv/
.env
*.pyc
__pycache__/
*.db
EOF

# Faire le premier commit:
git add .
git commit -m "Initial commit - Flask app ready"

# Créer un dépôt sur GitHub:
# 1. Va sur: https://github.com/new
# 2. Nom du dépôt: myflaskapp
# 3. Public ou Private (ton choix)
# 4. NE PAS COCHER "Initialize with README"
# 5. Clique "Create repository"

# Ajouter le remote et push:
git remote add origin https://github.com/TON_USERNAME/myflaskapp.git
git branch -M main
git push -u origin main

# Vérifie sur GitHub: ton code doit être visible!


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

# === MÉTHODE 1: VIA DASHBOARD (RECOMMANDÉ POUR DÉBUTANTS) ===

# === ÉTAPE 1: Aller sur le Dashboard Render ===

# 1. Connecte-toi: https://dashboard.render.com
# 2. Tu vois le Dashboard avec tes services (vide si premier déploiement)


# === ÉTAPE 2: Créer un Web Service ===

# 1. Clique sur "New +" (haut à droite)
# 2. Sélectionne "Web Service"
# 3. Tu vois la liste de tes dépôts GitHub

# Si tu ne vois PAS tes dépôts:
# - Clique "+ Connect account"
# - Autorise Render à accéder à GitHub
# - Choisis les dépôts à partager

# 4. Trouve ton dépôt (myflaskapp)
# 5. Clique "Connect"


# === ÉTAPE 3: Configurer le Web Service ===

# Render affiche un formulaire avec des champs à remplir:

# --- SECTION: General ---

# Name (Nom du service)
# = Le nom unique pour ton service
# Exemple: myflaskapp
# = Sera utilisé pour l'URL: myflaskapp.onrender.com
# IMPORTANT: Doit être unique sur Render (pas déjà pris!)

# Region (Région du serveur)
# = Où le serveur sera physiquement
# Options:
#   - Oregon (États-Unis Ouest) - Recommandé pour Asie/Pacifique
#   - Ohio (États-Unis Centre)
#   - Frankfurt (Europe) - Recommandé pour Europe/Afrique
#   - Singapore (Asie)
# Choisis la plus proche de tes utilisateurs!

# Branch (Branche git)
# = La branche GitHub à déployer
# Exemple: main ou master
# Render redéploie automatiquement quand tu push sur cette branche!


# --- SECTION: Build & Deploy ---

# Runtime (Environnement d'exécution)
# = Render détecte automatiquement "Python"
# Si pas détecté: sélectionne manuellement "Python"

# Build Command (Commande de construction)
# = Commande pour installer les dépendances
# Render suggère automatiquement:
pip install -r requirements.txt

# Si tu as besoin de plus:
pip install -r requirements.txt && python manage.py migrate

# Start Command (Commande de démarrage)
# = Commande pour lancer ton app
# Pour Flask:
gunicorn app:app

# Explications:
# - app:app = fichier.py:variable_app
# - "app:app" signifie: dans app.py, utilise la variable app

# Pour Django:
gunicorn myproject.wsgi

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

# IMPORTANT: Change "app:app" selon ton nom de fichier/variable!


# --- SECTION: Plan ---

# Instance Type (Type d'instance)
# = La puissance du serveur

# FREE (Gratuit)
# - 512 MB RAM
# - 0.1 CPU partagé
# - Peut "spin down" après inactivité
# - Redémarre en quelques secondes
# - PARFAIT pour prototypes, tests, petits projets

# STARTER ($7/mois)
# - 512 MB RAM
# - 0.5 CPU
# - TOUJOURS actif (ne s'endort jamais!)
# - Bon pour petites apps en production

# STANDARD ($25/mois)
# - 2 GB RAM
# - 1 CPU
# - Excellent pour apps moyennes

# PRO ($85/mois)
# - 4 GB RAM
# - 2 CPU
# - Pour apps avec beaucoup d'utilisateurs

# Pour commencer: Choisis FREE!


# --- SECTION: Advanced (Optionnel) ---

# Auto-Deploy (Déploiement automatique)
# = Redéployer quand tu push sur GitHub
# RECOMMANDÉ: Laisse activé (Yes)

# Health Check Path (Chemin de santé)
# = URL pour vérifier que l'app fonctionne
# Exemple: /health
# Render ping cette URL régulièrement
# Si pas de réponse: Render redémarre l'app!

# Environment Variables (Variables d'environnement)
# = Secrets et configuration
# Exemple:
# - SECRET_KEY = ta-cle-secrete-change-moi
# - FLASK_ENV = production
# Tu peux en ajouter maintenant ou plus tard


# === ÉTAPE 4: Créer le Web Service ===

# 1. Vérifie toute la configuration
# 2. Clique "Create Web Service" (en bas)

# Qu'il se passe:
# 1. Render clone ton code depuis GitHub
# 2. Installe Python 3.11 (ou version dans runtime.txt)
# 3. Exécute: pip install -r requirements.txt
# 4. Lance: gunicorn app:app
# 5. Assigne l'URL: myflaskapp.onrender.com

# Tu vois un écran avec des LOGS en temps réel:
# ==> Cloning from https://github.com/...
# ==> Detected Python 3.11.0
# ==> Installing dependencies...
# ==> Collecting Flask==3.0.0
# ==> Installing gunicorn==21.2.0
# ==> Build successful! [BRAVO]
# ==> Starting gunicorn...
# ==> Your service is live! [RAPIDE]

# Temps moyen: 2-5 minutes


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

# Une fois le déploiement terminé:

# 1. Tu vois l'état "Live" (vert) en haut à gauche
# 2. Clique sur l'URL: https://myflaskapp.onrender.com
# 3. Ton navigateur ouvre l'app
# 4. Tu vois: "Hello from Render!"

# BRAVO! Ton app est en ligne!

# Tu peux aussi:
# - Cliquer sur "Logs" pour voir les logs
# - Cliquer sur "Events" pour voir l'historique
# - Cliquer sur "Settings" pour modifier la config


# === MÉTHODE 2: VIA RENDER.YAML (AVANCÉ) ===

# Pour automatiser TOUT le déploiement

# 1. Crée render.yaml à la racine:

services:
  - type: web
    name: myflaskapp
    runtime: python
    plan: starter
    buildCommand: pip install -r requirements.txt
    startCommand: gunicorn app:app
    envVars:
      - key: FLASK_ENV
        value: production
      - key: SECRET_KEY
        generateValue: true

# 2. Push sur GitHub:
git add render.yaml
git commit -m "Add render.yaml"
git push origin main

# 3. Sur Render Dashboard:
#    - Clique "New +" > "Blueprint"
#    - Sélectionne ton dépôt
#    - Render lit render.yaml et crée TOUT automatiquement!

# Avantage: Configuration versionnée dans git!


[OK] APRÈS DÉPLOIEMENT: GESTION ET SURVEILLANCE

# === VOIR LES LOGS ===

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

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Logs" (onglet en haut)
# 3. Tu vois les logs en temps réel
# 4. Scroll vers le bas pour voir les plus récents

# Via CLI (si installée):
render logs -s myflaskapp
# Affiche les logs en temps réel

# Filtrer les logs:
# - Clique sur le filtre (icône entonnoir)
# - Choisis: All, Errors, Warnings
# - Cherche un mot-clé

# Télécharger les logs:
# - Clique sur "Download Logs"
# - Sauvegarde un fichier .txt


# === REDÉPLOYER MANUELLEMENT ===

# Si tu veux forcer un redéploiement:

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Manual Deploy" (bouton en haut à droite)
# 3. Sélectionne la branche
# 4. Clique "Deploy"

# Render refait tout le build et redémarre l'app!


# === SUSPENDRE / REPRENDRE UN SERVICE ===

# Si tu ne veux pas utiliser ton service temporairement:

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Settings" (onglet en haut)
# 3. Scroll vers le bas
# 4. Clique sur "Suspend Service"
# 5. Confirme: "Yes, suspend this service"

# Le service est maintenant suspendu:
# - Il ne consomme plus de ressources
# - L'URL ne fonctionne plus
# - Tu ne paies plus (si service payant)

# Pour reprendre:
# 1. Retourne dans "Settings"
# 2. Clique sur "Resume Service"
# 3. Le service redémarre en quelques secondes


# === REDÉPLOYER MANUELLEMENT ===

# Si tu veux forcer un redéploiement sans changer le code:

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Manual Deploy" (bouton en haut à droite)
# 3. Sélectionne la branche (main/master)
# 4. Clique "Deploy"

# Render refait tout le build et redémarre l'app!

# Pourquoi redéployer manuellement?
# - Tu as changé des variables d'environnement
# - Tu veux forcer une reconstruction
# - Tu as un problème et veux "redémarrer"


# === ROLLBACK (REVENIR À UNE VERSION PRÉCÉDENTE) ===

# Si ton dernier déploiement a cassé quelque chose:

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Events" (onglet)
# 3. Tu vois l'historique des déploiements
# 4. Trouve le déploiement qui marchait bien
# 5. Clique sur les "..." à droite
# 6. Clique "Rollback to this deploy"
# 7. Confirme

# Render redéploie la version précédente!
# Ton app revient à l'état d'avant


# === VOIR LES MÉTRIQUES ===

# Render fournit des statistiques sur ton service

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Metrics" (onglet)
# 3. Tu vois:
#    - CPU Usage (utilisation du processeur)
#    - Memory Usage (utilisation de la mémoire)
#    - Request Count (nombre de requêtes)
#    - Response Time (temps de réponse)
#    - Error Rate (taux d'erreur)

# Ces métriques t'aident à:
# - Détecter les problèmes de performance
# - Savoir si tu dois upgrader ton plan
# - Comprendre l'utilisation de ton app


# === CONFIGURER DES ALERTES ===

# Render peut t'envoyer des notifications

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Settings"
# 3. Scroll vers "Notifications"
# 4. Configure:
#    - Email notifications (par email)
#    - Slack notifications (dans Slack)
#    - Webhook notifications (URL personnalisée)

# Types d'alertes:
# - Deploy succeeded (déploiement réussi)
# - Deploy failed (déploiement échoué)
# - Service unhealthy (service en panne)
# - High memory usage (mémoire élevée)


[OK] VARIABLES D'ENVIRONNEMENT DÉTAILLÉES

# === QU'EST-CE QU'UNE VARIABLE D'ENVIRONNEMENT? ===

# Imagine que tu as une clé API secrète pour un 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 sur GitHub
# 3. N'IMPORTE QUI peut la voir et l'utiliser!

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

# Avantages:
# - Sécurité: secrets hors du code
# - Flexibilité: différentes valeurs selon l'environnement
# - Facilité: changement sans modifier le code


# === AJOUTER UNE VARIABLE VIA DASHBOARD ===

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Environment" (onglet)
# 3. Clique sur "Add Environment Variable"
# 4. Remplis:
#    Key: API_KEY
#    Value: secret_key_12345abcde
# 5. Clique "Save Changes"

# Render redémarre automatiquement le service!
# La variable est maintenant disponible


# === UTILISER UNE VARIABLE DANS TON CODE ===

# Dans ton app Python:

import os

# Récupérer la valeur
api_key = os.environ.get("API_KEY")

# Vérifier si elle existe
if api_key:
    print(f"API Key chargée: {api_key[:5]}...")
else:
    print("ERREUR: API_KEY manquante!")

# Avec valeur par défaut
debug = os.environ.get("DEBUG", "False")
# Si DEBUG n'existe pas, utilise "False"

# Convertir en booléen
debug_mode = os.environ.get("DEBUG", "False").lower() == "true"


# === EXEMPLE COMPLET: API EXTERNE ===

# app.py
from flask import Flask, jsonify
import os
import requests

app = Flask(__name__)

@app.route("/weather/<city>")
def weather(city):
    """Récupérer la météo via une API externe"""
    
    # Récupérer la clé API depuis les variables d'environnement
    api_key = os.environ.get("WEATHER_API_KEY")
    
    if not api_key:
        return jsonify({"error": "WEATHER_API_KEY non configurée"}), 500
    
    # Appeler l'API météo
    url = f"https://api.weatherapi.com/v1/current.json?key={api_key}&q={city}"
    response = requests.get(url)
    
    if response.status_code == 200:
        data = response.json()
        return jsonify({
            "city": city,
            "temperature": data["current"]["temp_c"],
            "condition": data["current"]["condition"]["text"]
        })
    else:
        return jsonify({"error": "Impossible de récupérer la météo"}), 500


# Sur Render Dashboard:
# Ajoute la variable: WEATHER_API_KEY = ta_vraie_cle_api

# Personne ne voit ta clé API dans le code!


# === VARIABLES PRÉDÉFINIES PAR RENDER ===

# Render crée automatiquement certaines variables:

# PORT
# = Le port sur lequel ton app doit écouter
# = TOUJOURS utiliser cette variable!
port = int(os.environ.get("PORT", 10000))
app.run(host="0.0.0.0", port=port)

# RENDER
# = Vaut "true" quand l'app tourne sur Render
# = Utile pour différencier local vs production
is_render = os.environ.get("RENDER") == "true"

# RENDER_SERVICE_NAME
# = Le nom de ton service Render
service_name = os.environ.get("RENDER_SERVICE_NAME")

# RENDER_GIT_COMMIT
# = Le hash du commit git déployé
commit_hash = os.environ.get("RENDER_GIT_COMMIT")

# DATABASE_URL (si tu as ajouté PostgreSQL)
# = URL de connexion à la base de données
database_url = os.environ.get("DATABASE_URL")


# === FICHIER .env POUR LE DÉVELOPPEMENT LOCAL ===

# Sur Render, tu utilises le Dashboard pour les variables
# 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
WEATHER_API_KEY=local_weather_key
DATABASE_URL=sqlite:///local.db

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

# Installer python-dotenv:
pip install python-dotenv
pip freeze > requirements.txt

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

# Charger le fichier .env (seulement en local!)
load_dotenv()

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

# Maintenant tu peux tester localement:
python app.py
# Les variables du .env sont chargées!


# === BONNES PRATIQUES ===

# 1. JAMAIS commiter de secrets dans git
#    [X] MAUVAIS: api_key = "secret123" dans app.py
#    [OK] BON: api_key = os.environ.get("API_KEY")

# 2. Toujours vérifier que les variables existent
if not os.environ.get("DATABASE_URL"):
    raise ValueError("DATABASE_URL manquante!")

# 3. Utiliser des valeurs par défaut sensées
debug = os.environ.get("DEBUG", "False").lower() == "true"

# 4. Documenter les variables nécessaires dans README.md
"""
## Variables d'environnement requises

- `API_KEY`: Clé API pour le service externe
- `DATABASE_URL`: URL de la base de données PostgreSQL
- `SECRET_KEY`: Clé secrète Flask (32+ caractères)
- `DEBUG`: Mode debug (True/False)
"""

# 5. Ne jamais logger les secrets
# [X] MAUVAIS:
print(f"API Key: {api_key}")

# [OK] BON:
print(f"API Key chargée: {api_key[:5]}***")


# === GÉNÉRER UNE CLÉ SECRÈTE SÉCURISÉE ===

# Pour Flask, Django, etc., tu as besoin d'une SECRET_KEY

# Python:
import secrets
print(secrets.token_urlsafe(32))
# Affiche: dF5k8jK3mN9pQ2rT7vY1zA4bC6eH0iL8mO3nP5qR2sT

# Linux/macOS (dans le terminal):
python3 -c "import secrets; print(secrets.token_urlsafe(32))"

# Windows PowerShell:
python -c "import secrets; print(secrets.token_urlsafe(32))"

# Copie ce résultat et ajoute-le sur Render:
# Key: SECRET_KEY
# Value: dF5k8jK3mN9pQ2rT7vY1zA4bC6eH0iL8mO3nP5qR2sT


[OK] BASE DE DONNÉES POSTGRESQL

# === POURQUOI POSTGRESQL? ===

# Sur ton ordinateur, tu utilises peut-être SQLite:
# - Fichier .db simple
# - Parfait pour le développement local
# - MAIS: ne marche pas bien en production!

# Problèmes de SQLite en production:
# - Fichier perdu quand le service redémarre
# - Pas de backups automatiques
# - Lent avec plusieurs utilisateurs simultanés
# - Pas sécurisé

# PostgreSQL = Base de données professionnelle
# - Données persistantes (jamais perdues)
# - Backups automatiques
# - Rapide même avec beaucoup d'utilisateurs
# - Transactions, indexes, sécurité
# - C'est LA solution pour la production!


# === AJOUTER POSTGRESQL À TON SERVICE ===

# Via Dashboard:
# 1. Va sur ton service Render
# 2. En haut à gauche, clique sur ton nom de compte
# 3. Clique sur "Dashboard" (retour à l'accueil)
# 4. Clique sur "New +" (en haut à droite)
# 5. Sélectionne "PostgreSQL"
# 6. Remplis:
#    Name: myapp-db
#    Database: myapp_database
#    User: myapp_user
#    Region: (même région que ton web service)
#    Plan: Free (gratuit, limité à 256MB)
# 7. Clique "Create Database"

# Render crée la base de données!
# Temps: ~30 secondes

# Une fois créée, tu vois les détails:
# - Internal Database URL (pour les services Render)
# - External Database URL (pour connexions externes)


# === CONNECTER LA DB À TON WEB SERVICE ===

# Via Dashboard:
# 1. Va sur ton web service
# 2. Clique sur "Environment" (onglet)
# 3. Clique "Add Environment Variable"
# 4. Key: DATABASE_URL
# 5. Value: Copie l'Internal Database URL depuis la page de ta DB
#    Exemple: postgresql://user:password@dpg-xxxxx-a/myapp_database
# 6. Clique "Save Changes"

# Render redémarre ton service avec la nouvelle variable!


# === UTILISER POSTGRESQL AVEC FLASK-SQLALCHEMY ===

# Installer les dépendances:
pip install flask-sqlalchemy psycopg2-binary
pip freeze > requirements.txt

# Dans app.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os

app = Flask(__name__)

# Configuration de la base de données
# Utilise DATABASE_URL de Render, ou SQLite en local
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
    'DATABASE_URL',
    'sqlite:///local.db'  # Fallback pour le développement local
)

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Initialiser SQLAlchemy
db = SQLAlchemy(app)


# Définir un modèle
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    
    def __repr__(self):
        return f'<User {self.username}>'


# Créer les tables
with app.app_context():
    db.create_all()


@app.route('/')
def index():
    # Compter les utilisateurs
    user_count = User.query.count()
    return f"Il y a {user_count} utilisateurs dans la base de données!"


@app.route('/add/<username>')
def add_user(username):
    # Ajouter un utilisateur
    new_user = User(username=username, email=f"{username}@example.com")
    db.session.add(new_user)
    db.session.commit()
    return f"Utilisateur {username} ajouté!"


# Lancer l'app
if __name__ == '__main__':
    port = int(os.environ.get('PORT', 10000))
    app.run(host='0.0.0.0', port=port, debug=False)


# === UTILISER POSTGRESQL AVEC DJANGO ===

# Installer les dépendances:
pip install django psycopg2-binary dj-database-url
pip freeze > requirements.txt

# Dans settings.py:

import os
import dj_database_url

# Configuration de la base de données
DATABASES = {
    'default': dj_database_url.config(
        default=os.environ.get('DATABASE_URL'),
        conn_max_age=600,  # Garde la connexion ouverte 10 minutes
        ssl_require=True   # Nécessaire pour Render
    )
}

# Fallback pour le développement local
if not os.environ.get('DATABASE_URL'):
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': BASE_DIR / 'db.sqlite3',
        }
    }


# Faire les migrations sur Render:
# Ajoute cette ligne dans ton Procfile:

web: gunicorn myproject.wsgi
release: python manage.py migrate

# Maintenant, à chaque déploiement:
# 1. Render exécute les migrations
# 2. Puis démarre gunicorn

# Créer un superuser:
# Tu ne peux pas le faire interactivement sur Render
# Solution: Utilise une commande Django personnalisée

# Créer: myapp/management/commands/create_superuser.py

from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
import os

class Command(BaseCommand):
    def handle(self, *args, **kwargs):
        User = get_user_model()
        username = os.environ.get('DJANGO_SUPERUSER_USERNAME', 'admin')
        email = os.environ.get('DJANGO_SUPERUSER_EMAIL', 'admin@example.com')
        password = os.environ.get('DJANGO_SUPERUSER_PASSWORD', 'admin')
        
        if not User.objects.filter(username=username).exists():
            User.objects.create_superuser(username, email, password)
            self.stdout.write('Superuser créé!')


# Puis dans Procfile:

release: python manage.py migrate && python manage.py create_superuser

# Et ajoute les variables sur Render:
# DJANGO_SUPERUSER_USERNAME=admin
# DJANGO_SUPERUSER_EMAIL=admin@example.com
# DJANGO_SUPERUSER_PASSWORD=un_mot_de_passe_fort


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

# Via Dashboard:
# 1. Va sur ta base de données PostgreSQL
# 2. Clique sur "Connect" (en haut à droite)
# 3. Tu vois les informations de connexion:
#    - Hostname
#    - Port
#    - Database
#    - Username
#    - Password

# Option 1: Utiliser psql (terminal PostgreSQL)
# Installe psql sur ton ordinateur (si pas déjà installé)
# macOS:
brew install postgresql

# Ubuntu/Debian:
sudo apt-get install postgresql-client

# Windows: Télécharge depuis postgresql.org

# Connecte-toi:
psql -h hostname -p port -U username -d database

# Entre le mot de passe quand demandé

# Commandes utiles:
\dt                          # Liste les tables
\d users                     # Décrit la table users
SELECT * FROM users;         # Voir tous les utilisateurs
SELECT COUNT(*) FROM users;  # Compter les utilisateurs
\q                           # Quitter


# Option 2: Utiliser un client GUI
# pgAdmin, DBeaver, TablePlus, etc.
# Entre les infos de connexion depuis le Dashboard Render


# === BACKUPS AUTOMATIQUES ===

# Render fait des backups automatiques de ta DB!

# Plan gratuit:
# - Backups quotidiens
# - Conservés 7 jours
# - Restauration possible

# Plans payants:
# - Backups plus fréquents
# - Conservés plus longtemps
# - Point-in-time recovery

# Via Dashboard:
# 1. Va sur ta base de données
# 2. Clique sur "Backups" (onglet)
# 3. Tu vois la liste des backups disponibles
# 4. Pour restaurer: Clique sur "Restore" à côté d'un backup


# === FAIRE UN BACKUP MANUEL ===

# Via Dashboard:
# 1. Va sur ta base de données
# 2. Clique sur "Backups"
# 3. Clique sur "Create Backup"
# 4. Le backup est créé immédiatement

# Via pg_dump (terminal):
pg_dump -h hostname -U username -d database > backup.sql

# Restaurer un backup:
psql -h hostname -U username -d database < backup.sql


# === MIGRATION DE SQLite VERS PostgreSQL ===

# Si tu as déjà des données dans SQLite local:

# 1. Exporter les données de SQLite:
# Python script (export_data.py):

from app import db, User  # Importe tes modèles
import json

with app.app_context():
    users = User.query.all()
    data = [{"username": u.username, "email": u.email} for u in users]
    
    with open("data.json", "w") as f:
        json.dump(data, f)

print("Données exportées dans data.json")


# 2. Importer dans PostgreSQL:
# Python script (import_data.py):

from app import db, User
import json

with app.app_context():
    with open("data.json", "r") as f:
        data = json.load(f)
    
    for item in data:
        user = User(username=item["username"], email=item["email"])
        db.session.add(user)
    
    db.session.commit()

print("Données importées dans PostgreSQL!")


# 3. Déployer sur Render avec PostgreSQL configuré


[OK] DISQUE PERSISTANT (RENDER DISK)

# === PROBLÈME: FICHIERS PERDUS AU REDÉMARRAGE ===

# Par défaut, Render utilise un système de fichiers éphémère
# Cela signifie:
# - Les fichiers uploadés par les utilisateurs sont PERDUS au redémarrage
# - Les fichiers créés par ton app sont PERDUS au redémarrage
# - Seul le code git est persistant

# Exemple de problème:
# Un utilisateur uploade une photo de profil
# Tu sauvegardes: /app/uploads/photo.jpg
# Render redémarre (nouveau déploiement)
# -> photo.jpg DISPARAÎT!


# === SOLUTION: RENDER DISK ===

# Render Disk = Stockage persistant
# Les fichiers restent même après redémarrage!

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Disks" (onglet)
# 3. Clique "Add Disk"
# 4. Remplis:
#    Name: uploads
#    Mount Path: /app/uploads
#    Size: 1 GB (gratuit jusqu'à 1GB)
# 5. Clique "Add Disk"

# Render redémarre ton service avec le disque!

# Maintenant, tout ce qui est dans /app/uploads persiste!


# === UTILISER LE DISQUE DANS TON CODE ===

# app.py
from flask import Flask, request, send_from_directory
import os
from werkzeug.utils import secure_filename

app = Flask(__name__)

# Dossier pour les uploads (correspond au Mount Path)
UPLOAD_FOLDER = '/app/uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

# Créer le dossier si inexistant
os.makedirs(UPLOAD_FOLDER, exist_ok=True)


@app.route('/upload', methods=['POST'])
def upload_file():
    """Upload un fichier"""
    
    if 'file' not in request.files:
        return "Pas de fichier", 400
    
    file = request.files['file']
    
    if file.filename == '':
        return "Nom de fichier vide", 400
    
    # Sécuriser le nom du fichier
    filename = secure_filename(file.filename)
    
    # Sauvegarder dans le disque persistant
    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    file.save(filepath)
    
    return f"Fichier {filename} uploadé!"


@app.route('/files/<filename>')
def serve_file(filename):
    """Servir un fichier uploadé"""
    return send_from_directory(app.config['UPLOAD_FOLDER'], filename)


@app.route('/files')
def list_files():
    """Lister tous les fichiers"""
    files = os.listdir(app.config['UPLOAD_FOLDER'])
    return {"files": files}


# Ces fichiers persistent même après redémarrage!


# === LIMITATIONS DU DISQUE ===

# Plan gratuit:
# - Maximum 1 GB par disque
# - Maximum 1 disque par service

# Plans payants:
# - Disques plus grands (jusqu'à 100+ GB)
# - Plusieurs disques par service


# === ALTERNATIVE: STOCKAGE CLOUD ===

# Pour les gros fichiers, utilise un service externe:
# - AWS S3
# - Google Cloud Storage
# - Cloudinary (images)
# - DigitalOcean Spaces

# Avantages:
# - Pas de limite de taille
# - CDN intégré (livraison rapide)
# - Sauvegardes automatiques
# - Moins cher pour les gros volumes

# Exemple avec AWS S3:

import boto3
from flask import Flask, request
import os

app = Flask(__name__)

# Configuration S3
s3_client = boto3.client(
    's3',
    aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID'),
    aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY'),
    region_name='us-east-1'
)

BUCKET_NAME = 'myapp-uploads'


@app.route('/upload', methods=['POST'])
def upload_to_s3():
    """Upload vers S3"""
    
    file = request.files['file']
    filename = secure_filename(file.filename)
    
    # Upload vers S3
    s3_client.upload_fileobj(
        file,
        BUCKET_NAME,
        filename,
        ExtraArgs={'ACL': 'public-read'}
    )
    
    # URL du fichier
    url = f"https://{BUCKET_NAME}.s3.amazonaws.com/{filename}"
    
    return {"url": url}


# Ajoute les variables sur Render:
# AWS_ACCESS_KEY_ID=ta_cle
# AWS_SECRET_ACCESS_KEY=ton_secret


[OK] BACKGROUND WORKERS & CRON JOBS

# === QU'EST-CE QU'UN BACKGROUND WORKER? ===

# Imagine tu as une app qui:
# - Envoie des emails de confirmation
# - Traite des images uploadées
# - Génère des rapports PDF
# - Scrappe des sites web

# Problème: Ces tâches sont LENTES!
# Si tu les fais dans ton web service:
# - L'utilisateur attend 30 secondes
# - Mauvaise expérience utilisateur
# - Timeout possible

# Solution: Background Worker!
# = Un service séparé qui fait les tâches lourdes en arrière-plan
# = L'utilisateur reçoit une réponse immédiate
# = La tâche continue en arrière-plan


# === CRÉER UN BACKGROUND WORKER ===

# Via Dashboard:
# 1. Clique "New +" (en haut à droite)
# 2. Sélectionne "Background Worker"
# 3. Connecte ton dépôt GitHub (comme pour web service)
# 4. Configure:
#    Name: myapp-worker
#    Build Command: pip install -r requirements.txt
#    Start Command: python worker.py
# 5. Clique "Create Background Worker"


# === EXEMPLE: WORKER AVEC CELERY ===

# Celery = Framework pour tâches asynchrones

# Installer:
pip install celery redis
pip freeze > requirements.txt

# Créer worker.py:

# worker.py
from celery import Celery
import os

# Configuration Redis (pour la queue)
redis_url = os.environ.get('REDIS_URL', 'redis://localhost:6379')

app = Celery('tasks', broker=redis_url, backend=redis_url)


@app.task
def send_email(to, subject, body):
    """Envoyer un email (tâche lente)"""
    import time
    import smtplib
    
    # Simuler un envoi lent
    time.sleep(5)
    
    # Envoyer l'email
    # ... code d'envoi ...
    
    return f"Email envoyé à {to}"


@app.task
def process_image(image_path):
    """Traiter une image (tâche lente)"""
    from PIL import Image
    import time
    
    time.sleep(10)  # Simuler traitement
    
    # Ouvrir et redimensionner l'image
    img = Image.open(image_path)
    img.thumbnail((800, 800))
    img.save(image_path)
    
    return f"Image {image_path} traitée"


# Créer app.py (web service):

# app.py
from flask import Flask, request
from worker import send_email, process_image

app = Flask(__name__)


@app.route('/register', methods=['POST'])
def register():
    """Inscription utilisateur"""
    email = request.form['email']
    
    # Lancer la tâche d'envoi d'email en arrière-plan
    send_email.delay(email, "Bienvenue!", "Merci de vous être inscrit!")
    
    # Réponse immédiate (sans attendre l'email)
    return "Inscription réussie! Un email vous sera envoyé."


@app.route('/upload', methods=['POST'])
def upload():
    """Upload image"""
    file = request.files['file']
    filepath = f"/app/uploads/{file.filename}"
    file.save(filepath)
    
    # Lancer le traitement en arrière-plan
    process_image.delay(filepath)
    
    # Réponse immédiate
    return "Image uploadée! Elle sera traitée sous peu."


# Ajouter Redis:
# Sur Render Dashboard, crée un service Redis
# Ou utilise un service externe (Upstash, Redis Labs, etc.)


# Dans Procfile:

web: gunicorn app:app
worker: celery -A worker worker --loglevel=info


# Déploie:
# - Le web service exécute gunicorn
# - Le background worker exécute celery
# - Ils communiquent via Redis


# === QU'EST-CE QU'UN CRON JOB? ===

# Cron Job = Tâche qui s'exécute automatiquement à intervalles réguliers

# === CRÉER UN CRON JOB ===

# Via Dashboard:
# 1. Clique "New +" (en haut à droite)
# 2. Sélectionne "Cron Job"
# 3. Connecte ton dépôt GitHub
# 4. Configure:
#    Name: myapp-backup
#    Build Command: pip install -r requirements.txt
#    Command: python backup.py
#    Schedule: @daily (ou autre)
# 5. Clique "Create Cron Job"


# === EXEMPLES DE SCHEDULES ===

# Formats acceptés:

# @hourly        # Chaque heure (00:00, 01:00, 02:00...)
# @daily         # Chaque jour à minuit
# @weekly        # Chaque semaine (dimanche minuit)
# @monthly       # Chaque mois (1er du mois à minuit)

# Format cron classique (plus flexible):
# * * * * *
# │ │ │ │ │
# │ │ │ │ └─── Jour de la semaine (0-6, 0=dimanche)
# │ │ │ └───── Mois (1-12)
# │ │ └─────── Jour du mois (1-31)
# │ └───────── Heure (0-23)
# └─────────── Minute (0-59)

# Exemples:
# 0 9 * * *      # Tous les jours à 9h00
# 0 */6 * * *    # Toutes les 6 heures
# 0 9 * * 1      # Tous les lundis à 9h00
# 0 0 1 * *      # Le 1er de chaque mois à minuit
# */15 * * * *   # Toutes les 15 minutes


# === EXEMPLE: BACKUP QUOTIDIEN ===

# backup.py
import os
import datetime
from app import db
import json

def backup_database():
    """Faire un backup de la base de données"""
    
    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_file = f"/app/backups/backup_{timestamp}.json"
    
    # Créer le dossier si inexistant
    os.makedirs("/app/backups", exist_ok=True)
    
    # Exporter les données
    from app import User  # Importe tes modèles
    
    with app.app_context():
        users = User.query.all()
        data = {
            "users": [
                {"id": u.id, "username": u.username, "email": u.email}
                for u in users
            ]
        }
        
        with open(backup_file, "w") as f:
            json.dump(data, f, indent=2)
    
    print(f"Backup créé: {backup_file}")
    
    # Optionnel: Upload vers S3 pour sécurité
    # upload_to_s3(backup_file)

if __name__ == "__main__":
    backup_database()


# === EXEMPLE: NETTOYAGE HEBDOMADAIRE ===

# cleanup.py
import os
import datetime

def cleanup_old_files():
    """Supprimer les fichiers de plus de 30 jours"""
    
    uploads_dir = "/app/uploads"
    now = datetime.datetime.now()
    deleted_count = 0
    
    for filename in os.listdir(uploads_dir):
        filepath = os.path.join(uploads_dir, filename)
        
        # Vérifier l'âge du fichier
        file_time = datetime.datetime.fromtimestamp(os.path.getmtime(filepath))
        age_days = (now - file_time).days
        
        if age_days > 30:
            os.remove(filepath)
            deleted_count += 1
            print(f"Supprimé: {filename} (âge: {age_days} jours)")
    
    print(f"Nettoyage terminé: {deleted_count} fichiers supprimés")

if __name__ == "__main__":
    cleanup_old_files()


# Schedule sur Render: @weekly


# === EXEMPLE: SCRAPING QUOTIDIEN ===

# scraper.py
import requests
from bs4 import BeautifulSoup
from app import db, Article

def scrape_news():
    """Scraper des articles de news quotidiennement"""
    
    url = "https://example-news.com/tech"
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')
    
    articles = soup.find_all('article', class_='news-item')
    
    for article in articles:
        title = article.find('h2').text
        link = article.find('a')['href']
        
        # Sauvegarder dans la DB
        new_article = Article(title=title, url=link)
        db.session.add(new_article)
    
    db.session.commit()
    print(f"{len(articles)} articles scrapés et sauvegardés")

if __name__ == "__main__":
    with app.app_context():
        scrape_news()


# Schedule sur Render: 0 6 * * * (tous les jours à 6h00)


# === EXEMPLE: ENVOI D'EMAIL HEBDOMADAIRE ===

# weekly_report.py
import smtplib
from email.mime.text import MIMEText
from app import db, User
import os

def send_weekly_report():
    """Envoyer un rapport hebdomadaire par email"""
    
    # Générer les statistiques
    with app.app_context():
        user_count = User.query.count()
        new_users_this_week = User.query.filter(
            User.created_at >= datetime.datetime.now() - datetime.timedelta(days=7)
        ).count()
    
    # Créer le message
    body = f"""
    Rapport hebdomadaire:
    
    - Total utilisateurs: {user_count}
    - Nouveaux cette semaine: {new_users_this_week}
    
    Cordialement,
    Votre App
    """
    
    msg = MIMEText(body)
    msg['Subject'] = 'Rapport hebdomadaire'
    msg['From'] = os.environ.get('SENDER_EMAIL')
    msg['To'] = os.environ.get('ADMIN_EMAIL')
    
    # Envoyer
    smtp_server = smtplib.SMTP(os.environ.get('SMTP_HOST'), 587)
    smtp_server.starttls()
    smtp_server.login(
        os.environ.get('SMTP_USER'),
        os.environ.get('SMTP_PASSWORD')
    )
    smtp_server.send_message(msg)
    smtp_server.quit()
    
    print("Rapport hebdomadaire envoyé!")

if __name__ == "__main__":
    send_weekly_report()


# Schedule sur Render: 0 9 * * 1 (tous les lundis à 9h00)


# === SURVEILLER LES CRON JOBS ===

# Via Dashboard:
# 1. Va sur ton Cron Job
# 2. Clique sur "Logs" pour voir les exécutions
# 3. Clique sur "Events" pour voir l'historique

# Chaque exécution est loggée:
# - Heure de début
# - Durée
# - Succès/Échec
# - Output du script


[OK] DOMAINE PERSONNALISÉ (CUSTOM DOMAIN)

# === POURQUOI UN DOMAINE PERSONNALISÉ? ===

# Par défaut, ton app a une URL Render:
# https://myapp.onrender.com

# Problèmes:
# - Pas professionnel
# - Difficile à mémoriser
# - Pas de branding

# Solution: Domaine personnalisé!
# https://www.monapp.com (beaucoup mieux!)


# === ÉTAPE 1: ACHETER UN DOMAINE ===

# Achète un domaine chez un registraire:
# - Namecheap: https://www.namecheap.com
# - Google Domains: https://domains.google
# - GoDaddy: https://www.godaddy.com
# - OVH: https://www.ovh.com
# - Gandi: https://www.gandi.net

# Prix moyen: $10-15/an


# === ÉTAPE 2: AJOUTER LE DOMAINE SUR RENDER ===

# Via Dashboard:
# 1. Va sur ton web service
# 2. Clique sur "Settings" (onglet)
# 3. Scroll vers "Custom Domains"
# 4. Clique "Add Custom Domain"
# 5. Entre ton domaine: www.monapp.com
# 6. Clique "Save"

# Render te donne des instructions DNS!


# === ÉTAPE 3: CONFIGURER LE DNS ===

# Render te donne 2 types de records:

# Pour www.monapp.com (sous-domaine):
# Type: CNAME
# Name: www
# Value: myapp.onrender.com

# Pour monapp.com (domaine racine):
# Type: A (ou ALIAS/ANAME selon le registraire)
# Name: @ (ou vide)
# Value: 216.24.57.1 (IP de Render)

# Instructions selon le registraire:

# === NAMECHEAP ===
# 1. Connecte-toi à Namecheap
# 2. Dashboard > Domain List > Manage
# 3. Advanced DNS
# 4. Ajoute les records:
#    Type: CNAME, Host: www, Value: myapp.onrender.com, TTL: Automatic
#    Type: A, Host: @, Value: 216.24.57.1, TTL: Automatic
# 5. Sauvegarde

# === GOOGLE DOMAINS ===
# 1. Connecte-toi à Google Domains
# 2. My domains > monapp.com > DNS
# 3. Manage custom records
# 4. Ajoute les records
# 5. Sauvegarde

# === GODADDY ===
# 1. Connecte-toi à GoDaddy
# 2. My Products > Domains > DNS
# 3. Ajoute les records
# 4. Sauvegarde


# === ÉTAPE 4: ATTENDRE LA PROPAGATION DNS ===

# Temps de propagation: 1 heure à 48 heures
# Généralement: 2-4 heures

# Vérifier la propagation:
# https://www.whatsmydns.net/
# Entre ton domaine et vérifie que les records sont propagés


# === ÉTAPE 5: ACTIVER HTTPS (SSL) ===

# Render active automatiquement HTTPS!
# - Certificat SSL gratuit (Let's Encrypt)
# - Renouvellement automatique
# - Redirection HTTP -> HTTPS automatique

# Via Dashboard:
# 1. Retourne sur ton service
# 2. Settings > Custom Domains
# 3. Attends que le statut passe à "Verified" (vert)
# 4. HTTPS est automatiquement activé!

# Maintenant: https://www.monapp.com fonctionne! [BRAVO]


# === FORCER HTTPS DANS TON APP (OPTIONNEL) ===

# Flask:
from flask import Flask, redirect, request

app = Flask(__name__)

@app.before_request
def force_https():
    if not request.is_secure and os.environ.get('RENDER'):
        url = request.url.replace('http://', 'https://', 1)
        return redirect(url, code=301)


# Django (settings.py):
if os.environ.get('RENDER'):
    SECURE_SSL_REDIRECT = True
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')


# === AJOUTER PLUSIEURS DOMAINES ===

# Tu peux ajouter plusieurs domaines:
# - www.monapp.com
# - monapp.com
# - api.monapp.com
# - app.monapp.com

# Via Dashboard:
# 1. Settings > Custom Domains
# 2. Clique "Add Custom Domain" pour chacun
# 3. Configure le DNS pour chacun


# === REDIRIGER LE DOMAINE RACINE VERS WWW ===

# Si tu veux que monapp.com -> www.monapp.com:

# Chez ton registraire, ajoute une redirection:
# - Source: monapp.com
# - Destination: https://www.monapp.com
# - Type: 301 (permanent)

# Ou configure les deux domaines sur Render
# et ajoute une redirection dans ton app


[OK] SCALING (AUGMENTER LA PUISSANCE)

# === POURQUOI SCALER? ===

# Ton app devient populaire!
# - Plus d'utilisateurs
# - Plus de requêtes
# - Temps de réponse lent
# - Erreurs de timeout

# Solution: Scaler (augmenter les ressources)


# === SCALING VERTICAL (PLUS DE PUISSANCE) ===

# = Augmenter la puissance d'une seule instance

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Settings"
# 3. Section "Instance Type"
# 4. Change le plan:
#    Free -> Starter ($7/mois)
#    Starter -> Standard ($25/mois)
#    Standard -> Pro ($85/mois)
# 5. Clique "Save Changes"

# Render migre automatiquement (sans downtime!)

# Quand scaler verticalement?
# - CPU/RAM saturé (voir Metrics)
# - Requêtes lentes
# - Timeouts fréquents


# === SCALING HORIZONTAL (PLUS D'INSTANCES) ===

# = Ajouter plusieurs instances identiques
# = Render distribue le trafic (load balancing)

# Via Dashboard:
# 1. Va sur ton service
# 2. Settings > Scaling
# 3. Number of instances: 1 -> 3
# 4. Clique "Save Changes"

# Render crée 3 instances identiques!
# Le trafic est réparti automatiquement

# Quand scaler horizontalement?
# - Beaucoup d'utilisateurs simultanés
# - Besoin de haute disponibilité
# - Trafic distribué géographiquement

# Prix:
# 3 instances Starter = 3 × $7 = $21/mois


# === AUTO-SCALING (AUTOMATIQUE) ===

# Disponible sur plans Pro et Enterprise
# = Render ajoute/retire des instances automatiquement

# Configuration:
# - Min instances: 2
# - Max instances: 10
# - Scaling metric: CPU > 70% ou Request count > 1000/min

# Avantages:
# - Adaptation automatique au trafic
# - Économies (paie seulement ce qui est utilisé)
# - Pas de downtime pendant les pics


# === LOAD BALANCING (RÉPARTITION DE CHARGE) ===

# Render inclut un load balancer gratuit!
# Fonctionnalités:
# - Round-robin (distribue équitablement)
# - Health checks (détecte les instances mortes)
# - Sticky sessions (utilisateur -> même instance)
# - SSL termination (HTTPS géré)

# Configuration automatique, rien à faire!


# === MONITORING ET MÉTRIQUES ===

# Via Dashboard:
# 1. Va sur ton service
# 2. Clique sur "Metrics"
# 3. Tu vois:
#    - CPU Usage (utilisation processeur)
#    - Memory Usage (utilisation mémoire)
#    - Request Count (nombre de requêtes)
#    - Response Time (temps de réponse)
#    - Error Rate (taux d'erreur)
#    - Bandwidth (bande passante)

# Alertes:
# - Configure des alertes email/Slack
# - Seuils personnalisables
# - Notifications en temps réel


[OK] EXEMPLE COMPLET: BLOG FLASK AVEC RENDER

# === ÉTAPE PAR ÉTAPE (ULTRA DÉTAILLÉ) ===

# === ÉTAPE 1: CRÉER LE PROJET LOCALEMENT ===

# Ouvre ton terminal

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

# Windows:
mkdir flask-blog-render
cd flask-blog-render

# Initialiser git:
git init


# === ÉTAPE 2: ENVIRONNEMENT VIRTUEL ===

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

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

# Tu vois: (venv) $


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

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

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


# === ÉTAPE 4: CRÉER L'APP ===

# app.py
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

load_dotenv()

app = Flask(__name__)

# Configuration
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
    'DATABASE_URL',
    'sqlite:///blog.db'
)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-key')

db = SQLAlchemy(app)


# Modèle Article
class Article(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    titre = db.Column(db.String(200), nullable=False)
    contenu = db.Column(db.Text, nullable=False)
    date_creation = db.Column(db.DateTime, default=datetime.now)
    
    def __repr__(self):
        return f'<Article {self.titre}>'


# Créer les tables
with app.app_context():
    db.create_all()


# Routes
@app.route('/')
def index():
    articles = Article.query.order_by(Article.date_creation.desc()).all()
    return render_template('index.html', articles=articles)


@app.route('/article/<int:id>')
def voir_article(id):
    article = Article.query.get_or_404(id)
    return render_template('article.html', article=article)


@app.route('/creer', methods=['GET', 'POST'])
def creer_article():
    if request.method == 'POST':
        titre = request.form['titre']
        contenu = request.form['contenu']
        
        if titre and contenu:
            nouvel_article = Article(titre=titre, contenu=contenu)
            db.session.add(nouvel_article)
            db.session.commit()
            return redirect(url_for('index'))
    
    return render_template('creer.html')


@app.route('/supprimer/<int:id>', methods=['POST'])
def supprimer_article(id):
    article = Article.query.get_or_404(id)
    db.session.delete(article)
    db.session.commit()
    return redirect(url_for('index'))


@app.route('/health')
def health():
    return {"status": "ok"}


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


# === ÉTAPE 5: CRÉER LES TEMPLATES ===

# Créer le dossier:
mkdir templates

# templates/base.html
"""
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Mon Blog{% endblock %}</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        .container {
            max-width: 900px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            padding: 40px;
        }
        header {
            text-align: center;
            margin-bottom: 40px;
            padding-bottom: 20px;
            border-bottom: 3px solid #667eea;
        }
        h1 {
            color: #667eea;
            font-size: 2.5em;
            margin-bottom: 10px;
        }
        nav {
            margin: 30px 0;
            text-align: center;
        }
        nav a {
            display: inline-block;
            padding: 12px 30px;
            margin: 0 10px;
            background: #667eea;
            color: white;
            text-decoration: none;
            border-radius: 25px;
            transition: all 0.3s;
        }
        nav a:hover {
            background: #764ba2;
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0,0,0,0.2);
        }
        .article-card {
            background: #f8f9fa;
            padding: 25px;
            margin: 20px 0;
            border-radius: 10px;
            border-left: 5px solid #667eea;
            transition: all 0.3s;
        }
        .article-card:hover {
            transform: translateX(10px);
            box-shadow: 0 5px 15px rgba(0,0,0,0.1);
        }
        .article-title {
            color: #333;
            font-size: 1.5em;
            margin-bottom: 10px;
        }
        .article-date {
            color: #666;
            font-size: 0.9em;
            margin-bottom: 15px;
        }
        .article-link {
            color: #667eea;
            text-decoration: none;
            font-weight: bold;
        }
        .article-link:hover {
            text-decoration: underline;
        }
        form {
            background: #f8f9fa;
            padding: 30px;
            border-radius: 10px;
        }
        label {
            display: block;
            margin: 20px 0 8px;
            color: #333;
            font-weight: bold;
        }
        input[type="text"], textarea {
            width: 100%;
            padding: 12px;
            border: 2px solid #ddd;
            border-radius: 8px;
            font-size: 16px;
            transition: border 0.3s;
        }
        input[type="text"]:focus, textarea:focus {
            outline: none;
            border-color: #667eea;
        }
        textarea {
            min-height: 200px;
            resize: vertical;
            font-family: inherit;
        }
        button {
            margin-top: 20px;
            padding: 15px 40px;
            background: #667eea;
            color: white;
            border: none;
            border-radius: 25px;
            font-size: 16px;
            cursor: pointer;
            transition: all 0.3s;
        }
        button:hover {
            background: #764ba2;
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0,0,0,0.2);
        }
        .delete-btn {
            background: #dc3545;
            padding: 10px 20px;
            font-size: 14px;
        }
        .delete-btn:hover {
            background: #c82333;
        }
        .footer {
            text-align: center;
            margin-top: 40px;
            padding-top: 20px;
            border-top: 2px solid #eee;
            color: #666;
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>[DOCS] Mon Blog Flask</h1>
            <p>Déployé sur Render avec [HEAVY_BLACK_HEART]</p>
        </header>

        <nav>
            <a href="{{ url_for('index') }}">[ACCUEIL] Accueil</a>
            <a href="{{ url_for('creer_article') }}">[EDIT] Nouvel Article</a>
        </nav>

        {% block content %}{% endblock %}

        <div class="footer">
            <p>Propulsé par Flask & Render | © 2024</p>
        </div>
    </div>
</body>
</html>
"""

# templates/index.html
"""
{% extends "base.html" %}

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

{% block content %}
    <h2 style="color: #333; margin-bottom: 30px;">[NEWSPAPER] Articles Récents</h2>
    
    {% if articles %}
        {% for article in articles %}
            <div class="article-card">
                <h3 class="article-title">
                    <a href="{{ url_for('voir_article', id=article.id) }}" class="article-link">
                        {{ article.titre }}
                    </a>
                </h3>
                <p class="article-date">
                    [CALENDRIER] {{ article.date_creation.strftime('%d/%m/%Y à %H:%M') }}
                </p>
                <p style="color: #555; line-height: 1.6;">
                    {{ article.contenu[:150] }}{% if article.contenu|length > 150 %}...{% endif %}
                </p>
                <a href="{{ url_for('voir_article', id=article.id) }}" class="article-link">
                    Lire la suite ->
                </a>
            </div>
        {% endfor %}
    {% else %}
        <div style="text-align: center; padding: 60px 20px; background: #f8f9fa; border-radius: 10px;">
            <p style="font-size: 1.2em; color: #666; margin-bottom: 20px;">
                [NOTE] Aucun article pour le moment
            </p>
            <a href="{{ url_for('creer_article') }}" class="article-link" style="font-size: 1.1em;">
                Créer le premier article!
            </a>
        </div>
    {% endif %}
{% endblock %}
"""

# templates/article.html
"""
{% extends "base.html" %}

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

{% block content %}
    <article>
        <h2 style="color: #333; margin-bottom: 15px;">{{ article.titre }}</h2>
        <p class="article-date" style="margin-bottom: 30px;">
            [CALENDRIER] Publié le {{ article.date_creation.strftime('%d/%m/%Y à %H:%M') }}
        </p>
        
        <div style="background: #f8f9fa; padding: 30px; border-radius: 10px; line-height: 1.8; color: #333;">
            {{ article.contenu }}
        </div>
        
        <div style="margin-top: 30px; text-align: center;">
            <form action="{{ url_for('supprimer_article', id=article.id) }}" method="POST" style="display: inline;">
                <button type="submit" class="delete-btn" onclick="return confirm('Êtes-vous sûr de vouloir supprimer cet article?')">
                    [SUPPRIMER] Supprimer cet article
                </button>
            </form>
        </div>
        
        <p style="margin-top: 30px; text-align: center;">
            <a href="{{ url_for('index') }}" class="article-link" style="font-size: 1.1em;">
                <- Retour à la liste
            </a>
        </p>
    </article>
{% endblock %}
"""

# templates/creer.html
"""
{% extends "base.html" %}

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

{% block content %}
    <h2 style="color: #333; margin-bottom: 30px;">[EDIT] Créer un Nouvel Article</h2>
    
    <form method="POST">
        <label for="titre">[IMPORTANT] Titre de l'article:</label>
        <input type="text" id="titre" name="titre" 
               placeholder="Entrez un titre accrocheur..." required>
        
        <label for="contenu">[NOTE] Contenu:</label>
        <textarea id="contenu" name="contenu" 
                  placeholder="Écrivez votre article ici..." required></textarea>
        
        <button type="submit">[RAPIDE] Publier l'article</button>
    </form>
{% endblock %}
"""


# === ÉTAPE 6: CRÉER LES FICHIERS DE CONFIG ===

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

# .env (local)
cat > .env << 'EOF'
FLASK_ENV=development
SECRET_KEY=dev-key-change-in-production
DATABASE_URL=sqlite:///blog.db
PORT=10000
EOF

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


# === ÉTAPE 7: TESTER LOCALEMENT ===

# Lancer l'app:
python app.py

# Ouvre: http://localhost:10000
# Teste:
# - Créer un article
# - Voir l'article
# - Supprimer l'article

# Arrête: Ctrl+C


# === ÉTAPE 8: COMMIT ET PUSH SUR GITHUB ===

git add .
git commit -m "Blog Flask complet pour Render"

# Créer un dépôt sur GitHub:
# 1. Va sur: https://github.com/new
# 2. Nom: flask-blog-render
# 3. Public ou Private (ton choix)
# 4. NE PAS cocher "Initialize with README"
# 5. Clique "Create repository"

# Ajouter le remote:
git remote add origin https://github.com/TON_USERNAME/flask-blog-render.git
git branch -M main
git push -u origin main

# Vérifie sur GitHub: ton code est là!


# === ÉTAPE 9: DÉPLOYER SUR RENDER ===

# Via Dashboard:
# 1. Connecte-toi: https://dashboard.render.com
# 2. Clique "New +" > "Web Service"
# 3. Connecte GitHub si pas fait
# 4. Sélectionne: flask-blog-render
# 5. Clique "Connect"

# Configuration:
# Name: flask-blog-render
# Region: Frankfurt (Europe) ou ta région
# Branch: main
# Runtime: Python
# Build Command: pip install -r requirements.txt
# Start Command: gunicorn app:app
# Instance Type: Free (pour commencer)

# Environment Variables (Ajoute):
# SECRET_KEY = (génère avec: python -c "import secrets; print(secrets.token_urlsafe(32))")

# Clique "Create Web Service"


# === ÉTAPE 10: AJOUTER POSTGRESQL ===

# Via Dashboard:
# 1. Retour au Dashboard principal
# 2. Clique "New +" > "PostgreSQL"
# 3. Configure:
#    Name: flask-blog-db
#    Database: blog_db
#    User: blog_user
#    Region: (même que ton web service)
#    Plan: Free
# 4. Clique "Create Database"

# Attends 30 secondes...

# Connecter à ton web service:
# 1. Va sur ta DB PostgreSQL
# 2. Copie "Internal Database URL"
# 3. Va sur ton web service
# 4. Environment > Add Environment Variable
# 5. Key: DATABASE_URL
# 6. Value: (colle l'Internal Database URL)
# 7. Save Changes

# Render redémarre automatiquement!


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

# Via Dashboard:
# 1. Va sur ton web service
# 2. Tu vois "Live" en haut (vert)
# 3. Clique sur l'URL: https://flask-blog-render.onrender.com
# 4. Tu vois ton blog! [BRAVO]

# Teste:
# - Créer un article
# - Le voir
# - Le supprimer
# - Tout persiste dans PostgreSQL!


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

# Modifie quelque chose dans app.py
# Par exemple, change le titre:

# Dans templates/base.html:
# <h1>[DOCS] Mon Super Blog Flask</h1>

# Commit et push:
git add .
git commit -m "Changement du titre"
git push origin main

# Render DÉTECTE automatiquement le push!
# Redéploiement automatique en 1-2 minutes
# Pas besoin de faire quoi que ce soit d'autre!

# Vérifie les logs en temps réel:
# Dashboard > Ton service > Logs


# === RÉSUMÉ: BLOG COMPLET DÉPLOYÉ! ===

# Tu as maintenant:
# [OK] Un blog Flask fonctionnel
# [OK] Base de données PostgreSQL
# [OK] Déployé sur Render (en ligne!)
# [OK] HTTPS automatique
# [OK] Déploiement automatique depuis GitHub
# [OK] URL: https://flask-blog-render.onrender.com

# Coût total: $0 (FREE!)


[OK] DÉPANNAGE DÉTAILLÉ

# === PROBLÈME: "Build Failed" ===

# Erreur lors de la construction (Build)

# Cause possible 1: requirements.txt manquant ou incorrect
# Solution:
# 1. Vérifie que requirements.txt existe:
ls -la requirements.txt  # Linux/macOS
dir requirements.txt     # Windows

# 2. Si manquant, crée-le:
pip freeze > requirements.txt
git add requirements.txt
git commit -m "Add requirements.txt"
git push origin main


# Cause possible 2: Version Python incompatible
# Solution:
# 1. Crée runtime.txt:
echo "python-3.11.0" > runtime.txt
git add runtime.txt
git commit -m "Add runtime.txt"
git push origin main

# 2. Ou change dans Dashboard:
# Settings > Environment > Python Version


# Cause possible 3: Package non installable
# Erreur dans les logs: "Could not find a version that satisfies..."
# Solution:
# Vérifie les versions dans requirements.txt
# Utilise des versions compatibles:
Flask>=3.0.0
gunicorn>=21.0.0
# Au lieu de:
Flask==999.0.0  # version n'existe pas!


# === PROBLÈME: "Application Error" ou "502 Bad Gateway" ===

# L'app est déployée mais affiche une erreur

# Cause possible 1: Port mal configuré
# Render utilise la variable PORT (pas hardcodé 5000!)
# Solution:
# Dans app.py, assure-toi:
import os
port = int(os.environ.get("PORT", 10000))
app.run(host="0.0.0.0", port=port)

# Puis redéploie:
git add app.py
git commit -m "Fix port configuration"
git push origin main


# Cause possible 2: Start Command incorrect
# Solution:
# Via Dashboard:
# 1. Settings > Build & Deploy
# 2. Start Command doit être:
gunicorn app:app
# Remplace "app:app" par "ton_fichier:ta_variable"

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

# Pour Django:
gunicorn myproject.wsgi


# Cause possible 3: Erreur Python dans le code
# Solution:
# Regarde les logs:
# Dashboard > Logs
# Cherche "Traceback" ou "Error"
# Corrige l'erreur dans ton code
# Redéploie


# === PROBLÈME: "Health Check Failed" ===

# Render ne peut pas vérifier que l'app fonctionne

# Cause: Pas de endpoint de santé ou timeout
# Solution:
# 1. Ajoute un endpoint /health dans app.py:

@app.route('/health')
def health():
    return {"status": "ok", "message": "healthy"}

# 2. Dans Dashboard > Settings:
# Health Check Path: /health

# 3. Redéploie


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

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

# Cause: Variable pas configurée sur Render
# Solution:
# 1. Dashboard > Environment
# 2. Add Environment Variable
# 3. Key: API_KEY
# 4. Value: ta_valeur
# 5. Save Changes

# Render redémarre automatiquement


# === PROBLÈME: Base de données "relation does not exist" ===

# Erreur PostgreSQL: table n'existe pas

# Cause: Tables pas créées
# Solution Flask-SQLAlchemy:
# 1. Ajoute dans app.py:
with app.app_context():
    db.create_all()

# 2. Redéploie

# Solution Django:
# 1. Ajoute "release" dans Start Command:
# Start Command:
gunicorn myproject.wsgi
# Ajoute dans "Pre-Deploy Command":
python manage.py migrate


# === PROBLÈME: Fichiers statiques (CSS/JS) ne chargent pas ===

# Les CSS/JS ne s'affichent pas

# Cause Flask: STATIC_FOLDER mal configuré
# Solution:
# 1. Structure des dossiers:
# myapp/
# ├── app.py
# ├── static/
# │   ├── css/
# │   ├── js/
# │   └── images/
# └── templates/

# 2. Dans templates:
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">

# Cause Django: STATIC_ROOT pas configuré
# Solution:
# Dans settings.py:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

# Collecte les static files:
# Pre-Deploy Command:
python manage.py collectstatic --noinput && python manage.py migrate


# === PROBLÈME: Déploiement lent (>10 minutes) ===

# Le build prend trop de temps

# Cause 1: Trop de dépendances
# Solution:
# Nettoie requirements.txt
# Garde seulement les packages nécessaires

# Cause 2: Packages lourds (numpy, pandas, etc)
# Solution:
# Utilise un plan payant (build plus rapide)
# Ou optimise les dépendances


# === PROBLÈME: Render Disk - Fichiers perdus ===

# Les fichiers uploadés disparaissent

# Cause: Pas de Render Disk configuré
# Solution:
# 1. Dashboard > Disks
# 2. Add Disk
# 3. Name: uploads
# 4. Mount Path: /app/uploads
# 5. Size: 1 GB

# 6. Dans app.py:
UPLOAD_FOLDER = '/app/uploads'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)


# === PROBLÈME: Logs ne s'affichent pas ===

# Les logs sont vides ou incomplets

# Cause: print() ne log pas correctement
# Solution:
# Utilise logging au lieu de print():

import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Au lieu de:
print("Utilisateur connecté")

# Utilise:
logger.info("Utilisateur connecté")


# === PROBLÈME: Timeout sur requêtes longues ===

# Erreur: 504 Gateway Timeout

# Cause: Requête prend trop de temps (>30 secondes)
# Solution:
# 1. Optimise ton code (queries DB, algorithmes)
# 2. Utilise un Background Worker pour tâches longues
# 3. Augmente le timeout (plans payants uniquement)


# === PROBLÈME: "Out of Memory" ===

# Erreur: Application killed due to memory usage

# Cause: App consomme trop de RAM
# Solution:
# 1. Optimise ton code:
#    - Ferme les connexions DB
#    - Libère la mémoire (del objets)
#    - Utilise generators au lieu de listes
# 2. Upgrade vers un plan avec plus de RAM


# === PROBLÈME: CORS Errors ===

# Erreur dans le navigateur: "CORS policy blocked"

# Cause: Frontend sur un domaine différent
# Solution Flask:
pip install flask-cors
pip freeze > requirements.txt

# Dans app.py:
from flask_cors import CORS
CORS(app)

# Ou plus restrictif:
CORS(app, origins=["https://monfrontend.com"])

# Solution Django:
# settings.py:
INSTALLED_APPS = [
    'corsheaders',
]
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
]
CORS_ALLOWED_ORIGINS = [
    "https://monfrontend.com",
]


# === PROBLÈME: SSL Certificate Error ===

# Erreur: SSL certificate verification failed

# Cause: Certificat pas encore généré ou domaine mal configuré
# Solution:
# 1. Attends 10-15 minutes (génération du certificat)
# 2. Vérifie le DNS (doit pointer vers Render)
# 3. Dashboard > Settings > Custom Domain > Verify


# === RESSOURCES POUR DÉPANNAGE ===

# Render Status Page:
# https://status.render.com/
# Vérifie si Render a des problèmes techniques

# Render Community Forum:
# https://community.render.com/
# Pose des questions, cherche des solutions

# Render Documentation:
# https://render.com/docs
# Guides officiels très détaillés

# Stack Overflow (tag: render):
# https://stackoverflow.com/questions/tagged/render
# Questions/réponses de la communauté


[OK] COMPARAISON DÉTAILLÉE: RENDER VS HEROKU

# === TABLEAU COMPARATIF ===

# ┌─────────────────────┬───────────────────────┬───────────────────────┐
# │ Critère             │ Render                │ Heroku                │
# ├─────────────────────┼───────────────────────┼───────────────────────┤
# │ FREE TIER           │                       │                       │
# │ - Web Service       │ [OK] 750h/mois           │ [X] Supprimé en 2022    │
# │ - RAM               │ 512 MB                │ N/A                   │
# │ - Sleep après       │ 15 min inactivité     │ N/A                   │
# │ - PostgreSQL        │ [OK] 256 MB gratuit      │ [X] Payant uniquement   │
# │ - Static Sites      │ [OK] Illimité            │ [X] Non disponible      │
# ├─────────────────────┼───────────────────────┼───────────────────────┤
# │ PRICING (Web)       │                       │                       │
# │ - Starter           │ $7/mois (512MB)       │ N/A                   │
# │ - Standard          │ $25/mois (2GB)        │ $25/mois (512MB)      │
# │ - Pro               │ $85/mois (4GB)        │ $50/mois (1GB)        │
# ├─────────────────────┼───────────────────────┼───────────────────────┤
# │ DÉPLOIEMENT         │                       │                       │
# │ - Méthode           │ Git push auto         │ Git push heroku       │
# │ - CLI nécessaire    │ [X] Non                 │ [OK] Oui                 │
# │ - GitHub intégré    │ [OK] Natif               │ [X] Via add-on          │
# │ - Auto-deploy       │ [OK] Gratuit             │ $ Payant              │
# ├─────────────────────┼───────────────────────┼───────────────────────┤
# │ FONCTIONNALITÉS     │                       │                       │
# │ - SSL/HTTPS         │ [OK] Gratuit auto        │ [OK] Gratuit auto        │
# │ - Custom Domain     │ [OK] Gratuit             │ [OK] Gratuit             │
# │ - Persistent Disk   │ [OK] 1GB gratuit         │ [X] Payant              │
# │ - Cron Jobs         │ [OK] Natif               │ $ Scheduler add-on    │
# │ - Background Workers│ [OK] Natif               │ [OK] Natif               │
# │ - Private Services  │ [OK] Gratuit             │ [X] Non disponible      │
# ├─────────────────────┼───────────────────────┼───────────────────────┤
# │ BASES DE DONNÉES    │                       │                       │
# │ - PostgreSQL        │ [OK] 256MB gratuit       │ [X] Payant              │
# │ - MySQL             │ [OK] Disponible          │ $ Via add-on          │
# │ - Redis             │ [OK] 25MB gratuit        │ $ Via add-on          │
# │ - MongoDB           │ [X] Via externe         │ $ Via add-on          │
# ├─────────────────────┼───────────────────────┼───────────────────────┤
# │ PERFORMANCE         │                       │                       │
# │ - Démarrage cold    │ 10-15 sec             │ 30+ sec (ancien)      │
# │ - Vitesse build     │ Rapide (2-5 min)      │ Moyen (3-8 min)       │
# │ - Load Balancer     │ [OK] Inclus              │ [OK] Inclus              │
# │ - Auto-scaling      │ $ Plans Pro+          │ $ Plans Standard+     │
# ├─────────────────────┼───────────────────────┼───────────────────────┤
# │ SUPPORT             │                       │                       │
# │ - Documentation     │ [OK][OK][OK] Excellente        │ [OK][OK] Bonne              │
# │ - Community         │ [OK][OK] Active             │ [OK][OK][OK] Très active       │
# │ - Support ticket    │ $ Plans Pro+          │ $ Plans Standard+     │
# ├─────────────────────┼───────────────────────┼───────────────────────┤
# │ INTERFACE           │                       │                       │
# │ - Dashboard Web     │ [OK][OK][OK] Moderne           │ [OK][OK] Classique          │
# │ - CLI               │ [OK] Optionnelle         │ [OK] Nécessaire          │
# │ - API               │ [OK] Disponible          │ [OK] Disponible          │
# ├─────────────────────┼───────────────────────┼───────────────────────┤
# │ ÉCOSYSTÈME          │                       │                       │
# │ - Add-ons           │ Limité (natif)        │ [OK][OK][OK] Énorme            │
# │ - Marketplace       │ En croissance         │ Mature                │
# │ - Intégrations      │ [OK] Principales         │ [OK][OK][OK] Nombreuses        │
# └─────────────────────┴───────────────────────┴───────────────────────┘


# === QUAND CHOISIR RENDER ? ===

# [OK] Tu veux un FREE tier généreux
# [OK] Tu débutes et veux une interface simple
# [OK] Tu ne veux pas installer de CLI
# [OK] Tu veux déployer depuis GitHub facilement
# [OK] Budget limité ($7/mois pour Starter)
# [OK] Projets personnels, prototypes, MVPs
# [OK] Tu veux du stockage persistant gratuit
# [OK] Tu veux PostgreSQL gratuit


# === QUAND CHOISIR HEROKU ? ===

# [OK] Tu as besoin de NOMBREUX add-ons
# [OK] Écosystème mature et établi
# [OK] Enterprise features (compliance, support)
# [OK] Tu es déjà familier avec Heroku
# [OK] Projets critiques nécessitant stabilité
# [OK] Support prioritaire nécessaire


# === VERDICT POUR DÉBUTANTS ===

# [TROPHEE] RENDER est MIEUX pour débutants car:
# 1. FREE tier plus généreux (vs Heroku sans free tier)
# 2. Interface plus moderne et intuitive
# 3. Pas besoin de CLI (tout via web)
# 4. Moins cher pour commencer ($7 vs $25)
# 5. Déploiement depuis GitHub natif
# 6. PostgreSQL gratuit inclus

# Heroku reste pertinent pour:
# - Entreprises avec besoins spécifiques
# - Projets nécessitant add-ons particuliers
# - Équipes déjà formées à Heroku


[OK] MIGRATION DE HEROKU VERS RENDER

# === POURQUOI MIGRER ? ===

# Raisons courantes:
# - Heroku a supprimé son free tier (2022)
# - Coût élevé ($25+ pour plans basiques)
# - Besoin de fonctionnalités similaires moins cher
# - Interface plus moderne


# === ÉTAPES DE MIGRATION ===

# === ÉTAPE 1: INVENTAIRE DE TON APP HEROKU ===

# Liste ce que tu utilises:
# 1. Dynos: web, worker, cron jobs?
# 2. Add-ons: PostgreSQL, Redis, SendGrid, etc?
# 3. Domaine personnalisé?
# 4. Variables d'environnement?
# 5. Fichiers persistants (volumes)?

# Via Heroku CLI:
heroku apps:info --app myapp
heroku addons --app myapp
heroku config --app myapp
heroku domains --app myapp


# === ÉTAPE 2: PRÉPARER TON CODE ===

# Modifications nécessaires:

# 1. Port: Heroku et Render utilisent PORT différemment
# Avant (Heroku):
port = int(os.environ.get("PORT", 5000))

# Après (Render - fonctionne aussi sur Heroku):
port = int(os.environ.get("PORT", 10000))

# 2. Procfile (optionnel sur Render)
# Heroku: OBLIGATOIRE
# Render: optionnel (peut être configuré via Dashboard)
# Garde ton Procfile, il fonctionnera sur les deux!


# === ÉTAPE 3: BACKUP DE LA BASE DE DONNÉES ===

# Si tu utilises PostgreSQL sur Heroku:

# 1. Créer un backup:
heroku pg:backups:capture --app myapp

# 2. Télécharger le backup:
heroku pg:backups:download --app myapp
# Crée un fichier: latest.dump

# 3. Exporter en SQL (plus compatible):
heroku pg:backups:url --app myapp
# Copie l'URL

# Télécharger avec curl:
curl -o backup.dump "URL_DU_BACKUP"

# Convertir en SQL:
pg_restore backup.dump > backup.sql


# === ÉTAPE 4: CRÉER LES SERVICES SUR RENDER ===

# Via Dashboard:
# 1. Crée une DB PostgreSQL
#    Dashboard > New + > PostgreSQL
#    Name: myapp-db
#    Plan: Starter ($7/mois) ou Free
#    Clique "Create Database"

# 2. Crée un Web Service
#    Dashboard > New + > Web Service
#    Connecte ton dépôt GitHub
#    Configure comme décrit précédemment


# === ÉTAPE 5: RESTAURER LA BASE DE DONNÉES ===

# Sur Render PostgreSQL:

# 1. Obtiens les credentials de connexion:
#    Dashboard > Ta DB > Connect
#    Copie: Host, Port, Database, User, Password

# 2. Restaurer depuis ton ordinateur:
# Si tu as backup.sql:
psql -h hostname -U username -d database -f backup.sql

# Si tu as backup.dump:
pg_restore -h hostname -U username -d database backup.dump

# 3. Vérifie les données:
psql -h hostname -U username -d database
# Dans psql:
\dt                     # Liste les tables
SELECT COUNT(*) FROM users;  # Vérifie les données


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

# 1. Exporte depuis Heroku:
heroku config --app myapp --shell > heroku_env.txt

# Contenu exemple:
# API_KEY=abc123
# SECRET_KEY=xyz789
# DEBUG=False

# 2. Importe sur Render:
# Dashboard > Web Service > Environment
# Ajoute chaque variable une par une
# Ou utilise render.yaml pour automatiser


# === ÉTAPE 7: MIGRER LES ADD-ONS ===

# Équivalents Render:

# HEROKU ADD-ON -> RENDER ÉQUIVALENT

# heroku-postgresql -> Render PostgreSQL (natif)
#   Créer: Dashboard > New + > PostgreSQL

# heroku-redis -> Render Redis (natif)
#   Créer: Dashboard > New + > Redis

# Scheduler -> Render Cron Jobs (natif)
#   Créer: Dashboard > New + > Cron Job

# SendGrid -> Utilise le même (configure API key)
#   Ajoute: SENDGRID_API_KEY dans Environment

# Papertrail (logs) -> Render Logs (natif gratuit)
#   Pas besoin d'add-on!

# New Relic -> Utilise le même ou alternatives
#   Configure avec variables d'environnement


# === ÉTAPE 8: TESTER SUR RENDER ===

# 1. Déploie sur Render (comme décrit précédemment)
# 2. Vérifie les logs: Dashboard > Logs
# 3. Teste toutes les fonctionnalités:
#    - Pages principales
#    - Inscription/connexion
#    - Opérations DB
#    - API endpoints
#    - Upload de fichiers
#    - Emails
# 4. Compare avec Heroku (les deux tournent en parallèle)


# === ÉTAPE 9: MIGRER LE DOMAINE PERSONNALISÉ ===

# Si tu as un domaine personnalisé:

# 1. Sur Render:
#    Dashboard > Web Service > Settings > Custom Domain
#    Ajoute: www.mondomaine.com
#    Note les instructions DNS

# 2. Chez ton registraire DNS:
#    Change les records CNAME/A pour pointer vers Render
#    (Au lieu de Heroku)

# 3. Attends la propagation DNS (2-24h)

# 4. Vérifie: https://www.mondomaine.com
#    Doit maintenant pointer vers Render!


# === ÉTAPE 10: CUTOVER (BASCULEMENT) ===

# Quand tout fonctionne sur Render:

# 1. Mets Heroku en "maintenance mode":
heroku maintenance:on --app myapp

# 2. Fais un dernier backup DB sur Heroku
heroku pg:backups:capture --app myapp

# 3. Importe ce backup sur Render (si données récentes)

# 4. Vérifie que Render fonctionne parfaitement

# 5. Change le DNS définitivement vers Render

# 6. Après 24-48h (DNS propagé partout):
#    Supprime l'app Heroku (pour arrêter de payer)


# === ÉTAPE 11: NETTOYAGE HEROKU ===

# Une fois satisfait de Render:

# Supprime l'app Heroku:
heroku apps:destroy --app myapp --confirm myapp

# Cela supprime:
# - L'app
# - Les dynos
# - Les add-ons
# - La configuration
# = Tu ne paies plus!


# === CHECKLIST DE MIGRATION ===

# [OK] Backup complet de la DB Heroku
# [OK] Code testé localement avec variables Render
# [OK] Services Render créés (Web, DB, etc)
# [OK] Variables d'environnement copiées
# [OK] DB restaurée sur Render
# [OK] App testée sur Render
# [OK] Add-ons migrés ou remplacés
# [OK] Domaine personnalisé configuré (si applicable)
# [OK] Tests de charge/performance
# [OK] Monitoring configuré
# [OK] DNS pointant vers Render
# [OK] Heroku supprimé après confirmation


# === COÛT DE LA MIGRATION ===

# Exemple: App Heroku Standard-1X

# HEROKU (avant):
# - Standard-1X Web: $25/mois
# - Heroku PostgreSQL Hobby: $9/mois
# - Total: $34/mois

# RENDER (après):
# - Starter Web Service: $7/mois
# - PostgreSQL Starter: $7/mois
# - Total: $14/mois

# Économie: $20/mois = $240/an! [ARGENT]


[OK] BONNES PRATIQUES RENDER

# === SÉCURITÉ ===

# 1. SECRETS dans variables d'environnement
# [X] JAMAIS dans le code:
api_key = "secret123"  # MAUVAIS!

# [OK] TOUJOURS via environment:
api_key = os.environ.get("API_KEY")  # BON!


# 2. GÉNÉRER des secrets sécurisés:
import secrets
secret_key = secrets.token_urlsafe(32)
# Utilise cette valeur sur Render


# 3. HTTPS forcé (automatique sur Render):
# Mais force dans l'app aussi:
@app.before_request
def force_https():
    if not request.is_secure and os.environ.get('RENDER'):
        return redirect(request.url.replace('http://', 'https://'), 301)


# 4. CORS configuré correctement:
from flask_cors import CORS
# Restrictif:
CORS(app, origins=["https://monfrontend.com"])
# Pas:
CORS(app, origins="*")  # Trop permissif!


# 5. RATE LIMITING pour éviter abus:
from flask_limiter import Limiter

limiter = Limiter(app, default_limits=["200 per day", "50 per hour"])

@app.route('/api/search')
@limiter.limit("10 per minute")
def search():
    # ...


# 6. VALIDATION des inputs:
from flask_wtf import FlaskForm
from wtforms import StringField, validators

class LoginForm(FlaskForm):
    username = StringField('Username', [validators.Length(min=4, max=25)])
    # Ne jamais faire confiance aux inputs utilisateur!


# === PERFORMANCE ===

# 1. CACHING avec Redis:
from flask_caching import Cache

cache = Cache(app, config={
    'CACHE_TYPE': 'redis',
    'CACHE_REDIS_URL': os.environ.get('REDIS_URL')
})

@app.route('/expensive-query')
@cache.cached(timeout=300)  # Cache 5 minutes
def expensive_query():
    # Requête coûteuse...


# 2. Optimiser les requêtes DB:
from flask_sqlalchemy import SQLAlchemy

@app.route('/articles')
def articles():
    # [X] MAUVAIS: N+1 queries
    articles = Article.query.all()
    for article in articles:
        print(article.author.name)  # Query pour chaque article!
    
    # [OK] BON: 1 query avec jointure
    articles = Article.query.options(
        db.joinedload(Article.author)
    ).all()


# 3. Compression gzip:
from flask_compress import Compress

Compress(app)
# Compresse automatiquement les réponses


# 4. Optimiser les images:
from PIL import Image

def optimize_image(filepath):
    img = Image.open(filepath)
    img.thumbnail((800, 800))  # Redimensionner
    img.save(filepath, optimize=True, quality=85)


# === MONITORING ===

# 1. Logs structurés:
import logging
import json

logger = logging.getLogger(__name__)

def log_event(event_type, data):
    logger.info(json.dumps({
        "event": event_type,
        "data": data,
        "timestamp": datetime.now().isoformat()
    }))

# Usage:
log_event("user_signup", {"user_id": 123, "email": "user@example.com"})


# 2. Métriques personnalisées (avec Prometheus):
from prometheus_flask_exporter import PrometheusMetrics

metrics = PrometheusMetrics(app)

@app.route('/api/process')
@metrics.counter('api_process_calls', 'Number of process calls')
def process():
    # ...
    return {"status": "processed"}


# 3. Health checks détaillés:
@app.route('/health/detailed')
def health_detailed():
    """Health check avec détails"""
    
    status = {
        "status": "ok",
        "checks": {}
    }
    
    # Check database
    try:
        db.session.execute('SELECT 1')
        status["checks"]["database"] = "ok"
    except Exception as e:
        status["checks"]["database"] = f"error: {str(e)}"
        status["status"] = "degraded"
    
    # Check Redis
    try:
        redis_client.ping()
        status["checks"]["redis"] = "ok"
    except Exception as e:
        status["checks"]["redis"] = f"error: {str(e)}"
        status["status"] = "degraded"
    
    # Check disk space
    import shutil
    stat = shutil.disk_usage("/")
    if stat.free / stat.total < 0.1:  # Moins de 10% libre
        status["checks"]["disk"] = "warning: low space"
        status["status"] = "degraded"
    else:
        status["checks"]["disk"] = "ok"
    
    return status, 200 if status["status"] == "ok" else 503


# === DÉPLOIEMENT ===

# 1. Utiliser des branches pour environments:

# Développement: branche "dev"
git push render dev
# Configure Render pour déployer depuis "dev"

# Production: branche "main"
git push render main


# 2. Blue-Green Deployment (plans Pro+):

# Via Dashboard:
# 1. Settings > Deployment > Zero Downtime Deploys
# 2. Active "Enable"
# Render démarre la nouvelle version AVANT d'arrêter l'ancienne
# = Zero downtime!


# 3. Rollback rapide:

# Via Dashboard:
# 1. Events > Trouve le déploiement qui marchait
# 2. Clique "..." > "Rollback to this deploy"

# Via CLI (si installée):
render rollback -s myapp


# === SÉCURITÉ AVANCÉE ===

# 1. Limiter les origines CORS précisément:
from flask_cors import CORS

CORS(app, resources={
    r"/api/*": {
        "origins": ["https://monfrontend.com", "https://www.monfrontend.com"],
        "methods": ["GET", "POST"],
        "allow_headers": ["Content-Type", "Authorization"]
    }
})


# 2. Headers de sécurité avec Talisman:
from flask_talisman import Talisman

Talisman(app, 
    content_security_policy={
        'default-src': "'self'",
        'img-src': '*',
        'script-src': ["'self'", 'cdn.example.com']
    },
    force_https=True
)


# 3. Protection CSRF:
from flask_wtf.csrf import CSRFProtect

csrf = CSRFProtect(app)

@app.route('/api/delete/<id>', methods=['POST'])
@csrf.exempt  # Exemption pour API (utilise tokens au lieu)
def delete_item(id):
    # Vérifie le token d'API dans les headers
    token = request.headers.get('X-API-Token')
    if not verify_token(token):
        return {"error": "Invalid token"}, 401
    # ...


# 4. Validation des entrées:
from marshmallow import Schema, fields, ValidationError

class ArticleSchema(Schema):
    titre = fields.Str(required=True, validate=lambda x: len(x) <= 200)
    contenu = fields.Str(required=True, validate=lambda x: len(x) <= 10000)

schema = ArticleSchema()

@app.route('/api/articles', methods=['POST'])
def create_article():
    try:
        data = schema.load(request.json)
    except ValidationError as err:
        return {"errors": err.messages}, 400
    
    # data est maintenant validé
    article = Article(**data)
    db.session.add(article)
    db.session.commit()
    return {"id": article.id}, 201


# === OPTIMISATION DES COÛTS ===

# 1. Utiliser le Free tier intelligemment:

# Pour plusieurs petits projets:
# - Crée un seul Web Service
# - Utilise des sous-routes pour chaque projet
# Exemple: myapp.onrender.com/project1, myapp.onrender.com/project2


# 2. Passer à Starter seulement si nécessaire:

# Indicateurs pour upgrader:
# - Trafic constant (app ne devrait jamais sleep)
# - Plus de 1000 requêtes/jour
# - Utilisateurs se plaignent de lenteur
# - Metrics montrent CPU > 80%


# 3. Combiner plusieurs services:

# Au lieu de:
# - Web Service 1: API ($7/mois)
# - Web Service 2: Frontend ($7/mois)
# Total: $14/mois

# Faire:
# - Web Service: API + Frontend servi statiquement ($7/mois)
# Économie: $7/mois = $84/an


# === INTÉGRATION CONTINUE (CI/CD) ===

# Render supporte le déploiement automatique depuis GitHub
# Mais tu peux ajouter des tests AVANT déploiement

# === Avec GitHub Actions ===

# Crée .github/workflows/deploy.yml:

"""
name: Deploy to Render

on:
  push:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest pytest-cov
      
      - name: Run tests
        run: |
          pytest --cov=app tests/
      
      - name: Check coverage
        run: |
          coverage report --fail-under=80
  
  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Trigger Render Deploy
        run: |
          curl -X POST ${{ secrets.RENDER_DEPLOY_HOOK }}
"""

# Configuration:
# 1. Va sur Render Dashboard > Service > Settings
# 2. Trouve "Deploy Hook" URL
# 3. Copie l'URL
# 4. Sur GitHub > Settings > Secrets > New secret
# 5. Name: RENDER_DEPLOY_HOOK
# 6. Value: (colle l'URL)


# === TESTS AVANCÉS ===

# 1. Tests unitaires avec pytest:

# tests/test_models.py
import pytest
from app import app, db, Article

@pytest.fixture
def client():
    app.config['TESTING'] = True
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
    
    with app.app_context():
        db.create_all()
        yield app.test_client()
        db.drop_all()

def test_create_article(client):
    """Test création d'un article"""
    response = client.post('/creer', data={
        'titre': 'Test Article',
        'contenu': 'Contenu de test'
    })
    assert response.status_code == 302  # Redirection
    
    article = Article.query.first()
    assert article.titre == 'Test Article'

def test_view_article(client):
    """Test affichage d'un article"""
    # Créer un article
    with app.app_context():
        article = Article(titre='Test', contenu='Contenu')
        db.session.add(article)
        db.session.commit()
        article_id = article.id
    
    # Voir l'article
    response = client.get(f'/article/{article_id}')
    assert response.status_code == 200
    assert b'Test' in response.data


# 2. Tests d'intégration:

# tests/test_integration.py
def test_full_workflow(client):
    """Test workflow complet: créer, lire, supprimer"""
    
    # Créer un article
    response = client.post('/creer', data={
        'titre': 'Integration Test',
        'contenu': 'Full workflow test'
    })
    assert response.status_code == 302
    
    # Vérifier qu'il apparaît sur la page d'accueil
    response = client.get('/')
    assert b'Integration Test' in response.data
    
    # Supprimer l'article
    article = Article.query.first()
    response = client.post(f'/supprimer/{article.id}')
    assert response.status_code == 302
    
    # Vérifier qu'il a disparu
    response = client.get('/')
    assert b'Integration Test' not in response.data


# 3. Tests de charge avec Locust:

# locustfile.py
from locust import HttpUser, task, between

class BlogUser(HttpUser):
    wait_time = between(1, 3)
    
    @task(3)
    def view_homepage(self):
        """Visiter la page d'accueil (fréquent)"""
        self.client.get("/")
    
    @task(1)
    def create_article(self):
        """Créer un article (moins fréquent)"""
        self.client.post("/creer", data={
            "titre": "Load Test Article",
            "contenu": "Testing performance"
        })
    
    @task(2)
    def view_article(self):
        """Voir un article"""
        self.client.get("/article/1")

# Lancer:
# locust -f locustfile.py --host=https://myapp.onrender.com


# === MONITORING EXTERNE ===

# 1. UptimeRobot (gratuit):
# - Ping ton app toutes les 5 minutes
# - Alerte si down
# - Garde l'app "réveillée" (Free tier)

# Configuration:
# 1. Va sur: https://uptimerobot.com
# 2. Crée un compte gratuit
# 3. Add New Monitor
# 4. Monitor Type: HTTP(s)
# 5. URL: https://myapp.onrender.com/health
# 6. Monitoring Interval: 5 minutes
# 7. Alertes: Email/SMS


# 2. Better Uptime (monitoring avancé):
# - Ping depuis plusieurs localisations
# - Status page publique
# - Incidents tracking

# Configuration:
# 1. Va sur: https://betteruptime.com
# 2. Crée un monitor
# 3. URL: https://myapp.onrender.com
# 4. Configure alertes


# 3. Sentry (erreurs en production):
# - Capture toutes les erreurs Python
# - Stack traces détaillées
# - Alertes en temps réel

# Installation:
pip install sentry-sdk[flask]
pip freeze > requirements.txt

# Configuration dans app.py:
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init(
    dsn=os.environ.get("SENTRY_DSN"),
    integrations=[FlaskIntegration()],
    traces_sample_rate=1.0
)

# Sur Render, ajoute la variable:
# SENTRY_DSN = https://xxx@sentry.io/xxx


# === MULTI-REGION DEPLOYMENT ===

# Pour apps mondiales, déploie dans plusieurs régions

# Stratégie:
# 1. Crée plusieurs Web Services:
#    - myapp-us (Oregon)
#    - myapp-eu (Frankfurt)
#    - myapp-asia (Singapore)

# 2. Utilise Cloudflare pour le routing:
#    - Load Balancing basé sur la géolocalisation
#    - Utilisateurs US -> Oregon
#    - Utilisateurs EU -> Frankfurt
#    - Utilisateurs Asie -> Singapore

# 3. Base de données partagée:
#    - PostgreSQL sur Render (une seule région)
#    - Ou utilise un service distribué (PlanetScale, CockroachDB)


# === EXEMPLES AVANCÉS ===

# === EXEMPLE 1: API REST Complète avec Authentification ===

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity
from werkzeug.security import generate_password_hash, check_password_hash
import os

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL')
app.config['JWT_SECRET_KEY'] = os.environ.get('JWT_SECRET_KEY')

db = SQLAlchemy(app)
jwt = JWTManager(app)

# Modèles
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(200), nullable=False)
    
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)
    
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    completed = db.Column(db.Boolean, default=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    user = db.relationship('User', backref='tasks')

# Routes d'authentification
@app.route('/api/register', methods=['POST'])
def register():
    """Inscription utilisateur"""
    data = request.get_json()
    
    if User.query.filter_by(username=data['username']).first():
        return {"error": "Username already exists"}, 400
    
    user = User(username=data['username'], email=data['email'])
    user.set_password(data['password'])
    db.session.add(user)
    db.session.commit()
    
    return {"message": "User created successfully"}, 201

@app.route('/api/login', methods=['POST'])
def login():
    """Connexion utilisateur"""
    data = request.get_json()
    
    user = User.query.filter_by(username=data['username']).first()
    if not user or not user.check_password(data['password']):
        return {"error": "Invalid credentials"}, 401
    
    access_token = create_access_token(identity=user.id)
    return {"access_token": access_token}, 200

# Routes API (protégées)
@app.route('/api/tasks', methods=['GET'])
@jwt_required()
def get_tasks():
    """Liste des tâches de l'utilisateur"""
    user_id = get_jwt_identity()
    tasks = Task.query.filter_by(user_id=user_id).all()
    
    return {
        "tasks": [
            {"id": t.id, "title": t.title, "completed": t.completed}
            for t in tasks
        ]
    }, 200

@app.route('/api/tasks', methods=['POST'])
@jwt_required()
def create_task():
    """Créer une tâche"""
    user_id = get_jwt_identity()
    data = request.get_json()
    
    task = Task(title=data['title'], user_id=user_id)
    db.session.add(task)
    db.session.commit()
    
    return {"id": task.id, "title": task.title}, 201

@app.route('/api/tasks/<int:id>', methods=['PATCH'])
@jwt_required()
def update_task(id):
    """Mettre à jour une tâche"""
    user_id = get_jwt_identity()
    task = Task.query.filter_by(id=id, user_id=user_id).first_or_404()
    
    data = request.get_json()
    if 'title' in data:
        task.title = data['title']
    if 'completed' in data:
        task.completed = data['completed']
    
    db.session.commit()
    return {"message": "Task updated"}, 200

@app.route('/api/tasks/<int:id>', methods=['DELETE'])
@jwt_required()
def delete_task(id):
    """Supprimer une tâche"""
    user_id = get_jwt_identity()
    task = Task.query.filter_by(id=id, user_id=user_id).first_or_404()
    
    db.session.delete(task)
    db.session.commit()
    return {"message": "Task deleted"}, 200

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


# === EXEMPLE 2: WebSocket avec Flask-SocketIO ===

from flask import Flask, render_template
from flask_socketio import SocketIO, emit, join_room, leave_room
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
socketio = SocketIO(app, cors_allowed_origins="*")

# Stockage en mémoire (utilise Redis en production!)
active_users = {}

@app.route('/')
def index():
    return render_template('chat.html')

@socketio.on('connect')
def handle_connect():
    """Utilisateur connecté"""
    print(f'Client connected: {request.sid}')
    emit('user_count', {'count': len(active_users)}, broadcast=True)

@socketio.on('disconnect')
def handle_disconnect():
    """Utilisateur déconnecté"""
    if request.sid in active_users:
        username = active_users[request.sid]
        del active_users[request.sid]
        emit('user_left', {'username': username}, broadcast=True)
        emit('user_count', {'count': len(active_users)}, broadcast=True)

@socketio.on('join')
def handle_join(data):
    """Utilisateur rejoint le chat"""
    username = data['username']
    room = data.get('room', 'general')
    
    active_users[request.sid] = username
    join_room(room)
    
    emit('user_joined', {
        'username': username,
        'message': f'{username} a rejoint le chat'
    }, room=room)
    
    emit('user_count', {'count': len(active_users)}, broadcast=True)

@socketio.on('message')
def handle_message(data):
    """Recevoir et diffuser un message"""
    room = data.get('room', 'general')
    username = active_users.get(request.sid, 'Anonymous')
    
    emit('message', {
        'username': username,
        'message': data['message'],
        'timestamp': datetime.now().isoformat()
    }, room=room)

@socketio.on('typing')
def handle_typing(data):
    """Utilisateur est en train de taper"""
    room = data.get('room', 'general')
    username = active_users.get(request.sid, 'Anonymous')
    
    emit('typing', {
        'username': username,
        'is_typing': data['is_typing']
    }, room=room, include_self=False)

if __name__ == '__main__':
    port = int(os.environ.get('PORT', 10000))
    socketio.run(app, host='0.0.0.0', port=port)

# Procfile pour WebSocket:
# web: gunicorn --worker-class eventlet -w 1 app:app


# === EXEMPLE 3: Tâches Asynchrones avec Celery ===

from flask import Flask, jsonify, request
from celery import Celery
import os
import time

app = Flask(__name__)

# Configuration Celery
app.config['CELERY_BROKER_URL'] = os.environ.get('REDIS_URL')
app.config['CELERY_RESULT_BACKEND'] = os.environ.get('REDIS_URL')

celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)

# Tâches asynchrones
@celery.task
def send_email(to, subject, body):
    """Envoyer un email (simulé)"""
    time.sleep(5)  # Simuler l'envoi
    print(f"Email envoyé à {to}: {subject}")
    return {"status": "sent", "to": to}

@celery.task
def process_large_file(filepath):
    """Traiter un gros fichier"""
    time.sleep(10)  # Simuler le traitement
    print(f"Fichier traité: {filepath}")
    return {"status": "processed", "file": filepath}

@celery.task
def generate_report(user_id):
    """Générer un rapport"""
    time.sleep(15)  # Simuler la génération
    report = f"Report for user {user_id}"
    return {"status": "generated", "report": report}

# Routes API
@app.route('/api/send-email', methods=['POST'])
def api_send_email():
    """Lancer l'envoi d'email en arrière-plan"""
    data = request.get_json()
    
    task = send_email.delay(data['to'], data['subject'], data['body'])
    
    return {
        "message": "Email queued",
        "task_id": task.id
    }, 202

@app.route('/api/task/<task_id>')
def get_task_status(task_id):
    """Vérifier le statut d'une tâche"""
    task = celery.AsyncResult(task_id)
    
    if task.state == 'PENDING':
        response = {"state": "PENDING", "status": "Task is waiting..."}
    elif task.state == 'SUCCESS':
        response = {"state": "SUCCESS", "result": task.result}
    elif task.state == 'FAILURE':
        response = {"state": "FAILURE", "error": str(task.info)}
    else:
        response = {"state": task.state}
    
    return response, 200

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

# Sur Render:
# 1. Ajoute un service Redis (gratuit)
# 2. Web Service: gunicorn app:app
# 3. Background Worker: celery -A app.celery worker --loglevel=info


# === RESSOURCES SUPPLÉMENTAIRES ===

# Documentation officielle Render:
# https://render.com/docs

# Guides spécifiques:
# - Python: https://render.com/docs/deploy-flask
# - Django: https://render.com/docs/deploy-django
# - PostgreSQL: https://render.com/docs/databases
# - Redis: https://render.com/docs/redis

# Comparaisons:
# Render vs Heroku: https://render.com/render-vs-heroku-comparison
# Render vs AWS: https://render.com/blog/render-vs-aws

# Community & Support:
# Forum: https://community.render.com
# Status page: https://status.render.com
# Blog: https://render.com/blog

# Exemples de projets:
# https://github.com/render-examples

# Pricing calculator:
# https://render.com/pricing


# === CHECKLIST FINALE DE PRODUCTION ===

# [OK] Sécurité:
# - [ ] HTTPS activé (automatique sur Render)
# - [ ] Variables d'environnement pour secrets
# - [ ] CORS configuré correctement
# - [ ] Rate limiting implémenté
# - [ ] Validation des inputs
# - [ ] Headers de sécurité (Talisman)
# - [ ] CSRF protection
# - [ ] SQL injection prevention (ORM)

# [OK] Performance:
# - [ ] Cache Redis configuré
# - [ ] Queries DB optimisées
# - [ ] Images optimisées
# - [ ] Gzip compression activée
# - [ ] Static files servis efficacement
# - [ ] Instance type approprié

# [OK] Monitoring:
# - [ ] Logs structurés
# - [ ] Health check endpoint
# - [ ] Uptime monitoring (UptimeRobot)
# - [ ] Error tracking (Sentry)
# - [ ] Métriques configurées

# [OK] Backup & Recovery:
# - [ ] Backups DB automatiques
# - [ ] Plan de rollback
# - [ ] Code versionné (git)
# - [ ] Documentation à jour

# [OK] Tests:
# - [ ] Tests unitaires (pytest)
# - [ ] Tests d'intégration
# - [ ] Tests de charge (Locust)
# - [ ] CI/CD configuré (GitHub Actions)

# [OK] Coûts:
# - [ ] Plan approprié (Free/Starter/Standard)
# - [ ] Metrics surveillées
# - [ ] Scaling configuré si nécessaire
# - [ ] Budget défini

# Si TOUTES les cases sont cochées:
# [BRAVO] TON APP EST PRODUCTION-READY! [BRAVO]


# === MIGRATION COMPLÈTE DE HEROKU VERS RENDER ===

# Guide pas à pas pour migrer une app existante

# === PHASE 1: PRÉPARATION (1 jour) ===

# 1. Inventaire complet:
# - Liste tous les dynos Heroku
heroku ps --app myapp

# - Liste tous les add-ons
heroku addons --app myapp

# - Exporte toutes les variables
heroku config --app myapp --shell > heroku_vars.txt

# - Liste les domaines
heroku domains --app myapp

# 2. Backup complet:
# - Base de données
heroku pg:backups:capture --app myapp
heroku pg:backups:download --app myapp

# - Code (si pas sur GitHub)
git clone https://git.heroku.com/myapp.git myapp-backup


# === PHASE 2: PRÉPARATION RENDER (1-2 jours) ===

# 1. Créer les services Render:

# PostgreSQL:
# Dashboard > New + > PostgreSQL
# Name: myapp-db
# Plan: Starter ($7/mois) ou équivalent à Heroku

# Redis (si utilisé):
# Dashboard > New + > Redis
# Name: myapp-redis
# Plan: Starter ($10/mois)

# 2. Restaurer la base de données:
# Obtiens les credentials de la DB Render
# Dashboard > myapp-db > Connect

# Restaure depuis le backup Heroku:
pg_restore -h render-host -U render-user -d render-db latest.dump

# 3. Adapter le code pour Render:

# Dans app.py, remplace:
# Heroku:
port = int(os.environ.get("PORT", 5000))

# Render (fonctionne aussi pour Heroku):
port = int(os.environ.get("PORT", 10000))


# === PHASE 3: TEST SUR RENDER (2-3 jours) ===

# 1. Créer le Web Service:
# Dashboard > New + > Web Service
# Connecte GitHub
# Configure comme décrit précédemment

# 2. Configurer les variables d'environnement:
# Copie toutes les variables depuis heroku_vars.txt
# Dashboard > Web Service > Environment > Add

# 3. Tester exhaustivement:
# - Toutes les fonctionnalités
# - Tous les endpoints API
# - Uploads de fichiers
# - Emails
# - Tâches background
# - Performances

# 4. Tests de charge:
# Lance Locust contre l'URL Render
locust -f locustfile.py --host=https://myapp.onrender.com


# === PHASE 4: MIGRATION DU TRAFIC (1 jour) ===

# 1. Synchroniser les données:
# Si ton app a accumulé de nouvelles données sur Heroku:

# Export depuis Heroku:
heroku pg:backups:capture --app myapp
heroku pg:backups:download --app myapp

# Import vers Render:
pg_restore -h render-host -U render-user -d render-db latest.dump

# 2. Mettre Heroku en maintenance:
heroku maintenance:on --app myapp

# 3. Dernière synchro des données (si nécessaire)

# 4. Changer le DNS:
# Chez ton registraire de domaine:
# Avant: C