# Fichier: python_cheats/cheatsheets/railway.txt
# Cheatsheet Railway.app - Guide Complet PaaS


[OK] INTRODUCTION

# Railway.app - C'est quoi ?
# Railway est comme Heroku mais en plus moderne et moins cher.
# C'est une plateforme qui héberge vos applications web automatiquement.
# 
# Imaginez : vous codez votre app localement, vous faites "git push",
# et boom [IMPACT] votre app est en ligne avec une vraie URL accessible !
# 
# Railway s'occupe de TOUT :
# - Serveurs (vous ne les voyez jamais)
# - Base de données (PostgreSQL, MySQL, MongoDB, Redis)
# - HTTPS automatique (sécurité SSL gratuite)
# - Scaling (plus de visiteurs ? Railway s'adapte automatiquement)
# - Monitoring (vous voyez si votre app crash en temps réel)

# Pourquoi utiliser Railway ?
# [OK] Déploiement Git automatique -> Git push = mise en ligne
# [OK] Bases de données en 1 clic -> Pas besoin d'installer PostgreSQL
# [OK] Variables d'environnement sécurisées -> Vos secrets en sécurité
# [OK] CLI puissante -> Contrôlez tout depuis votre terminal
# [OK] Domaines personnalisés -> Utilisez votre propre nom de domaine
# [OK] HTTPS automatique -> Certificat SSL gratuit inclus
# [OK] Logs en temps réel -> Voyez ce qui se passe dans votre app
# [OK] Metrics et monitoring -> CPU, RAM, requêtes, tout est visible
# [OK] Prix transparent -> $5 gratuit/mois puis pay-as-you-go

# Cas d'usage typiques:
# - Déployer votre projet Django/Flask/FastAPI
# - Héberger votre API backend
# - Mettre en ligne votre portfolio avec backend
# - Créer un SaaS avec base de données
# - Prototypes et MVPs rapides

# Alternative à:
# - Heroku (plus cher, moins moderne)
# - Render (similaire mais interface moins intuitive)
# - Vercel (meilleur pour frontend, Railway pour backend)
# - DigitalOcean (nécessite plus de configuration manuelle)

# Site officiel: https://railway.app
# Documentation: https://docs.railway.app
# Status: https://status.railway.app

# IMPORTANT: Railway est payant mais généreux
# - $5 de crédit GRATUIT chaque mois (renouvelé automatiquement)
# - Suffisant pour petits projets et prototypes
# - Au-delà de $5, vous payez ce que vous consommez
# - Pas de surprise, vous voyez votre usage en temps réel


[OK] INSTALLATION CLI

# La CLI (Command Line Interface) c'est le "couteau suisse" de Railway
# Elle permet de tout contrôler depuis votre terminal sans ouvrir le navigateur

# === Installation ===

# Méthode 1: NPM (Node.js Package Manager)
# Si vous avez Node.js installé (vérifier avec: node --version)
npm install -g @railway/cli
# Le flag -g installe globalement = disponible partout sur votre machine

# Méthode 2: Homebrew (Mac/Linux uniquement)
# Homebrew est le gestionnaire de paquets pour Mac
brew install railway

# Méthode 3: Scoop (Windows uniquement)
# Scoop est comme Homebrew mais pour Windows
scoop install railway

# Méthode 4: Installation manuelle (RECOMMANDÉ pour débutants)
# Cette méthode fonctionne sur tous les systèmes

# Si vous êtes sur Linux ou Mac (terminal Bash/Zsh)
curl -fsSL https://railway.app/install.sh | sh
# curl = télécharge le script d'installation
# sh = exécute le script

# Si vous êtes sur Windows (PowerShell)
iwr https://railway.app/install.ps1 | iex
# iwr = Invoke-WebRequest (télécharge)
# iex = Invoke-Expression (exécute)

# Vérifier que l'installation a réussi
railway --version
# Devrait afficher quelque chose comme: railway version 3.x.x

railway version
# Pareil, affiche la version installée

# Mettre à jour Railway CLI quand une nouvelle version sort
npm update -g @railway/cli      # Si installé via NPM
brew upgrade railway            # Si installé via Homebrew

# ASTUCE: Si "railway" ne fonctionne pas après installation
# Fermez et rouvrez votre terminal
# Sur Windows, redémarrez PowerShell en mode Administrateur


[OK] AUTHENTIFICATION

# Avant de pouvoir déployer, vous devez connecter Railway CLI à votre compte

# Étape 1: Créer un compte Railway (si pas déjà fait)
# Aller sur https://railway.app et s'inscrire (gratuit)
# Vous pouvez utiliser GitHub, Google ou email

# Étape 2: Se connecter via la CLI
railway login
# Cette commande va:
# 1. Ouvrir votre navigateur automatiquement
# 2. Vous demander d'autoriser la CLI
# 3. Vous ramener au terminal une fois connecté
# 
# Résultat: Vous verrez "[OK] Logged in as votre-email@example.com"

# Alternative: Se connecter avec un token API
railway login --token <YOUR_TOKEN>
# Utile pour CI/CD (déploiement automatique) ou serveurs
# Ne pas partager ce token ! C'est comme un mot de passe

# Où obtenir un token API ?
# 1. Aller sur https://railway.app
# 2. Cliquer sur votre profil (en haut à droite)
# 3. Settings -> Tokens -> Create Token
# 4. Copier le token (vous ne le reverrez plus !)
# 5. Utiliser: railway login --token le_token_copié

# Vérifier que vous êtes bien connecté
railway whoami
# Affiche votre email ou nom d'utilisateur
# Si pas connecté, affiche une erreur

# Se déconnecter (utile si vous changez de compte)
railway logout
# Supprime vos credentials locaux

# Lier la CLI à un projet existant
railway link
# Cas d'usage: Vous avez créé un projet sur le dashboard web
# et vous voulez le contrôler depuis votre terminal
# Railway vous montrera une liste de vos projets, choisissez avec les flèches

railway link <PROJECT_ID>
# Si vous connaissez déjà l'ID du projet
# L'ID ressemble à: a1b2c3d4-e5f6-7890-abcd-ef1234567890

# Fichier de configuration locale: .railway
# Railway crée automatiquement ce fichier après "railway link"
# Il contient l'ID de votre projet et environnement
# 
# Contenu du fichier .railway:
{
  "projectId": "xxx",        # L'ID unique de votre projet
  "environmentId": "xxx"     # production, staging, ou development
}
# 
# NE PAS commiter ce fichier dans Git si vous travaillez en équipe
# Ajoutez .railway dans votre .gitignore


[OK] PROJETS

# Un PROJET Railway = un ensemble de services qui fonctionnent ensemble
# Exemple: votre app Django + PostgreSQL + Redis = 1 projet
# Chaque projet a sa propre URL, ses variables, ses logs

# === Création et gestion ===

# Créer un NOUVEAU projet (première fois)
railway init
# Cette commande:
# 1. Détecte automatiquement le type de votre app (Python, Node, etc.)
# 2. Crée un projet sur Railway
# 3. Lie votre dossier local au projet Railway
# 4. Crée le fichier .railway pour se souvenir du lien
# 
# Vous verrez: "[OK] Created project my-project"

# Créer projet avec un nom personnalisé (plus clair)
railway init --name "My Project"
railway init --name "Portfolio Backend"
railway init --name "API E-commerce"
# Recommandé ! Sinon Railway génère un nom aléatoire

# Lister TOUS vos projets Railway
railway list
# Affiche une liste de tous vos projets avec leurs IDs
# Utile quand vous avez plusieurs projets et oubliez lequel est lequel

# Ouvrir le projet actuel dans le dashboard web
railway open
# Lance votre navigateur et ouvre l'interface web de Railway
# Plus visuel que la CLI, pratique pour voir les metrics et logs

# Obtenir des infos sur le projet actuel
railway status
# Affiche:
# - Nom du projet
# - ID du projet
# - Environnement actif (production, staging, etc.)
# - Services déployés
# - État des déploiements

# Voir toutes les variables d'environnement du projet
railway variables
# Liste toutes les variables (DATABASE_URL, API_KEY, etc.)
# Les valeurs sensibles sont masquées (****)

# Supprimer un projet
# [ATTENTION] ATTENTION: Pas de commande CLI pour supprimer !
# Vous DEVEZ aller sur le dashboard web:
# 1. Ouvrir https://railway.app
# 2. Sélectionner le projet
# 3. Settings -> Danger Zone -> Delete Project
# 4. Confirmer (action irréversible!)
# 
# Cela supprime TOUT: code, base de données, backups !

# WORKFLOW TYPIQUE (première utilisation):
# 
# 1. Vous avez codé votre app en local
# 2. cd mon-projet-django
# 3. railway login
# 4. railway init --name "Mon Super Projet"
# 5. railway add (pour ajouter PostgreSQL par exemple)
# 6. railway up (déployer)
# 7. railway open (voir dans le navigateur)


[OK] DÉPLOIEMENT

# DÉPLOYER = mettre votre app en ligne sur Internet avec une vraie URL
# Railway rend ça super simple, pas besoin de configurer Apache/Nginx !

# === Déploiement depuis votre ordinateur ===

# Commande MAGIQUE qui fait tout
railway up
# Cette commande:
# 1. Envoie votre code vers Railway
# 2. Détecte automatiquement le langage (Python, Node, etc.)
# 3. Installe les dépendances (pip install, npm install)
# 4. Build votre app
# 5. Lance votre app
# 6. Génère une URL publique (ex: myapp-production.up.railway.app)
# 
# Vous verrez les logs défiler en temps réel
# À la fin: "[OK] Deployment successful"

# Déployer en mode détaché (non-bloquant)
railway up --detach
railway up -d
# Pratique si vous voulez continuer à travailler pendant le déploiement
# Railway déploie en arrière-plan
# Pour voir les logs après: railway logs -f

# Déployer un service spécifique (si vous avez plusieurs services)
railway up --service backend
railway up --service api
# Utile quand vous avez: frontend + backend + worker
# Vous déployez seulement ce qui a changé

# === Déploiement depuis GitHub (RECOMMANDÉ pour production) ===

# Pourquoi GitHub ? 
# - Déploiement automatique à chaque "git push"
# - Historique des versions
# - Rollback facile en cas de bug
# - Travail en équipe simplifié

# Configuration (une seule fois):
# 1. Pushez votre code sur GitHub
#    git init
#    git add .
#    git commit -m "Initial commit"
#    git remote add origin https://github.com/username/repo.git
#    git push -u origin main
# 
# 2. Sur Railway dashboard: https://railway.app
#    - Cliquer sur votre projet
#    - Settings -> Connect to GitHub
#    - Autoriser Railway à accéder à vos repos
#    - Sélectionner le repo de votre app
# 
# 3. Configurer auto-deploy:
#    - Branch to deploy: main (ou master)
#    - [OK] Enable automatic deployments
# 
# 4. C'EST TOUT ! Maintenant:
#    - Vous codez localement
#    - git add . && git commit -m "Fix bug"
#    - git push
#    - Railway détecte le push et redéploie automatiquement !
#    - Vous recevez une notification par email

# Railway crée un webhook GitHub automatiquement
# Webhook = GitHub dit à Railway "Hey, nouveau code !"

# === Déploiement depuis GitLab / Bitbucket ===

# Même principe que GitHub:
# Dashboard -> Settings -> Deployments -> Connect Repository
# Sélectionner GitLab ou Bitbucket
# Autoriser et choisir le repo

# === Watch mode (redéploiement automatique LOCAL) ===

railway up --watch
# Surveille les changements de fichiers sur votre ordinateur
# Dès que vous sauvegardez un fichier, Railway redéploie !
# 
# Pratique pendant le développement pour tester rapidement
# [ATTENTION] Attention: consomme votre crédit à chaque redéploiement
# 
# Pour arrêter: Ctrl+C

# WORKFLOW COMPLET (première app Django):
# 
# 1. Développement local:
#    python manage.py runserver
#    # Tester sur http://localhost:8000
# 
# 2. Prêt à déployer ? Créer requirements.txt:
#    pip freeze > requirements.txt
# 
# 3. S'assurer que settings.py est configuré:
#    ALLOWED_HOSTS = ['*']  # Ou votre domaine
#    DEBUG = False
# 
# 4. Déployer:
#    railway up
# 
# 5. Migrations base de données:
#    railway run python manage.py migrate
# 
# 6. Créer superuser:
#    railway run python manage.py createsuperuser
# 
# 7. Votre app est en ligne ! [BRAVO]


[OK] ENVIRONNEMENTS

# Un ENVIRONNEMENT = une version isolée de votre app
# Pensez-y comme des "univers parallèles" pour votre code

# POURQUOI plusieurs environnements ?
# - Production: version publique, utilisée par vrais utilisateurs
# - Staging: version de test avant mise en production
# - Development: version pour expérimenter sans casser prod

# Railway supporte plusieurs environnements par projet
# Chaque environnement a:
# - Sa propre URL
# - Ses propres variables d'environnement
# - Sa propre base de données (si vous voulez)
# - Ses propres déploiements

# Environnements par défaut créés automatiquement:
# - production -> L'app que vos utilisateurs voient
# - staging -> Pour tester avant de déployer en prod
# - development -> Pour développer sans risque

# Lister tous les environnements de votre projet
railway environment
# Affiche la liste avec un * devant l'environnement actif

# Changer d'environnement actif (celui que vous contrôlez)
railway environment production
# Maintenant toutes vos commandes affectent production

railway environment staging
# Maintenant vous travaillez sur staging

railway environment development
# Retour sur développement

# Créer un nouvel environnement personnalisé
# [ATTENTION] Pas de commande CLI, vous devez passer par le dashboard:
# 1. Ouvrir https://railway.app
# 2. Sélectionner votre projet
# 3. En haut à droite: sélecteur d'environnement
# 4. "+ New Environment"
# 5. Nommer (ex: "testing", "demo", "preview")
# 
# Cas d'usage: environnement "demo" pour montrer à des clients

# Déployer sur un environnement spécifique
railway up --environment staging
# Déploie sur staging, pas sur production
# Super utile pour tester avant de mettre en prod

railway up --environment production
# Déploiement en production après tests réussis

# Voir les variables d'un environnement spécifique
railway variables --environment production
railway variables --environment staging
# Les variables peuvent être différentes entre environnements
# Ex: DEBUG=True en dev, DEBUG=False en production

# WORKFLOW TYPIQUE avec environnements:
# 
# 1. Développer en local:
#    python manage.py runserver
# 
# 2. Tester sur staging:
#    railway environment staging
#    railway up
#    # Tester sur: myapp-staging.up.railway.app
# 
# 3. Si OK, déployer en production:
#    railway environment production
#    railway up
#    # Mise en ligne sur: myapp-production.up.railway.app
# 
# 4. Si bug en prod, revenir rapidement à staging:
#    railway environment staging
#    # Investiguer sans affecter la prod

# ASTUCE: Bases de données par environnement
# Vous pouvez avoir:
# - PostgreSQL pour production (vraies données)
# - PostgreSQL pour staging (données de test)
# - SQLite en local (développement)
# 
# Comme ça, vos tests ne détruisent pas les vraies données !

# IMPORTANT: Variables partagées vs spécifiques
# 
# Variables PARTAGÉES (tous environnements):
# - STRIPE_PUBLIC_KEY (même clé partout)
# 
# Variables SPÉCIFIQUES (par environnement):
# - DEBUG=True (development) vs DEBUG=False (production)
# - DATABASE_URL différent selon environnement
# - API_KEY de test vs API_KEY de production


[OK] VARIABLES D'ENVIRONNEMENT

# Les VARIABLES D'ENVIRONNEMENT = des paramètres secrets ou configurables
# Exemples: mots de passe DB, clés API, tokens, URLs, modes debug
# 
# POURQUOI les utiliser ?
# [OK] Ne JAMAIS mettre de secrets dans le code (risque de fuite)
# [OK] Changer config sans modifier le code
# [OK] Différentes valeurs selon environnement (dev vs prod)
# [OK] Sécurité: Railway les chiffre

# === Gestion des variables ===

# Lister TOUTES les variables du projet actuel
railway variables
# Affiche quelque chose comme:
# DATABASE_URL=postgresql://user:***@host/db
# SECRET_KEY=***
# DEBUG=False
# 
# Les valeurs sensibles sont masquées (***) pour sécurité

# Définir UNE variable
railway variables set KEY=value
# Exemples concrets:
railway variables set DEBUG=True
railway variables set SECRET_KEY=ma-clé-super-secrète-123
railway variables set API_URL=https://api.example.com

# Cas d'usage réels:
railway variables set DATABASE_URL=postgresql://...
# URL complète de connexion à votre base de données

railway variables set PORT=8000
# Port sur lequel votre app écoute (Railway fournit $PORT automatiquement)

railway variables set STRIPE_API_KEY=sk_test_...
# Clé API Stripe pour paiements

# Définir PLUSIEURS variables en une commande
railway variables set KEY1=value1 KEY2=value2 KEY3=value3
railway variables set DEBUG=False ALLOWED_HOSTS=example.com MAX_UPLOAD_SIZE=10485760

# Supprimer une variable (si vous ne l'utilisez plus)
railway variables delete KEY
railway variables delete API_KEY_OLD

# Supprimer plusieurs variables d'un coup
railway variables delete KEY1 KEY2 KEY3
railway variables delete OLD_DB_URL DEPRECATED_TOKEN

# Importer TOUTES les variables depuis un fichier .env
railway variables set --from-file .env
# 
# Votre fichier .env ressemble à:
# DEBUG=True
# SECRET_KEY=abc123
# DATABASE_URL=postgresql://...
# 
# Railway lit ce fichier et crée toutes les variables automatiquement
# [ATTENTION] N'oubliez pas d'ajouter .env dans .gitignore !

# Exporter toutes les variables vers un fichier
railway variables > .env.railway
# Crée un fichier avec toutes vos variables
# Utile pour backup ou migration

# === Variables système (fournies AUTOMATIQUEMENT par Railway) ===

# Railway injecte ces variables sans que vous les créiez:

RAILWAY_ENVIRONMENT_NAME    # Nom environnement: "production", "staging", etc.
# Utilisez pour adapter comportement: if os.getenv('RAILWAY_ENVIRONMENT_NAME') == 'production'

RAILWAY_PROJECT_NAME        # Nom de votre projet: "Mon Super Projet"
# Utile pour logs: f"[{RAILWAY_PROJECT_NAME}] Starting app..."

RAILWAY_PROJECT_ID          # ID unique du projet: "a1b2c3d4-..."
# Rarement utilisé directement

RAILWAY_SERVICE_NAME        # Nom du service: "backend", "api", "worker"
# Si vous avez plusieurs services dans le projet

RAILWAY_DEPLOYMENT_ID       # ID unique de ce déploiement spécifique
# Pour tracking et debugging

RAILWAY_GIT_COMMIT_SHA      # SHA du commit Git: "7f8e9a2b..."
# Pour savoir quelle version du code tourne

RAILWAY_GIT_BRANCH          # Branche Git: "main", "develop", "feature/login"
# Pour logs: f"Running from branch {RAILWAY_GIT_BRANCH}"

RAILWAY_PUBLIC_DOMAIN       # Votre URL publique: "myapp-production.up.railway.app"
# Utilisez pour générer des URLs absolues

RAILWAY_PRIVATE_DOMAIN      # Domaine interne: "myservice.railway.internal"
# Pour communication entre services (voir section NETWORKING)

# Exemple d'utilisation dans votre code Python:
import os

# Savoir si on est en production ou développement
is_production = os.getenv('RAILWAY_ENVIRONMENT_NAME') == 'production'
DEBUG = not is_production  # Debug OFF en production, ON ailleurs

# Logger quelle version tourne
commit = os.getenv('RAILWAY_GIT_COMMIT_SHA', 'unknown')[:7]
print(f"Running version {commit}")

# === Variables de build (uniquement pendant construction) ===

# Certaines variables sont nécessaires SEULEMENT lors du build
# Pas besoin qu'elles existent au runtime

railway variables set --build NODE_ENV=production
# Node.js: optimise le build pour production

railway variables set --build PYTHON_VERSION=3.11
# Force version Python spécifique

