Non publié
Planned
- LinkedIn recommendations / testimonials (pending responses from colleagues)
Toutes les modifications notables de ce portfolio.
Format Keep a Changelog
app/error.tsx (root) and app/[locale]/error.tsx (locale-level) catch unhandled rendering errors (Supabase timeout, Redis crash, etc.). Both expose a reset() button that re-renders the crashed segment without a full page reload. Locale detected via useParams() in the locale boundary (zero flash), via navigator.language in the root fallback. Error digest shown in development only.app/[locale]/not-found.tsx — locale-level 404 page, takes priority over the root not-found.tsx for all routes under /[locale]/. Locale resolved synchronously via useParams() (no hydration flash). Root not-found.tsx redesigned with compass icon + entrance animation.GlobalErrorHandler (client component, mounted in root layout) intercepts window.onerror and unhandledrejection. Sends a structured payload to POST /api/errors via navigator.sendBeacon (non-blocking, survives page unload). Per-session dedup (module-level Set) avoids flooding the same error. Browser extension errors and cross-origin "Script error." filtered out. Dev mode: console only (no API call).POST /api/errors — new endpoint receiving client error reports. Rate-limited at 10 req / 60 s / IP (errorRatelimit). Strict payload validation (type, message required, size-bounded). Logs structured JSON tagged [CLIENT-ERROR] to stdout (captured by Vercel Runtime Logs, forward-compatible with homelab log aggregators).errorRatelimit — new Upstash Redis sliding-window limiter in lib/ratelimit.ts (10 req / 60 s, prefix portfolio:rl:errors). Fail-closed in production; permissive no-op in dev/CI.lib/analytics.ts now fire from actual components:areaServed expanded — ProfessionalService now lists France, Algeria, Belgium, Switzerland, Luxembourg, Canada as structured Country objects, plus "Worldwide" (Schema.org Text value) for remote-international coverage.networkFirst bug — on a non-ok server response (e.g. Supabase returning 500), the previous implementation returned the error response directly without consulting the cache. Fixed: on !response.ok, the cache is checked first; if a cached version exists it is served; only if absent is the error response returned (React error boundaries handle it client-side). Strategy explicitly documented as "without timeout" (intentional: no AbortController + setTimeout, avoids serving stale content to users on slow-but-available connections).adminRatelimit doc — SECURITY.md showed outdated limit (10 req / 5 min); corrected to current value (5 req / 15 min / IP)..env.example — 9 missing variables documented: ADMIN_USERNAME, ADMIN_PASSWORD, REVALIDATE_SECRET, CRON_SECRET, IP_HASH_SALT, TESTIMONIAL_SUBMIT_TOKEN, VERCEL_ENV, VERCEL_URL (new sections: Admin, Cron jobs, Security, Testimonials, Vercel runtime).animate-[fadeIn_0.4s_ease_forwards] entrance animation, type="button" on all <button> elements.'unsafe-inline' removed from script-src. A cryptographically unique nonce (btoa(crypto.randomUUID())) is generated per request in proxy.ts (Edge Runtime) via the Web Crypto API. The nonce is injected into the Content-Security-Policy response header and forwarded to Server Components via the x-nonce request header. Inline scripts that require explicit authorisation (JSON-LD in root layout, Vercel Analytics, Vercel Speed Insights, Next.js hydration) receive the nonce attribute. Any injected inline script without a matching nonce is now blocked by the browser.next.config.mjs) to dynamic (proxy.ts) — static CSP headers cannot embed per-request nonces. next.config.mjs no longer contains a Content-Security-Policy entry; all other security headers (HSTS, X-Frame-Options, Permissions-Policy, Report-To, etc.) remain static.SECURITY.md, /colophon, /changelog reflect the current security posture. Version numbers removed from public-facing tech stack listings. RLS policy on uptime_pings tightened (anon SELECT removed)./admin — proxy.ts now applies a Redis rate-limit (10 attempts / 5 min / IP) before the HTTP Basic Auth check; returns 429 Too Many Requests with Retry-After: 300 when the limit is exceeded. Prior to this fix, credentials could be brute-forced without any server-side friction./api/redis-test disabled in production — diagnostic route now returns 404 when NODE_ENV !== "development". Previously accessible publicly, it confirmed Redis presence and allowed unauthenticated writes to the cache key healthcheck./api/newsletter — 3 submissions / hour / IP via Upstash Redis. Without this, the endpoint was open to Brevo API quota exhaustion, email enumeration via distinct response codes, and unsolicited third-party submissions./api/health — reuses the blogRatelimit limiter (30 req / 60 s / IP) to prevent loop-based infrastructure probing./api/health restricted — Access-Control-Allow-Origin changed from * to NEXT_PUBLIC_SITE_URL. The wildcard allowed any third-party page to silently read internal service latencies (Supabase, Redis) via fetch() from a browser context. Uptime monitoring tools operate server-to-server and do not require CORS.poweredByHeader: false — suppresses X-Powered-By: Next.js response header. Removes framework fingerprinting that enabled targeted CVE scanning.Cross-Origin-Opener-Policy: same-origin — prevents cross-origin windows from accessing the browsing context (mitigates window.opener hijack and Spectre-class attacks via shared contexts).Cross-Origin-Resource-Policy: same-origin — prevents other origins from loading site resources (images, JSON, fonts) into their own context without explicit CORS.project_assets hardened — policy changed from USING (true) to a sub-select requiring projects.status = 'published'. Previously, assets of draft and archived projects were publicly readable via the Supabase anon key REST API regardless of the parent project's visibility.robots.txt updated — added Disallow: /cv/ (prevents direct PDF indexing by search engines) and Disallow: /admin/ (explicit exclusion of the admin panel from crawlers).adminRatelimit + newsletterRatelimit added to lib/ratelimit.ts — two new Upstash sliding-window limiters centralised alongside existing limiters; both fall back to a no-op if Redis is unavailable (development / CI)./about — dedicated opengraph-image.tsx (1200×630) with indigo accent, journey subtitle, and skill tags; consistent with certifications and blog post OG imagessupabase/update-sections.sql: 22 UPDATE statements (11 projects × 2 locales) populating hero_subtitle and sections JSONB (text, bullets, metrics types) extracted from jury evaluation Word documents/en/resources & /fr/resources, 20 tools in 4 categories (Salesforce, Dev Stack, IT Ops, Learning), static Server Component/en/status & /fr/status, shows operational status of all services; linked in footeraria-label dynamic (open/close), ProjectsSection tab arrow-key navigation (APG), ContactForm aria-describedby on status region, ScrollToTop aria-label via i18n, ServicesFaq focus-visible ringdesktopActiveId mapping; removed truncation at 1280px (5 items, 2xl:px-3)ProfileFactsCard, About page, i18n translations (messages/fr.json, messages/en.json), content/about.ts goals: CDI · CDD · Freelance · Mission, Full remote · Hybrid · On-site, Paris/IDF · Marseille/PACAvercel.json changed from */5 * * * * to 0 8 * * * (Vercel Hobby plan compatibility)aria-label now reads "Close menu" when menu is open (was always "Open menu")app/[locale]/projects-test/ — debug page listing all project slugs; was publicly accessiblepublic/projects/python-network-scanner/scanner-ui/node_modules/ — accidentally committed node_modules was being served as static assetsrehype-pretty-code (shiki) integrated into the MDX pipelineCodeBlockServer.tsx — now forwards data-language and all shiki attrs to <pre>mdx-components.tsx — pre override now spreads all props (not just children)globals.css — added shiki CSS variable rules, language badge styles, highlighted line styles⌘K / Ctrl+K) — cmdk v1.1.1 + Radix UI DialogCommandPaletteTrigger inline component dispatching KeyboardEventReveal.tsx — migrated to Framer Motion useInView + useReducedMotionAnimatedCounter.tsx — scroll-triggered counting animation via Framer Motion useSpringanimate-pulse skeleton for Supabase loading statesloading.tsx — page-level loading skeleton (Next.js Suspense boundary)/certifications (dedicated) — diplomas, active certifications, in-preparationlg screens, auto-generated from ##/###BackToTop client button on blog post pagesRelatedPosts componentTrustedBy.tsx — "Trusted by" logos section (replaces empty testimonials placeholder)NEXT_PUBLIC_SHOW_TESTIMONIALS — false by default (shows logos), true activates real testimonials from SupabaseScrollToTop.tsx — sticky scroll-to-top button (appears after 300px, smooth scroll)next-themes with darkMode: ["class"] Tailwind configapp/manifest.ts — PWA manifest (standalone, icons, theme color)SECURITY.md — security policy, responsible disclosure contact/.well-known/security.txt — RFC 9116 compliantnext.config.mjs:ProfileFactsCard and ProfileNarrative moved to /about/about (dedicated) — ProfileFactsCard, AboutTrackIntro, timeline, values, goals/colophon — full stack documentation (frontend, backend, infra, security)DevConsoleMessage.tsx — styled console.log for developers opening DevToolsSkipToContent.tsx — accessible skip link (visible on keyboard focus)ScrollProgress.tsx — reading progress bar on blog postsPerson, WebSite, ProfessionalService graph in root layoutopengraph-image.tsx (1200×630) and twitter-image.tsx via @vercel/og[slug]/opengraph-image.tsx/feed.xml (RSS 2.0, 38 EN articles, <link rel="alternate"> in <head>)/sitemap.xml with priorities, blog posts, and project slugs from Supabaserobots.txt — allow all, sitemap URL declaredalternates.languages in metadata for EN/FR canonical handling/api/contactscripts/apply-supabase-sql.ts — idempotent migration script (npm run db:migrate)Accept-Language detectionnext/font/google@next/mdx, remark-frontmatter), 38 articles (EN + FR)npm ci, lint → typecheck → test → buildLICENSE — MITREADME.md — badges CI/live/license, stack table, architecture, env vars, scriptsAccordion.tsx for experience section (accessible: aria-controls, aria-expanded)Navbar.tsx with active section indicatorsContactForm.tsx with Supabase API route + Formspree fallback + Calendly modal