mirror of
https://github.com/mmahdium/portfolio.git
synced 2026-08-17 05:24:30 +03:30
docs(epic-3): add tech spec and story drafts for PDF Export
Epic 3: PDF Export - Planning Phase Complete Tech Spec: - Create tech-spec-epic-3.md with 10 acceptance criteria - Define API contracts, workflows, and NFRs - Document dependencies (puppeteer, @sparticuz/chromium) - Add Vercel deployment configuration - Include traceability mapping and test strategy Story Drafts: - 3-1: Create PDF Generation API Route (8 ACs, Puppeteer integration) - 3-2: Create PDF Download Composable (8 ACs, download logic) - 3-3: Connect Download Button to PDF Generation (8 ACs, integration) Sprint Status Updates: - epic-3: backlog → contexted - All 3 stories: backlog → drafted Files Created: - docs/sprint-artifacts/tech-spec-epic-3.md - 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 Files Modified: - docs/sprint-artifacts/sprint-status.yaml Ready for: Story context generation or implementation
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
# Story 3.1: Create PDF Generation API Route
|
||||
|
||||
Status: drafted
|
||||
|
||||
## 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)
|
||||
Reference in New Issue
Block a user