# === Variables partagées entre services ===

# Si vous avez plusieurs services (app + worker + api):
# Les variables "Shared" sont accessibles par TOUS les services
# 
# Définir dans dashboard uniquement:
# 1. Projet -> Settings
# 2. Shared Variables
# 3. Add Variable
# 4. Tous les services voient cette variable

# Cas d'usage:
# - STRIPE_API_KEY (même clé pour app et worker)
# - SENTRY_DSN (même monitoring pour tout)
# - REDIS_URL (Redis partagé entre services)

# ASTUCE: Variables de référence (super pratique!)
# Au lieu de copier-coller, référencez directement une variable d'un autre service

railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'
# Signifie: "Utilise la variable DATABASE_URL du service Postgres"
# Si l'URL Postgres change, votre app est automatiquement mise à jour!

railway variables set REDIS_URL='${{Redis.REDIS_URL}}'
# Même chose pour Redis

# SÉCURITÉ IMPORTANTE:
# [OK] Variables chiffrées au repos dans Railway
# [OK] Transmises de manière sécurisée à votre app
# [OK] Non visibles dans les logs (Railway les masque)
# [X] Ne JAMAIS commiter dans Git (.env dans .gitignore!)
# [X] Ne JAMAIS partager dans Slack/Discord
# [X] Ne JAMAIS exposer dans votre code frontend


[OK] SERVICES

# Un SERVICE = un composant de votre architecture
# Exemples: votre app Django, votre base PostgreSQL, Redis, un worker Celery
# 
# Pensez à un SERVICE comme une "boîte" qui fait UNE chose spécifique
# Plusieurs services peuvent communiquer entre eux dans un même projet

# === Types de services ===

# 1. APPLICATION (votre code)
#    - Votre app Django/Flask/FastAPI
#    - Votre API backend
#    - Votre worker Celery
#    - Votre frontend Next.js

# 2. DATABASE (bases de données gérées par Railway)
#    - PostgreSQL (relationnel, le plus courant)
#    - MySQL (alternatif à PostgreSQL)
#    - MongoDB (NoSQL, documents JSON)
#    - Redis (cache ultra-rapide, sessions)

# 3. TEMPLATE (services pré-configurés)
#    - Services populaires prêts à l'emploi
#    - 1 clic = service fonctionnel
#    - Exemples: Meilisearch, MinIO, RabbitMQ

# === Ajouter un service depuis template ===

# Commande interactive qui vous montre tous les templates disponibles
railway add
# 
# Vous verrez une liste:
# > PostgreSQL       (Base de données relationnelle)
#   MySQL            (Alternative à PostgreSQL)
#   MongoDB          (Base NoSQL)
#   Redis            (Cache et sessions)
#   Meilisearch      (Moteur de recherche)
#   MinIO            (Stockage S3-compatible)
#   ... et bien d'autres
# 
# Utilisez les flèches ^v pour naviguer, Entrée pour sélectionner

# Templates disponibles les plus utilisés:

# PostgreSQL - LA base de données relationnelle (recommandé pour débuter)
railway add --template postgres
# Cas d'usage:
# - Applications web classiques (Django, Flask)
# - Données structurées (utilisateurs, produits, commandes)
# - Besoin de relations entre tables
# - Transactions ACID (fiabilité garantie)

# MySQL - Alternative à PostgreSQL (si vous préférez)
railway add --template mysql
# Similaire à PostgreSQL
# Utilisez si votre app est déjà en MySQL

# MongoDB - Base NoSQL (documents JSON)
railway add --template mongo
# Cas d'usage:
# - Données non structurées ou flexibles
# - Documents JSON complexes
# - Besoin de scaler horizontalement
# - Pas besoin de relations complexes

# Redis - Cache ultra-rapide en mémoire
railway add --template redis
# Cas d'usage CRITIQUES:
# - Cache de requêtes (pages, API responses)
# - Sessions utilisateur (login/logout)
# - File d'attente Celery (tasks asynchrones)
# - Rate limiting (limiter requêtes par IP)
# - Compteurs temps réel (likes, vues)
# 
# Redis est 100x plus rapide que PostgreSQL pour lecture!

# Meilisearch - Moteur de recherche moderne
railway add --template meilisearch
# Cas d'usage:
# - Barre de recherche dans votre app
# - Recherche full-text super rapide
# - Suggestions de recherche (autocompletion)
# - Filtres et facettes

# RabbitMQ - File de messages (message broker)
railway add --template rabbitmq
# Cas d'usage avancé:
# - Communication entre microservices
# - Tasks asynchrones distribuées
# - Event-driven architecture

# MinIO - Stockage compatible AWS S3
railway add --template minio
# Cas d'usage:
# - Stocker fichiers uploadés (images, PDFs)
# - Alternative gratuite à AWS S3
# - Backups automatisés

# Lister TOUS les services de votre projet actuel
railway list
# Affiche:
# [OK] backend (application)
# [OK] Postgres (database)
# [OK] Redis (cache)
# 
# Vous voyez leur état (running, crashed, building)

# Obtenir infos détaillées sur UN service spécifique
railway status --service backend
railway status --service Postgres
# Affiche:
# - État actuel (running, stopped)
# - CPU et RAM utilisés
# - Nombre de requêtes
# - Dernier déploiement

# === Networking entre services (SUPER IMPORTANT!) ===

# Vos services peuvent communiquer entre eux de 2 façons:

# MÉTHODE 1: Domaine privé interne
# Format: <NOM_SERVICE>.railway.internal
# 
# Exemples:
# - postgres.railway.internal
# - redis.railway.internal
# - backend.railway.internal
# 
# Ces domaines sont PRIVÉS = seulement accessibles entre vos services
# Pas accessible depuis Internet (sécurité!)

# Exemple Django settings.py:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'postgres.railway.internal',  # <- Domaine privé
        'PORT': '5432',
        'NAME': 'railway',
        'USER': os.getenv('PGUSER'),
        'PASSWORD': os.getenv('PGPASSWORD'),
    }
}

# Exemple connexion Redis:
REDIS_HOST = 'redis.railway.internal'  # <- Domaine privé
REDIS_PORT = 6379

# MÉTHODE 2: Variables de référence (PLUS SIMPLE!)
# Au lieu de taper manuellement host/port/user/password:
# Référencez directement la variable d'un autre service

railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'
# Signifie: "Prends la DATABASE_URL du service Postgres"
# 
# Railway remplace automatiquement par:
# postgresql://user:password@postgres.railway.internal:5432/railway

railway variables set REDIS_URL='${{Redis.REDIS_URL}}'
# Remplacé par:
# redis://:password@redis.railway.internal:6379

# AVANTAGE de la méthode 2:
# Si Railway change le mot de passe Postgres, votre app continue de fonctionner!
# Pas besoin de mettre à jour manuellement

# ARCHITECTURE TYPIQUE d'un projet Django complet:
# 
# Projet "Mon SaaS"
# ├── Service: backend (Django app)
# │   └── Variables: DATABASE_URL=${{Postgres.DATABASE_URL}}
# │                  REDIS_URL=${{Redis.REDIS_URL}}
# ├── Service: worker (Celery worker)
# │   └── Variables: DATABASE_URL=${{Postgres.DATABASE_URL}}
# │                  REDIS_URL=${{Redis.REDIS_URL}}
# ├── Service: Postgres (base de données)
# │   └── Variables: DATABASE_URL (auto-générée)
# └── Service: Redis (cache + Celery broker)
#     └── Variables: REDIS_URL (auto-générée)
# 
# Tous les services communiquent via domaines privés!

# EXEMPLE COMPLET: Ajouter PostgreSQL et Redis à votre projet
# 
# 1. Créer projet
railway init --name "Mon Projet"

# 2. Ajouter PostgreSQL
railway add --template postgres
# Railway crée automatiquement les variables:
# - PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE
# - DATABASE_URL (URL complète)

# 3. Ajouter Redis
railway add --template redis
# Railway crée:
# - REDISHOST, REDISPORT, REDISPASSWORD
# - REDIS_URL (URL complète)

# 4. Configurer votre app pour utiliser ces services
railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'
railway variables set REDIS_URL='${{Redis.REDIS_URL}}'

# 5. Déployer
railway up

# 6. Votre app peut maintenant:
#    - Lire/écrire dans PostgreSQL
#    - Utiliser Redis pour cache/sessions
#    - Tout communique en privé et sécurisé!


[OK] BASES DE DONNÉES

# Railway gère les bases de données pour vous = ZÉRO configuration serveur!
# Pas besoin d'installer PostgreSQL, MySQL, etc. sur votre machine
# Railway s'occupe de: installation, backups, mises à jour, sécurité

# === PostgreSQL (RECOMMANDÉ pour débuter) ===

# PostgreSQL = base de données relationnelle la plus populaire
# Utilisée par: Instagram, Spotify, Apple, Reddit
# 
# Quand utiliser PostgreSQL ?
# [OK] Applications web classiques (blog, e-commerce, SaaS)
# [OK] Données structurées avec relations (users -> posts -> comments)
# [OK] Besoin de transactions fiables (paiements, commandes)
# [OK] Requêtes SQL complexes avec JOIN
# [OK] C'est le choix par défaut pour 90% des projets!

# Ajouter PostgreSQL à votre projet
railway add --template postgres
# 
# Railway crée AUTOMATIQUEMENT ces variables:
PGHOST=postgres.railway.internal        # Où est le serveur PostgreSQL
PGPORT=5432                            # Port PostgreSQL (standard)
PGUSER=postgres                        # Nom d'utilisateur
PGPASSWORD=xxx                         # Mot de passe (généré aléatoirement)
PGDATABASE=railway                     # Nom de la base de données
DATABASE_URL=postgresql://postgres:xxx@postgres.railway.internal:5432/railway
# ^ URL complète, prête à l'emploi!

# Se connecter à PostgreSQL depuis votre terminal
railway connect postgres
# 
# Cette commande ouvre un shell PostgreSQL interactif
# Vous pouvez taper des requêtes SQL directement:
# 
# railway=# \dt                    -- Lister toutes les tables
# railway=# SELECT * FROM users;   -- Voir les utilisateurs
# railway=# \q                     -- Quitter

# Utiliser PostgreSQL dans votre code Python (Django):
# 
# 1. Installer le driver:
#    pip install psycopg2-binary
#    pip freeze > requirements.txt
# 
# 2. Dans settings.py:
import os
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': os.getenv('PGHOST'),
        'PORT': os.getenv('PGPORT'),
        'NAME': os.getenv('PGDATABASE'),
        'USER': os.getenv('PGUSER'),
        'PASSWORD': os.getenv('PGPASSWORD'),
    }
}
# 
# OU plus simple avec dj-database-url:
import dj_database_url
DATABASES = {
    'default': dj_database_url.config(
        default=os.getenv('DATABASE_URL'),
        conn_max_age=600  # Connection pooling
    )
}

# === MySQL ===

# MySQL = alternative à PostgreSQL (moins moderne)
# Utilisé par: Facebook, YouTube, Twitter (avant)
# 
# Utilisez MySQL seulement si:
# - Votre app existante est déjà en MySQL
# - Vous avez des raisons spécifiques
# - Sinon, préférez PostgreSQL!

# Ajouter MySQL
railway add --template mysql

# Variables créées automatiquement:
MYSQLHOST=mysql.railway.internal
MYSQLPORT=3306
MYSQLUSER=root
MYSQLPASSWORD=xxx
MYSQLDATABASE=railway
MYSQL_URL=mysql://root:xxx@mysql.railway.internal:3306/railway

# Se connecter à MySQL
railway connect mysql
# 
# Shell MySQL:
# mysql> SHOW TABLES;
# mysql> SELECT * FROM users;
# mysql> EXIT;

# === MongoDB (NoSQL) ===

# MongoDB = base de données NoSQL (documents JSON)
# Pas de tables, pas de SQL, juste des documents JSON
# 
# Quand utiliser MongoDB ?
# [OK] Données non structurées ou très flexibles
# [OK] Schéma qui change souvent
# [OK] Documents JSON complexes et imbriqués
# [OK] Besoin de scale horizontal (millions d'utilisateurs)
# [OK] Temps réel (chat, notifications)
# 
# [X] NE PAS utiliser pour:
# - Relations complexes entre données
# - Transactions financières (préférer PostgreSQL)
# - Si vous débutez (PostgreSQL est plus simple)

# Ajouter MongoDB
railway add --template mongo

# Variables créées:
MONGO_URL=mongodb://mongo:xxx@mongo.railway.internal:27017
MONGOHOST=mongo.railway.internal
MONGOPORT=27017
MONGOUSER=mongo
MONGOPASSWORD=xxx

# Se connecter à MongoDB
railway connect mongo
# 
# Shell MongoDB:
# > show dbs                       -- Lister bases
# > use railway                    -- Sélectionner base
# > db.users.find()                -- Voir documents users
# > exit

# Utiliser MongoDB en Python (avec pymongo):
from pymongo import MongoClient
import os

client = MongoClient(os.getenv('MONGO_URL'))
db = client.railway
users = db.users

# Insérer document
users.insert_one({'name': 'Alice', 'age': 25})

# Trouver documents
user = users.find_one({'name': 'Alice'})

# === Redis (CACHE ultra-rapide) ===

# Redis = base en MÉMOIRE (RAM) = super rapide!
# Redis n'est PAS pour stocker vos données principales
# C'est un COMPLÉMENT à PostgreSQL/MySQL
# 
# Redis stocke en RAM = 100x plus rapide que disque
# Mais RAM limitée et données perdues si crash (sauf persistence)
# 
# Cas d'usage ESSENTIELS:
# 
# 1. CACHE de requêtes
#    - Au lieu de requêter PostgreSQL 1000x, stocker résultat dans Redis
#    - Exemples: liste de produits, profil utilisateur, page d'accueil
#    - Gain: de 500ms à 5ms !
# 
# 2. SESSIONS utilisateur
#    - Stocker qui est connecté
#    - Token de session, préférences
#    - Django/Flask utilisent Redis pour ça
# 
# 3. CELERY (tasks asynchrones)
#    - File d'attente pour tâches longues
#    - Ex: envoyer 1000 emails, générer PDF, traiter images
#    - Redis stocke les tasks en attente
# 
# 4. RATE LIMITING
#    - Limiter à 100 requêtes/heure par IP
#    - Compteur stocké dans Redis
# 
# 5. REAL-TIME
#    - Compteurs (likes, vues)
#    - Leaderboards (classements)
#    - Chat en temps réel

# Ajouter Redis
railway add --template redis

# Variables créées:
REDISHOST=redis.railway.internal
REDISPORT=6379
REDISPASSWORD=xxx
REDIS_URL=redis://:xxx@redis.railway.internal:6379

# Se connecter à Redis
railway connect redis
# 
# Shell Redis:
# > SET mykey "Hello Railway"
# > GET mykey
# > KEYS *                         -- Voir toutes les clés
# > FLUSHALL                       -- ATTENTION: Efface tout!
# > EXIT

# Utiliser Redis en Python:
import redis
import os

# Se connecter
r = redis.from_url(os.getenv('REDIS_URL'))

# Stocker/récupérer
r.set('user:123:name', 'Alice')
name = r.get('user:123:name')

# Cache avec expiration (TTL)
r.setex('cache:products', 3600, 'data')  # Expire après 1h

# Django cache avec Redis:
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': os.getenv('REDIS_URL'),
    }
}

# Utiliser le cache:
from django.core.cache import cache

# Stocker
cache.set('products', products_list, 3600)  # 1 heure

# Récupérer
products = cache.get('products')
if not products:
    products = Product.objects.all()  # Requête DB seulement si pas en cache
    cache.set('products', products, 3600)

# === Migrations de base de données ===

# Après avoir ajouté PostgreSQL, vous devez créer les tables!
# 
# Django:
railway run python manage.py migrate
# Cette commande:
# 1. Se connecte à PostgreSQL sur Railway
# 2. Crée toutes les tables définies dans vos models
# 3. Applique les migrations

# Flask avec Flask-Migrate:
railway run flask db upgrade

# Alembic (SQLAlchemy):
railway run alembic upgrade head

# IMPORTANT: Toujours tester migrations sur STAGING avant production!
railway environment staging
railway run python manage.py migrate
# Tester que tout fonctionne
# 
# Si OK:
railway environment production
railway run python manage.py migrate

# === Créer un superuser Django ===

railway run python manage.py createsuperuser
# Vous pourrez alors:
# 1. Accéder à /admin sur votre site
# 2. Gérer vos données via interface Django Admin

# === Voir les données directement ===

# PostgreSQL: Utiliser TablePlus, pgAdmin, ou DBeaver
# Vous aurez besoin de:
# - Host: Voir dans Railway dashboard ou PGHOST
# - Port: 5432
# - Database: railway
# - User: postgres
# - Password: Voir PGPASSWORD

# ASTUCE: Railway affiche ces infos dans le dashboard
# Service Postgres -> Connect -> Database URL

# === IMPORTANT: Backups ===

# Railway ne fait PAS de backups automatiques sur plan gratuit!
# Vous devez faire vos backups manuellement (voir section BACKUP plus bas)
# 
# Ou utiliser service externe:
# - AWS RDS (backups automatiques)
# - DigitalOcean Managed Database
# - Heroku Postgres (payant mais backups inclus)

# === Architecture typique avec bases de données ===

# Projet e-commerce Django:
# 
# ┌─────────────────────────────────────┐
# │ Service: backend (Django)           │
# │ Variables: DATABASE_URL, REDIS_URL  │
# └──────────────┬──────────────────────┘
#                │
#    ┌───────────┴───────────┐
#    │                       │
#    [BLACK_DOWN-POINTING_TRIANGLE]                       [BLACK_DOWN-POINTING_TRIANGLE]
# ┌──────────────┐    ┌──────────────┐
# │ PostgreSQL   │    │ Redis        │
# │ (données)    │    │ (cache)      │
# └──────────────┘    └──────────────┘
# 
# PostgreSQL stocke: users, products, orders, payments
# Redis cache: liste produits, panier, sessions

# Projet avec Celery (tasks asynchrones):
# 
# ┌────────────────┐      ┌────────────────┐
# │ backend        │      │ worker         │
# │ (Django)       │[BLACK_LEFT-POINTING_POINTER]────[BLACK_RIGHT-POINTING_POINTER]│ (Celery)       │
# └────┬───────────┘      └────┬───────────┘
#      │                       │
#      │   ┌───────────────────┘
#      │   │
#      [BLACK_DOWN-POINTING_TRIANGLE]   [BLACK_DOWN-POINTING_TRIANGLE]
# ┌────────────────┐    ┌──────────────┐
# │ PostgreSQL     │    │ Redis        │
# │ (DB principale)│    │ (task queue) │
# └────────────────┘    └──────────────┘
# 
# Backend: crée tasks (envoi emails, PDF)
# Redis: stocke tasks en attente
# Worker: exécute tasks depuis Redis
# PostgreSQL: stocke résultats


[OK] CONFIGURATION BUILD

# Le BUILD = transformation de votre code en application fonctionnelle
# Railway doit savoir COMMENT builder et lancer votre app
# 
# Par défaut, Railway devine automatiquement (via Nixpacks)
# Mais vous pouvez personnaliser avec railway.json ou railway.toml

# === railway.json (RECOMMANDÉ) ===

# Créez ce fichier à la RACINE de votre projet
# À côté de requirements.txt, manage.py, etc.

# Configuration COMPLÈTE pour une app Django:
{
  "$schema": "https://railway.app/railway.schema.json",
  "build": {
    "builder": "NIXPACKS",                              # Détection auto (défaut)
    "buildCommand": "pip install -r requirements.txt"   # Commande pour installer dépendances
  },
  "deploy": {
    "startCommand": "gunicorn myapp.wsgi:application --bind 0.0.0.0:$PORT",  # Comment lancer l'app
    "restartPolicyType": "ON_FAILURE",                  # Redémarre si crash
    "restartPolicyMaxRetries": 10,                      # Max 10 tentatives
    "healthcheckPath": "/health",                       # URL pour vérifier que app fonctionne
    "healthcheckTimeout": 100                           # Timeout en secondes
  }
}

# EXPLICATION détaillée de chaque option:

