- Replace pdf2pic/GraphicsMagick with pdfjs-dist + @napi-rs/canvas for
Vercel-compatible PDF rasterization
- Replace MinIO with Supabase Storage (S3-compatible); rename minio.ts
to storage.ts and update all imports
- Replace in-memory job queue with Upstash QStash; upload route now
persists files to storage before enqueuing, /api/jobs/process handles
the QStash callback
- Convert email watcher from persistent IMAP connection to stateless
scanInbox() polled by Vercel Cron every 2 minutes
- Add FTP watcher (basic-ftp) with cron polling for scanner integration
via Dreamhost FTP drop directory
- Add FTP config fields to AppSettings schema
- Remove folder watcher (chokidar), standalone output, Docker-only code
- Update next.config.ts, middleware, instrumentation for serverless
- Add vercel.json with cron schedules for email and FTP polling
- Add migration scripts for database (pg_dump/restore) and storage
(S3-to-S3 copy) with verification
Made-with: Cursor
The old getPdfPageCount used a fragile regex on raw PDF binary that
could undercount pages, causing survey sides to be skipped entirely.
Replaced with pdf-lib's PDFDocument.load() for reliable counting,
with the old regex as a fallback.
Added logging for page counts and pairing to diagnose missing backs.
Made-with: Cursor
Duplex scanners flip the sheet between sides, so odd pages (back) are
180° rotated relative to even pages (front). Landscape back pages now
rotate CCW instead of CW, and portrait back pages get a 180° flip.
Also swapped "Front"/"Back" labels to match the physical card sides.
Made-with: Cursor
Scanners store landscape content with a PDF /Rotate flag that pdf2pic
doesn't always apply. Now uses a square bounding box with
preserveAspectRatio, then detects landscape output and rotates to
portrait with sharp.
Made-with: Cursor