diff --git a/PERFORMANCE_OPTIMIZATIONS.md b/PERFORMANCE_OPTIMIZATIONS.md deleted file mode 100644 index d84cba4..0000000 --- a/PERFORMANCE_OPTIMIZATIONS.md +++ /dev/null @@ -1,215 +0,0 @@ -# đ Optimisations de Performance - Portfolio - -## RĂ©sumĂ© des amĂ©liorations - -Ce document liste toutes les optimisations effectuĂ©es pour amĂ©liorer les performances du site, notamment le **LCP (Largest Contentful Paint)** et le **temps de chargement initial**. - ---- - -## â Optimisations effectuĂ©es - -### 1. đŒïž **Optimisation des images (93% de rĂ©duction)** -- â Conversion de toutes les images PNG/JPG en WebP -- â RĂ©duction de **45.52 MB** Ă **2.81 MB** (Ă©conomie de 42.7 MB) -- â RĂ©duction de **93.83%** de la taille totale des images - -**Impact:** Temps de tĂ©lĂ©chargement des images divisĂ© par ~16x - ---- - -### 2. ⥠**Optimisation du chemin critique de rendu** - -#### a) Defer Google Tag Manager -- â GTM chargĂ© aprĂšs le `window.load` event -- â Ne bloque plus le rendu initial - -#### b) DNS Prefetch & Preconnect -- â Ajout de `dns-prefetch` pour Google Fonts et GTM -- â Ajout de `preconnect` pour Ă©tablir les connexions plus tĂŽt -- â RĂ©duction de la latence rĂ©seau - -**Avant:** -``` -Latence de chemin d'accĂšs critique maximale : 279 ms -``` - -**AprĂšs (attendu):** -``` -Latence de chemin d'accĂšs critique maximale : < 150 ms -``` - ---- - -### 3. đŠ **Code Splitting et Lazy Loading** - -#### Lazy Loading des composants lourds -- â Pages chargĂ©es Ă la demande (Index, ProjectPage, NotFound) -- â ParticlesBackground chargĂ© de maniĂšre asynchrone -- â RĂ©duction du bundle initial de ~40% - -#### Optimisation Vite Build -- â SĂ©paration des vendors (react, ui, particles) -- â Nommage cohĂ©rent des chunks pour meilleur cache -- â Target ES Next pour code plus moderne et compact -- â CSS Code Splitting activĂ© - -**Bundle avant:** -``` -index.js: ~200 KB -Total initial: ~250 KB -``` - -**Bundle aprĂšs (attendu):** -``` -index.js: ~100 KB -react-vendor.js: ~50 KB (mis en cache) -ui-vendor.js: ~30 KB (mis en cache) -Total initial: ~130 KB -``` - ---- - -### 4. đïž **Compression et Cache Nginx** - -#### Headers de cache optimisĂ©s -- â Images WebP: cache 1 an (immutable) -- â CSS/JS avec hash: cache 1 an (immutable) -- â Fonts: cache 1 an -- â index.html: no-cache (toujours frais) - -#### Compression Gzip amĂ©liorĂ©e -- â `gzip_min_length 256` (ne compresse que les gros fichiers) -- â Support WASM -- â Ajout du header `Vary: Accept-Encoding` - -#### Headers de performance -- â `Strict-Transport-Security` pour HTTPS -- â Link headers pour preconnect dans index.html -- â Headers de sĂ©curitĂ© renforcĂ©s - ---- - -### 5. đ ïž **Outils de dĂ©veloppement** - -#### Scripts utiles -```bash -# Optimiser les images -npm run optimize-images - -# Build et analyser le bundle -npm run build:analyze - -# Analyser le bundle -npm run analyze -``` - -#### Composant OptimizedImage -- â Lazy loading natif des images -- â Transition smooth Ă l'apparition -- â Support du mode prioritaire pour les images critiques - ---- - -## đ RĂ©sultats attendus - -### Core Web Vitals - -| MĂ©trique | Avant | AprĂšs (attendu) | AmĂ©lioration | -|----------|-------|-----------------|--------------| -| **LCP** | 2.5s | < 1.5s | đą 40% | -| **FID** | < 100ms | < 50ms | đą 50% | -| **CLS** | 0.1 | < 0.05 | đą 50% | - -### Autres mĂ©triques - -| MĂ©trique | Avant | AprĂšs | AmĂ©lioration | -|----------|-------|-------|--------------| -| **Taille images** | 45.52 MB | 2.81 MB | đą 93.8% | -| **Bundle initial** | ~250 KB | ~130 KB | đą 48% | -| **Latence critique** | 279 ms | < 150 ms | đą 46% | -| **Temps de chargement** | 3-4s | < 2s | đą 50% | - ---- - -## đ Prochaines Ă©tapes (optionnel) - -### Optimisations supplĂ©mentaires possibles - -1. **Service Worker & PWA** - - Mise en cache offline - - StratĂ©gies de cache avancĂ©es - -2. **Brotli Compression** - - Compression encore meilleure que Gzip (~20% de gain) - - Requiert module nginx-brotli - -3. **HTTP/2 Server Push** - - Push des ressources critiques - - Requiert configuration Traefik - -4. **Image Responsive** - - GĂ©nĂ©rer plusieurs tailles d'images - - Utiliser srcset pour diffĂ©rentes rĂ©solutions - -5. **CDN** - - Distribution gĂ©ographique des assets - - Cloudflare ou autre CDN - ---- - -## đ DĂ©ploiement - -Pour appliquer toutes ces optimisations: - -```bash -# Sur votre serveur -docker-compose down -docker-compose up -d --build - -# Ou via Portainer -# 1. Aller dans Stacks -# 2. SĂ©lectionner portfolio -# 3. Update stack / Pull and redeploy -``` - ---- - -## đ Monitoring - -### Outils recommandĂ©s pour tester - -1. **Google PageSpeed Insights** - - https://pagespeed.web.dev/ - - Tester avant/aprĂšs - -2. **WebPageTest** - - https://www.webpagetest.org/ - - Tests dĂ©taillĂ©s de performance - -3. **Lighthouse (Chrome DevTools)** - - F12 > Lighthouse > Analyze - - Tests locaux et rapides - -4. **GTmetrix** - - https://gtmetrix.com/ - - Vue d'ensemble complĂšte - ---- - -## đŻ Checklist de vĂ©rification - -AprĂšs dĂ©ploiement, vĂ©rifier: - -- [ ] Les images WebP se chargent correctement -- [ ] Le site charge en < 2 secondes -- [ ] Les fonts Google Fonts s'affichent sans FOUT -- [ ] GTM est bien chargĂ© (vĂ©rifier dans Network tab) -- [ ] Les pages se chargent en lazy (vĂ©rifier dans Network) -- [ ] Le cache fonctionne (2Ăšme visite instantanĂ©e) -- [ ] Score Lighthouse > 90 - ---- - -**Créé le:** 2 octobre 2025 -**Auteur:** GitHub Copilot -**Version:** 1.0 diff --git a/PORTAINER_DEPLOYMENT.md b/PORTAINER_DEPLOYMENT.md deleted file mode 100644 index 9833194..0000000 --- a/PORTAINER_DEPLOYMENT.md +++ /dev/null @@ -1,315 +0,0 @@ -# đ Guide de DĂ©ploiement sur Portainer - -## đ PrĂ©requis - -1. Portainer installĂ© et accessible -2. Traefik configurĂ© avec le rĂ©seau `portfolio` -3. Git installĂ© sur le serveur Docker -4. AccĂšs Ă votre repository GitHub - ---- - -## đ§ Option 1 : DĂ©ploiement via Stack Portainer (RecommandĂ©) - -### Ătape 1 : Connexion Ă Portainer -1. Ouvrez votre interface Portainer (ex: https://portainer.votredomaine.com) -2. Connectez-vous avec vos identifiants - -### Ătape 2 : CrĂ©er une nouvelle Stack -1. Dans le menu de gauche, cliquez sur **"Stacks"** -2. Cliquez sur **"+ Add stack"** -3. Donnez un nom : `portfolio-website` - -### Ătape 3 : Configuration de la Stack - -**Option A - DĂ©ploiement depuis Git (RecommandĂ©) :** - -1. SĂ©lectionnez **"Repository"** sous "Build method" -2. Remplissez les champs : - - **Repository URL** : `https://github.com/kinou-p/portfolio-website` - - **Repository reference** : `refs/heads/main` - - **Compose path** : `docker-compose.yml` - -3. **Si votre repository est PRIVĂ** : - - **MĂ©thode 1 - Personal Access Token (RecommandĂ©) :** - - â Cochez **"Authentication"** - - **Username** : `kinou-p` - - **Personal Access Token** : Votre token GitHub (ex: `ghp_xxxxxxxxxxxx`) - - Pour crĂ©er un token : - - GitHub â Settings â Developer settings â Personal access tokens â Tokens (classic) - - Generate new token â Cochez `repo` â Generate - - **MĂ©thode 2 - SSH Deploy Key (Plus sĂ©curisĂ©) :** - - Repository URL : `git@github.com:kinou-p/portfolio-website.git` - - Authentication : SSH - - Collez votre clĂ© SSH privĂ©e - - Pour crĂ©er une deploy key : - ```bash - ssh-keygen -t ed25519 -C "portainer" -f ~/.ssh/portainer_deploy - # Ajoutez la clĂ© publique (.pub) dans GitHub â Settings â Deploy keys - ``` - -4. **Si votre repository est PUBLIC** : - - Pas besoin d'authentification - - Laissez "Authentication" dĂ©cochĂ© - -**Option B - Copier/Coller le docker-compose.yml :** - -1. SĂ©lectionnez **"Web editor"** -2. Copiez-collez le contenu du fichier `docker-compose.yml` - -### Ătape 4 : Variables d'environnement (optionnel) - -Cliquez sur **"Advanced mode"** et ajoutez si nĂ©cessaire : -``` -GTM_ID=GTM-5V6TCG4C -NODE_ENV=production -``` - -### Ătape 5 : DĂ©ployer - -1. VĂ©rifiez que le rĂ©seau `portfolio` existe -2. Cliquez sur **"Deploy the stack"** -3. Attendez la fin du build (peut prendre 2-5 minutes) - -### Ătape 6 : VĂ©rification - -1. Allez dans **"Containers"** -2. VĂ©rifiez que `portfolio-website` est **"running"** (vert) -3. Cliquez sur le container pour voir les logs -4. Testez votre site : https://alexandre-pommier.com - ---- - -## đ§ Option 2 : DĂ©ploiement via Container Portainer - -### Ătape 1 : Construire l'image en local - -```bash -# Sur votre machine locale -cd /path/to/portfolio-website -docker build -t portfolio-website:latest . - -# Tag pour votre registry (optionnel) -docker tag portfolio-website:latest votre-registry/portfolio-website:latest - -# Push vers votre registry -docker push votre-registry/portfolio-website:latest -``` - -### Ătape 2 : CrĂ©er le container dans Portainer - -1. Dans Portainer, allez dans **"Containers"** -2. Cliquez sur **"+ Add container"** -3. Remplissez les champs : - -**Configuration de base :** -- **Name** : `portfolio-website` -- **Image** : `portfolio-website:latest` (ou `votre-registry/portfolio-website:latest`) -- **Always pull the image** : CochĂ© -- **Restart policy** : `Unless stopped` - -**Network :** -- **Network** : SĂ©lectionnez `portfolio` - -**Labels (pour Traefik) :** - -Cliquez sur **"+ add label"** et ajoutez : - -``` -traefik.enable=true -traefik.http.routers.portfolio-website.rule=Host(`alexandre-pommier.com`) || Host(`www.alexandre-pommier.com`) -traefik.http.routers.portfolio-website.entrypoints=websecure -traefik.http.routers.portfolio-website.tls=true -traefik.http.routers.portfolio-website.tls.certresolver=letsencrypt -traefik.http.services.portfolio-website.loadbalancer.server.port=80 -traefik.http.routers.portfolio-website-http.rule=Host(`alexandre-pommier.com`) || Host(`www.alexandre-pommier.com`) -traefik.http.routers.portfolio-website-http.entrypoints=web -traefik.http.routers.portfolio-website-http.middlewares=redirect-to-https -traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https -traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true -``` - -4. Cliquez sur **"Deploy the container"** - ---- - -## đ Mise Ă jour du Portfolio - -### Via Stack (Option 1) - -1. Allez dans **"Stacks"** -2. Cliquez sur votre stack `portfolio-website` -3. Cliquez sur **"Pull and redeploy"** ou **"Git pull and redeploy"** -4. Confirmez l'action - -### Via Webhook (Automatique - RecommandĂ©) - -1. Dans votre stack, allez dans **"Webhooks"** -2. Cliquez sur **"+ Add webhook"** -3. Donnez un nom : `github-auto-deploy` -4. Copiez l'URL gĂ©nĂ©rĂ©e (ex: `https://portainer.com/api/webhooks/xxx`) - -5. Sur GitHub : - - Allez dans **Settings** > **Webhooks** > **Add webhook** - - Collez l'URL Portainer - - Content type : `application/json` - - ĂvĂ©nements : `Just the push event` - - Active : CochĂ© - - Cliquez sur **"Add webhook"** - -Maintenant, chaque `git push` sur `main` dĂ©clenchera automatiquement un redĂ©ploiement ! đ - ---- - -## đ Monitoring et Logs - -### Voir les logs en temps rĂ©el - -1. Allez dans **"Containers"** -2. Cliquez sur `portfolio-website` -3. Cliquez sur **"Logs"** -4. Activez **"Auto-refresh"** - -### Statistiques de performance - -1. Dans le container, cliquez sur **"Stats"** -2. Visualisez CPU, RAM, Network en temps rĂ©el - -### Health Check - -Le container inclut un health check qui vĂ©rifie : -- Toutes les 30 secondes -- Si Nginx rĂ©pond sur le port 80 -- 3 tentatives avant de marquer comme "unhealthy" - ---- - -## đ ïž Commandes utiles - -### Reconstruire l'image aprĂšs changement - -```bash -# Si vous utilisez Stack avec Git -# Portainer fait tout automatiquement avec "Git pull and redeploy" - -# Si vous gĂ©rez manuellement -docker-compose build --no-cache -docker-compose up -d -``` - -### Voir les containers actifs - -```bash -docker ps -``` - -### AccĂ©der aux logs - -```bash -docker logs portfolio-website -f -``` - -### RedĂ©marrer le container - -```bash -docker restart portfolio-website -``` - -### Supprimer et recrĂ©er - -```bash -docker-compose down -docker-compose up -d --build -``` - ---- - -## đ SĂ©curitĂ© - -### Variables sensibles - -Si vous avez des secrets (API keys, etc.), utilisez les **Secrets** de Portainer : - -1. Allez dans **"Secrets"** -2. CrĂ©ez un nouveau secret -3. RĂ©fĂ©rencez-le dans votre stack avec `secrets:` - -### Limiter les ressources - -Dans le docker-compose, ajoutez : - -```yaml -deploy: - resources: - limits: - cpus: '0.5' - memory: 512M - reservations: - cpus: '0.25' - memory: 256M -``` - ---- - -## â Checklist de dĂ©ploiement - -- [ ] Portainer accessible et connectĂ© -- [ ] RĂ©seau `portfolio` créé -- [ ] Traefik configurĂ© et fonctionnel -- [ ] Repository GitHub accessible -- [ ] Docker-compose.yml Ă jour sur le repo -- [ ] Stack créée dans Portainer -- [ ] Container dĂ©marrĂ© avec succĂšs -- [ ] Site accessible via HTTPS -- [ ] Webhook configurĂ© pour auto-deploy -- [ ] Logs vĂ©rifiĂ©s (pas d'erreurs) -- [ ] Health check en vert - ---- - -## đš Troubleshooting - -### Le container ne dĂ©marre pas - -1. VĂ©rifiez les logs : `docker logs portfolio-website` -2. VĂ©rifiez que le rĂ©seau `portfolio` existe -3. VĂ©rifiez que le port 80 n'est pas dĂ©jĂ utilisĂ© - -### Le site n'est pas accessible - -1. VĂ©rifiez que Traefik tourne : `docker ps | grep traefik` -2. VĂ©rifiez les labels Traefik dans Portainer -3. VĂ©rifiez les logs Traefik : `docker logs traefik` -4. VĂ©rifiez le DNS : `nslookup alexandre-pommier.com` - -### Le build Ă©choue - -1. VĂ©rifiez l'accĂšs au repository GitHub -2. VĂ©rifiez que le Dockerfile est prĂ©sent -3. VĂ©rifiez les logs de build dans Portainer -4. Essayez un build manuel : `docker build -t portfolio-website .` - -### Le certificat SSL ne se gĂ©nĂšre pas - -1. VĂ©rifiez que les ports 80 et 443 sont ouverts -2. VĂ©rifiez la configuration du rĂ©solveur Let's Encrypt dans Traefik -3. VĂ©rifiez les logs Traefik pour les erreurs ACME - ---- - -## đ Support - -Si vous rencontrez des problĂšmes : - -1. Consultez les logs du container -2. VĂ©rifiez la documentation Traefik -3. VĂ©rifiez la documentation Portainer -4. Ouvrez une issue sur GitHub - ---- - -Bonne chance avec votre dĂ©ploiement ! đ diff --git a/PORTAINER_QUICK_START.md b/PORTAINER_QUICK_START.md deleted file mode 100644 index b76aedf..0000000 --- a/PORTAINER_QUICK_START.md +++ /dev/null @@ -1,102 +0,0 @@ -# đ DĂ©ploiement Rapide sur Portainer - -## ⥠MĂ©thode Express (3 minutes) - -### 1ïžâŁ Connectez-vous Ă Portainer - -Ouvrez votre Portainer : `https://votre-portainer.com` - -### 2ïžâŁ CrĂ©ez une Stack - -1. Menu **Stacks** â **+ Add stack** -2. Nom : `portfolio-website` -3. SĂ©lectionnez **"Repository"** - -### 3ïžâŁ Configuration Git - -**Pour repository PUBLIC :** -``` -Repository URL: https://github.com/kinou-p/portfolio-website -Reference: refs/heads/main -Compose path: docker-compose.yml -Authentication: â DĂ©cochĂ© -``` - -**Pour repository PRIVĂ :** -``` -Repository URL: https://github.com/kinou-p/portfolio-website -Reference: refs/heads/main -Compose path: docker-compose.yml -Authentication: â CochĂ© -Username: kinou-p -Token: ghp_votre_token_github -``` - -**Comment obtenir un token GitHub :** -1. GitHub â Settings â Developer settings â Personal access tokens -2. Generate new token (classic) -3. Cochez `repo` â Generate -4. Copiez le token (commence par `ghp_`) - -### 4ïžâŁ DĂ©ployez - -Cliquez sur **"Deploy the stack"** â Attendez 2-5 min â C'est fait ! â - -### 5ïžâŁ VĂ©rifiez - -Allez sur : `https://alexandre-pommier.com` đ - ---- - -## đ Mise Ă jour automatique - -### Configurez le Webhook GitHub - -1. Dans Portainer : **Stacks** â `portfolio-website` â **Webhooks** â **+ Add webhook** -2. Copiez l'URL gĂ©nĂ©rĂ©e -3. Sur GitHub : **Settings** â **Webhooks** â **Add webhook** -4. Collez l'URL Portainer -5. ĂvĂ©nement : `push` - -Maintenant chaque `git push` met Ă jour automatiquement votre site ! đ - ---- - -## đ Documentation complĂšte - -Voir [PORTAINER_DEPLOYMENT.md](./PORTAINER_DEPLOYMENT.md) pour : -- Guide dĂ©taillĂ© Ă©tape par Ă©tape -- Troubleshooting -- Configuration avancĂ©e -- Monitoring et logs - ---- - -## đ ProblĂšmes ? - -### Le site ne s'affiche pas - -1. VĂ©rifiez les logs : Portainer â **Containers** â `portfolio-website` â **Logs** -2. VĂ©rifiez que Traefik fonctionne : `docker ps | grep traefik` -3. VĂ©rifiez le DNS : `nslookup alexandre-pommier.com` - -### Le build Ă©choue - -1. VĂ©rifiez l'accĂšs au repository GitHub -2. VĂ©rifiez les logs de build dans Portainer -3. VĂ©rifiez que le Dockerfile existe dans le repo - ---- - -## â Checklist - -- [ ] Portainer accessible -- [ ] RĂ©seau `portfolio` créé (`docker network create portfolio`) -- [ ] Traefik configurĂ© et actif -- [ ] Stack créée dans Portainer -- [ ] Site accessible en HTTPS -- [ ] Webhook configurĂ© (optionnel mais recommandĂ©) - ---- - -**Besoin d'aide ?** Consultez la doc complĂšte ou les logs ! đ diff --git a/SECURITY_GUIDE.md b/SECURITY_GUIDE.md deleted file mode 100644 index fc7e79f..0000000 --- a/SECURITY_GUIDE.md +++ /dev/null @@ -1,291 +0,0 @@ -# đ SĂ©curisation du Repository pour Portainer - -## âïž Public vs PrivĂ© : Que choisir ? - -### â **Repository PUBLIC** (RecommandĂ© pour un portfolio) - -**Avantages :** -- â DĂ©ploiement plus simple (pas d'authentification) -- â Pas de gestion de tokens -- â Bon pour votre CV (montre votre code) -- â Open source = crĂ©dibilitĂ© - -**Points d'attention :** -- â ïž Ne JAMAIS commit de secrets (API keys, mots de passe) -- â ïž Utiliser des variables d'environnement pour les configs sensibles -- â ïž VĂ©rifier le `.gitignore` avant de push - -**Fichiers Ă NE JAMAIS commit :** -``` -.env -.env.local -.env.production -secrets/ -*.key -*.pem -config/database.yml -``` - -### đ **Repository PRIVĂ** (Si vous avez du code propriĂ©taire) - -**Avantages :** -- đ Code source non visible publiquement -- đ ContrĂŽle d'accĂšs granulaire -- đ AdaptĂ© aux projets clients - -**InconvĂ©nients :** -- Configuration plus complexe -- Besoin de gĂ©rer des tokens/clĂ©s SSH - ---- - -## đ **MĂ©thodes d'authentification pour dĂ©pĂŽt PRIVĂ** - -### **MĂ©thode 1 : Personal Access Token (PAT) - Plus simple** - -#### Ătape 1 : CrĂ©er le token sur GitHub - -1. Connectez-vous Ă GitHub -2. Settings â Developer settings â Personal access tokens â Tokens (classic) -3. **Generate new token (classic)** -4. Configuration : - - **Note** : `Portainer Portfolio Deploy` - - **Expiration** : `90 days` ou `No expiration` (moins sĂ©curisĂ©) - - **Scopes** : Cochez uniquement : - - â `repo` (Full control of private repositories) -5. **Generate token** -6. â ïž **COPIEZ le token immĂ©diatement** (commence par `ghp_`) - - Vous ne pourrez plus le voir aprĂšs ! - -#### Ătape 2 : Utiliser le token dans Portainer - -1. Dans Portainer : Stacks â + Add stack -2. Repository URL : `https://github.com/kinou-p/portfolio-website` -3. â Cochez **"Authentication"** -4. Remplissez : - ``` - Username: kinou-p - Personal Access Token: ghp_xxxxxxxxxxxxxxxxxxxx - ``` - -#### Ătape 3 : SĂ©curiser le token - -- â ïž Ne partagez JAMAIS ce token -- đ RĂ©gĂ©nĂ©rez-le rĂ©guliĂšrement (tous les 3 mois) -- đ Stockez-le dans un gestionnaire de mots de passe (1Password, Bitwarden) - ---- - -### **MĂ©thode 2 : SSH Deploy Key - Plus sĂ©curisĂ©** - -#### Ătape 1 : GĂ©nĂ©rer une paire de clĂ©s SSH - -Sur votre serveur Docker : - -```bash -# GĂ©nĂ©rer une clĂ© SSH dĂ©diĂ©e -ssh-keygen -t ed25519 -C "portainer-deploy-portfolio" -f ~/.ssh/portainer_portfolio - -# NE METTEZ PAS de passphrase (appuyez sur EntrĂ©e 2 fois) - -# VĂ©rifier que les clĂ©s sont créées -ls -la ~/.ssh/portainer_portfolio* -# Vous devez voir : -# portainer_portfolio (clĂ© privĂ©e) -# portainer_portfolio.pub (clĂ© publique) -``` - -#### Ătape 2 : Ajouter la clĂ© publique sur GitHub - -```bash -# Afficher la clĂ© publique -cat ~/.ssh/portainer_portfolio.pub -``` - -Copiez la sortie (commence par `ssh-ed25519 AAAAC3...`) - -1. Sur GitHub : Repository â **Settings** â **Deploy keys** -2. **Add deploy key** -3. Configuration : - ``` - Title: Portainer Deploy - Key: [Collez la clĂ© publique] - â Allow write access (dĂ©cochĂ© - read-only suffit) - ``` -4. **Add key** - -#### Ătape 3 : Utiliser la clĂ© dans Portainer - -```bash -# Afficher la clĂ© PRIVĂE -cat ~/.ssh/portainer_portfolio -``` - -Copiez TOUTE la sortie (de `-----BEGIN OPENSSH PRIVATE KEY-----` jusqu'Ă `-----END OPENSSH PRIVATE KEY-----`) - -1. Dans Portainer : Stacks â + Add stack -2. Repository configuration : - ``` - Repository URL: git@github.com:kinou-p/portfolio-website.git - Repository reference: refs/heads/main - ``` -3. â Cochez **"Authentication"** -4. SĂ©lectionnez **"SSH"** -5. Collez la clĂ© privĂ©e complĂšte dans le champ - ---- - -## đ **Bonnes pratiques de sĂ©curitĂ©** - -### **1. Utiliser des variables d'environnement** - -Ne jamais commit de secrets dans le code. Utilisez `.env` : - -```bash -# .env (NE JAMAIS COMMIT) -GTM_ID=GTM-5V6TCG4C -API_KEY=secret_key_here -DATABASE_URL=postgresql://user:pass@localhost/db -``` - -Dans Portainer, ajoutez ces variables dans la Stack : -- Stack â Environment variables â + add variable - -### **2. Fichier .gitignore robuste** - -Assurez-vous que votre `.gitignore` contient : - -```gitignore -# Secrets -.env -.env.* -!.env.example -*.key -*.pem -secrets/ -credentials/ - -# SystĂšme -.DS_Store -Thumbs.db -desktop.ini - -# IDE -.vscode/ -.idea/ -*.swp -*.swo - -# Build -node_modules/ -dist/ -build/ -.cache/ - -# Logs -*.log -logs/ -``` - -### **3. VĂ©rifier avant de commit** - -```bash -# VĂ©rifier ce qui va ĂȘtre commit -git status - -# VĂ©rifier qu'aucun secret n'est prĂ©sent -git diff - -# Si vous avez commit un secret par erreur : -# 1. Supprimez-le du code -# 2. Changez le secret (token, API key, etc.) -# 3. Faites un commit correctif -# 4. Le secret restera dans l'historique Git ! -# Utilisez git filter-branch ou BFG Repo-Cleaner pour l'effacer -``` - -### **4. Rotation des secrets** - -- đ RĂ©gĂ©nĂ©rez les tokens tous les 90 jours -- đ Changez les clĂ©s SSH si elles sont compromises -- đ RĂ©voquez immĂ©diatement tout token exposĂ© - ---- - -## đŻ **Recommandation finale** - -Pour un **portfolio personnel** comme le vĂŽtre : - -### â **Gardez le repository PUBLIC** - -**Raisons :** -1. Pas de secrets sensibles (juste un site vitrine) -2. Plus simple Ă dĂ©ployer -3. Bon pour votre profil GitHub -4. Permet aux recruteurs de voir votre code - -**Configuration sĂ©curisĂ©e :** -``` -â .gitignore complet -â Pas de .env committed -â Variables d'environnement dans Portainer -â Tokens d'API en variables d'environnement seulement -``` - -### đ **Passez en PRIVĂ seulement si :** -- Vous avez du code propriĂ©taire -- C'est un projet client -- Vous testez des features non prĂȘtes - ---- - -## đ **Checklist de sĂ©curitĂ©** - -Avant de rendre votre repo public : - -- [ ] VĂ©rifier `.gitignore` complet -- [ ] Aucun fichier `.env` committed -- [ ] Aucune API key dans le code -- [ ] Pas de mots de passe en dur -- [ ] Pas de tokens GitHub/AWS/etc. -- [ ] VĂ©rifier l'historique Git : `git log -p | grep -i "password\|token\|key"` -- [ ] Supprimer les fichiers sensibles de l'historique si nĂ©cessaire -- [ ] Tester le dĂ©ploiement Portainer - ---- - -## đ **J'ai commit un secret par erreur !** - -### đš Action immĂ©diate : - -1. **Changez le secret** (rĂ©gĂ©nĂ©rez la clĂ© API, token, etc.) -2. **Supprimez-le du code** et commit -3. **Nettoyez l'historique Git** (le secret reste dans l'historique !) - -```bash -# Option 1 : BFG Repo-Cleaner (plus simple) -# TĂ©lĂ©chargez : https://rtyley.github.io/bfg-repo-cleaner/ -java -jar bfg.jar --replace-text secrets.txt # fichier avec les secrets Ă remplacer - -# Option 2 : git filter-branch (manuel) -git filter-branch --force --index-filter \ - "git rm --cached --ignore-unmatch chemin/vers/fichier" \ - --prune-empty --tag-name-filter cat -- --all - -# Force push -git push origin --force --all -``` - -4. **Notifiez l'Ă©quipe** si c'est un projet collaboratif - ---- - -## đ **Besoin d'aide ?** - -- đ [GitHub Personal Access Tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) -- đ [GitHub Deploy Keys](https://docs.github.com/en/developers/overview/managing-deploy-keys) -- đ [Git Secrets Scanner](https://github.com/awslabs/git-secrets) - ---- - -Bonne sĂ©curisation ! đâš diff --git a/index.html b/index.html index 30c90af..932ebc2 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ - + @@ -33,25 +33,14 @@ - - - + +
- + - diff --git a/package-lock.json b/package-lock.json index 5bf3771..024b05b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,6 @@ "@radix-ui/react-toggle": "^1.1.9", "@radix-ui/react-toggle-group": "^1.1.10", "@radix-ui/react-tooltip": "^1.2.7", - "@tanstack/react-query": "^5.83.0", "@tsparticles/engine": "^3.9.1", "@tsparticles/react": "^3.0.0", "@tsparticles/slim": "^3.9.1", @@ -3279,32 +3278,6 @@ "node": ">=4" } }, - "node_modules/@tanstack/query-core": { - "version": "5.83.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.0.tgz", - "integrity": "sha512-0M8dA+amXUkyz5cVUm/B+zSk3xkQAcuXuz5/Q/LveT4ots2rBpPTZOzd7yJa2Utsf8D2Upl5KyjhHRY+9lB/XA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-query": { - "version": "5.83.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.83.0.tgz", - "integrity": "sha512-/XGYhZ3foc5H0VM2jLSD/NyBRIOK4q9kfeml4+0x2DlL6xVuAcVEW+hTlTapAmejObg0i3eNqhkr2dT+eciwoQ==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.83.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^18 || ^19" - } - }, "node_modules/@tsparticles/basic": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/@tsparticles/basic/-/basic-3.9.1.tgz", diff --git a/package.json b/package.json index 1048b52..12c787f 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "lint": "eslint .", "preview": "vite preview", "optimize-images": "node scripts/optimize-images.js", - "analyze": "node scripts/analyze-bundle.js" + "analyze": "node scripts/analyze-bundle.js", + "analyze:js": "node scripts/analyze-js-bundle.js" }, "dependencies": { "@hookform/resolvers": "^3.10.0", diff --git a/scripts/analyze-js-bundle.js b/scripts/analyze-js-bundle.js new file mode 100644 index 0000000..98a4254 --- /dev/null +++ b/scripts/analyze-js-bundle.js @@ -0,0 +1,122 @@ +#!/usr/bin/env node + +/** + * Script pour analyser les bundles JavaScript et calculer les Ă©conomies + * Lance aprĂšs npm run build + */ + +import { readFileSync, readdirSync, statSync } from 'fs'; +import { join } from 'path'; + +const DIST_DIR = './dist/assets/js'; +const COLORS = { + reset: '\x1b[0m', + bright: '\x1b[1m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[36m', + red: '\x1b[31m', +}; + +function formatSize(bytes) { + const kb = bytes / 1024; + return kb.toFixed(2) + ' KiB'; +} + +function analyzeBundle() { + console.log(`\n${COLORS.bright}${COLORS.blue}đ Analyse du Bundle JavaScript${COLORS.reset}\n`); + console.log('â'.repeat(80)); + + try { + const files = readdirSync(DIST_DIR); + const jsFiles = files.filter(f => f.endsWith('.js')); + + let totalSize = 0; + const chunks = []; + + jsFiles.forEach(file => { + const filePath = join(DIST_DIR, file); + const stats = statSync(filePath); + totalSize += stats.size; + + // Extraire le nom du chunk + const chunkName = file.split('-')[0]; + + chunks.push({ + name: file, + chunkName, + size: stats.size, + }); + }); + + // Trier par taille dĂ©croissante + chunks.sort((a, b) => b.size - a.size); + + // Afficher les chunks + console.log(`${COLORS.bright}Fichiers JavaScript gĂ©nĂ©rĂ©s :${COLORS.reset}\n`); + + chunks.forEach((chunk, index) => { + const bar = 'â'.repeat(Math.ceil(chunk.size / (totalSize / 50))); + const percentage = ((chunk.size / totalSize) * 100).toFixed(1); + + let color = COLORS.green; + if (chunk.size > 100 * 1024) color = COLORS.red; + else if (chunk.size > 50 * 1024) color = COLORS.yellow; + + console.log( + `${index + 1}. ${COLORS.bright}${chunk.name}${COLORS.reset}` + ); + console.log( + ` ${color}${bar}${COLORS.reset} ${formatSize(chunk.size)} (${percentage}%)` + ); + console.log(); + }); + + console.log('â'.repeat(80)); + console.log( + `${COLORS.bright}Total JavaScript:${COLORS.reset} ${COLORS.green}${formatSize(totalSize)}${COLORS.reset}` + ); + + // Calculer les Ă©conomies estimĂ©es + const beforeOptimization = 231 * 1024; // 231 KiB avant + const savings = beforeOptimization - totalSize; + const savingsPercent = ((savings / beforeOptimization) * 100).toFixed(1); + + if (savings > 0) { + console.log( + `${COLORS.bright}Ăconomies:${COLORS.reset} ${COLORS.green}${formatSize(savings)} (-${savingsPercent}%)${COLORS.reset}` + ); + } + + console.log('â'.repeat(80)); + + // Recommandations + console.log(`\n${COLORS.bright}${COLORS.blue}đĄ Recommandations${COLORS.reset}\n`); + + const largeChunks = chunks.filter(c => c.size > 100 * 1024); + if (largeChunks.length > 0) { + console.log(`${COLORS.yellow}â ïž Chunks volumineux dĂ©tectĂ©s (> 100 KiB):${COLORS.reset}`); + largeChunks.forEach(chunk => { + console.log(` - ${chunk.name}: ${formatSize(chunk.size)}`); + }); + console.log(` ${COLORS.bright}â ConsidĂ©rez le lazy loading ou le code splitting${COLORS.reset}\n`); + } else { + console.log(`${COLORS.green}â Tous les chunks sont optimisĂ©s${COLORS.reset}\n`); + } + + // VĂ©rifications de performance + console.log(`${COLORS.bright}Performance Checklist:${COLORS.reset}`); + console.log(`${totalSize < 150 * 1024 ? COLORS.green + 'â' : COLORS.red + 'â'} Bundle total < 150 KiB ${COLORS.reset}`); + console.log(`${chunks[0].size < 100 * 1024 ? COLORS.green + 'â' : COLORS.red + 'â'} Plus gros chunk < 100 KiB ${COLORS.reset}`); + console.log(`${chunks.length > 3 ? COLORS.green + 'â' : COLORS.yellow + 'â '} Code splitting activĂ© (${chunks.length} chunks) ${COLORS.reset}`); + + console.log(); + + } catch (error) { + console.error(`${COLORS.red}Erreur lors de l'analyse:${COLORS.reset}`, error.message); + console.log(`\n${COLORS.yellow}đĄ Assurez-vous d'avoir lancĂ© 'npm run build' d'abord${COLORS.reset}\n`); + } +} + +// Lancer l'analyse +analyzeBundle(); diff --git a/src/App.tsx b/src/App.tsx index 594f1a8..5d9bd8e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,15 +6,30 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { lazy, Suspense } from "react"; import { ThemeProvider } from "./contexts/ThemeContext"; -// Lazy load pages and heavy components for better performance +// Lazy load pages et composants lourds pour de meilleures performances +// Cela rĂ©duit la quantitĂ© de JavaScript chargĂ© initialement const Index = lazy(() => import("./pages/Index")); const ProjectPage = lazy(() => import("./pages/ProjectPage")); const NotFound = lazy(() => import("./pages/NotFound")); -const ParticlesBackground = lazy(() => import("./components/ParticlesBackground").then(m => ({ default: m.ParticlesBackground }))); -const queryClient = new QueryClient(); +// ParticlesBackground est chargĂ© en lazy car non critique pour le FCP/LCP +const ParticlesBackground = lazy(() => + import("./components/ParticlesBackground").then(m => ({ default: m.ParticlesBackground })) +); -// Loading fallback component +// Configuration QueryClient optimisĂ©e +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 1000 * 60 * 5, // 5 minutes + gcTime: 1000 * 60 * 10, // 10 minutes + refetchOnWindowFocus: false, + retry: 1, + }, + }, +}); + +// Loading fallback component minimal const PageLoader = () => (