Commit Graph

18 Commits

Author SHA1 Message Date
Claude
d54278969a fix(img-api): proxy image content instead of redirecting to fix Google indexing errors
Replace all NextResponse.redirect() calls with a proxyImage() helper that
fetches the upstream URL server-side and streams the response body directly.

This eliminates:
- Redirect chains (API → Supabase signed URL → S3/CDN)
- Overly long redirect URLs (Supabase JWT tokens)
- Potential empty/invalid redirect targets

Also adds X-Robots-Tag: noindex, nofollow on all responses from this
technical route to prevent Google from crawling it directly.

https://claude.ai/code/session_01PzA98VhLMmsHpzs7gnLHGs
2026-02-23 07:27:07 +00:00
Claude
800a9c08b4 feat(upload): optimisation automatique WebP avec Sharp
- Installe sharp pour le traitement d'image côté serveur
- Conversion automatique de tout upload en WebP (meilleur ratio qualité/web)
- Auto-rotation basée sur l'orientation EXIF (corrige les photos de téléphone)
- Strip de toutes les métadonnées personnelles (GPS, appareil, EXIF)
- Compression adaptative par paliers (q82 → q72 → q62 → q50) pour viser ≤ 1 Mo
- Augmentation de la limite brute à 20 Mo (avant optimisation)
- Métadonnées Supabase Storage : Content-Type + Cache-Control 1 an
- UI : stats d'optimisation affichées après upload (ex: "2400 Ko → 680 Ko (WebP q82)")
- Mise à jour du texte d'aide dans l'admin images

https://claude.ai/code/session_01PzA98VhLMmsHpzs7gnLHGs
2026-02-21 09:17:39 +00:00
Claude
3843595e18 security: corriger les vraies vulnérabilités détectées par l'audit
1. MIME spoofing (upload) — app/api/admin/upload/route.ts
   - Ajout de la validation par magic bytes : lit les premiers octets du
     fichier et vérifie la signature binaire réelle (JPEG FF D8 FF,
     PNG 89 50 4E 47, GIF 47 49 46 38, WebP RIFF+WEBP, AVIF ftyp box)
   - Extension dérivée exclusivement du MIME validé côté serveur
     (MIME_TO_EXT), jamais du nom de fichier fourni par le client
   - Un fichier .exe renommé en .jpg est désormais rejeté

2. Générateur de mot de passe non-cryptographique — stripe/webhook/route.ts
   - Remplace Math.random() (non-déterministe mais prévisible) par
     crypto.getRandomValues() (CSPRNG, conforme Web Crypto API)

3. Headers HTTP de sécurité manquants — middleware.ts (nouveau)
   - X-Content-Type-Options: nosniff (anti MIME-sniffing navigateur)
   - X-Frame-Options: SAMEORIGIN (anti clickjacking)
   - Referrer-Policy: strict-origin-when-cross-origin
   - Permissions-Policy: désactive camera, micro, geolocation
   - Content-Security-Policy: whitelist stricte par type de ressource
     (scripts, styles, images Unsplash/Supabase/Sanity, connect Supabase/Stripe,
     frames Stripe uniquement, object-src none, form-action self)

https://claude.ai/code/session_01PzA98VhLMmsHpzs7gnLHGs
2026-02-21 09:01:21 +00:00
Claude
22d3c0658e fix: corriger l'erreur TypeScript dans le proxy /api/img/[key]
Le typage Supabase inférait "never" sur data car site_images n'est pas
dans le schéma TypeScript généré. Cast de res.data après le await au
lieu du cast global sur .single().

https://claude.ai/code/session_01PzA98VhLMmsHpzs7gnLHGs
2026-02-20 08:09:07 +00:00
Claude
9fe1f8bea2 fix: éliminer définitivement les bugs d'affichage des images privées
Cause racine : les Signed URLs Supabase étaient générées au moment du
rendu ISR et embarquées dans le HTML mis en cache. Si Vercel servait
le HTML avant régénération ou si la signed URL expirait, l'image
était cassée et necessitait plusieurs rechargements.