# $schema: Active l'autocomplétion dans VSCode
# Optionnel mais pratique

# builder: Quel système utiliser pour builder
# - NIXPACKS: Détection auto (Python, Node, Go, etc.)
# - DOCKERFILE: Utilise votre Dockerfile
# - PAKETO: Buildpacks Cloud Native (avancé)

# buildCommand: Commande AVANT de lancer l'app
# Exemples:
# - "pip install -r requirements.txt" -> Installe dépendances Python
# - "npm install" -> Installe dépendances Node
# - "python manage.py collectstatic --noinput" -> Collecte fichiers statiques Django

# startCommand: Comment LANCER votre app
# CRITIQUE! Si mal configuré, l'app ne démarre pas
# 
# Django avec Gunicorn:
# "gunicorn myapp.wsgi:application --bind 0.0.0.0:$PORT"
# ^ myapp = votre dossier avec wsgi.py
# ^ $PORT = Railway fournit le port automatiquement
# 
# Flask avec Gunicorn:
# "gunicorn app:app --bind 0.0.0.0:$PORT"
# ^ app = nom de votre fichier app.py
# ^ app = nom de votre variable Flask app = Flask(__name__)
# 
# FastAPI avec Uvicorn:
# "uvicorn main:app --host 0.0.0.0 --port $PORT"
# ^ main = votre fichier main.py
# ^ app = votre variable FastAPI app = FastAPI()

# restartPolicyType: Que faire si l'app crash ?
# - "ON_FAILURE": Redémarre automatiquement (RECOMMANDÉ)
# - "ALWAYS": Redémarre toujours, même arrêt volontaire
# - "NEVER": Ne redémarre jamais (déconseillé)

# restartPolicyMaxRetries: Nombre max de redémarrages
# Si l'app crash 10 fois, Railway arrête d'essayer
# Évite la boucle infinie crash -> restart -> crash

# healthcheckPath: URL pour vérifier santé de l'app
# Railway fait des requêtes GET à cette URL
# Si réponse 200 OK -> App en bonne santé
# Si timeout ou erreur -> App malade
# 
# Créez cet endpoint dans votre app:
# 
# Django (views.py):
from django.http import JsonResponse

def health(request):
    return JsonResponse({'status': 'healthy'})

# urls.py:
urlpatterns = [
    path('health', health),
]

# Flask:
@app.route('/health')
def health():
    return {'status': 'healthy'}, 200

# FastAPI:
@app.get("/health")
def health():
    return {"status": "healthy"}

# healthcheckTimeout: Temps max d'attente (secondes)
# Si l'app ne répond pas en 100s -> considérée morte

# === railway.toml (format alternatif) ===

# Même chose que JSON mais en format TOML
# Certains préfèrent TOML car plus lisible

[build]
builder = "NIXPACKS"
buildCommand = "pip install -r requirements.txt"

[deploy]
startCommand = "gunicorn myapp.wsgi:application --bind 0.0.0.0:$PORT"
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 10
healthcheckPath = "/health"
healthcheckTimeout = 100

# === Builders disponibles ===

# NIXPACKS (défaut - RECOMMANDÉ pour débuter)
# - Détecte automatiquement Python, Node, Go, Rust, etc.
# - Installe dépendances automatiquement
# - Configure l'environnement
# - 90% du temps, vous n'avez rien à faire!
{
  "build": {
    "builder": "NIXPACKS"
  }
}

# DOCKERFILE (si vous avez déjà un Dockerfile)
# - Vous contrôlez TOUT le build
# - Plus complexe mais plus flexible
{
  "build": {
    "builder": "DOCKERFILE",
    "dockerfilePath": "Dockerfile"           # Chemin vers votre Dockerfile
  }
}
# Si votre Dockerfile s'appelle "Dockerfile.prod":
{
  "build": {
    "builder": "DOCKERFILE",
    "dockerfilePath": "Dockerfile.prod"
  }
}

# PAKETO (avancé - buildpacks Cloud Native)
# Utilisé par Cloud Foundry, Heroku
# Rarement nécessaire, Nixpacks suffit généralement

# === Build command (installation dépendances) ===

# Python avec requirements.txt:
{
  "build": {
    "buildCommand": "pip install -r requirements.txt"
  }
}

# Python avec Poetry:
{
  "build": {
    "buildCommand": "poetry install --no-dev"
  }
}

# Node.js:
{
  "build": {
    "buildCommand": "npm install && npm run build"
  }
}

# MULTIPLE commandes (avec &&):
{
  "build": {
    "buildCommand": "pip install -r requirements.txt && python manage.py collectstatic --noinput"
  }
}
# ^ Installe dépendances ET collecte fichiers statiques Django
# && = exécute commande suivante seulement si précédente réussit

# === Start command (lancer l'app) ===

# Django production avec Gunicorn:
{
  "deploy": {
    "startCommand": "gunicorn myapp.wsgi:application --bind 0.0.0.0:$PORT --workers 4"
  }
}
# --workers 4 = 4 processus parallèles (meilleure performance)
# Règle: workers = (2 × CPU cores) + 1
# Railway gratuit: 1 vCPU -> 3 workers recommandés

# FastAPI avec Uvicorn et workers:
{
  "deploy": {
    "startCommand": "uvicorn main:app --host 0.0.0.0 --port $PORT --workers 4"
  }
}

# Flask development (NE PAS utiliser en production):
{
  "deploy": {
    "startCommand": "python app.py"
  }
}
# [ATTENTION] flask run est trop lent pour production!
# Utilisez Gunicorn:
{
  "deploy": {
    "startCommand": "gunicorn app:app --bind 0.0.0.0:$PORT --workers 3"
  }
}

# Migrations AVANT de lancer (Django):
{
  "deploy": {
    "startCommand": "python manage.py migrate && gunicorn myapp.wsgi --bind 0.0.0.0:$PORT"
  }
}
# ^ Applique migrations PUIS lance Gunicorn
# Pratique pour automatiser!

# === Restart Policy (politique de redémarrage) ===

# ON_FAILURE - Redémarre seulement si crash (RECOMMANDÉ)
{
  "deploy": {
    "restartPolicyType": "ON_FAILURE",
    "restartPolicyMaxRetries": 10
  }
}
# Cas d'usage: App production normale
# Si bug -> crash -> redémarre automatiquement
# Vous avez le temps de fixer le bug

# ALWAYS - Redémarre toujours (même si arrêt manuel)
{
  "deploy": {
    "restartPolicyType": "ALWAYS"
  }
}
# Cas d'usage: Services critiques qui ne doivent JAMAIS s'arrêter
# Utilisez rarement, peut masquer des bugs

# NEVER - Ne redémarre jamais
{
  "deploy": {
    "restartPolicyType": "NEVER"
  }
}
# Cas d'usage: Scripts one-shot, migrations, cron jobs
# Pas pour apps web normales!

# === EXEMPLES COMPLETS par framework ===

# Django complet (railway.json):
{
  "build": {
    "builder": "NIXPACKS",
    "buildCommand": "pip install -r requirements.txt && python manage.py collectstatic --noinput"
  },
  "deploy": {
    "startCommand": "python manage.py migrate && gunicorn myproject.wsgi --bind 0.0.0.0:$PORT --workers 3",
    "restartPolicyType": "ON_FAILURE",
    "restartPolicyMaxRetries": 10,
    "healthcheckPath": "/health",
    "healthcheckTimeout": 100
  }
}

# FastAPI complet:
{
  "build": {
    "builder": "NIXPACKS",
    "buildCommand": "pip install -r requirements.txt"
  },
  "deploy": {
    "startCommand": "uvicorn main:app --host 0.0.0.0 --port $PORT --workers 3",
    "restartPolicyType": "ON_FAILURE",
    "healthcheckPath": "/health",
    "healthcheckTimeout": 100
  }
}

# Flask complet:
{
  "build": {
    "builder": "NIXPACKS",
    "buildCommand": "pip install -r requirements.txt"
  },
  "deploy": {
    "startCommand": "gunicorn app:app --bind 0.0.0.0:$PORT --workers 3 --timeout 120",
    "restartPolicyType": "ON_FAILURE",
    "healthcheckPath": "/health"
  }
}

# Next.js:
{
  "build": {
    "builder": "NIXPACKS",
    "buildCommand": "npm install && npm run build"
  },
  "deploy": {
    "startCommand": "npm start",
    "restartPolicyType": "ON_FAILURE"
  }
}

# ASTUCE: Si railway.json absent
# Railway utilise Nixpacks qui détecte automatiquement
# Mais mieux vaut être explicite avec railway.json!


[OK] NIXPACKS (DÉTECTION AUTO)

# Railway utilise Nixpacks par défaut pour détecter automatiquement
# le type d'application et builder approprié

# === Python ===

# Détection via:
# - requirements.txt
# - Pipfile
# - pyproject.toml
# - setup.py

# Build automatique:
pip install -r requirements.txt

# Start automatique (détecté):
# - Django: gunicorn myapp.wsgi
# - Flask: gunicorn app:app
# - FastAPI: uvicorn main:app

# Personnaliser avec nixpacks.toml

[phases.setup]
nixPkgs = ['python311', 'postgresql']

[phases.install]
cmds = ['pip install -r requirements.txt']

[phases.build]
cmds = ['python manage.py collectstatic --noinput']

[start]
cmd = 'gunicorn myapp.wsgi --bind 0.0.0.0:$PORT'

# === Node.js ===

# Détection via:
# - package.json

# Build automatique:
npm install

# Start automatique:
# - npm start
# - node index.js

# === Autres langages supportés ===

# Go, Rust, Ruby, PHP, Java, .NET, etc.


[OK] DOCKERFILE PERSONNALISÉ

# Si railway.json spécifie DOCKERFILE builder

# Exemple Dockerfile pour Django

FROM python:3.11-slim

WORKDIR /app

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

# Copy project
COPY . .

# Collect static files
RUN python manage.py collectstatic --noinput

# Expose port
EXPOSE 8000

# Start command
CMD ["gunicorn", "myapp.wsgi:application", "--bind", "0.0.0.0:8000"]

# railway.json
{
  "build": {
    "builder": "DOCKERFILE",
    "dockerfilePath": "Dockerfile"
  }
}

# Exemple Dockerfile pour FastAPI

FROM python:3.11-slim

WORKDIR /app

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

COPY . .

EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]


[OK] DOMAINES ET NETWORKING

# Après déploiement, votre app a besoin d'une URL pour être accessible!
# Railway fournit des domaines automatiquement + vous pouvez utiliser votre propre domaine

# === Domaines Railway (automatiques et GRATUITS) ===

# Chaque service reçoit DEUX types de domaines:

# 1. DOMAINE PUBLIC (accessible depuis Internet)
# Format: <service>-<project>-<environment>.up.railway.app
# Exemples:
# - backend-monprojet-production.up.railway.app
# - api-ecommerce-staging.up.railway.app
# 
# Ce domaine est HTTPS automatique (certificat SSL gratuit)
# Vous pouvez le partager avec vos utilisateurs

# 2. DOMAINE PRIVÉ (seulement entre vos services)
# Format: <service>.railway.internal
# Exemples:
# - postgres.railway.internal
# - redis.railway.internal
# - backend.railway.internal
# 
# Ces domaines sont INVISIBLES depuis Internet
# Seulement vos services Railway peuvent les voir
# Parfait pour sécurité (DB pas exposée publiquement)

# Activer le domaine public pour un service
railway domain
# Cette commande:
# 1. Génère un domaine .up.railway.app
# 2. Active HTTPS automatiquement (Let's Encrypt)
# 3. Affiche l'URL dans le terminal
# 
# Résultat: "[OK] Domain created: backend-production.up.railway.app"

# === Domaines personnalisés (votre propre nom) ===

# Vous avez acheté www.monsite.com et voulez l'utiliser ?
# Railway rend ça super simple!

# ÉTAPE 1: Ajouter domaine dans Railway dashboard
# 1. Ouvrir https://railway.app
# 2. Sélectionner votre projet
# 3. Cliquer sur le service (ex: backend)
# 4. Onglet "Settings"
# 5. Section "Domains"
# 6. Cliquer "Add Custom Domain"
# 7. Entrer: www.monsite.com (ou monsite.com)
# 8. Cliquer "Add"
# 
# Railway vous donne maintenant un TARGET à configurer dans votre DNS

# ÉTAPE 2: Configurer DNS chez votre registrar
# (Registrar = où vous avez acheté le domaine: Namecheap, GoDaddy, OVH, etc.)
# 
# Vous devez créer un enregistrement CNAME:
# 
# Type:   CNAME
# Name:   www (ou @ pour domaine racine)
# Value:  backend-production.up.railway.app (fourni par Railway)
# TTL:    Auto ou 3600
# 
# Exemple concret:
# Nom:     www.monsite.com
# Pointe vers: backend-production.up.railway.app
# 
# Résultat: www.monsite.com -> Railway -> Votre app!

# CONFIGURATIONS DNS courantes:

# Pour www.monsite.com:
# Type: CNAME
# Name: www
# Value: backend-production.up.railway.app

# Pour monsite.com (sans www, domaine racine):
# ATTENTION: CNAME ne marche pas pour domaine racine!
# Solution 1: ALIAS record (si votre registrar supporte)
# Type: ALIAS
# Name: @
# Value: backend-production.up.railway.app
# 
# Solution 2: Redirection www -> non-www
# La plupart des registrars offrent cette option
# monsite.com -> redirige vers -> www.monsite.com

# Pour sous-domaine (api.monsite.com):
# Type: CNAME
# Name: api
# Value: backend-production.up.railway.app

# ÉTAPE 3: Attendre propagation DNS
# Peut prendre de 5 minutes à 48 heures (généralement < 1h)
# Vérifier avec:
dig www.monsite.com
nslookup www.monsite.com
# 
# Quand propagé, vous verrez:
# www.monsite.com CNAME backend-production.up.railway.app

# ÉTAPE 4: Railway génère certificat SSL automatiquement!
# Let's Encrypt gratuit
# Renouvellement automatique tous les 90 jours
# Vous n'avez RIEN à faire!
# 
# Résultat: https://www.monsite.com fonctionne avec cadenas vert [VERROUILLE]

# === Redirection HTTP -> HTTPS ===

# Railway redirige AUTOMATIQUEMENT http:// vers https://
# Vous n'avez rien à configurer!
# 
# http://www.monsite.com -> redirige vers -> https://www.monsite.com

# === Networking privé (communication entre services) ===

# VOS SERVICES communiquent via domaines PRIVÉS
# Personne sur Internet ne peut y accéder!

# Architecture typique:
# 
# Internet
#    v
# [Frontend: www.monsite.com] (public)
#    v (appelle)
# [Backend: backend.railway.internal] (privé)
#    v (se connecte)
# [PostgreSQL: postgres.railway.internal] (privé)
# [Redis: redis.railway.internal] (privé)
# 
# Seul Frontend est accessible publiquement
# Backend, DB, Redis sont INVISIBLES depuis Internet

# Exemple Django settings.py avec domaines privés:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'postgres.railway.internal',  # <- Domaine privé
        'PORT': '5432',
        'NAME': os.getenv('PGDATABASE'),
        'USER': os.getenv('PGUSER'),
        'PASSWORD': os.getenv('PGPASSWORD'),
    }
}

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://redis.railway.internal:6379',  # <- Domaine privé
    }
}

# OU plus simple avec variables de référence:
DATABASES = {
    'default': dj_database_url.config(
        default=os.getenv('DATABASE_URL')  # <- Variable auto-générée par Railway
    )
}

# === Multiples domaines pour un service ===

# Vous pouvez avoir plusieurs domaines pointant vers le MÊME service:
# - www.monsite.com
# - monsite.com
# - app.monsite.com
# - api.monsite.com
# 
# Ajoutez-les tous dans Railway dashboard
# Configurez CNAME pour chacun dans votre DNS

# === Redirection apex/www ===

# Beaucoup de sites redirigent:
# monsite.com -> www.monsite.com (ou inverse)
# 
# Configurez dans votre registrar (pas dans Railway)
# Ou utilisez middleware dans votre app:

# Django middleware pour forcer www:
class WWWRedirectMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        host = request.get_host()
        if not host.startswith('www.'):
            return redirect(f'https://www.{host}{request.path}', permanent=True)
        return self.get_response(request)

# === Domaines par environnement ===

# Bonne pratique: domaines différents par environnement
# 
# Production:  www.monsite.com
# Staging:     staging.monsite.com
# Development: dev.monsite.com
# 
# Configuration DNS:
# Type: CNAME, Name: staging, Value: backend-staging.up.railway.app
# Type: CNAME, Name: dev, Value: backend-development.up.railway.app

# === Problèmes courants DNS ===

# 1. "ERR_NAME_NOT_RESOLVED"
# -> DNS pas encore propagé (attendre 1h)
# -> Ou mauvais CNAME (vérifier dans registrar)

# 2. "Certificate invalid"
# -> Railway génère SSL automatiquement
# -> Attendre 10-15 minutes après ajout domaine
# -> Si persiste, supprimer domaine et re-ajouter

# 3. "Too many redirects"
# -> Problème avec proxy (ex: Cloudflare)
# -> Vérifier SSL mode dans Cloudflare = Full (not Flexible)

# 4. Domaine ne pointe pas vers Railway
# -> Vérifier CNAME avec: dig www.monsite.com
# -> Doit montrer: CNAME backend-production.up.railway.app


[OK] LOGS ET MONITORING

# Les LOGS = ce qui se passe dans votre app en temps réel
# Essentiel pour debugging: voir erreurs, requêtes, comportement

# === Logs en temps réel (depuis CLI) ===

# Afficher les logs actuels
railway logs
# Affiche les derniers logs (environ 100 lignes)
# Vous verrez:
# - Requêtes HTTP (GET /api/users, POST /login)
# - Erreurs Python (exceptions, stack traces)
# - Print statements de votre code
# - Warnings système

# Suivre les logs en CONTINU (comme tail -f)
railway logs --follow
railway logs -f
# 
# Mode LIVE: les logs apparaissent en temps réel
# Vous voyez IMMÉDIATEMENT quand:
# - Un utilisateur fait une requête
# - Une erreur se produit
# - Votre app démarre/crash
# 
# Pour arrêter: Ctrl+C

# Logs d'un service spécifique (si plusieurs services)
railway logs --service backend
railway logs --service worker
railway logs --service api
# 
# Utile quand vous avez:
# - backend (Django)
# - worker (Celery)
# - api (FastAPI)
# 
# Vous isolez les logs du service qui vous intéresse

# Filtrer logs par déploiement
railway logs --deployment <DEPLOYMENT_ID>
# Chaque déploiement a un ID unique
# Voir ID dans dashboard ou avec: railway status
# 
# Cas d'usage: comparer logs avant/après un déploiement

# Logs depuis un certain temps
railway logs --since 1h     # Dernière heure
railway logs --since 30m    # Derniers 30 minutes
railway logs --since 24h    # Dernières 24 heures
# 
# Pratique pour investiguer un bug qui s'est produit plus tôt

# === Dashboard logs (interface web) ===

# Les logs sont AUSSI disponibles sur le dashboard web
# Plus confortable visuellement que la CLI

# Accès:
# 1. Ouvrir https://railway.app
# 2. Sélectionner votre projet
# 3. Cliquer sur le service
# 4. Onglet "Deployments"
# 5. Sélectionner un déploiement
# 6. Onglet "Logs"

# Fonctionnalités dashboard:
# 
# [OK] Filtres par niveau:
#   - Info (requêtes normales)
#   - Warning (alertes non critiques)
#   - Error (erreurs à fixer)
# 
# [OK] Recherche textuelle:
#   - Chercher "user@example.com"
#   - Trouver toutes mentions d'un user
# 
# [OK] Filtres par timestamp:
#   - Voir logs d'une période précise
#   - "Between 14:00 and 15:00"
# 
# [OK] Copier/partager logs:
#   - Sélectionner des lignes
#   - Copier pour partager avec équipe

# === LOGGER correctement dans votre app ===

# Python: Utiliser logging (pas print!)
import logging

# Configurer logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

logger = logging.getLogger(__name__)

