Files
portfolio/docs/sprint-artifacts/3-1-create-pdf-generation-api-route.md
T
mahdiarghyani 6571c83025 docs(epic-3): add story context XML files for all stories
Story Context Generation Complete:
- 3-1: PDF Generation API Route context (8 ACs, Puppeteer interfaces)
- 3-2: PDF Download Composable context (8 ACs, blob handling)
- 3-3: Download Button Integration context (8 ACs, UButton binding)

Each context includes:
- Acceptance criteria mapping
- Relevant code artifacts and snippets
- Interface definitions
- Constraints and dependencies
- Test ideas per AC

Status Updates:
- All 3 stories: drafted → ready-for-dev

Files Created:
- docs/sprint-artifacts/3-1-create-pdf-generation-api-route.context.xml
- docs/sprint-artifacts/3-2-create-pdf-download-composable.context.xml
- docs/sprint-artifacts/3-3-connect-download-button-to-pdf-generation.context.xml

Files Modified:
- docs/sprint-artifacts/sprint-status.yaml
- docs/sprint-artifacts/3-1-create-pdf-generation-api-route.md
- docs/sprint-artifacts/3-2-create-pdf-download-composable.md
- docs/sprint-artifacts/3-3-connect-download-button-to-pdf-generation.md

Ready for: Dev Agent implementation
2025-12-01 12:09:58 +03:30

176 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Story 3.1: Create PDF Generation API Route
Status: ready-for-dev
## Story
As a system,
I want a server endpoint that generates PDF from the resume page,
so that users get consistent, high-quality PDF output.
## Acceptance Criteria
1. **AC1:** Given a GET request to `/api/resume/pdf`, when the server processes the request, then it returns a PDF binary with Content-Type `application/pdf`
2. **AC2:** Response includes `Content-Disposition: attachment; filename="Ali_Arghyani_Resume.pdf"`
3. **AC3:** PDF matches the web preview exactly (WYSIWYG)
4. **AC4:** PDF text is selectable and copy-able (ATS-compatible)
5. **AC5:** PDF is A4 format (210mm × 297mm)
6. **AC6:** PDF generation completes in under 3 seconds
7. **AC7:** Given an error occurs, when caught, then it returns status 500 with JSON error message
8. **AC8:** Timeout is set to 10 seconds max
## Tasks / Subtasks
- [ ] Create API route file (AC: #1, #2)
- [ ] Create `server/api/resume/pdf.get.ts`
- [ ] Set up Nuxt event handler with `defineEventHandler`
- [ ] Configure response headers (Content-Type, Content-Disposition)
- [ ] Implement Puppeteer PDF generation (AC: #3-#6)
- [ ] Import puppeteer (dev) or puppeteer-core + chromium (prod)
- [ ] Get base URL from request headers
- [ ] Launch browser in headless mode
- [ ] Navigate to `/resume?print=true`
- [ ] Wait for `networkidle0` (fonts loaded)
- [ ] Generate PDF with options: format A4, printBackground true
- [ ] Close browser in finally block
- [ ] Return PDF buffer
- [ ] Add error handling (AC: #7, #8)
- [ ] Wrap in try-catch block
- [ ] Set timeout to 10 seconds
- [ ] Return 500 status with error JSON on failure
- [ ] Log errors to console
- [ ] Configure for Vercel deployment
- [ ] Use environment detection for puppeteer vs puppeteer-core
- [ ] Import @sparticuz/chromium for production
- [ ] Update vercel.json with function config (memory: 1024, maxDuration: 10)
- [ ] Test API endpoint
- [ ] Test locally with `curl` or browser
- [ ] Verify PDF opens correctly
- [ ] Check text is selectable
- [ ] Measure generation time
- [ ] Test error handling
## Dev Notes
### Architecture Alignment
**From Architecture Doc:**
- File location: `server/api/resume/pdf.get.ts`
- Uses Puppeteer server-side (ADR-001)
- Navigates to `/resume?print=true` for WYSIWYG
- Returns PDF buffer with proper headers
**From Tech Spec Epic 3:**
- AC1-AC4 map to this story
- Performance target: < 3 seconds
- Memory limit: 1024MB on Vercel
### Implementation Notes
**Local Development (puppeteer with bundled Chromium):**
```typescript
import puppeteer from 'puppeteer'
const browser = await puppeteer.launch({
headless: true
})
```
**Production (Vercel with @sparticuz/chromium):**
```typescript
import puppeteer from 'puppeteer-core'
import chromium from '@sparticuz/chromium'
const browser = await puppeteer.launch({
args: chromium.args,
executablePath: await chromium.executablePath(),
headless: chromium.headless
})
```
**PDF Generation Options:**
```typescript
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
margin: { top: 0, right: 0, bottom: 0, left: 0 }
})
```
**Response Headers:**
```typescript
setResponseHeaders(event, {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="Ali_Arghyani_Resume.pdf"'
})
```
### Dependencies
**Install:**
```bash
pnpm add puppeteer puppeteer-core @sparticuz/chromium
```
**vercel.json:**
```json
{
"functions": {
"server/api/resume/pdf.get.ts": {
"memory": 1024,
"maxDuration": 10
}
}
}
```
### Testing Checklist
- [ ] API returns PDF binary
- [ ] Content-Type is application/pdf
- [ ] Content-Disposition has correct filename
- [ ] PDF opens in viewer
- [ ] Text is selectable in PDF
- [ ] Colors are correct (blue headers)
- [ ] Generation time < 3 seconds
- [ ] Error returns 500 with JSON
- [ ] Works on Vercel deployment
### References
- [Source: docs/architecture.md#API-Contracts]
- [Source: docs/architecture.md#Novel-Pattern-WYSIWYG-PDF-Export]
- [Source: docs/architecture.md#Deployment-Architecture]
- [Source: docs/sprint-artifacts/tech-spec-epic-3.md#AC1-AC4]
## Dev Agent Record
### Context Reference
<!-- Will be filled by SM agent -->
### Agent Model Used
<!-- Will be filled by dev agent -->
### Debug Log References
<!-- Will be filled by dev agent during implementation -->
### Completion Notes List
<!-- Will be filled by dev agent after completion -->
### File List
<!-- Will be filled by dev agent with created/modified files -->
---
**Change Log:**
- 2025-12-01: Story drafted by SM agent (Bob)