Solution — proxy /api/img/[key] :
- app/api/img/[key]/route.ts : nouvelle route publique force-dynamic
  qui génère une Signed URL fraîche à chaque requête image (TTL 1h),
  avec Cache-Control 55 min côté navigateur/CDN ; fallback sur l'image
  Unsplash par défaut si createSignedUrl échoue ; 302 direct pour les
  URLs externes
- lib/site-images.ts : getSiteImages() ne génère plus jamais de Signed
  URL — les chemins storage: retournent /api/img/<key> (URL permanente),
  les URLs externes sont retournées telles quelles

Résultat : le HTML statique/ISR ne contient plus jamais de signed URL
éphémère → zéro image cassée, zéro rechargement nécessaire.

https://claude.ai/code/session_01PzA98VhLMmsHpzs7gnLHGs
2026-02-20 07:16:20 +00:00
Claude
eb0c2150e5 fix: résoudre le cache Next.js qui bloquait la mise à jour des images
- app/macon/page.tsx + app/paysagiste/page.tsx : ajout de
  export const revalidate = 60 pour activer l'ISR (les pages étaient
  générées statiquement à la build, getSiteImages() n'était jamais
  rappelé entre deux déploiements)
- app/api/admin/site-images/route.ts : appel de revalidatePath() après
  chaque PUT réussi pour purger immédiatement le cache de la page
  concernée (macon_, paysagiste_ → leur page démo, sinon → /)
  Résultat : la page se met à jour dans la seconde qui suit la sauvegarde
  dans l'admin, sans attendre le délai de 60s

https://claude.ai/code/session_01PzA98VhLMmsHpzs7gnLHGs
2026-02-19 11:28:39 +00:00
Claude
7a46501ba3 feat: upload images vers bucket Supabase privé avec Signed URLs
- Nouvelle route POST /api/admin/upload : upload multipart vers le bucket
  private-gallery, validation MIME + taille (max 5 Mo), retourne storage:path
- lib/site-images.ts : détecte le préfixe "storage:" et génère une Signed
  URL temporaire (60 min) côté serveur avant chaque rendu de page
- GET /api/admin/site-images : résout aussi les signed URLs pour les previews
  admin (champ previewUrl distinct de url brute)
- PUT /api/admin/site-images : accepte désormais les chemins "storage:..."
  en plus des URLs externes
- Page admin images : drag & drop + input file avec upload automatique +
  sauvegarde en BDD, badge "bucket privé", instructions SQL pour créer
  la table et la policy du bucket private-gallery

https://claude.ai/code/session_01PzA98VhLMmsHpzs7gnLHGs
2026-02-19 06:16:15 +00:00
Claude
c62998d0c2 feat: redesign homepage with triptych process + admin image management
- Redesign Hero section with new copy focused on the triptych offering
- Add Process component (replaces System) with zigzag layout for 3 pillars:
  Google Maps reviews, managed Facebook, converting website
- Redesign AboutMe with orange background and stats row
- Add admin panel for managing site image URLs (replaces Sanity dependency)
- Create site_images API routes and Supabase-backed storage with defaults
- Update FAQ to reference built-in admin panel
- Add site_images table type to database types
- Pass images prop through homepage components