# Dans votre code:
logger.info("User logged in: user@example.com")
logger.warning("Slow query detected: 2.5s")
logger.error("Payment failed", exc_info=True)

# Django: Configurer dans settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'INFO',
    },
}

# IMPORTANT: Forcer flush des logs
# Sinon logs peuvent être bufferisés et apparaître en retard
import sys
print("Message important", flush=True)

# Ou configurer Python pour unbuffered output
# Dans railway.json:
{
  "deploy": {
    "startCommand": "python -u manage.py runserver"
  }
}
# Le flag -u = unbuffered

# === Metrics (statistiques de performance) ===

# Railway affiche automatiquement des METRICS dans le dashboard
# Pas besoin de configuration!

# Metrics disponibles:
# 
# 1. CPU USAGE (utilisation processeur)
#    - Graphique en temps réel
#    - % utilisé (0-100%)
#    - Si toujours à 100% -> besoin de plus de CPU
# 
# 2. MEMORY USAGE (utilisation RAM)
#    - MB utilisés / MB totaux
#    - Si proche du max -> risque crash (OOM)
#    - Graphique historique
# 
# 3. NETWORK I/O (entrées/sorties réseau)
#    - Données reçues (inbound)
#    - Données envoyées (outbound)
#    - En MB/s
# 
# 4. REQUEST RATE (nombre de requêtes)
#    - Requêtes HTTP par seconde
#    - Voir pics de trafic
#    - Identifier problèmes de performance
# 
# 5. RESPONSE TIME (temps de réponse)
#    - Temps moyen par requête
#    - P95, P99 (percentiles)
#    - Si > 1s -> optimiser code

# Accès aux metrics:
# Dashboard -> Service -> Onglet "Metrics"
# 
# Vous voyez graphiques pour:
# - Dernière heure
# - Dernières 24 heures
# - Derniers 7 jours

# === Monitoring externe (pour production) ===

# Railway fournit des metrics basiques
# Pour production sérieuse, utilisez outils spécialisés:

# 1. SENTRY (error tracking - RECOMMANDÉ!)
# 
# Sentry capture TOUTES vos erreurs Python automatiquement
# Vous recevez email/Slack quand erreur se produit
# 
# Installation:
pip install sentry-sdk
pip freeze > requirements.txt

# Configuration Django (settings.py):
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

sentry_sdk.init(
    dsn=os.getenv('SENTRY_DSN'),  # Depuis Sentry.io
    integrations=[DjangoIntegration()],
    traces_sample_rate=1.0,  # 100% des requêtes
    send_default_pii=True,   # Envoyer données utilisateur
)

# Ajouter variable Railway:
railway variables set SENTRY_DSN=https://xxx@sentry.io/xxx
# 
# RÉSULTAT: Toutes erreurs Python -> Sentry -> Vous êtes notifié!

# 2. DataDog (APM - Application Performance Monitoring)
# 
# Monitoring avancé: CPU, RAM, requêtes DB, temps réponse
# Très complet mais payant (~$30/mois)

# 3. New Relic (APM alternatif)
# 
# Similaire à DataDog
# Bon pour grosses apps avec beaucoup de trafic

# 4. LogDNA / Logtail (logs centralisés)
# 
# Garde vos logs plus longtemps que Railway
# Recherche avancée, alertes, analytics

# 5. UptimeRobot (monitoring uptime)
# 
# Vérifie que votre site est en ligne
# Ping toutes les 5 minutes
# Email si site down
# GRATUIT!
# 
# Configuration:
# 1. Créer compte: https://uptimerobot.com
# 2. Add Monitor
# 3. URL: https://votre-app.up.railway.app/health
# 4. Interval: 5 minutes
# 5. Ajouter votre email
# 
# Vous êtes notifié si app crash!

# === Alertes personnalisées ===

# Vous pouvez créer alertes dans votre code:

# Envoyer email si erreur critique:
from django.core.mail import mail_admins

try:
    process_payment(user, amount)
except PaymentError as e:
    logger.error(f"Payment failed: {e}")
    mail_admins(
        "URGENT: Payment Failed",
        f"User {user.email} payment of ${amount} failed",
        fail_silently=True
    )

# Envoyer à Slack:
import requests

def send_slack_alert(message):
    webhook_url = os.getenv('SLACK_WEBHOOK_URL')
    requests.post(webhook_url, json={'text': message})

# Dans votre code:
if database_lag > 5:  # seconds
    send_slack_alert(f"[ATTENTION] Database lag: {database_lag}s")

# === Debugging en production ===

# NE JAMAIS utiliser DEBUG=True en production!
# Expose infos sensibles: variables, code source, stack traces
# 
# À la place:

# 1. Utiliser Sentry pour capturer erreurs
# 2. Logger abondamment:
logger.info(f"Processing order {order_id}")
logger.debug(f"User cart: {cart}")
logger.error(f"Failed to charge card", exc_info=True)

# 3. Utiliser railway run pour commands debug:
railway run python manage.py shell
# Ouvrir shell Python avec accès à votre DB
# Investiguer données directement

railway run python manage.py dbshell
# Ouvrir shell PostgreSQL
# Faire requêtes SQL manuelles

# === Performance monitoring (avancé) ===

# Mesurer performance de votre code:

# Django Debug Toolbar (développement uniquement!)
# NE PAS utiliser en production
if DEBUG:
    INSTALLED_APPS += ['debug_toolbar']

# Django Silk (profiling production)
# Enregistre temps d'exécution de chaque requête
pip install django-silk

INSTALLED_APPS = [
    'silk',
]

MIDDLEWARE = [
    'silk.middleware.SilkyMiddleware',
]

# Accès: https://votre-app.com/silk/

# Flask: flask-profiler
pip install flask_profiler

from flask_profiler import Profiler
profiler = Profiler()
profiler.init_app(app)

# ASTUCE: Logs structurés (JSON)
# Plus facile à parser automatiquement

import json
import logging

class JsonFormatter(logging.Formatter):
    def format(self, record):
        return json.dumps({
            'timestamp': record.created,
            'level': record.levelname,
            'message': record.getMessage(),
            'module': record.module,
        })

handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logger.addHandler(handler)

# Résultat dans logs:
# {"timestamp": 1234567890, "level": "INFO", "message": "User logged in"}


[OK] COMMANDES CLI

# === Exécution de commandes ===

# Exécuter commande dans service
railway run <command>

# Exemples:
railway run python manage.py migrate
railway run python manage.py createsuperuser
railway run npm run seed
railway run bundle exec rake db:migrate

# Exécuter dans environnement spécifique
railway run --environment production python manage.py migrate

# Exécuter dans service spécifique
railway run --service api python manage.py shell

# === Shell interactif ===

# Ouvrir shell dans service
railway shell

# Exemples:
railway shell
# Puis dans le shell:
python manage.py shell
rails console
npm run repl

# === Environnement local ===

# Charger variables Railway localement
railway run python app.py

# Exécute app.py avec toutes les variables d'environnement Railway


[OK] WORKFLOWS COURANTS

# === Déployer application Django ===

# 1. Préparer projet
# requirements.txt
django>=4.2
gunicorn>=21.0
psycopg2-binary>=2.9
python-decouple>=3.8

# settings.py
import os
from decouple import config

DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = ['*']  # Ou domaine spécifique

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': config('PGHOST'),
        'PORT': config('PGPORT', cast=int),
        'NAME': config('PGDATABASE'),
        'USER': config('PGUSER'),
        'PASSWORD': config('PGPASSWORD'),
    }
}

# Ou avec DATABASE_URL:
import dj_database_url
DATABASES = {
    'default': dj_database_url.config(conn_max_age=600)
}

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

# 2. Créer railway.json
{
  "build": {
    "builder": "NIXPACKS"
  },
  "deploy": {
    "startCommand": "python manage.py migrate && python manage.py collectstatic --noinput && gunicorn myproject.wsgi --bind 0.0.0.0:$PORT",
    "restartPolicyType": "ON_FAILURE"
  }
}

# 3. Initialiser Railway
railway init
railway add --template postgres

# 4. Configurer variables
railway variables set DEBUG=False
railway variables set SECRET_KEY=your-secret-key
railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'

# 5. Déployer
railway up

# 6. Créer superuser
railway run python manage.py createsuperuser

# === Déployer application FastAPI ===

# 1. Préparer projet
# requirements.txt
fastapi>=0.104
uvicorn[standard]>=0.24
sqlalchemy>=2.0
psycopg2-binary>=2.9

# main.py
from fastapi import FastAPI
import os

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello Railway"}

@app.get("/health")
def health():
    return {"status": "healthy"}

# 2. railway.json
{
  "deploy": {
    "startCommand": "uvicorn main:app --host 0.0.0.0 --port $PORT",
    "healthcheckPath": "/health"
  }
}

# 3. Déployer
railway init
railway add --template postgres
railway up

# === Déployer application Flask ===

# requirements.txt
flask>=3.0
gunicorn>=21.0

# app.py
from flask import Flask
import os

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello Railway!'

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

if __name__ == '__main__':
    port = int(os.getenv('PORT', 5000))
    app.run(host='0.0.0.0', port=port)

# railway.json
{
  "deploy": {
    "startCommand": "gunicorn app:app --bind 0.0.0.0:$PORT"
  }
}

# === Déployer application Node.js ===

# package.json
{
  "scripts": {
    "start": "node index.js"
  }
}

# index.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({ message: 'Hello Railway' });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

# Déployer
railway init
railway up


[OK] CRON JOBS

# Railway supporte cron jobs via configuration

# railway.json
{
  "build": {
    "builder": "NIXPACKS"
  },
  "deploy": {
    "startCommand": "python manage.py runcrons",
    "cronJobs": [
      {
        "schedule": "0 0 * * *",
        "command": "python manage.py cleanup"
      },
      {
        "schedule": "*/15 * * * *",
        "command": "python manage.py send_emails"
      }
    ]
  }
}

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

# Exemples de schedules:
# "0 0 * * *"        - Tous les jours à minuit
# "*/15 * * * *"     - Toutes les 15 minutes
# "0 */2 * * *"      - Toutes les 2 heures
# "0 9 * * 1"        - Chaque lundi à 9h
# "0 0 1 * *"        - Premier jour de chaque mois


[OK] VOLUMES ET STORAGE

# Par défaut, Railway utilise STOCKAGE ÉPHÉMÈRE
# Éphémère = temporaire, perdu au redéploiement
# 
# Exemple:
# 1. Utilisateur uploade photo.jpg
# 2. Photo sauvegardée dans /app/uploads/photo.jpg
# 3. Vous déployez une mise à jour
# 4. [X] photo.jpg est PERDUE!
# 
# C'est normal! Les containers Railway sont "stateless"

# === Volumes persistants (stockage permanent) ===

# Un VOLUME = disque dur virtuel qui survit aux redéploiements
# Les fichiers restent même après redémarrage/redéploiement

# ÉTAPE 1: Créer volume via dashboard
# (Pas de commande CLI pour créer volumes)
# 
# 1. Ouvrir https://railway.app
# 2. Sélectionner votre projet
# 3. Cliquer sur le service (ex: backend)
# 4. Onglet "Settings"
# 5. Section "Volumes"
# 6. Cliquer "Add Volume"
# 7. Donner un nom: "app-data"
# 8. Spécifier mount path: /data

# ÉTAPE 2: Configurer mount path
# Mount path = où le volume est accessible dans votre app
# 
# Exemples de mount paths courants:
# /data         -> Stockage général
# /app/uploads  -> Fichiers uploadés
# /app/media    -> Media files Django
# /app/storage  -> Stockage custom

# EXEMPLE: Django avec media files (uploads utilisateur)

# settings.py
import os

MEDIA_URL = '/media/'
MEDIA_ROOT = '/data/media'  # <- Volume monté sur /data
# 
# Maintenant tous les uploads vont dans /data/media
# Ce dossier PERSISTE entre déploiements!

# urls.py (développement)
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # vos URLs
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

# railway.json (spécifier volume)
{
  "deploy": {
    "startCommand": "gunicorn myapp.wsgi --bind 0.0.0.0:$PORT",
    "volumes": [
      {
        "mountPath": "/data",
        "name": "app-data"
      }
    ]
  }
}

# EXEMPLE: Stockage de fichiers uploadés

# Flask avec uploads
import os
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = '/data/uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        return 'No file', 400
    
    file = request.files['file']
    if file.filename == '':
        return 'No selected file', 400
    
    filename = secure_filename(file.filename)
    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    file.save(filepath)
    
    return f'File saved to {filepath}', 200

# Créer dossier au démarrage si n'existe pas
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

# === IMPORTANT: Limitations volumes ===

# [ATTENTION] Volumes Railway ont des limitations:
# 
# 1. TAILLE limitée (selon plan)
#    - Plan gratuit: 1 GB
#    - Plan payant: jusqu'à 100 GB
#    - Si dépassé, app peut crasher!
# 
# 2. PERFORMANCE
#    - Volumes sont sur disque (pas SSD ultra-rapide)
#    - Pour performance, utilisez Redis (cache en RAM)
# 
# 3. PAS DE BACKUP automatique
#    - Railway ne sauvegarde pas vos volumes!
#    - Vous devez faire backups manuellement
# 
# 4. UN SEUL volume par service
#    - Vous ne pouvez pas monter plusieurs volumes
#    - Solution: utilisez sous-dossiers dans le volume

# === Stockage externe (RECOMMANDÉ pour production) ===

# Pour production, NE PAS utiliser volumes Railway!
# Utilisez services cloud spécialisés:

# 1. AWS S3 (RECOMMANDÉ - le plus populaire)
# 
# Avantages:
# [OK] Illimité (payez ce que vous utilisez)
# [OK] Ultra fiable (99.999999999% durability)
# [OK] CDN intégré (CloudFront)
# [OK] Backups automatiques
# [OK] Accès depuis plusieurs services
# 
# Prix: ~$0.023/GB/mois (très cheap!)
# Premier 5 GB gratuit pendant 12 mois

# Installation Django avec S3:
pip install django-storages boto3
pip freeze > requirements.txt

# settings.py
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME')
AWS_S3_REGION_NAME = 'us-east-1'
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'

# Stockage media sur S3
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/'

# Variables Railway:
railway variables set AWS_ACCESS_KEY_ID=AKIAXXXXXXXX
railway variables set AWS_SECRET_ACCESS_KEY=xxxxxxxxxx
railway variables set AWS_STORAGE_BUCKET_NAME=mon-bucket

# Maintenant tous les uploads vont automatiquement sur S3!

# 2. Cloudinary (Images/Vidéos - SUPER SIMPLE!)
# 
# Spécialisé pour images et vidéos
# Transformations automatiques (resize, crop, optimize)
# CDN ultra-rapide mondial
# 
# Plan gratuit: 25 GB storage, 25 GB bandwidth/mois
# Parfait pour portfolios, blogs, petits e-commerce

pip install cloudinary django-cloudinary-storage

# settings.py
CLOUDINARY_STORAGE = {
    'CLOUD_NAME': os.getenv('CLOUDINARY_CLOUD_NAME'),
    'API_KEY': os.getenv('CLOUDINARY_API_KEY'),
    'API_SECRET': os.getenv('CLOUDINARY_API_SECRET'),
}

DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'

# Variables Railway:
railway variables set CLOUDINARY_CLOUD_NAME=your_cloud_name
railway variables set CLOUDINARY_API_KEY=your_api_key
railway variables set CLOUDINARY_API_SECRET=your_api_secret

# 3. Backblaze B2 (Alternative S3 moins chère)
# 
# Compatible S3 API
# 3x moins cher que AWS S3!
# 10 GB gratuit
# 
# Parfait si budget serré

# 4. DigitalOcean Spaces (Similaire à S3)
# 
# Compatible S3 API
# $5/mois pour 250 GB
# Simple à configurer

# 5. Google Cloud Storage (GCS)
# 
# Similaire à S3
# Bien intégré avec autres services Google
# Bon si vous utilisez déjà Google Cloud

# === Quand utiliser volumes vs stockage externe ? ===

# UTILISER VOLUMES Railway si:
# [OK] Prototypage rapide
# [OK] Petit projet personnel
# [OK] Fichiers temporaires (logs, cache)
# [OK] < 1 GB de données
# [OK] Pas critique si perte de données

# UTILISER STOCKAGE EXTERNE (S3, Cloudinary) si:
# [OK] Production sérieuse
# [OK] Données critiques (uploads utilisateurs)
# [OK] > 1 GB de données
# [OK] Besoin de backups
# [OK] Multiples services accèdent aux fichiers
# [OK] Besoin de CDN pour performance
# [OK] Images/vidéos (transformations automatiques)

# === Migrations de volumes ===

# Si vous devez migrer fichiers Railway -> S3:

# Script Python pour migrer:
import os
import boto3
from pathlib import Path

# Connexion S3
s3 = boto3.client(
    's3',
    aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'),
    aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY')
)

# Parcourir dossier local
local_folder = '/data/media'
bucket_name = 'mon-bucket'

for filepath in Path(local_folder).rglob('*'):
    if filepath.is_file():
        s3_key = str(filepath.relative_to(local_folder))
        print(f"Uploading {filepath} to {s3_key}")
        s3.upload_file(str(filepath), bucket_name, s3_key)

print("Migration completed!")

# Exécuter sur Railway:
railway run python migrate_to_s3.py

# === Nettoyage automatique volumes ===

# Pour éviter que volume se remplisse:

# Django management command
from django.core.management.base import BaseCommand
from datetime import timedelta
from django.utils import timezone
import os

class Command(BaseCommand):
    help = 'Supprime fichiers temporaires vieux de > 7 jours'
    
    def handle(self, *args, **options):
        temp_dir = '/data/temp'
        now = timezone.now()
        deleted = 0
        
        for filename in os.listdir(temp_dir):
            filepath = os.path.join(temp_dir, filename)
            file_time = datetime.fromtimestamp(os.path.getmtime(filepath))
            
            if now - file_time > timedelta(days=7):
                os.remove(filepath)
                deleted += 1
        
        self.stdout.write(f'[OK] {deleted} fichiers supprimés')

# Ajouter à cron jobs (railway.json):
{
  "deploy": {
    "cronJobs": [
      {
        "schedule": "0 2 * * *",
        "command": "python manage.py cleanup_temp_files"
      }
    ]
  }
}

# === Monitoring usage volume ===

# Vérifier espace disque utilisé:
railway run df -h /data
# Affiche:
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/sdb1       1.0G  450M  550M  45% /data

# Script Python pour alerter si presque plein:
import shutil
from django.core.mail import mail_admins

def check_disk_space():
    total, used, free = shutil.disk_usage('/data')
    used_percent = (used / total) * 100
    
    if used_percent > 90:
        mail_admins(
            'ALERTE: Disque presque plein',
            f'Volume à {used_percent:.1f}% plein ({used / (1024**3):.1f} GB utilisés)',
        )

# Exécuter quotidiennement via cron job


[OK] WEBHOOKS

# Railway peut envoyer webhooks pour événements

# Configurer dans dashboard:
# Settings -> Webhooks -> Add Webhook

# Événements disponibles:
# - deployment.created
# - deployment.completed
# - deployment.failed
# - deployment.crashed
# - service.created
# - service.deleted

# Payload exemple:
{
  "event": "deployment.completed",
  "project": {
    "id": "xxx",
    "name": "My Project"
  },
  "environment": {
    "id": "xxx",
    "name": "production"
  },
  "deployment": {
    "id": "xxx",
    "status": "SUCCESS",
    "createdAt": "2024-01-01T00:00:00.000Z"
  }
}


[OK] CI/CD INTÉGRATION

# === GitHub Actions ===

# .github/workflows/deploy.yml
name: Deploy to Railway

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Install Railway CLI
        run: npm install -g @railway/cli
      
      - name: Deploy to Railway
        run: railway up --service api --detach
        env:
          RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}

# Créer RAILWAY_TOKEN:
# Dashboard -> Settings -> Tokens -> Create Token
# GitHub -> Settings -> Secrets -> New secret

# === GitLab CI ===

# .gitlab-ci.yml
deploy:
  stage: deploy
  script:
    - npm install -g @railway/cli
    - railway up --detach
  only:
    - main
  variables:
    RAILWAY_TOKEN: $CI_RAILWAY_TOKEN

