feat(candidature): flux complet offre → score de match → CV généré - #21
Conversation
Deux bugs qui cassaient tout le parcours profil : 1. getAuthSession utilisait toWebRequest(event), qui touche au flux du corps de la requête : le readBody(event) des handlers PUT/POST attendait ensuite un corps déjà verrouillé → toutes les écritures /api/profile* pendaient indéfiniment en build de prod (les GET, sans corps, passaient). On passe event.headers directement à auth.api.getSession, comme le fait déjà le middleware serveur. 2. Le middleware de navigation `auth` testait la truthiness de l'atom nanostores de useAuth() — toujours vrai → aucune redirection, et les visiteurs non connectés voyaient « Impossible de charger ton profil » (401) sur /profil. On vérifie désormais la session via GET /api/auth/get-session (cookies transférés en SSR). Vérifié sur build de prod + Postgres : PUT/POST/DELETE profil en ~10 ms, /profil anonyme → 302 /connexion, /profil connecté → 200. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…HI-124/125) La page « Nouvelle candidature » (/candidature) concrétise le cœur du produit : coller une offre, obtenir un score de match honnête et justifié, puis générer un CV adapté — contenu issu du profil réel uniquement (garde-fou provenance), dans la limite des 2 générations offertes. Serveur : - packages/shared/src/match.ts : contrats du flux (MatchReport, seuils 40/70, matchVerdict, clampScore, computeKeywordCoverage déterministe, chemins API, QUOTA_EXCEEDED_CODE). - services/match-report.ts : score LLM (Sonnet, effort low) borné côté code, raisons nettoyées, garde-fou anti-score-flatteur (score > 70 avec couverture nulle → plafonné à 60), mots-clés matched/missing calculés par le code (reproductibles). - POST /api/candidature/analyze : zod 50–20000 chars, 409 profile_empty, metering 'extraction' avec tokens. - POST /api/candidature/generate : gate quota AVANT tout appel LLM (403 quota_exceeded), génération via matchProfileToOffer (provenance), metering 'generation' avec tokens. ProvenanceError → 422, LlmError → 502. - anthropic.ts : onUsage (tokens pour le metering) + ANTHROPIC_BASE_URL (proxy / mock de test). .env.example documente ANTHROPIC_API_KEY. - Migration metering_usage_tables : usage_events/usage_counters existaient dans le schéma mais aucune migration ne les créait — les quotas tournaient dans le vide. UI : - pages/candidature.vue : stepper input → score (verdict coloré, raisons, chips atouts/manques, alerte < 40 % « générer quand même ? ») → génération (loader informatif) → aperçu CvTemplate + export PDF. Badge quota en tête (décompte live), panneaux profil vide / quota épuisé (lien /#tarifs), a11y (aria-live, focus géré). - Nav : « Nouvelle candidature » en premier lien. Vérifié de bout en bout (Postgres jetable + mock LLM scripts/mock-llm.mjs via ANTHROPIC_BASE_URL) : 401/400/409 corrects, analyse → score 78 avec mots-clés déterministes, 2 générations OK (provenance validée), 3e bloquée 403, compteurs metering exacts (tokens compris), parcours UI complet testé au navigateur. 75 tests verts (25 nouveaux), lint, typecheck, build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThis PR implements a complete "Nouvelle candidature" (New Application) feature: users analyze job offers, receive deterministic match scoring against their profile, and generate tailored CVs. The feature includes shared TypeScript contracts, two-step API endpoints backed by Claude, database usage tracking, a multi-step Nuxt UI with quota enforcement, and testing infrastructure. It also fixes authentication middleware and session handling bugs from issue ChangesNouvelle candidature feature
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser as Client/Browser
participant Frontend as Frontend Page
participant Analyze as POST /api/candidature/analyze
participant Claude1 as Claude (Offer Analysis)
participant Claude2 as Claude (Match Report)
participant Generate as POST /api/candidature/generate
participant Claude3 as Claude (CV Generation)
participant Export as POST /api/cv/export-pdf
User->>Browser: Navigate to /candidature
Browser->>Frontend: Load page (auth middleware)
Frontend->>Frontend: Fetch profile & quota
Frontend->>Frontend: Render input form
User->>Frontend: Paste job offer, click analyze
Frontend->>Analyze: POST offerText
Analyze->>Analyze: Validate text, load profile
Analyze->>Claude1: Call analyzeOffer(offerText)
Claude1-->>Analyze: AnalyzedOffer
Analyze->>Claude2: Call buildMatchReport(profile, offer)
Claude2-->>Analyze: MatchReport {score, reasons, keywords}
Analyze->>Analyze: Record usage event (extraction, non-billable)
Analyze-->>Frontend: {offer, match}
Frontend->>Frontend: Render match verdict + keyword coverage
User->>Frontend: Click generate CV
Frontend->>Frontend: Check quota, enforce guard
Frontend->>Generate: POST {offer}
Generate->>Generate: Check quota, return 403 if exhausted
Generate->>Generate: Load profile
Generate->>Claude3: Call matchProfileToOffer(profile, offer)
Claude3-->>Generate: RenderableCv
Generate->>Generate: Record usage event (generation, billable)
Generate-->>Frontend: {cv}
Frontend->>Frontend: Render CV preview
User->>Frontend: Click export PDF
Frontend->>Export: POST /api/cv/export-pdf
Export-->>Frontend: ArrayBuffer (PDF)
Frontend->>Browser: Trigger download
Browser->>User: Save PDF file
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Deploy Preview for monumental-speculoos-a69398 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Le cœur du produit
La page « Nouvelle candidature » (
/candidature) : l'utilisateur colle une offre, obtient un score de match honnête et justifié, puis génère un CV adapté — contenu issu de son profil réel uniquement (garde-fou provenance), dans la limite des 2 générations offertes.Parcours (conforme au brief produit)
matchProfileToOfferexistant (provenance vérifiée hors LLM), gate quota avant tout appel LLM (403quota_exceeded+ panneau « Recharger en crédits » →/#tarifs).Infrastructure
usage_events/usage_countersexistaient dans le schéma mais aucune migration ne les créait — les quotas tournaient dans le vide. Corrigé ; chaque analyse/génération enregistre ses tokens.anthropic.ts: callbackonUsage(metering) +ANTHROPIC_BASE_URL(proxy/mock).ANTHROPIC_API_KEYdocumentée dans.env.example— à renseigner pour utiliser le flux en réel.scripts/mock-llm.mjs: mock local de l'API Claude pour tester le flux complet sans consommer de tokens (node scripts/mock-llm.mjs+ANTHROPIC_API_KEY=mock ANTHROPIC_BASE_URL=http://localhost:8787 pnpm dev).Vérification (E2E réel sur build de prod + Postgres + mock LLM)
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Refactor