https://claude.ai/code/session_01V8YAjpqRQ3bfBYsABYsEgo
2026-02-17 18:40:30 +00:00
fe0df7448f Update fromEmail to new HookLab address 2026-02-10 21:42:13 +01:00
Claude
a6f32dd77a feat: branded HookLab approval email + decouple email from Stripe
- Email sending now independent from Stripe payment link generation
- Professional dark-themed HTML email template matching HookLab branding
- Return emailSent/emailError/stripeError status in API response
- Admin UI shows detailed status after approve action
- Default to onboarding@resend.dev when no custom domain

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
2026-02-10 19:43:05 +00:00
Claude
eafa783040 fix: use configurable RESEND_FROM_EMAIL with onboarding@resend.dev fallback
Allow sending emails without custom domain by defaulting to Resend's
free onboarding address. Set RESEND_FROM_EMAIL env var later when
hooklab.fr domain is purchased and verified.

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
2026-02-10 19:02:43 +00:00
Claude
9ae7dd2d2d feat: add admin setup page and login redirect for admins
- Add /setup-admin page for first-time admin account creation
- Add /api/admin/setup route (only works when no admin exists)
- Update login to redirect admins to /admin instead of /dashboard
- Setup page creates auth user + profile with is_admin=true

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
2026-02-10 13:33:52 +00:00
Claude
1d0bd349fd feat: secure admin panel with Supabase auth + course management CRUD
- Replace ADMIN_SECRET query param with proper Supabase auth + is_admin flag
- Add admin layout with auth check (redirects non-admin to /)
- Add AdminShell component with sidebar navigation (Dashboard, Candidatures, Cours)
- Add admin dashboard with stats (candidatures, users, modules)
- Add admin candidatures page with filters and approve/reject
- Add admin course management page (create, edit, delete, publish/unpublish)
- Add API routes: GET/POST /api/admin/modules, GET/PUT/DELETE /api/admin/modules/[id]
- Add verifyAdmin() helper for API route protection
- Update database types with is_admin on profiles

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
2026-02-10 13:25:58 +00:00
Claude
c4934f5669 feat: add admin panel to manage candidatures and approve with Stripe link
- /admin page with secret-key authentication
- List all candidatures with details (expandable cards)
- Approve: updates status + generates Stripe checkout URL + sends email
- Reject: updates status
- Checkout URL displayed on screen for manual copy if Resend not configured
- Protected by ADMIN_SECRET env var

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
2026-02-09 19:39:33 +00:00
Claude
db51e21075 fix: configure admin client with service role options and better error logging
- Add auth options (autoRefreshToken: false, persistSession: false) to
  createAdminClient so service role key works correctly with supabase-js
- Return actual Supabase error message in candidature API for debugging

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
2026-02-09 18:52:45 +00:00
Claude
231667c2c6 feat: fix accents, improve candidature API, add legal pages
- Fix all missing French accents across 16 files (é, è, ê, à, ô, ç)
- Improve candidature API error handling: check env vars, better messages
- Add Mentions légales page (/mentions-legales)
- Add CGV page (/cgv)
- Add Politique de confidentialité page (/confidentialite)
- Business info: Enguerrand Ozano, SIREN 994538932, TVA FR16994538932

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
2026-02-08 20:49:54 +00:00
Claude
ee6870d73e fix: resolve __dirname error by forcing Node.js runtime on all server routes
- Add serverExternalPackages for @supabase/ssr in next.config.ts
- Add export const runtime = 'nodejs' to all pages/routes using Supabase
- Replace createAdminClient to use @supabase/supabase-js directly (no SSR)
- Prevents @supabase/ssr from running in Edge runtime on Vercel

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
2026-02-08 19:08:32 +00:00
Claude
41e686c560 feat: complete HookLab MVP - TikTok Shop coaching platform
Full-stack Next.js 15 application with:
- Landing page with marketing components (Hero, Testimonials, Pricing, FAQ)
- Multi-step candidature form with API route
- Stripe Checkout integration (subscription + webhooks)
- Supabase Auth (login/register) with middleware protection
- Dashboard with progress tracking and module system
- Formations pages with completion tracking
- Profile management with password change
- Database schema with RLS policies
- Resend email integration for transactional emails

Stack: Next.js 15, TypeScript, Tailwind CSS v4, Supabase, Stripe, Resend

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
2026-02-08 12:39:18 +00:00