# === Tests avant déploiement ===

# .github/workflows/test-and-deploy.yml
name: Test and Deploy

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Run tests
        run: pytest

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Deploy to Railway
        run: |
          npm install -g @railway/cli
          railway up --detach
        env:
          RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}


[OK] TEMPLATES POPULAIRES

# Railway fournit templates pré-configurés

# === Créer depuis template ===

# Via CLI
railway add

# Sélectionner dans la liste

# Via dashboard
# New Project -> Deploy from Template

# === Templates disponibles ===

# Databases:
- PostgreSQL
- MySQL
- MongoDB
- Redis
- Valkey

# Search:
- Meilisearch
- Elasticsearch
- Typesense

# Message Queues:
- RabbitMQ
- Apache Kafka

# Storage:
- MinIO (S3-compatible)

# Monitoring:
- Grafana
- Prometheus

# CMS:
- Ghost
- WordPress
- Strapi

# Full-stack apps:
- Next.js + PostgreSQL
- Django + PostgreSQL
- FastAPI + PostgreSQL


[OK] PRICING (TARIFICATION)

# Railway utilise un modèle PAY-AS-YOU-GO
# Vous payez SEULEMENT ce que vous consommez
# Pas de forfaits mensuels fixes comme Heroku

# === Plan Gratuit (Hobby) ===

# $5 de CRÉDIT GRATUIT chaque mois (renouvelé automatiquement)
# Pas besoin de carte bancaire pour commencer!
# 
# Ce crédit vous donne environ:
# - 500 heures d'exécution par mois (service toujours allumé)
# - 512 MB RAM par service
# - 1 GB stockage disque
# - Domaines .railway.app illimités
# - SSL/HTTPS gratuit
# 
# Suffisant pour:
# [OK] Projets personnels
# [OK] Prototypes et MVPs
# [OK] Portfolios
# [OK] Applications légères (< 1000 utilisateurs/mois)
# [OK] Bots Discord/Telegram
# [OK] APIs simples
# 
# Limitations:
# [X] Pas de domaines personnalisés (www.votresite.com)
# [X] Service s'éteint si dépassement crédit
# [X] Pas de support prioritaire

# Exemple d'usage typique du crédit gratuit:
# 
# Service backend Django:
# - RAM: 512 MB
# - CPU: 0.5 vCPU
# - Running 24/7: ~$3-4/mois
# 
# Service PostgreSQL:
# - RAM: 256 MB
# - CPU: 0.25 vCPU
# - Running 24/7: ~$1-2/mois
# 
# TOTAL: ~$4-6/mois -> COUVERT par crédit gratuit! [BRAVO]

# === Plan Developer ($5/mois minimum) ===

# $5 usage inclus par mois + usage supplémentaire facturé
# Nécessite carte bancaire
# 
# Avantages sur plan gratuit:
# [OK] 8 GB RAM par service (vs 512 MB)
# [OK] 100 GB disque (vs 1 GB)
# [OK] Domaines personnalisés (www.votresite.com)
# [OK] Support prioritaire
# [OK] Plus de CPU disponible
# [OK] Pas de coupure si dépassement (facturé)
# 
# Pour qui ?
# - Projets production
# - Applications avec trafic réel
# - Sites e-commerce
# - SaaS B2B/B2C

# === Comment Railway calcule l'usage ===

# Railway facture 4 métriques:

# 1. CPU (vCPU-hour)
#    - 1 vCPU pendant 1 heure = 1 vCPU-hour
#    - Prix: ~$0.02/vCPU-hour
#    - Exemple: 0.5 vCPU × 720h/mois = 360 vCPU-hours = ~$7/mois

# 2. RAM (GB-hour)
#    - 1 GB pendant 1 heure = 1 GB-hour
#    - Prix: ~$0.01/GB-hour
#    - Exemple: 512 MB × 720h/mois = 360 GB-hours = ~$3.60/mois

# 3. Storage (GB-month)
#    - 1 GB stocké pendant 1 mois = 1 GB-month
#    - Prix: ~$0.25/GB-month
#    - Exemple: 2 GB stockage = ~$0.50/mois

# 4. Network Egress (GB sortant)
#    - Données envoyées vers Internet
#    - Prix: ~$0.10/GB
#    - Les 100 premiers GB/mois sont GRATUITS
#    - Exemple: 50 GB trafic = $0 (dans limite gratuite)

# === Calcul coût mensuel typique ===

# Petit projet Django (1 service):
# - CPU: 0.5 vCPU × 720h = 360 vCPU-hours × $0.02 = $7.20
# - RAM: 512 MB × 720h = 360 GB-hours × $0.01 = $3.60
# - Storage: 1 GB × 1 month = $0.25
# - Network: < 100 GB = $0
# TOTAL: ~$11/mois

# Projet moyen avec DB (2 services):
# - Backend: 1 vCPU, 1 GB RAM = ~$14/mois
# - PostgreSQL: 0.5 vCPU, 512 MB RAM = ~$7/mois
# - Storage: 5 GB = ~$1.25/mois
# - Network: 80 GB = $0
# TOTAL: ~$22/mois

# Projet production avec Redis (4 services):
# - Backend: 2 vCPU, 2 GB RAM = ~$28/mois
# - Worker Celery: 1 vCPU, 1 GB RAM = ~$14/mois
# - PostgreSQL: 1 vCPU, 2 GB RAM = ~$14/mois
# - Redis: 0.5 vCPU, 512 MB RAM = ~$7/mois
# - Storage: 20 GB = ~$5/mois
# - Network: 200 GB = ~$10/mois (100 GB gratuit + 100 GB payant)
# TOTAL: ~$78/mois

# === Voir votre usage en temps réel ===

# Dashboard Railway affiche usage actuel:
# 1. Ouvrir https://railway.app
# 2. Cliquer sur "Usage" (en haut à droite)
# 3. Voir:
#    - Usage ce mois-ci ($X.XX / $5.00)
#    - Graphiques par jour
#    - Détail par service
#    - Projection fin de mois

# Alertes automatiques:
# Railway envoie email quand:
# - Vous atteignez 80% de votre crédit
# - Vous dépassez votre crédit (plan payant)
# - Usage anormal détecté

# === Optimiser coûts (économiser) ===

# 1. Scale down services non-critiques
#    - Développement: 256 MB RAM suffit
#    - Staging: 512 MB RAM
#    - Production: 1-2 GB RAM selon besoin

# 2. Éteindre services de dev/staging la nuit
#    - Via dashboard: Service -> Settings -> Stop Service
#    - Rallumer le matin
#    - Économie: 50% du coût!

# 3. Optimiser images Docker
#    - Utiliser images Alpine (plus petites)
#    - Multi-stage builds
#    - Moins de RAM = moins cher

# Dockerfile optimisé:
FROM python:3.11-slim  # Au lieu de python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["gunicorn", "myapp.wsgi", "--bind", "0.0.0.0:8000"]

# 4. Utiliser CDN pour assets statiques
#    - Images, CSS, JS sur Cloudflare/CloudFront
#    - Réduit network egress Railway
#    - Plus rapide pour utilisateurs!

# Django avec WhiteNoise (CDN gratuit):
pip install whitenoise

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # <- Ajouter
]

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

# 5. Cron jobs au lieu de services always-on
#    - Au lieu de worker tournant 24/7
#    - Utilisez cron job qui s'exécute 1x/jour
#    - Économie massive!

# Exemple: Au lieu de worker Celery permanent
# Utilisez cron job:
{
  "cronJobs": [
    {
      "schedule": "0 2 * * *",
      "command": "python manage.py process_tasks"
    }
  ]
}

# 6. Monitoring et alertes
#    - Surveillez usage quotidiennement
#    - Identifiez services qui consomment trop
#    - Optimisez code (requêtes DB lentes, etc.)

# 7. Utiliser stockage externe pour fichiers
#    - S3 moins cher que volumes Railway
#    - Cloudinary plan gratuit généreux
#    - Ne payez pas stockage Railway inutilement

# === Comparaison prix avec concurrents ===

# Même app Django + PostgreSQL:

# Railway:
# ~$20-30/mois (flexible selon usage)
# [OK] Pay-as-you-go (juste et transparent)
# [OK] $5 gratuit/mois
# [OK] Pas de coûts cachés

# Heroku:
# $7/mois (Eco Dyno) + $5/mois (PostgreSQL) = $12/mois minimum
# Mais limité (sleep après 30 min inactivité)
# Pour production: $25-50/mois minimum
# [X] Plus cher pour équivalent

# Render:
# ~$7-25/mois selon plan
# Similaire à Railway
# Légèrement moins cher mais moins flexible

# DigitalOcean App Platform:
# ~$12-24/mois
# Similaire à Railway
# Bon si déjà client DigitalOcean

# VPS traditionnel (DigitalOcean Droplet):
# $6-12/mois
# [OK] Moins cher
# [X] Vous gérez TOUT (serveur, sécurité, updates)
# [X] Temps = argent!

# VERDICT: Railway excellent rapport qualité/prix
# Plus cher que VPS nu, mais gain de temps énorme
# Moins cher que Heroku pour performance équivalente

# === Paiement et facturation ===

# Ajouter carte bancaire:
# 1. Dashboard -> Settings -> Billing
# 2. Add Payment Method
# 3. Entrer infos carte

# Facturation:
# - Facturée automatiquement fin de mois
# - Invoice envoyée par email
# - Détail complet de l'usage
# - Téléchargeable en PDF

# Annuler abonnement:
# - Aucun engagement
# - Supprimez projets pour arrêter facturation
# - Ou passez en plan gratuit

# === Estimateur de coûts ===

# Railway fournit un calculator:
# https://railway.app/pricing

# Entrez vos besoins:
# - Nombre de services
# - RAM par service
# - CPU par service
# - Heures d'exécution
# - Stockage
# 
# -> Estimation mensuelle

# === Conseils pour débutants ===

# 1. Commencez avec plan GRATUIT
#    - Testez Railway sans risque
#    - $5 gratuit largement suffisant pour débuter
#    - Pas besoin de carte bancaire

# 2. Surveillez usage première semaine
#    - Dashboard -> Usage
#    - Vérifiez que consommation normale
#    - Ajustez ressources si nécessaire

# 3. Passez au plan payant SEULEMENT si besoin
#    - Quand dépassez $5/mois régulièrement
#    - Quand besoin domaine personnalisé
#    - Quand app en production avec vrais users

# 4. Optimisez avant de scaler
#    - Code optimisé > Plus de RAM
#    - Ajoutez Redis cache avant d'augmenter CPU
#    - Profiling pour identifier bottlenecks

# 5. Considérez alternatives pour réduire coûts
#    - Cloudflare CDN (gratuit)
#    - Cloudinary images (25 GB gratuit/mois)
#    - SendGrid emails (100 emails/jour gratuit)
#    - Ces services gratuits réduisent usage Railway!


[OK] SCALING (MISE À L'ÉCHELLE)

# SCALING = augmenter ressources quand votre app a plus d'utilisateurs
# 2 types: Vertical (plus puissant) et Horizontal (plus d'instances)

# === Vertical Scaling (plus de puissance) ===

# VERTICAL = augmenter CPU et RAM d'un service existant
# Comme upgrader votre ordinateur: plus de RAM, meilleur processeur

# Configuration actuelle par défaut:
# - CPU: 0.5 vCPU (demi-cœur)
# - RAM: 512 MB
# 
# Suffisant pour:
# [OK] Prototypes
# [OK] Projets personnels
# [OK] < 100 utilisateurs simultanés
# [X] Production avec trafic réel

# Comment augmenter ressources:
# 1. Dashboard -> Service -> Settings -> Resources
# 2. Ajuster sliders:
#    - CPU: 0.5 -> 1 -> 2 -> 4 -> 8 -> 16 -> 32 vCPU
#    - RAM: 512 MB -> 1 GB -> 2 GB -> 4 GB -> 8 GB -> 16 GB -> 32 GB
# 3. Save
# 4. Service redémarre automatiquement

# Recommandations par charge:

# Développement / Staging:
# CPU: 0.5 vCPU
# RAM: 512 MB
# Coût: ~$7/mois
# Pour: Tests, développement

# Production légère (< 1000 users/jour):
# CPU: 1 vCPU
# RAM: 1 GB
# Coût: ~$14/mois
# Pour: Blogs, portfolios, petites APIs

# Production moyenne (1000-10000 users/jour):
# CPU: 2 vCPU
# RAM: 2 GB
# Coût: ~$28/mois
# Pour: Apps web, APIs moyennes, SaaS small

# Production importante (10000-100000 users/jour):
# CPU: 4 vCPU
# RAM: 4-8 GB
# Coût: ~$56-100/mois
# Pour: E-commerce, SaaS medium, APIs haute charge

# Production critique (> 100000 users/jour):
# CPU: 8+ vCPU
# RAM: 8-16 GB
# Coût: ~$150-300/mois
# Pour: Grosses apps, SaaS large scale
# À ce stade, considérez horizontal scaling aussi!

# QUAND scale up (augmenter) ?
# 
# Indicateurs que vous avez besoin de plus de ressources:
# 
# [X] CPU toujours à 100% (dashboard metrics)
# [X] RAM pleine, app crashe avec "Out of Memory"
# [X] Temps de réponse > 2 secondes
# [X] Timeouts fréquents
# [X] Logs montrent "slow requests"
# [X] Utilisateurs se plaignent de lenteur

# Vérifier metrics:
# Dashboard -> Service -> Metrics
# 
# Regardez graphiques:
# - CPU usage: devrait être 40-70% en moyenne
# - Memory usage: devrait être < 80%
# - Response time: devrait être < 500ms

# === Horizontal Scaling (plus d'instances) ===

# HORIZONTAL = avoir plusieurs COPIES (replicas) de votre service
# Comme avoir plusieurs serveurs identiques qui se partagent le travail

# Avantages horizontal scaling:
# [OK] Haute disponibilité (si 1 tombe, autres continuent)
# [OK] Load balancing automatique (Railway distribue requêtes)
# [OK] Gérer pics de trafic (Black Friday, posts viraux)
# [OK] Zero-downtime deployments

# Configuration replicas:
# Dashboard -> Service -> Settings -> Replicas
# 
# Nombre de replicas: 1 -> 2 -> 3 -> 4 -> 5 -> 10
# 
# Chaque replica = instance complète du service
# Railway balance automatiquement requêtes entre replicas

# IMPORTANT: Load balancing automatique
# Railway utilise un load balancer qui:
# 1. Reçoit requête utilisateur
# 2. Choisit replica avec moins de charge
# 3. Envoie requête à ce replica
# 4. Retourne réponse utilisateur
# 
# Vous n'avez RIEN à configurer! C'est automatique!

# Exemple architecture avec replicas:
# 
# Internet
#    v
# [Railway Load Balancer]
#    v
# ┌──────┬──────┬──────┐
# │Rep 1 │Rep 2 │Rep 3 │  <- 3 replicas identiques
# └──────┴──────┴──────┘
#    v
# [PostgreSQL unique]  <- Une seule DB partagée

# QUAND utiliser horizontal scaling ?
# 
# [OK] Trafic très variable (pics imprévisibles)
# [OK] Besoin haute disponibilité (99.99% uptime)
# [OK] Plus de 10000 requêtes/heure
# [OK] Services stateless (sans état local)
# 
# [X] Évitez si:
# - App utilise stockage local (fichiers)
# - App a état en mémoire (sessions sans Redis)
# - App fait tasks longues (utilisez queue/Celery)

# Règles pour horizontal scaling:

# 1. STATELESS: App ne doit PAS stocker données localement
# [X] Mauvais:
session_data = {}  # Stocké en RAM du replica
@app.route('/login')
def login():
    session_data[user_id] = user_info  # Perdu si autre replica!

# [OK] Bon:
from django.contrib.sessions.backends.cache import SessionStore
# Sessions dans Redis, accessibles par tous replicas

# 2. DATABASE partagée: Tous replicas utilisent MÊME DB
# Railway gère ça automatiquement
# Votre DATABASE_URL pointe vers PostgreSQL unique

# 3. CACHE partagé: Utilisez Redis pour cache
# Pas de cache local en RAM du replica

# 4. UPLOADS: Stockage externe (S3, Cloudinary)
# Pas de fichiers uploadés localement

# === Auto-scaling (pas encore disponible) ===

# Railway ne fait PAS d'auto-scaling automatique pour l'instant
# Vous devez configurer manuellement nombre de replicas
# 
# Workaround: Monitoring + alertes
# - Créez alerte si CPU > 80%
# - Augmentez replicas manuellement
# - Réduisez après pic de trafic

# === Scaling de la base de données ===

# PostgreSQL ne scale PAS horizontalement facilement
# Solution: Vertical scaling uniquement

# Configuration DB:
# Dashboard -> Service PostgreSQL -> Settings -> Resources
# 
# Développement: 256 MB RAM, 0.25 vCPU
# Production: 2-4 GB RAM, 1-2 vCPU

# Optimisations DB pour éviter scaling:

# 1. INDEXATION (CRUCIAL!)
# Indexer colonnes fréquemment requêtées

# Django:
class User(models.Model):
    email = models.EmailField(db_index=True)  # <- Index
    created_at = models.DateTimeField(db_index=True)
    
    class Meta:
        indexes = [
            models.Index(fields=['email', 'created_at']),  # Index composite
        ]

# 2. CONNECTION POOLING
# Réutiliser connexions DB au lieu d'en créer nouvelles

# Django settings.py:
DATABASES = {
    'default': {
        'CONN_MAX_AGE': 600,  # Garde connexions 10 min
    }
}

# 3. CACHING avec Redis
# Cache requêtes fréquentes

from django.core.cache import cache

def get_products():
    products = cache.get('products')
    if not products:
        products = Product.objects.all()
        cache.set('products', products, 3600)  # Cache 1h
    return products

# 4. QUERY OPTIMIZATION
# Éviter N+1 queries

# [X] Mauvais (N+1):
for user in User.objects.all():
    print(user.profile.bio)  # 1 requête par user!

# [OK] Bon (2 requêtes totales):
users = User.objects.select_related('profile').all()
for user in users:
    print(user.profile.bio)

# 5. PAGINATION
# Ne jamais charger TOUTES les lignes

# Django:
from django.core.paginator import Paginator

users = User.objects.all()
paginator = Paginator(users, 50)  # 50 par page
page = paginator.get_page(1)

# === Scaling Redis ===

# Redis est en RAM = très cher si trop de données
# 
# Optimisations:
# 1. TTL (expiration) sur toutes les clés
cache.set('key', value, timeout=3600)  # Expire après 1h

# 2. Ne PAS stocker gros objets
# Stockez seulement IDs, pas objets complets

# 3. Utilisez Redis pour cache/sessions uniquement
# Pas pour stockage permanent (utilisez PostgreSQL)

# 4. Monitoring utilisation mémoire
railway run redis-cli INFO memory

# === Stratégie de scaling complète ===

# Phase 1: Petit projet (< 100 users)
# - 1 service backend: 0.5 vCPU, 512 MB
# - 1 PostgreSQL: 0.25 vCPU, 256 MB
# Coût: ~$10/mois

# Phase 2: Croissance (100-1000 users)
# - 1 service backend: 1 vCPU, 1 GB
# - 1 PostgreSQL: 0.5 vCPU, 512 MB
# - 1 Redis: 0.25 vCPU, 256 MB
# Coût: ~$25/mois

# Phase 3: Scale up (1000-10000 users)
# - 2 replicas backend: 2 vCPU, 2 GB chacun
# - 1 PostgreSQL: 1 vCPU, 2 GB
# - 1 Redis: 0.5 vCPU, 512 MB
# Coût: ~$80/mois

# Phase 4: Scale majeur (> 10000 users)
# - 5 replicas backend: 2 vCPU, 2 GB chacun
# - 1 PostgreSQL: 4 vCPU, 8 GB
# - 1 Redis: 1 vCPU, 2 GB
# - 1 Worker Celery: 1 vCPU, 1 GB
# Coût: ~$300/mois
# 
# À ce stade, considérez:
# - Database read replicas (PostgreSQL secondaires)
# - CDN pour assets (Cloudflare)
# - Caching agressif
# - Microservices architecture

# === Tests de charge (Load Testing) ===

# Avant de scaler, testez combien votre app supporte!

# Outil: Locust (Python)
pip install locust

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

class WebsiteUser(HttpUser):
    wait_time = between(1, 5)
    
    @task
    def index_page(self):
        self.client.get("/")
    
    @task(3)
    def api_endpoint(self):
        self.client.get("/api/products")

# Lancer test:
locust -f locustfile.py --host=https://votre-app.up.railway.app

# Interface web: http://localhost:8089
# Simulez 100, 1000, 5000 utilisateurs
# Voyez à quel point app commence à ralentir
# -> Décidez combien de ressources nécessaires

# === Monitoring performance ===

# Outils essentiels:

# 1. Railway Metrics (basique, gratuit)
# Dashboard -> Metrics
# CPU, RAM, Response time

# 2. New Relic / DataDog (avancé, payant)
# APM complet, profiling, alertes

# 3. Sentry (erreurs, gratuit plan basic)
# Capture toutes erreurs en production

# 4. Custom monitoring
# Logger vos métriques custom:

import time
import logging

logger = logging.getLogger(__name__)

@app.route('/api/products')
def get_products():
    start = time.time()
    products = Product.objects.all()
    duration = time.time() - start
    
    logger.info(f"get_products took {duration:.2f}s")
    
    if duration > 1.0:
        logger.warning(f"SLOW QUERY: get_products took {duration:.2f}s")
    
    return products


[OK] SÉCURITÉ

# La sécurité est CRITIQUE pour protéger vos utilisateurs et données
# Railway fournit sécurité de base, mais VOUS devez sécuriser votre code!

# === Variables d'environnement sécurisées ===

# Railway chiffre TOUTES vos variables d'environnement
# 
# [OK] Chiffrées au repos (stockage)
# [OK] Transmises de manière sécurisée à votre app
# [OK] Non exposées dans logs (Railway les masque automatiquement)
# [OK] Accessibles uniquement par vos services
# 
# Comment Railway protège vos secrets:
# 1. Vous définissez: railway variables set API_KEY=secret123
# 2. Railway chiffre avec AES-256
# 3. Stocké sécurisé dans leur infrastructure
# 4. Déchiffré uniquement au runtime dans votre container
# 5. Jamais visible dans logs ou dashboard (affiché ****)

# RÈGLES D'OR pour secrets:

# [X] NE JAMAIS commiter dans Git
# Toujours dans .gitignore:
.env
.env.local
.env.production
railway.json  # Si contient secrets

# [X] NE JAMAIS partager dans Slack/Discord/Email
# Si vous devez partager:
# "La variable API_KEY est définie sur Railway"
# Pas: "L'API_KEY est abc123xyz"

# [X] NE JAMAIS logger secrets
# [X] Mauvais:
logger.info(f"API Key: {API_KEY}")

# [OK] Bon:
logger.info("API Key configured successfully")

# [X] NE JAMAIS exposer dans frontend
# Secrets doivent rester côté backend!
# [X] Mauvais:
<script>
  const API_KEY = "{{ api_key }}";  // Visible dans source HTML!
</script>

# [OK] Bon:
# Backend fait l'appel API, frontend reçoit seulement résultat

# === HTTPS/TLS (chiffrement connexions) ===

# Railway active AUTOMATIQUEMENT HTTPS sur TOUS vos domaines
# Certificats Let's Encrypt gratuits
# Renouvellement automatique tous les 90 jours
# 
# [OK] Toutes requêtes HTTP -> redirigées vers HTTPS
# [OK] TLS 1.2+ obligatoire (versions anciennes non supportées)
# [OK] Certificats valides reconnus par tous navigateurs
# 
# Vous n'avez RIEN à faire! C'est automatique! [VERROUILLE]

# Vérifier HTTPS fonctionne:
# 1. Ouvrir votre app: https://votre-app.up.railway.app
# 2. Voir cadenas vert dans navigateur
# 3. Cliquer cadenas -> Certificat valide

# Forcer HTTPS dans Django (recommandé):
# settings.py
if not DEBUG:
    SECURE_SSL_REDIRECT = True              # HTTP -> HTTPS redirect
    SESSION_COOKIE_SECURE = True            # Cookie seulement via HTTPS
    CSRF_COOKIE_SECURE = True               # CSRF token seulement via HTTPS
    SECURE_HSTS_SECONDS = 31536000          # Force HTTPS pendant 1 an
    SECURE_HSTS_INCLUDE_SUBDOMAINS = True   # Inclut sous-domaines
    SECURE_HSTS_PRELOAD = True              # Preload list navigateurs

# === Networking (isolation réseau) ===

# Railway isole vos services automatiquement:
# 
# [OK] Services par défaut NON accessibles depuis Internet
# [OK] Seulement domaine public exposé (.up.railway.app)
# [OK] Base de données PostgreSQL/Redis PRIVÉES
# [OK] Communication inter-services via domaines privés .railway.internal
# 
# Architecture sécurisée par défaut:
# 
# Internet (non sécurisé)
#    v HTTPS uniquement
# [Backend public: backend.up.railway.app]
#    v Réseau privé Railway
# [PostgreSQL privé: postgres.railway.internal] <- INVISIBLE depuis Internet
# [Redis privé: redis.railway.internal] <- INVISIBLE depuis Internet
# 
# Personne ne peut accéder directement à votre DB!

# === Vulnérabilités des dépendances ===

# Vos dépendances (packages) peuvent avoir failles de sécurité
# TOUJOURS les mettre à jour régulièrement!

# Python: Utiliser Safety
pip install safety
pip freeze > requirements.txt

# Scanner vulnérabilités:
safety check

# Exemple output:
# ╒══════════════════════════════════════════════════════════════════════════════╕
# │                                                                              │
# │                               /$$$            /$                         │
# │                              /$__  $          | $                         │
# │           /$$$$  /$$$ | $  \__//$$$  /$$$   /$   /$          │
# │          /$_____/ |____  $| $$   /$__  $|_  $_/  | $  | $          │
# │         |  $$$   /$$$$| $_/  | $$$$  | $    | $  | $          │
# │          \____  $ /$__  $| $    | $_____/  | $ /$| $  | $          │
# │          /$$$$/|  $$$$| $    |  $$$$  |  $$/|  $$$$          │
# │         |_______/  \_______/|__/     \_______/   \___/   \____  $          │
# │                                                            /$  | $          │
# │                                                           |  $$$/          │
# │  by pyup.io                                                \______/           │
# │                                                                              │
# ╘══════════════════════════════════════════════════════════════════════════════╛
# 
# django==3.2.0 (vulnérabilité CVE-2021-xxxxx)
# Solution: Mettre à jour vers django>=3.2.13

# Mettre à jour dépendances:
pip install --upgrade django requests
pip freeze > requirements.txt

# Node.js: Audit NPM
npm audit
npm audit fix  # Corrige automatiquement

# Automatiser avec GitHub Dependabot:
# GitHub -> Settings -> Security -> Dependabot
# Active automatiquement les PRs pour updates sécurité

# === Authentification forte ===

# Toujours utiliser authentification robuste:

# Django: Utilisez django-allauth ou dj-rest-auth
pip install django-allauth

# Features essentielles:
# [OK] Mots de passe hachés (PBKDF2 par défaut Django)
# [OK] Email verification
# [OK] Password reset sécurisé
# [OK] Rate limiting (anti-brute force)
# [OK] 2FA (authentification à 2 facteurs)

# Configuration Django sécurisée:
# settings.py
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        'OPTIONS': {'min_length': 12}  # Minimum 12 caractères
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# FastAPI avec JWT:
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from passlib.context import CryptContext

# Hasher mots de passe
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# Créer token JWT
def create_access_token(data: dict):
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(minutes=30)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm="HS256")
    return encoded_jwt

