ConsoleCV
A SaaS that reads unstructured resume PDFs with Llama 3 and rebuilds them properly - editor, PDF export, public portfolio page included.
- Timeline
- 2024
- Role
- Solo build
- Team
- Solo
- Status
- live
Technology Stack
✦Overview
ConsoleCV is an AI-powered resume builder SaaS where every PDF upload enters a pipeline: Llama 3 via the Groq SDK parses the unstructured content, extracts structured fields (education, experience, skills), and hands it to a live editor - so a resume locked in a PDF becomes an editable document in seconds.
It is built as a proper SaaS: gated flows behind NextAuth v5 sessions, Upstash Redis rate limiting on expensive AI endpoints, and an AuditLog model that records every unauthorized request with severity levels. Resume data is personal - audit trails, rate limits and access controls are non-negotiable, not an afterthought.
✦Key Features
Llama 3 AI parsing via Groq - Unstructured resume PDFs are fed to Llama 3 through the Groq SDK - sub-second inference on Groq's custom chips - and the output maps to a structured Resume Zod schema ready for editing.
Resume editor + PDF export - Parsed data drops into a live editor. @react-pdf/renderer generates a professional layout client-side on demand - no server round-trip, no layout drift between editor view and download.
NextAuth v5 + Redis rate limiting - Sessions are handled by NextAuth v5 (Edge Runtime compatible). AI endpoints are rate-limited per authenticated user ID via Upstash Redis - cost spikes from a single account are blocked before they reach the Groq API.
AuditLog - unauthorized access is observable - Every unauthenticated request to a protected endpoint is logged to MongoDB with IP address, user agent, endpoint, method and severity level. Abuse patterns surface before they compound.
Public portfolio page - Parsed and edited resumes can be published as a public page at /[username] - shareable with recruiters directly, a second deliverable that makes ConsoleCV more than a PDF generator.
✦Technical Implementation
01Audit logging - every unauthorized request recorded
The resume API logs every unauthorized access attempt before returning 401. Audit trails are a compliance pattern more common in fintech than resume builders - applied here because the data is personal and the attack surface (public SaaS) demands it.
// Every unauthenticated request is logged with severity HIGH
if (!session?.user?.id) {
await AuditLog.log("UNAUTHORIZED_ACCESS", {
ipAddress: getClientIp(request),
userAgent: request.headers.get("user-agent") ?? "Unknown",
details: { endpoint: "/api/resume", method: request.method },
severity: "HIGH",
});
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Rate-limited per authenticated user (not just IP)
const rl = await rateLimitAuthenticated(session.user.id);
if (!rl.success)
return rateLimitExceededResponse(createRateLimitHeaders(rl));02Groq + Llama 3 parsing pipeline
PDFs are converted to plain text, sent to Llama 3 through the Groq SDK with a structured extraction prompt, and the output is validated against a Zod schema before being written to MongoDB. The prompt includes explicit negative instructions ('if the field is not present, return null - never invent') to prevent the model hallucinating missing experience or skills.
03Data model
Three Mongoose models: Resume (structured schema matching the editor fields), User (NextAuth-integrated, hashed passwords), and AuditLog (time-series append-only - severity indexed for alerting). Resume ownership is enforced at the query level - every resume endpoint filters by session.user.id.
| Model | Key fields | Purpose |
|---|---|---|
| Resume | userId, sections[], parsedAt, publishedAt | The parsed + edited resume document |
| User | email, hashedPassword, username | Auth identity, links to public /[username] page |
| AuditLog | event, userId?, ipAddress, severity, timestamp | Append-only access trail for monitoring |
✦Project Structure
consolecv/
├─ src/
│ ├─ app/
│ │ ├─ (auth)/ # login, register
│ │ ├─ dashboard/ # user dashboard
│ │ ├─ editor/ # resume editor
│ │ ├─ [username]/ # public portfolio page
│ │ └─ api/ # AI parsing, resume CRUD, user endpoints
│ ├─ models/ # Resume, User, AuditLog
│ ├─ auth.ts # NextAuth v5 config
│ └─ middleware.ts # route protection
└─ public/ # static assets✦Design Decisions
Groq over OpenAI for speed
The parsing step needs to feel instant - a multi-second spinner kills the moment when the resume appears structured. Groq's custom inference chips serve Llama 3 faster than OpenAI's API at this price point. The difference is perceptible in the UX.
AuditLog as a first-class model
Resume data is personal. An observable audit trail means suspicious access patterns - mass enumeration, repeated 401s from the same IP - surface without log-scraping. The AuditLog model is append-only and severity-indexed so a simple query identifies incidents.
✦Challenges
PDF extraction loses layout context
PDF → plain text loses the formatting cues that matter for resume parsing: columns become garbled runs of text, tables collapse, header hierarchy disappears. Mitigated by a parsing prompt that targets semantic structure (section names, date ranges, bullet patterns) rather than visual layout.
Model hallucinations on sparse resumes
Llama 3 sometimes extrapolates missing fields - dates, employer names - when the PDF is thin. Fixed with explicit negative instructions in the system prompt ('never invent data') and post-parse Zod validation that rejects any field value not derivable from the source text.
Want the full walkthrough - architecture, decisions, war stories?
ask me about it