# === Rate Limiting (limite de requêtes) ===

# Protéger contre attaques brute-force et DDoS

# Flask-Limiter:
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(
    app,
    key_func=get_remote_address,
    storage_uri=os.getenv('REDIS_URL')  # Stockage dans Redis
)

# Limiter endpoints sensibles:
@app.route('/api/login', methods=['POST'])
@limiter.limit("5 per minute")  # Max 5 tentatives/minute
def login():
    # Code login
    pass

@app.route('/api/register', methods=['POST'])
@limiter.limit("3 per hour")  # Max 3 inscriptions/heure par IP
def register():
    pass

# Django avec django-ratelimit:
from django_ratelimit.decorators import ratelimit

@ratelimit(key='ip', rate='5/m', method='POST')
def login_view(request):
    pass

@ratelimit(key='user', rate='100/h')
def api_view(request):
    pass

# === CORS (Cross-Origin Resource Sharing) ===

# Si votre frontend et backend sont sur domaines différents:
# Frontend: www.monsite.com
# Backend API: api.monsite.com
# 
# Vous devez configurer CORS pour autoriser requêtes

# Django avec django-cors-headers:
pip install django-cors-headers

# settings.py
INSTALLED_APPS = [
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
]

# Option 1: Autoriser domaines spécifiques (RECOMMANDÉ)
CORS_ALLOWED_ORIGINS = [
    "https://www.monsite.com",
    "https://monsite.com",
]

# Option 2: Autoriser TOUT (développement uniquement!)
CORS_ALLOW_ALL_ORIGINS = True  # [ATTENTION] Dangereux en production!

# FastAPI:
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://www.monsite.com"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# === Validation des inputs (CRITIQUE!) ===

# TOUJOURS valider et nettoyer données utilisateur
# Protège contre injections SQL, XSS, etc.

# Django Forms (validation automatique):
from django import forms

class ContactForm(forms.Form):
    email = forms.EmailField()  # Valide format email
    message = forms.CharField(max_length=1000)  # Limite longueur
    age = forms.IntegerField(min_value=0, max_value=150)  # Range

# FastAPI avec Pydantic (validation automatique):
from pydantic import BaseModel, EmailStr, validator

class UserCreate(BaseModel):
    email: EmailStr  # Valide format email automatiquement
    password: str
    age: int
    
    @validator('password')
    def password_strength(cls, v):
        if len(v) < 12:
            raise ValueError('Password trop court')
        return v
    
    @validator('age')
    def age_realistic(cls, v):
        if v < 0 or v > 150:
            raise ValueError('Age invalide')
        return v

# Protection XSS (Cross-Site Scripting):
# Django échappe HTML automatiquement dans templates:
{{ user_input }}  # Sécurisé, échappe <script> tags

# Si vous devez afficher HTML raw (DANGER!):
{{ user_input|safe }}  # [ATTENTION] Utilisez seulement pour contenu de confiance!

# Mieux: Utiliser bleach pour nettoyer HTML:
import bleach

clean_html = bleach.clean(
    user_input,
    tags=['p', 'strong', 'em', 'a'],  # Tags autorisés
    attributes={'a': ['href']},  # Attributs autorisés
    strip=True
)

# Protection SQL Injection:
# Django ORM protège automatiquement:
# [OK] Bon (sécurisé):
User.objects.filter(email=user_email)

# [X] Mauvais (vulnérable):
cursor.execute(f"SELECT * FROM users WHERE email = '{user_email}'")

# Si raw SQL nécessaire, utilisez paramètres:
cursor.execute("SELECT * FROM users WHERE email = %s", [user_email])

# === Monitoring de sécurité ===

# Surveiller activité suspecte:

# 1. Logger tentatives login échouées
import logging

logger = logging.getLogger(__name__)

def login(request):
    user = authenticate(username=username, password=password)
    if user is None:
        logger.warning(
            f"Failed login attempt for {username} from {request.META['REMOTE_ADDR']}"
        )
        # Si > 5 échecs, bloquer IP
        return HttpResponse('Invalid credentials', status=401)

# 2. Alertes email pour activité anormale
from django.core.mail import mail_admins

def detect_suspicious_activity(user, action):
    if action == 'password_change' and user.last_password_change < timezone.now() - timedelta(minutes=5):
        mail_admins(
            'ALERTE SÉCURITÉ',
            f'User {user.email} changed password twice in 5 minutes'
        )

# 3. Intégrer Sentry pour monitoring
# Sentry capture automatiquement toutes erreurs
import sentry_sdk

sentry_sdk.init(
    dsn=os.getenv('SENTRY_DSN'),
    traces_sample_rate=1.0,
    send_default_pii=False,  # Ne pas envoyer données personnelles
)

# === Backups (sauvegardes) ===

# Railway ne fait PAS backups automatiques!
# VOUS devez organiser vos backups

# Backup PostgreSQL quotidien (cron job):
# railway.json
{
  "cronJobs": [
    {
      "schedule": "0 2 * * *",
      "command": "python manage.py backup_database"
    }
  ]
}

# Management command backup:
from django.core.management.base import BaseCommand
import subprocess
import boto3
from datetime import datetime

class Command(BaseCommand):
    def handle(self, *args, **options):
        # Dump database
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        filename = f'backup_{timestamp}.sql'
        
        subprocess.run([
            'pg_dump',
            os.getenv('DATABASE_URL'),
            '-f', filename
        ])
        
        # Upload vers S3
        s3 = boto3.client('s3')
        s3.upload_file(
            filename,
            'my-backups-bucket',
            f'database/{filename}'
        )
        
        # Supprimer fichier local
        os.remove(filename)
        
        self.stdout.write('[OK] Backup completed')

# === Checklist sécurité pré-production ===

# Avant de mettre en production, vérifiez:

# [OK] DEBUG = False
# [OK] SECRET_KEY fort et unique (pas celui par défaut!)
# [OK] ALLOWED_HOSTS configuré correctement
# [OK] HTTPS forcé (SECURE_SSL_REDIRECT = True)
# [OK] Cookies sécurisés (SESSION_COOKIE_SECURE = True)
# [OK] CORS configuré correctement (pas allow_all en prod!)
# [OK] Rate limiting sur endpoints sensibles
# [OK] Validation inputs partout
# [OK] Dépendances à jour (pas de vulnérabilités)
# [OK] Backups automatisés configurés
# [OK] Monitoring/alertes actifs (Sentry)
# [OK] Logs ne contiennent pas de secrets
# [OK] Variables d'environnement pour tous secrets
# [OK] .env dans .gitignore
# [OK] Authentification forte (passwords > 12 chars, 2FA optionnel)
# [OK] Tests sécurité basiques effectués

# Script Django check sécurité:
python manage.py check --deploy

# Affiche warnings sécurité à corriger

# === Outils audit sécurité ===

# 1. OWASP ZAP (scanner vulnérabilités web)
# https://www.zaproxy.org
# Gratuit, open-source
# Détecte XSS, SQL injection, etc.

# 2. Bandit (scanner code Python)
pip install bandit
bandit -r myapp/

# Trouve failles dans votre code Python

# 3. Safety (vulnérabilités dépendances)
pip install safety
safety check

# 4. Mozilla Observatory (test site)
# https://observatory.mozilla.org
# Entrer votre URL, voir score sécurité

# 5. SSL Labs (test HTTPS/TLS)
# https://www.ssllabs.com/ssltest/
# Analyser qualité certificat SSL

# === En cas de faille découverte ===

# 1. NE PAS paniquer
# 2. Évaluer gravité (données exposées?)
# 3. Corriger immédiatement
# 4. Déployer fix en urgence
# 5. Révoquer credentials compromis
# 6. Notifier utilisateurs si données personnelles exposées (RGPD!)
# 7. Analyser cause racine
# 8. Améliorer process pour éviter répétition


[OK] MIGRATION DEPUIS HEROKU

# Heroku devient cher et moins moderne
# Railway est une excellente alternative!

# === Différences principales Heroku -> Railway ===

# HEROKU                          RAILWAY
# Procfile                    ->   railway.json
# Heroku Postgres             ->   Railway PostgreSQL
# Heroku Redis                ->   Railway Redis  
# Config vars (heroku config) ->   Environment variables
# Heroku CLI                  ->   Railway CLI
# Dynos                       ->   Services
# Add-ons                     ->   Templates
# heroku ps:scale             ->   Dashboard Resources
# Buildpacks                  ->   Nixpacks/Dockerfile
# $7-$25/dyno                 ->   Pay-as-you-go (~$10-20/service)

# === Étapes migration complètes ===

# ÉTAPE 1: Exporter variables Heroku

# Sur votre machine locale (dans dossier projet):
heroku config -s > .env.heroku
# 
# Crée fichier .env.heroku avec toutes vos variables:
# DATABASE_URL=postgres://...
# REDIS_URL=redis://...
# SECRET_KEY=xxx
# API_KEY=yyy

# Voir le contenu:
cat .env.heroku

# ÉTAPE 2: Créer projet Railway

railway login
railway init --name "Mon Projet (migré depuis Heroku)"
# 
# Railway détecte automatiquement votre app
# Crée fichier .railway avec IDs projet

# ÉTAPE 3: Ajouter services (DB, Redis, etc.)

# Si vous aviez Heroku Postgres:
railway add --template postgres
# Railway crée PostgreSQL avec variables automatiques

# Si vous aviez Heroku Redis:
railway add --template redis

# Autres add-ons Heroku -> équivalents Railway:
# Heroku Scheduler -> Cron jobs (railway.json)
# Memcachier -> Redis
# Papertrail -> Logs Railway (ou service externe)
# SendGrid -> Même SendGrid (variable SENDGRID_API_KEY)
# Mailgun -> Même Mailgun
# Stripe -> Même Stripe (variables identiques)

# ÉTAPE 4: Importer variables Heroku -> Railway

# Nettoyer variables avant import:
# Supprimer lignes spécifiques Heroku qui ne servent pas:
# - HEROKU_APP_NAME
# - HEROKU_SLUG_COMMIT
# - DATABASE_URL (Railway le génère automatiquement)
# - REDIS_URL (Railway le génère automatiquement)

# Créer .env.railway propre:
# Copier .env.heroku et supprimer lignes Heroku

# Importer dans Railway:
railway variables set --from-file .env.railway

# Ou manuellement une par une:
railway variables set SECRET_KEY=your-secret-key
railway variables set STRIPE_API_KEY=sk_xxx
railway variables set AWS_ACCESS_KEY_ID=xxx
railway variables set SENDGRID_API_KEY=xxx

# IMPORTANT: Lier services Railway
railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'
railway variables set REDIS_URL='${{Redis.REDIS_URL}}'

# ÉTAPE 5: Adapter configuration Procfile -> railway.json

# Votre ancien Procfile Heroku:
# web: gunicorn myapp.wsgi
# worker: celery -A myapp worker
# release: python manage.py migrate

# Créer railway.json équivalent:
{
  "build": {
    "builder": "NIXPACKS",
    "buildCommand": "pip install -r requirements.txt"
  },
  "deploy": {
    "startCommand": "python manage.py migrate && gunicorn myapp.wsgi --bind 0.0.0.0:$PORT",
    "restartPolicyType": "ON_FAILURE",
    "healthcheckPath": "/health"
  }
}

# Si vous aviez worker Celery sur Heroku:
# Sur Railway, créez SERVICE SÉPARÉ pour worker:
# 1. Dashboard -> New Service
# 2. Lier même repo GitHub
# 3. Créer railway.json dans dossier worker/:
{
  "deploy": {
    "startCommand": "celery -A myapp worker -l info"
  }
}

# ÉTAPE 6: Adapter code Django si nécessaire

# Heroku injecte automatiquement DATABASE_URL
# Railway aussi! Donc pas de changement normalement

# Si vous utilisiez dj-database-url:
import dj_database_url
DATABASES = {
    'default': dj_database_url.config(
        default=os.getenv('DATABASE_URL'),
        conn_max_age=600
    )
}
# [OK] Fonctionne identique sur Railway!

# Si vous utilisiez Whitenoise pour static files:
# [OK] Fonctionne identique sur Railway!
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

# ÉTAPE 7: Migrer base de données (CRITIQUE!)

# Option 1: pg_dump/pg_restore (RECOMMANDÉ)

# 1. Créer backup depuis Heroku:
heroku pg:backups:capture
heroku pg:backups:download
# Crée fichier: latest.dump

# 2. Obtenir URL PostgreSQL Railway:
railway variables | grep DATABASE_URL
# Copier l'URL: postgresql://user:pass@host:5432/railway

# 3. Restaurer backup dans Railway:
pg_restore --verbose --clean --no-acl --no-owner \
  -d postgresql://user:pass@host:5432/railway \
  latest.dump

# [ATTENTION] Vérifier que restore a réussi:
railway run python manage.py shell
>>> from myapp.models import User
>>> User.objects.count()
# Doit afficher nombre users de Heroku

# Option 2: Dump SQL puis import

# 1. Export SQL depuis Heroku:
heroku pg:psql -c "COPY (SELECT * FROM users) TO STDOUT WITH CSV HEADER" > users.csv

# 2. Import dans Railway:
railway run psql $DATABASE_URL
\copy users FROM 'users.csv' WITH CSV HEADER

# Option 3: Pour petites DB (< 1000 rows)
# Utiliser Django fixtures:

# Sur Heroku:
heroku run python manage.py dumpdata > data.json

# Sur Railway:
railway run python manage.py loaddata data.json

# ÉTAPE 8: Migrer fichiers uploadés (si vous en avez)

# Si vous utilisiez Heroku Ephemeral Filesystem:
# [ATTENTION] Fichiers perdus à chaque redéploiement (comme Railway)
# Vous devriez déjà utiliser S3/Cloudinary
# Si non, configurez maintenant!

# Si fichiers dans Heroku (localement):
# Télécharger tous les fichiers:
heroku run tar czf uploads.tar.gz /app/uploads
heroku run cat uploads.tar.gz > uploads.tar.gz

# Uploader vers S3 ou Cloudinary
# Puis configurer Django pour utiliser S3 sur Railway

# ÉTAPE 9: Tester sur Railway staging

railway environment staging
railway up
# 
# Tester TOUT:
# [OK] App démarre
# [OK] Pages chargent
# [OK] Login fonctionne
# [OK] Création données fonctionne
# [OK] Emails s'envoient
# [OK] Payments fonctionnent (mode test)
# [OK] Cron jobs s'exécutent
# [OK] Uploads fonctionnent

# ÉTAPE 10: Migrer domaine custom

# 1. Ajouter domaine dans Railway:
# Dashboard -> Service -> Settings -> Domains -> Add Custom Domain
# Entrer: www.votresite.com

# 2. Railway donne target CNAME:
# Target: backend-production.up.railway.app

# 3. Ne PAS changer DNS tout de suite!
# D'abord vérifier que app Railway fonctionne 100%

# 4. Changer DNS (MOMENT CRITIQUE):
# Aller chez votre registrar (Namecheap, GoDaddy, etc.)
# 
# AVANT (Heroku):
# Type: CNAME
# Name: www
# Value: votresite.herokuapp.com
# 
# APRÈS (Railway):
# Type: CNAME
# Name: www
# Value: backend-production.up.railway.app
# 
# Sauvegarder!

# 5. Attendre propagation DNS (5-60 minutes)
# Vérifier avec:
dig www.votresite.com
# Doit montrer: backend-production.up.railway.app

# 6. SSL automatique
# Railway génère certificat Let's Encrypt automatiquement
# Attendre 10-15 minutes après propagation DNS
# Visiter https://www.votresite.com -> Doit fonctionner!

# ÉTAPE 11: Supprimer ressources Heroku

# [ATTENTION] Seulement APRÈS vérification que tout fonctionne sur Railway!

# 1. Attendre 1-2 jours pour être sûr
# 2. Télécharger dernier backup Heroku:
heroku pg:backups:download

# 3. Supprimer app Heroku:
heroku apps:destroy --app votreapp --confirm votreapp

# 4. Annuler abonnements add-ons payants

# === Différences à connaître ===

# 1. Redémarrages
# Heroku: Redémarre dynos quotidiennement (rotation)
# Railway: Ne redémarre que si crash ou déploiement
# 
# Impact: Aucun si app bien codée

# 2. Scaling
# Heroku: heroku ps:scale web=2
# Railway: Dashboard -> Replicas
# 
# Railway plus visuel, Heroku plus CLI

# 3. Logs
# Heroku: heroku logs --tail
# Railway: railway logs -f
# 
# Identique!

# 4. Scheduler/Cron
# Heroku: Add-on Heroku Scheduler
# Railway: Cron jobs dans railway.json
# 
# Railway plus simple et gratuit!

# 5. Review Apps (apps éphémères par PR)
# Heroku: Review Apps intégrés
# Railway: Pas de review apps automatiques
# Workaround: Environnement staging manuel

# === Comparaison coûts Heroku vs Railway ===

# Exemple: App Django + PostgreSQL + Redis

# HEROKU:
# - Eco dyno web: $5/mois
# - Eco dyno worker: $5/mois
# - Mini PostgreSQL: $5/mois
# - Mini Redis: $3/mois (Heroku Data for Redis)
# TOTAL: $18/mois
# 
# Limitations Eco:
# - Sleep après 30 min inactivité (mauvais UX!)
# - 512 MB RAM seulement
# - Pas SSL custom domains

# RAILWAY:
# - Web service (1 vCPU, 1 GB): ~$14/mois
# - Worker (0.5 vCPU, 512 MB): ~$7/mois
# - PostgreSQL (0.5 vCPU, 512 MB): ~$7/mois
# - Redis (0.25 vCPU, 256 MB): ~$3/mois
# TOTAL: ~$31/mois
# 
# Mais avantages:
# [OK] Jamais de sleep (toujours réactif)
# [OK] Plus de RAM (1 GB vs 512 MB)
# [OK] SSL custom domains inclus
# [OK] Meilleure performance CPU
# [OK] Scaling granulaire

# Pour production sérieuse:
# HEROKU Standard-1X: $25/dyno = $50-75/mois minimum
# RAILWAY équivalent: $30-40/mois
# -> Railway moins cher!

# === Checklist migration complète ===

# Avant migration:
# [OK] Backup complet base de données
# [OK] Liste toutes variables d'environnement
# [OK] Liste tous add-ons Heroku utilisés
# [OK] Documentation domaines et DNS
# [OK] Tests locaux passent

# Pendant migration:
# [OK] Projet Railway créé
# [OK] Services ajoutés (PostgreSQL, Redis)
# [OK] Variables importées
# [OK] railway.json configuré
# [OK] Code adapté si nécessaire
# [OK] Base de données migrée
# [OK] Fichiers uploadés migrés
# [OK] Tests staging passent

# Après migration:
# [OK] DNS pointent vers Railway
# [OK] SSL fonctionne
# [OK] Tous features testés en production
# [OK] Monitoring actif (Sentry, etc.)
# [OK] Backups configurés
# [OK] Heroku conservé 7 jours (rollback possible)
# [OK] Équipe notifiée

# === Rollback en cas de problème ===

# Si problème sur Railway après migration DNS:

# 1. Rollback DNS immédiat
# Registrar -> CNAME -> Remettre valeur Heroku
# Propagation: 5-15 minutes

# 2. Diagnostiquer problème Railway
railway logs -f
# Identifier erreur

# 3. Fixer sur Railway
# Une fois fixé, re-pointer DNS vers Railway

# === Support migration ===

# Railway n'a pas d'équipe migration dédiée
# Mais documentation excellente:
# https://docs.railway.app/guides/migrate-from-heroku

# Communauté Discord très active:
# https://discord.gg/railway

# Si bloqué:
# 1. Chercher dans docs
# 2. Demander sur Discord
# 3. Ouvrir GitHub issue: https://github.com/railwayapp


[OK] DÉPANNAGE COMPLET

# Cette section couvre TOUS les problèmes courants et leurs solutions
# Utilisez Ctrl+F pour chercher votre erreur spécifique

# === PROBLÈME 1: Déploiement échoue ===

# SYMPTÔMES:
# - "Deployment failed" dans dashboard
# - Build s'arrête avec erreur
# - Service ne démarre jamais

# DIAGNOSTIC:

[OK] WORKFLOWS COMPLETS (GUIDES PRATIQUES)

# Cette section montre workflows complets du début à la fin
# Suivez étape par étape pour réussir votre déploiement

# === WORKFLOW 1: Première app Django (débutant complet) ===

# Vous avez développé app Django localement, maintenant déployer!

# ÉTAPE 1: Préparer projet localement

# 1.1 - S'assurer que app fonctionne localement
python manage.py runserver
# Tester sur http://localhost:8000
# Vérifier que tout fonctionne (login, pages, etc.)

# 1.2 - Créer requirements.txt avec TOUTES dépendances
pip freeze > requirements.txt
# Vérifier contenu:
cat requirements.txt
# Doit contenir: django, gunicorn, psycopg2-binary, etc.

# 1.3 - Configurer settings.py pour production
# À la fin de settings.py, ajouter:

import os
import dj_database_url

# DEBUG False en production
DEBUG = os.getenv('DEBUG', 'False') == 'True'

# Hosts autorisés
ALLOWED_HOSTS = ['*']  # Ou liste domaines spécifiques

# Database (PostgreSQL)
DATABASES = {
    'default': dj_database_url.config(
        default=os.getenv('DATABASE_URL'),
        conn_max_age=600
    )
}

# Static files (avec WhiteNoise)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # Ajouter!
    # ... autres middleware
]

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

# Media files (si uploads)
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# HTTPS en production
if not DEBUG:
    SECURE_SSL_REDIRECT = True
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True

# 1.4 - Installer dépendances manquantes
pip install gunicorn whitenoise dj-database-url psycopg2-binary
pip freeze > requirements.txt

# 1.5 - Créer railway.json à la racine du projet
{
  "build": {
    "builder": "NIXPACKS"
  },
  "deploy": {
    "startCommand": "python manage.py migrate && python manage.py collectstatic --noinput && gunicorn myproject.wsgi --bind 0.0.0.0:$PORT --workers 3",
    "restartPolicyType": "ON_FAILURE",
    "healthcheckPath": "/admin/login/"
  }
}
# Remplacer "myproject" par nom de votre projet!

# 1.6 - Créer endpoint health (optionnel mais recommandé)
# Dans views.py:
from django.http import JsonResponse

def health(request):
    return JsonResponse({'status': 'healthy'})

# Dans urls.py:
from myapp.views import health

urlpatterns = [
    path('health/', health),
    # ... autres URLs
]

# 1.7 - Vérifier .gitignore
# Doit contenir:
__pycache__/
*.py[cod]
.env
.env.local
db.sqlite3
/staticfiles/
/media/
.venv/
venv/

# 1.8 - Commiter tout dans Git
git init
git add .
git commit -m "Initial commit - Ready for Railway"

# ÉTAPE 2: Créer compte Railway

# 2.1 - Aller sur https://railway.app
# 2.2 - Sign up avec GitHub (recommandé) ou email
# 2.3 - Vérifier email si inscription par email
# 2.4 - Vous avez $5 crédit gratuit/mois!

# ÉTAPE 3: Installer Railway CLI

# 3.1 - Installer CLI
npm install -g @railway/cli
# Ou: curl -fsSL https://railway.app/install.sh | sh

# 3.2 - Vérifier installation
railway --version

# 3.3 - Se connecter
railway login
# Navigateur s'ouvre, autoriser Railway CLI

# 3.4 - Vérifier connexion
railway whoami
# Doit afficher votre email

# ÉTAPE 4: Créer projet Railway et déployer

# 4.1 - Dans dossier de votre projet Django
cd mon-projet-django

# 4.2 - Initialiser projet Railway
railway init --name "Mon Projet Django"
# Railway détecte Python automatiquement

# 4.3 - Ajouter PostgreSQL
railway add --template postgres
# Railway crée service PostgreSQL et génère variables

# 4.4 - Configurer variables d'environnement
railway variables set DEBUG=False
railway variables set SECRET_KEY=$(python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())')
railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'

# 4.5 - Déployer!
railway up
# Railway va:
# - Envoyer code
# - Installer dépendances (requirements.txt)
# - Migrer DB
# - Collecter static files
# - Lancer Gunicorn
# - Générer URL .up.railway.app

# Attendre que build termine... (~2-5 minutes première fois)

# ÉTAPE 5: Tester déploiement

# 5.1 - Ouvrir app dans navigateur
railway open
# Ou copier URL depuis logs

# 5.2 - Vérifier page d'accueil s'affiche
# Si erreur 404 ou 500, voir logs:
railway logs -f

# 5.3 - Créer superuser Django
railway run python manage.py createsuperuser
# Suivre instructions, créer admin

# 5.4 - Tester admin Django
# Aller sur: https://votre-app.up.railway.app/admin
# Login avec superuser créé
# [OK] Si admin accessible, déploiement réussi!

# ÉTAPE 6: Connecter GitHub pour auto-deploy

# 6.1 - Pusher code sur GitHub
git remote add origin https://github.com/username/mon-projet.git
git branch -M main
git push -u origin main

# 6.2 - Dans Railway dashboard
# - Aller sur https://railway.app
# - Sélectionner votre projet
# - Service -> Settings
# - Section "Source"
# - "Connect GitHub repository"
# - Autoriser Railway
# - Sélectionner votre repo
# - Branch: main
# - [OK] Enable automatic deployments

# 6.3 - Maintenant, chaque git push déploie automatiquement!
# Tester:
# - Modifier fichier (ex: views.py)
git add .
git commit -m "Test auto-deploy"
git push
# - Railway détecte push et redéploie automatiquement
# - Email de notification envoyé

# FÉLICITATIONS! Votre app Django est en production! [BRAVO]

# === WORKFLOW 2: App FastAPI avec Redis ===

# Déployer API FastAPI avec cache Redis

# ÉTAPE 1: Préparer projet localement

# Structure projet:
# mon-api/
# ├── main.py
# ├── requirements.txt
# ├── railway.json
# └── .gitignore

# main.py:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import redis
import os

app = FastAPI()

# CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

# Redis connection
redis_client = redis.from_url(
    os.getenv('REDIS_URL', 'redis://localhost:6379')
)

@app.get("/")
def read_root():
    return {"message": "API is running"}

@app.get("/health")
def health():
    try:
        redis_client.ping()
        return {"status": "healthy", "redis": "connected"}
    except:
        return {"status": "unhealthy", "redis": "disconnected"}

@app.get("/count")
def increment_counter():
    count = redis_client.incr('api_calls')
    return {"count": count}

# requirements.txt:
fastapi==0.104.1
uvicorn[standard]==0.24.0
redis==5.0.1
python-dotenv==1.0.0

# railway.json:
{
  "deploy": {
    "startCommand": "uvicorn main:app --host 0.0.0.0 --port $PORT --workers 2",
    "healthcheckPath": "/health",
    "restartPolicyType": "ON_FAILURE"
  }
}

# ÉTAPE 2: Déployer sur Railway

railway init --name "FastAPI Redis API"
railway add --template redis
railway variables set REDIS_URL='${{Redis.REDIS_URL}}'
railway up

# ÉTAPE 3: Tester
railway open
# Visiter /health -> doit montrer redis connected

# === WORKFLOW 3: Full-stack Next.js + Django ===

# Architecture:
# - Frontend: Next.js (Railway service 1)
# - Backend API: Django REST (Railway service 2)
# - Database: PostgreSQL (Railway service 3)

# ÉTAPE 1: Déployer Backend Django d'abord

# Suivre WORKFLOW 1 ci-dessus
# Important: Activer CORS dans Django
# settings.py:
INSTALLED_APPS += ['corsheaders']
MIDDLEWARE = ['corsheaders.middleware.CorsMiddleware', ...autres]
CORS_ALLOWED_ORIGINS = [
    "https://votre-frontend.up.railway.app",
    "http://localhost:3000",  # Développement local
]

# ÉTAPE 2: Préparer Frontend Next.js

# Structure:
# mon-frontend/
# ├── pages/
# ├── package.json
# ├── railway.json
# └── .env.example

# .env.example:
NEXT_PUBLIC_API_URL=https://backend.up.railway.app

# railway.json:
{
  "build": {
    "builder": "NIXPACKS"
  },
  "deploy": {
    "startCommand": "npm start",
    "restartPolicyType": "ON_FAILURE"
  }
}

# ÉTAPE 3: Déployer Frontend

railway init --name "Frontend Next.js"
railway variables set NEXT_PUBLIC_API_URL=https://backend-xxx.up.railway.app
railway up

# Les deux services communiquent maintenant!

# === WORKFLOW 4: Projet avec Celery (tasks asynchrones) ===

# Architecture:
# - Web (Django)
# - Worker (Celery)
# - PostgreSQL
# - Redis (broker Celery)

# ÉTAPE 1: Configurer Celery dans Django

# myproject/celery.py:
from celery import Celery
import os

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
app = Celery('myproject')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()

# settings.py:
CELERY_BROKER_URL = os.getenv('REDIS_URL')
CELERY_RESULT_BACKEND = os.getenv('REDIS_URL')

# myproject/__init__.py:
from .celery import app as celery_app
__all__ = ('celery_app',)

# tasks.py (dans une app):
from celery import shared_task

@shared_task
def send_email_task(user_email):
    # Envoyer email
    print(f"Sending email to {user_email}")
    return True

# ÉTAPE 2: Créer deux services Railway

# Service 1: Web (Django)
# railway.json:
{
  "deploy": {
    "startCommand": "python manage.py migrate && gunicorn myproject.wsgi --bind 0.0.0.0:$PORT"
  }
}

railway init --name "Django Web"
railway add --template postgres
railway add --template redis
railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'
railway variables set REDIS_URL='${{Redis.REDIS_URL}}'
railway up

# Service 2: Worker (Celery)
# Créer NOUVEAU service dans même projet
# Dashboard -> New Service -> Link existing repo
# Sélectionner même repo GitHub
# 
# railway.json (dans dossier worker/ ou root):
{
  "deploy": {
    "startCommand": "celery -A myproject worker -l info"
  }
}

# Variables pour worker (mêmes que web):
railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'
railway variables set REDIS_URL='${{Redis.REDIS_URL}}'

# Maintenant:
# - Web reçoit requêtes HTTP
# - Web crée tasks Celery (stockées dans Redis)
# - Worker exécute tasks en arrière-plan
# - Parfait pour emails, PDFs, scraping, etc.

# === WORKFLOW 5: Migration progressive Heroku -> Railway ===

# Migrer sans downtime (temps d'arrêt)

# PHASE 1: Préparation (1 jour)
# - Créer projet Railway
# - Déployer sur Railway en parallèle de Heroku
# - Tester TOUT sur Railway
# - Ne PAS toucher DNS encore

# PHASE 2: Synchronisation DB (1-2 heures)
# - Mettre Heroku en "maintenance mode"
heroku maintenance:on
# - Backup Heroku DB
heroku pg:backups:capture
heroku pg:backups:download
# - Import dans Railway
railway run pg_restore -d $DATABASE_URL latest.dump
# - Vérifier données copiées
railway run python manage.py shell
>>> User.objects.count()

# PHASE 3: Switch DNS (15-60 minutes)
# - Changer CNAME chez registrar
# - Pointer vers Railway
# - Attendre propagation DNS
# - Tester nouveau domaine

# PHASE 4: Cleanup (1 jour plus tard)
# - Vérifier tout fonctionne sur Railway
# - Garder Heroku actif 24-48h (au cas où)
# - Si OK, supprimer app Heroku
heroku apps:destroy

# PHASE 5: Monitoring (1 semaine)
# - Surveiller metrics Railway
# - Vérifier pas d'erreurs
# - Ajuster ressources si nécessaire

# === WORKFLOW 6: Setup environnements multiples ===

# Development -> Staging -> Production

# ÉTAPE 1: Créer environnements
# Dashboard -> Environments -> Create
# Créer: development, staging, production

# ÉTAPE 2: Configurer chaque environnement

# Development (LOCAL + Railway dev):
railway environment development
railway variables set DEBUG=True
railway variables set ALLOWED_HOSTS=*
# Base de données SQLite locale suffit

# Staging (tests avant prod):
railway environment staging
railway add --template postgres
railway variables set DEBUG=False
railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'

# Production (utilisateurs réels):
railway environment production
railway add --template postgres
railway add --template redis
railway variables set DEBUG=False
railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'
railway variables set REDIS_URL='${{Redis.REDIS_URL}}'

# ÉTAPE 3: Workflow déploiement

# 1. Développer localement
python manage.py runserver

# 2. Tester sur staging
railway environment staging
railway up
# Tester tout fonctionne

# 3. Si OK, déployer production
railway environment production
railway up

# 4. Si bug en prod, rollback
# Dashboard -> Deployments -> Redeploy version précédente

# === WORKFLOW 7: CI/CD avec GitHub Actions ===

# Automatiser tests + déploiement

# .github/workflows/deploy.yml:
name: Deploy to Railway

on:
  push:
    branches: [main]
  pull_request:
    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-django
      
      - name: Run tests
        run: pytest
      
      - name: Lint code
        run: |
          pip install flake8
          flake8 . --count --select=E9,F63,F7,F82 --show-source

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Install Railway CLI
        run: npm install -g @railway/cli
      
      - name: Deploy to Railway
        run: railway up --detach
        env:
          RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}

# Configuration:
# 1. Créer token Railway: Dashboard -> Settings -> Tokens
# 2. GitHub -> Settings -> Secrets -> New secret
# 3. Name: RAILWAY_TOKEN, Value: le token
# 
# Maintenant:
# - Push sur main -> Tests automatiques -> Deploy si tests OK
# - Pull Request -> Tests automatiques seulement

# Logs de build
railway logs --build

# Erreurs communes:

# 1. Port binding incorrect
# [X] Mauvais: app.run(port=5000)
# [OK] Bon: app.run(host='0.0.0.0', port=int(os.getenv('PORT', 5000)))

# 2. Build command manquant
# Ajouter dans railway.json:
{
  "build": {
    "buildCommand": "pip install -r requirements.txt"
  }
}

# 3. Start command incorrect
railway logs
# Vérifier que la commande démarre correctement

# Tester localement:
railway run gunicorn myapp.wsgi

# 4. Dépendances manquantes
# Vérifier requirements.txt à jour:
pip freeze > requirements.txt

# === Application crash au démarrage ===

# Vérifier logs détaillés
railway logs -f

# Erreurs communes:

# 1. Variables d'environnement manquantes
railway variables
# Ajouter variables manquantes:
railway variables set KEY=value

# 2. Migration DB non exécutée
railway run python manage.py migrate

# 3. Port non défini
# Toujours utiliser variable PORT:
PORT = int(os.getenv('PORT', 8000))

# 4. Permissions fichiers
# Vérifier que app a droits lecture/écriture

# === Base de données inaccessible ===

# Vérifier connexion
railway run psql $DATABASE_URL

# Test connexion Python:
railway run python << EOF
import os
import psycopg2
try:
    conn = psycopg2.connect(os.getenv('DATABASE_URL'))
    print("[OK] Connexion réussie")
    conn.close()
except Exception as e:
    print(f"[X] Erreur: {e}")
EOF

# Vérifier variables DB
railway variables | grep -E '(DATABASE|POSTGRES|PG)'

# Recréer service DB si nécessaire
# Dashboard -> Service -> Settings -> Delete Service
railway add --template postgres

# === Variables d'environnement non chargées ===

# Vérifier variables définies
railway variables

# Vérifier environnement actif
railway environment

# Recharger service après changement variables
# Dashboard -> Service -> Restart

# === Domaine personnalisé ne fonctionne pas ===

# 1. Vérifier DNS propagation
dig www.example.com
nslookup www.example.com

# 2. Vérifier configuration CNAME
# Doit pointer vers: <service>.up.railway.app

# 3. Attendre propagation DNS (jusqu'à 48h)

# 4. Vérifier certificat SSL
# Railway génère automatiquement via Let's Encrypt

# === CLI ne se connecte pas ===

# Vérifier authentification
railway whoami

# Re-login
railway logout
railway login

# Vérifier version CLI
railway --version

# Mettre à jour CLI
npm update -g @railway/cli

# === Service lent ou timeout ===

# Vérifier metrics
# Dashboard -> Service -> Metrics

# Augmenter ressources si nécessaire
# Settings -> Resources

# Optimiser application:
# - Ajouter caching (Redis)
# - Optimiser requêtes DB
# - Utiliser CDN pour assets
# - Activer gzip compression

# === Out of Memory (OOM) ===

# Logs montrent: "Killed" ou "OOM"

# Solutions:
# 1. Augmenter RAM
# Dashboard -> Settings -> Resources

# 2. Optimiser code
# - Réduire workers Gunicorn
# - Limiter taille uploads
# - Utiliser pagination
# - Nettoyer objets non utilisés

# 3. Monitoring mémoire
import psutil
print(f"Memory usage: {psutil.virtual_memory().percent}%")

# === Build timeout ===

# Build prend trop de temps (>10 min)

# Solutions:
# 1. Optimiser dépendances
# Supprimer packages inutilisés

# 2. Utiliser build cache
# Railway cache automatiquement

# 3. Réduire taille image Docker
# Utiliser images slim/alpine

# === Problèmes de networking ===

# Service ne peut pas atteindre autre service

# Utiliser domaine privé:
http://service-name.railway.internal

# Ou variable de référence:
DATABASE_URL=${{Postgres.DATABASE_URL}}

# Vérifier que services sont dans même projet

# === Logs incomplets ===

# Forcer flush des logs
import sys
print("Message", flush=True)

# Python logging
import logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[logging.StreamHandler(sys.stdout)]
)

# === Token CLI expiré ===

railway login
# Ou regénérer token dans dashboard

# === Projet non trouvé ===

railway list
railway link <PROJECT_ID>

# === Rollback déploiement ===

# Via dashboard:
# Service -> Deployments -> Sélectionner déploiement précédent -> Redeploy

# Pas de commande CLI directe pour rollback


[OK] BONNES PRATIQUES

# === 1. Structure projet ===

myproject/
├── .railway/              # Config Railway locale
├── app/                   # Code source
├── tests/                 # Tests
├── .env.example           # Variables exemple
├── .gitignore
├── railway.json           # Config Railway
├── requirements.txt       # Dépendances production
├── requirements-dev.txt   # Dépendances dev
└── README.md

# === 2. Environnements multiples ===

# Toujours avoir:
# - production (main branch)
# - staging (develop branch)
# - development (local)

# Déployer staging avant production
railway up --environment staging
# Tester
railway up --environment production

# === 3. Variables d'environnement ===

# [OK] Utiliser variables pour tout
# [OK] Ne JAMAIS commiter secrets
# [OK] Documenter dans .env.example
# [OK] Utiliser références pour inter-service

# .env.example
DEBUG=False
SECRET_KEY=change-me-in-production
DATABASE_URL=${{Postgres.DATABASE_URL}}
REDIS_URL=${{Redis.REDIS_URL}}
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=

# === 4. Base de données ===

# [OK] Toujours utiliser migrations
# [OK] Backups réguliers
# [OK] Tester migrations sur staging d'abord
# [OK] Éviter modifications destructives

# Django migrations workflow:
railway run --environment staging python manage.py migrate
# Vérifier que tout fonctionne
railway run --environment production python manage.py migrate

# === 5. Monitoring ===

# [OK] Implémenter endpoint /health
# [OK] Logger erreurs importantes
# [OK] Surveiller metrics
# [OK] Configurer alertes

# Flask health endpoint:
@app.route('/health')
def health():
    try:
        # Vérifier DB
        db.session.execute('SELECT 1')
        # Vérifier Redis
        redis_client.ping()
        return {'status': 'healthy'}, 200
    except Exception as e:
        return {'status': 'unhealthy', 'error': str(e)}, 503

# === 6. Sécurité ===

# [OK] Activer HTTPS uniquement
# [OK] Utiliser variables pour secrets
# [OK] Scanner vulnérabilités régulièrement
# [OK] Limiter rate limiting
# [OK] Valider inputs utilisateur

# Django settings.py:
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000

# === 7. Performance ===

# [OK] Utiliser caching (Redis)
# [OK] Optimiser requêtes DB
# [OK] Compression gzip
# [OK] CDN pour assets statiques
# [OK] Pagination pour listes

# Gunicorn avec workers:
gunicorn myapp.wsgi --bind 0.0.0.0:$PORT --workers 4 --timeout 120

# === 8. Déploiements ===

# [OK] Tester localement d'abord
# [OK] Déployer staging avant production
# [OK] CI/CD pour automatiser
# [OK] Versionner avec Git tags

# === 9. Coûts ===

# [OK] Surveiller usage mensuel
# [OK] Scale down services non-critiques
# [OK] Optimiser images Docker
# [OK] Utiliser storage externe pour fichiers

# === 10. Documentation ===

# README.md doit contenir:
# - Setup local
# - Variables d'environnement requises
# - Commandes déploiement
# - Architecture services
# - Procédures rollback


[OK] EXEMPLES AVANCÉS

# === Django avec Celery ===

# Structure services:
# 1. Web (Django)
# 2. Worker (Celery)
# 3. Redis (message broker)
# 4. PostgreSQL

# requirements.txt
django>=4.2
celery>=5.3
redis>=5.0
psycopg2-binary>=2.9
gunicorn>=21.0

# myproject/celery.py
from celery import Celery
import os

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
app = Celery('myproject')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()

# settings.py
CELERY_BROKER_URL = os.getenv('REDIS_URL')
CELERY_RESULT_BACKEND = os.getenv('REDIS_URL')

# Web service railway.json
{
  "deploy": {
    "startCommand": "gunicorn myproject.wsgi --bind 0.0.0.0:$PORT"
  }
}

# Worker service railway.json (créer service séparé)
{
  "deploy": {
    "startCommand": "celery -A myproject worker -l info"
  }
}

# Variables Railway:
railway variables set REDIS_URL='${{Redis.REDIS_URL}}'
railway variables set DATABASE_URL='${{Postgres.DATABASE_URL}}'

# === FastAPI avec Background Tasks ===

# main.py
from fastapi import FastAPI, BackgroundTasks
from sqlalchemy import create_engine
import os

DATABASE_URL = os.getenv('DATABASE_URL')
engine = create_engine(DATABASE_URL)

app = FastAPI()

def process_task(data: dict):
    # Traitement long
    pass

@app.post("/tasks/")
async def create_task(data: dict, background_tasks: BackgroundTasks):
    background_tasks.add_task(process_task, data)
    return {"message": "Task added"}

@app.get("/health")
def health():
    return {"status": "healthy"}

# railway.json
{
  "deploy": {
    "startCommand": "uvicorn main:app --host 0.0.0.0 --port $PORT --workers 4",
    "healthcheckPath": "/health"
  }
}

# === Next.js avec API Backend ===

# Structure:
# Service 1: Next.js frontend
# Service 2: Python/FastAPI backend
# Service 3: PostgreSQL

# Next.js .env
NEXT_PUBLIC_API_URL=${{Backend.RAILWAY_PUBLIC_DOMAIN}}

# Backend disponible à:
# https://backend-production.up.railway.app

# === Multi-tenant SaaS ===

# Structure:
# - Main API
# - Admin dashboard
# - Worker services
# - Shared PostgreSQL
# - Redis cache

# Utiliser schemas PostgreSQL pour isolation:
CREATE SCHEMA tenant_1;
CREATE SCHEMA tenant_2;

# Django settings.py:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'OPTIONS': {
            'options': f'-c search_path={get_tenant_schema()}'
        }
    }
}

# === Microservices Architecture ===

# Services Railway:
# 1. API Gateway (routing)
# 2. Auth Service (authentification)
# 3. User Service (utilisateurs)
# 4. Payment Service (paiements)
# 5. Notification Service (emails/SMS)
# 6. PostgreSQL (shared)
# 7. Redis (cache/sessions)

# Communication inter-services:
# Via domaines privés: service.railway.internal
# Ou via API Gateway

# API Gateway (Nginx/Traefik):
upstream auth {
    server auth.railway.internal:8000;
}

upstream users {
    server users.railway.internal:8000;
}

location /api/auth {
    proxy_pass http://auth;
}

location /api/users {
    proxy_pass http://users;
}


[OK] INTÉGRATIONS EXTERNES

# === Sentry (Error Tracking) ===

# 1. Créer projet Sentry: https://sentry.io

# 2. Installer SDK
pip install sentry-sdk

# 3. Configurer
# settings.py (Django)
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

sentry_sdk.init(
    dsn=os.getenv('SENTRY_DSN'),
    integrations=[DjangoIntegration()],
    traces_sample_rate=1.0,
    send_default_pii=True
)

# 4. Ajouter variable Railway
railway variables set SENTRY_DSN=https://xxx@sentry.io/xxx

# === Stripe (Payments) ===

railway variables set STRIPE_PUBLIC_KEY=pk_xxx
railway variables set STRIPE_SECRET_KEY=sk_xxx
railway variables set STRIPE_WEBHOOK_SECRET=whsec_xxx

# Configurer webhook Stripe:
# URL: https://your-app.up.railway.app/api/stripe/webhook

# === SendGrid (Emails) ===

railway variables set SENDGRID_API_KEY=SG.xxx

# Django settings.py:
EMAIL_BACKEND = 'sendgrid_backend.SendgridBackend'
SENDGRID_API_KEY = os.getenv('SENDGRID_API_KEY')

# === AWS S3 (Storage) ===

railway variables set AWS_ACCESS_KEY_ID=xxx
railway variables set AWS_SECRET_ACCESS_KEY=xxx
railway variables set AWS_STORAGE_BUCKET_NAME=mybucket
railway variables set AWS_S3_REGION_NAME=us-east-1

# Django storages:
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

# === Cloudflare (CDN/DNS) ===

# 1. Ajouter domaine dans Cloudflare
# 2. Configurer CNAME:
#    Type: CNAME
#    Name: @
#    Content: your-app.up.railway.app
#    Proxy: Activé (orange cloud)

# 3. Railway détecte automatiquement proxy Cloudflare

# === Redis Cloud ===

# Alternative à Railway Redis pour plus de capacité

# 1. Créer DB: https://redis.com/cloud
# 2. Obtenir connection string
# 3. Configurer Railway:

railway variables set REDIS_URL=redis://user:pass@host:port

# === MongoDB Atlas ===

# Alternative à Railway MongoDB

railway variables set MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/db


[OK] PERFORMANCE OPTIMIZATION

# === 1. Gunicorn Configuration ===

# Calcul workers optimal:
# workers = (2 × CPU cores) + 1

# Pour 1 vCPU: 3 workers
# Pour 2 vCPU: 5 workers

gunicorn myapp.wsgi \
  --bind 0.0.0.0:$PORT \
  --workers 4 \
  --threads 2 \
  --worker-class gthread \
  --timeout 120 \
  --keep-alive 5 \
  --max-requests 1000 \
  --max-requests-jitter 50 \
  --access-logfile - \
  --error-logfile -

# === 2. Database Connection Pooling ===

# Django settings.py:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'CONN_MAX_AGE': 600,  # 10 minutes
    }
}

# SQLAlchemy:
from sqlalchemy import create_engine
engine = create_engine(
    DATABASE_URL,
    pool_size=10,
    max_overflow=20,
    pool_pre_ping=True,
    pool_recycle=3600
)

# === 3. Redis Caching ===

# Django cache:
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': os.getenv('REDIS_URL'),
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        },
        'KEY_PREFIX': 'myapp',
        'TIMEOUT': 300,
    }
}

# Flask-Caching:
from flask_caching import Cache
cache = Cache(app, config={
    'CACHE_TYPE': 'redis',
    'CACHE_REDIS_URL': os.getenv('REDIS_URL')
})

@cache.memoize(timeout=300)
def expensive_function():
    pass

# === 4. Static Files & CDN ===

# Django avec WhiteNoise:
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    # ...
]

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

# Ou utiliser CDN (Cloudflare, AWS CloudFront):
STATIC_URL = 'https://cdn.example.com/static/'

# === 5. Database Query Optimization ===

# Django: Utiliser select_related et prefetch_related
users = User.objects.select_related('profile').all()
posts = Post.objects.prefetch_related('comments').all()

# Indexer colonnes fréquemment requêtées:
class Meta:
    indexes = [
        models.Index(fields=['email']),
        models.Index(fields=['created_at']),
    ]

# === 6. Compression ===

# Gunicorn ne fait pas de compression
# Utiliser middleware ou reverse proxy

# Django compression:
MIDDLEWARE = [
    'django.middleware.gzip.GZipMiddleware',
    # ...
]

# Flask compression:
from flask_compress import Compress
Compress(app)

# === 7. Rate Limiting ===

# Flask-Limiter:
from flask_limiter import Limiter
limiter = Limiter(
    app,
    key_func=lambda: request.remote_addr,
    storage_uri=os.getenv('REDIS_URL')
)

@app.route('/api')
@limiter.limit("100 per hour")
def api():
    pass

# Django Ratelimit:
from django_ratelimit.decorators import ratelimit

@ratelimit(key='ip', rate='100/h')
def my_view(request):
    pass


[OK] BACKUP ET DISASTER RECOVERY

# === Backup PostgreSQL ===

# 1. Backup manuel
railway run pg_dump $DATABASE_URL > backup.sql

# 2. Backup avec compression
railway run pg_dump $DATABASE_URL | gzip > backup.sql.gz

# 3. Backup automatisé (cron)
# Créer service séparé pour backups

# backup.sh
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
railway run pg_dump $DATABASE_URL | gzip > backup_$DATE.sql.gz
# Upload vers S3
aws s3 cp backup_$DATE.sql.gz s3://mybucket/backups/

# railway.json
{
  "deploy": {
    "cronJobs": [
      {
        "schedule": "0 2 * * *",
        "command": "./backup.sh"
      }
    ]
  }
}

# === Restore PostgreSQL ===

# 1. Télécharger backup
aws s3 cp s3://mybucket/backups/backup_20240101.sql.gz .
gunzip backup_20240101.sql.gz

# 2. Restore
railway run psql $DATABASE_URL < backup_20240101.sql

# Ou avec pg_restore pour format custom:
railway run pg_restore -d $DATABASE_URL backup.dump

# === Backup Redis ===

# Redis automatic persistence configuré par Railway
# Backups manuels:
railway run redis-cli --rdb backup.rdb

# === Point-in-Time Recovery ===

# PostgreSQL PITR nécessite service géré externe
# Utiliser AWS RDS, DigitalOcean Managed DB, etc.

# === Disaster Recovery Plan ===

# 1. Backups automatisés quotidiens
# 2. Tester restores régulièrement
# 3. Documenter procédure recovery
# 4. Avoir environnement staging identique
# 5. Monitoring et alertes


[OK] RESSOURCES

# Documentation officielle
https://docs.railway.app

# Railway Blog
https://blog.railway.app

# Railway Discord (support communauté)
https://discord.gg/railway

# Railway Status
https://status.railway.app

# Templates Railway
https://railway.app/templates

# Railway GitHub
https://github.com/railwayapp

# Guides et tutoriels:
# - Deploying Django: https://docs.railway.app/guides/django
# - Deploying FastAPI: https://docs.railway.app/guides/fastapi
# - Deploying Flask: https://docs.railway.app/guides/flask
# - Deploying Next.js: https://docs.railway.app/guides/nextjs

# Comparaisons:
# Railway vs Heroku: https://railway.app/heroku-alternative
# Railway vs Render: https://railway.app/render-alternative
# Railway vs Fly.io: https://railway.app/flyio-alternative

# Pricing calculator:
https://railway.app/pricing


[OK] COMMANDES RAPIDES (CHEAT SHEET)

# Installation et Auth
npm install -g @railway/cli
railway login
railway whoami

# Projets
railway init
railway list
railway link
railway open
railway status

# Déploiement
railway up
railway up -d
railway up --environment staging

# Variables
railway variables
railway variables set KEY=value
railway variables delete KEY
railway variables set --from-file .env

# Services
railway add
railway add --template postgres
railway connect postgres

# Logs et Debugging
railway logs
railway logs -f
railway logs --service api

# Exécution commandes
railway run python manage.py migrate
railway run python manage.py createsuperuser
railway shell

# Environnements
railway environment
railway environment production

# Domaines
railway domain

# Mise à jour CLI
npm update -g @railway/cli


# FIN DE LA CHEATSHEET RAILWAY.APP