Commit graph

64 commits

Author SHA1 Message Date
Randall Stillwell
19270ef6ec Strip sslmode from connection string to fix TLS override
Newer pg versions treat sslmode=require as verify-full, overriding
the programmatic ssl.rejectUnauthorized=false config. Strip sslmode
from the URL and handle SSL entirely via the Pool ssl option.

Made-with: Cursor
2026-04-16 00:08:47 -05:00
Randall Stillwell
fd3e1ba150 Fix TLS certificate error for Supabase pooler connection
Supabase's PgBouncer pooler uses a certificate not in Node's default
CA store, causing "self-signed certificate in certificate chain" errors
on Vercel. Configure pg.Pool with ssl.rejectUnauthorized: false.

Made-with: Cursor
2026-04-16 00:05:00 -05:00
Randall Stillwell
21c863da01 Migrate to Vercel + Supabase + Upstash production stack
- 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
2026-04-15 23:51:44 -05:00
Randall Stillwell
20bc4cbb03 Add missing org API routes and fix registration gate
- Create /api/org (GET details, PUT update)
- Create /api/org/members (GET list)
- Create /api/org/invitations (GET list, POST create)
- Create /api/org/locations (GET list with collection days, POST create)
- Fix registration: check org count instead of user count so
  a fresh domain setup works even if orphaned users exist
  from a prior AUTH_SECRET rotation

Made-with: Cursor
2026-04-15 14:47:15 -05:00
Randall Stillwell
1b669bb1b7 Fix onboarding redirect and integrations empty state
- Middleware now redirects to /onboarding when user has no org
  (previously required orgRole=owner which was undefined for new users)
- Refresh JWT cookie after onboarding completes so middleware sees
  updated orgId/onboardingComplete on next navigation
- Integrations page shows meaningful error instead of empty grid
  when API returns 401 due to missing org

Made-with: Cursor
2026-04-15 14:18:51 -05:00
Randall Stillwell
48959190a0 Fix middleware cookie detection behind HTTPS reverse proxy
getToken() defaults secureCookie to false, so it looks for
"authjs.session-token" cookie. Behind Traefik over HTTPS, Auth.js
sets "__Secure-authjs.session-token". Detect HTTPS via
x-forwarded-proto header and pass secureCookie accordingly.

Made-with: Cursor
2026-04-15 14:11:50 -05:00
Randall Stillwell
04e9811572 Fix Auth.js server error behind reverse proxy
Add trustHost: true to NextAuth config so Auth.js accepts requests
when running behind Traefik/reverse proxy. Without this, Auth.js
rejects the credential callback because the forwarded host doesn't
match its expectations.

Made-with: Cursor
2026-04-15 01:50:44 -05:00
Randall Stillwell
95a672a6cb Fix build: wrap useSearchParams in Suspense boundary
Next.js requires useSearchParams() to be inside a Suspense boundary
for static page generation. Wraps the new integration page component.

Made-with: Cursor
2026-04-15 01:35:54 -05:00
Randall Stillwell
a975043670 Add onboarding wizard, email verification, integration architecture, and settings restructure
- Auto-sign-in after registration instead of redirect to login
- Email verification system with token generation, send/confirm API routes, and persistent banner
- 7-step onboarding wizard (org, location, services, upload source, AI, integrations, complete)
- Middleware redirects owners with incomplete onboarding to /onboarding
- Integration provider plugin architecture with registry and 6 providers (Planning Center, Monday.com, Airtable, Google Sheets, Webhook, CSV Export)
- Full integration CRUD API with test, sync, fields, and OAuth authorize/callback routes
- Refactored fireIntegrationEvent to use Integration model with legacy AppSettings fallback
- Migration script for existing Monday.com/webhook config to Integration rows
- Settings page restructured from monolithic 1290-line file into focused sub-routes with section navigation
- Integration hub UI with provider tiles, connect flow, and individual config pages
- Post-onboarding contextual guidance cards on dashboard with dismissible hints
- Schema: Integration model, onboardingComplete/onboardingStep on Organization, dismissedHints on OrgMember

Made-with: Cursor
2026-04-15 01:29:13 -05:00
Randall Stillwell
d3e7374439 Add SaaS foundation: Auth.js, dashboard shell, org model, auto-assignment
Major architectural upgrade preparing Echo OCR for self-hosted SaaS deployment:

- Auth: Built-in Auth.js v5 with credentials + Authentik OIDC SSO, JWT sessions,
  middleware route protection, login/signup/setup pages, registration API
- UI: Dashboard layout with collapsible sidebar nav, AppShell wrapper, route
  groups for (dashboard) and (auth), new pages for events/people/reports
- Schema: Auth.js tables (Account, Session, VerificationToken), Organization,
  OrgMember, Location, CollectionDay, Invitation, SystemConfig, ApiKey models;
  proper User relations to ResponseCard/ActivityLog/Notification
- Permissions: Role hierarchy (owner/admin/editor/reviewer/viewer) with
  action-based permission map and requirePermission/requireAuth helpers
- Onboarding: Multi-step setup wizard for first-user bootstrap (account, org,
  location) with SystemConfig tracking
- Events: CollectionDay model with rrule support for recurring church services
- Auto-assign: Event-aware card assignment engine replacing getPreviousSunday()
- Migration: seed-migration.ts script for upgrading existing deployments

Made-with: Cursor
2026-04-14 23:59:38 -05:00
Randall Stillwell
0ab9932599 Add multi-user card review workflow with role-based access
- Add User model synced from Authentik headers with admin/reviewer/viewer roles
- Add assignment fields (assignedToId, assignedById, reviewedById, etc.) to ResponseCard
- Add userId tracking to ActivityLog and Notification models
- Create auth.ts with getOrCreateUser() and role mapping from Authentik groups
- Create /api/users endpoint and /api/cards/assign batch assignment endpoint
- Gate card mutations behind role checks (viewers read-only, reviewers edit assigned only)
- Gate Monday.com push behind reviewStatus=reviewed instead of ocr_complete
- Add "My Cards" stat card, Assigned To filter, and assignment columns to table
- Add Assign button with user picker to batch selection toolbar (admin only)
- Update card detail: assignment banner, Mark Complete button, prev/next nav, reassign
- Make all field components accept readOnly prop for role-based editing
- Gate settings page behind admin role
- Add userId to stats API for per-user card counts
- Expose dbUser and role through UserProfileProvider context

Made-with: Cursor
2026-04-11 00:00:11 -05:00
Randall Stillwell
aacf9c776b Add manual Scan Inbox button for email monitoring
- Add scanInbox function that opens a separate IMAP connection and
  processes all unseen messages in the configured folder
- Wire to email-watch API as action "scan"
- Add Scan Inbox button to settings page next to Start/Stop Monitoring
- Useful for processing emails that arrived before monitoring started,
  since IMAP IDLE only notifies about new messages

Made-with: Cursor
2026-04-10 13:04:45 -05:00
Randall Stillwell
b2c29120cd Add First Time Guest Date and Salvation Date fields
- Add firstTimeGuestDate and salvationDate (DateTime?) to Prisma schema
- Auto-compute dates during OCR: find previous Sunday from card createdAt
  when visitType indicates first/second time guest or nextStep includes
  Baptism
- Add editable date inputs on card detail page in Workflow section
- Add to Monday.com mappable fields in settings for column mapping
- Add table columns (hidden by default) for both date fields
- Handle full ISO datetime strings in Monday.com date parser
- Apply same logic during reprocessing

Made-with: Cursor
2026-04-09 17:20:24 -05:00
Randall Stillwell
d6d209cf9d Add Monday.com sync-all with push+update support and batch toolbar button
- Sync-all endpoint now supports mode param: "push" (new only), "update"
  (existing only), or "all" (both); also accepts cardIds for batch ops
- Response includes separate created/updated/failed counts
- Settings button renamed to "Sync All to Monday.com" and shows breakdown
- Add "Monday.com" button to the floating selection toolbar for syncing
  selected cards in batch

Made-with: Cursor
2026-04-09 15:14:48 -05:00
Randall Stillwell
05a40cca51 Export CSV based on currently visible columns instead of hardcoded list
- CSV export now respects column visibility settings, exporting only the
  columns the user has toggled on
- Boolean fields export as "Yes"/"No", arrays as comma-separated values
- Toast message shows row and column counts for confirmation

Made-with: Cursor
2026-04-09 10:35:58 -05:00
Randall Stillwell
ec51c049c3 Fix Monday.com date parsing and normalize dropdown labels
- Rewrite parseToISO to handle many OCR date formats: "july 20, 02",
  "20/May/1999", "08/JUN/26", "Oct / 02 / 2004", "Feb 19, 2016", etc.
- Return null instead of raw string when date parsing fails, preventing
  Monday.com ColumnValueException for unparseable dates
- Normalize long nextStep text from pre-update OCR runs (e.g.
  "Expressing my faith in Jesus..." -> "Baptism") before sending to
  Monday.com dropdowns

Made-with: Cursor
2026-04-08 23:14:46 -05:00
Randall Stillwell
daec81884c Restore row-click navigation, add image lightbox, refine OCR prompts
- Change cell copy from click-to-copy to a small hover button so clicking
  a table row navigates to the card detail page again
- Add fullscreen lightbox popup on card detail page images with Escape to
  close and click-outside dismiss
- Further strengthen nextStep OCR prompts to emphasize inspecting each
  checkbox square independently and note most respondents check only one

Made-with: Cursor
2026-04-08 21:10:45 -05:00
Randall Stillwell
13fb94fee9 Make survey fields editable and improve OCR option normalization
- Replace read-only badge chips with toggleable multi-select chips for
  messageTopics, nextStep, campusPreference, and howHeard on card detail page
- Add editable Switch toggles for all boolean fields (prayerForTeam,
  prayerConfidential, iSaidYesBookSent, ftGuestLetterSent)
- Change edits state to Record<string, unknown> to support array and
  boolean values alongside strings
- Update OCR prompts to normalize visitType variations (e.g., "I am a
  first or second time guest at Echo Life" -> "First/Second Time Guest")
- Update message topics list in OCR schema to match actual survey form
  options (Stress/Anxiety, Hearing God's Voice, Dealing With Doubt, etc.)

Made-with: Cursor
2026-04-08 20:22:36 -05:00
Randall Stillwell
cf418bd197 Improve OCR checkbox detection prompts to reduce false positives
Strengthened system prompts and Zod schema descriptions in both ai-ocr.ts
and ollama.ts with explicit checkbox rules: only mark as checked when a
visible X/checkmark/fill is inside the box, treat empty boxes as unchecked,
and err on the side of unchecked when uncertain. Clarified the ambiguous
nextStep field to distinguish the two checkbox options from the section
heading.

Made-with: Cursor
2026-04-08 17:31:50 -05:00
Randall Stillwell
8c84b2a0ac Enhanced table view with cell copy, column toggles, and ClickUp-style toolbar
- Expand ResponseCard type to include all 30+ Prisma fields
- Add 18 new toggleable columns (Personal, Survey, Prayer, Workflow groups)
- ClickUp-style Copy popover in selection toolbar with field toggle switches
- Click-to-copy on individual cells with visual feedback
- Ctrl/Cmd+click column headers to copy column values
- Columns dropdown in filter bar for column visibility management
- Sticky select/name columns when scrolling horizontally
- Keyboard shortcuts: Ctrl+C to copy, Escape to clear selection
- Grouped context menu for column visibility (Core, Personal, Survey, Prayer, Workflow)

Made-with: Cursor
2026-04-08 15:49:07 -05:00
Randall Stillwell
3cbba0cf4e Lower build memory ceiling to 1024MB and clean npm cache in deps layer
The 3GB Coolify server OOMs during Next.js build when node uses 1.5GB
because Docker overhead + OS leaves less than 2GB available. Also
clean npm cache after install to free ~100MB in the deps layer.

Made-with: Cursor
2026-04-07 23:14:09 -05:00
Randall Stillwell
27bdf9d415 Add survey side recovery from source PDFs
Card-level reprocessing only re-OCRs existing images. When the old
page count bug caused survey pages to never be extracted, those images
don't exist to reprocess.

New /api/cards/recover-survey endpoint:
- Finds all cards missing frontImagePath (survey side)
- Locates their source PDFs in MinIO via the ProcessingJob
- Re-extracts the correct survey page for each card
- Stores the image and runs OCR to fill in survey data
- Processes sequentially with rate-limit delays

Settings page shows amber banner when missing backs are detected
with a one-click "Recover Missing Backs" button.

Made-with: Cursor
2026-04-07 23:10:59 -05:00
Randall Stillwell
393114e9be Fix PDF page count detection and add processing diagnostics
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
2026-04-07 22:12:21 -05:00
Randall Stillwell
1d20932ef5 Add OCR rate limit handling and batch reprocess action
Rate limiting:
- Retry with exponential backoff (5s, 10s, 20s, 40s) on rate limit errors
- Reduce concurrent OCR jobs from 2 to 1
- Add 2s delay between AI calls within a card (response vs survey side)
- Add 3s delay between cards in a multi-page PDF
- Add 3s delay between jobs in the processing queue

Batch reprocess:
- New /api/cards/reprocess-batch endpoint processes cards sequentially
  with delays to respect rate limits
- Reprocess button added to selection toolbar on dashboard

Made-with: Cursor
2026-04-07 17:22:33 -05:00
Randall Stillwell
9b27041970 Fix Monday.com column type names: checkbox, status, numbers
Monday.com API returns column types as "checkbox" (not "boolean"),
"status" (not "color"), and "numbers" (not "numeric"). Added these
as aliases in formatForColumnType so values are properly formatted.

Made-with: Cursor
2026-04-07 17:09:59 -05:00
Randall Stillwell
d7d859f49d Fix Monday.com push: format column values by column type
The root cause was sending raw text to checkbox, status, dropdown, and
date columns. Monday.com requires specific JSON structures per type.

- Store column types in _columnTypes when Fetch Columns is clicked
- Format boolean/checkbox: {"checked": "true"/"false"}
- Format status/color: {"label": "value"}
- Format dropdown: {"labels": ["val1", "val2"]}
- Format date: parse MM/DD/YYYY to {"date": "YYYY-MM-DD"}
- Format email/phone/link with proper nested structures
- Text columns pass through as plain strings

Made-with: Cursor
2026-04-07 16:01:44 -05:00
Randall Stillwell
a0f5022de2 Fix Monday.com push failures and improve error visibility
- Fix toast showing success when all cards fail (now shows error/warning)
- Fix column value formatting: booleans use checkbox format, JSON arrays
  join as comma-separated strings, objects serialize properly
- Filter out empty/null/undefined string values before sending to API
- Add verbose logging to Monday.com GraphQL client for debugging

Made-with: Cursor
2026-04-07 13:50:44 -05:00
Randall Stillwell
034c73128f Add runtime safeguards: OCR concurrency limiter, upload size cap, and batched Monday sync
- Processing queue limits OCR to 2 concurrent jobs across all sources (upload, email, folder watcher)
- 50 MB file size cap enforced on uploads, email attachments, and folder watcher
- Monday.com bulk sync now processes in batches of 5 with 1s delay between batches

Made-with: Cursor
2026-04-07 13:44:04 -05:00
Randall Stillwell
7aefe7c4a1 Add manual push to Monday.com (single card + bulk sync)
- Extract reusable pushCardToMonday() that creates or updates Monday items
- New /api/integrations/monday/push/[id] endpoint for single-card push
- New /api/integrations/monday/sync-all endpoint for bulk push of all
  unsynced cards (ocrStatus=complete, no mondayItemId)
- Add "Push to Monday.com" button on card detail page header (shows
  "Update Monday" when card already has a Monday item)
- Add "Push All to Monday.com" button in Monday.com settings card

Made-with: Cursor
2026-04-07 13:32:15 -05:00
Randall Stillwell
2fcdd687f7 Fix duplex scan rotation and swap front/back image labels
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
2026-04-07 13:27:16 -05:00
Randall Stillwell
6cef79309b Auto-resume email and folder watchers on server startup
Added instrumentation.ts that reads persisted watcher state from the
database and restarts active watchers when the Next.js server boots.
Also added imapflow/mailparser to serverExternalPackages.

Made-with: Cursor
2026-04-07 13:14:41 -05:00
Randall Stillwell
d56b1a27ec Fix sideways scanned images by detecting and rotating landscape pages
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
2026-04-07 13:10:03 -05:00
Randall Stillwell
f87ab36493 Fix Monday.com webhook subscription and improve API client
- Add API-Version header to all Monday.com GraphQL requests
- Switch create_webhook/delete_webhook to inline values matching Monday's
  documented syntax (fixes Internal Server Error on subscribe)
- Add extended error details from Monday.com GraphQL responses

Made-with: Cursor
2026-04-07 13:07:46 -05:00
Randall Stillwell
d58285785e Add Monday.com board fields and expand column mapper
- Add followUp, notes, serviceTime, planningCenter, iSaidYesBookSent,
  ftGuestLetterSent fields to ResponseCard to match Monday.com board
- Expand column mapper from 8 fields to all 32 ResponseCard fields with
  friendly labels matching the Easter Survey spreadsheet columns
- Add Workflow & Tracking section on card detail page for the new fields
- Fix pre-existing Buffer type error in images API route

Made-with: Cursor
2026-04-07 12:50:03 -05:00
Randall Stillwell
58f38d92ed Fix broken images by proxying through the app instead of redirecting to internal MinIO
The presigned URLs pointed to a private IP unreachable from the browser.
The image route now streams bytes directly and the card API returns
proxy paths matching the dashboard's existing approach.

Made-with: Cursor
2026-04-07 12:38:08 -05:00
Randall Stillwell
72375d2fae Add Integration Hub: Monday.com sync, webhooks, activity log, notifications
- Bidirectional Monday.com integration with column mapping and file uploads
- Generic webhook system with HMAC-SHA256 signing and configurable events
- Field-level activity log with diff tracking on card detail page
- Persistent notification center with unread badge in header
- Event dispatcher wired into OCR pipeline, card edits, exports, and deletes
- New Prisma models: ActivityLog, Notification; new fields on AppSettings and ResponseCard
- Settings UI with full Monday.com and webhook configuration panels

Made-with: Cursor
2026-04-07 12:23:29 -05:00
Randall Stillwell
6fdf68bc89 Fix profile dropdown crash by wrapping GroupLabel in Group
Menu.GroupLabel requires a parent Menu.Group for context. Without it,
clicking the profile avatar crashed the client.

Made-with: Cursor
2026-04-07 12:13:16 -05:00
Randall Stillwell
41000337e3 Add IMAP email monitoring for scanned card attachments
Watches an email inbox via IMAP IDLE for incoming scanned cards,
extracts PDF/image attachments, and feeds them through the existing
OCR pipeline. Configurable via the Settings page with test connection
support.

Made-with: Cursor
2026-04-07 11:41:52 -05:00
Randall Stillwell
3dd4dc16a3 Fix dropdown crash by removing submenu/group components
Replace DropdownMenuSub/DropdownMenuGroup with flat menu items
to avoid base-ui runtime errors. Theme now cycles with a single
click (Light → Dark → System).

Made-with: Cursor
2026-03-12 12:38:02 -05:00
Randall Stillwell
9ea9ca741c Allow reprocessing any card, not just errored ones
The Reprocess button and dropdown item were only visible for cards
with ocrStatus "error". Now they appear for any card that isn't
currently processing, so users can re-run OCR on completed cards too
(e.g. after changing AI settings).

Made-with: Cursor
2026-03-12 12:36:22 -05:00
Randall Stillwell
9f6e9c3c40 Fix Docker build: use npx for next build command
Bare `next build` fails in Docker (exit code 127) because
node_modules/.bin is not in PATH. Use `npx next build` instead.

Made-with: Cursor
2026-03-12 12:26:08 -05:00
Randall Stillwell
81184040bb Fix Docker build: add explicit gateway dep and reduce memory pressure
- Add @ai-sdk/gateway as direct dependency (was only a transitive dep
  of ai, fragile in Docker builds with --ignore-scripts)
- Avoid double prisma generate in Dockerfile by calling next build
  directly instead of npm run build
- Set NODE_OPTIONS max-old-space-size=1536 for the builder stage to
  prevent OOM on the 3GB Coolify build server

Made-with: Cursor
2026-03-12 11:07:59 -05:00
Randall Stillwell
a6270a605a Add reprocess action to data table row dropdown
Cards with OCR error status now show a "Reprocess" option in the
row actions dropdown menu on the dashboard, triggering the existing
reprocess API endpoint.

Made-with: Cursor
2026-03-12 10:46:15 -05:00
Randall Stillwell
38f4229d3b Add profile page, user dropdown, and Authentik SSO integration
- Profile dropdown in header shows user name/email/avatar with links
  to profile, settings, support, theme submenu, and sign out
- New /profile page with editable user info (job title, company, bio)
  and read-only SSO fields when behind Authentik forward-auth
- /api/auth/me endpoint reads X-authentik-* headers and enriches
  with avatar from Authentik API
- Settings page gains Preferences tab with appearance theme picker
  and notification toggle switches
- User profile context backed by localStorage, merges with Authentik
  data when available

Made-with: Cursor
2026-03-12 10:42:39 -05:00
Randall Stillwell
59c4af5db6 Show placeholder text in filter dropdowns instead of __all__
Pass null instead of "__all__" as the Select value when no filter is
active, so the placeholder text (Visit Type, Attendance, Service) is
displayed by the base-ui SelectValue component.

Made-with: Cursor
2026-03-11 18:40:29 -05:00
Randall Stillwell
1168c96ae2 Fix right-click column visibility context menu crash
Replace base-ui DropdownMenu (requires anchor trigger) with a custom
positioned panel for the header right-click context menu. The base-ui
Menu primitive cannot be rendered free-floating without a trigger element.

Made-with: Cursor
2026-03-11 16:16:00 -05:00
Randall Stillwell
7ff9724a63 Unified dashboard redesign: Quicksand font, charcoal theme, merged upload
- Switch font from Geist to Google Quicksand
- Replace indigo/violet primary with charcoal gray (rgb 31,32,29)
- Update gradient-mesh to warm neutral tones (amber/sage)
- Remove sidebar, add fixed top header bar with profile dropdown
- Merge dashboard and upload pages into single table-centric view
- Stat cards now act as clickable table filters with active highlight
- Simplified filter toolbar: search + dropdowns left, export + upload right
- Upload modal with drag-drop, per-file XHR progress, background uploads
- Global drag-and-drop listener opens upload modal from anywhere
- Table rows are clickable (navigate to detail page)
- Right-click column headers for visibility context menu
- Mobile-responsive card layout below md breakpoint
- Uploading row placeholders with animated progress bars
- Floating selection toolbar slides in at bottom on multi-select
- Delete sidebar, dashboard-hero, quick-actions, upload page

Made-with: Cursor
2026-03-11 15:26:56 -05:00
Randall Stillwell
eabfb4935d Fix nullable Json field in reprocess and Prisma import path
Use Prisma.DbNull instead of null for the cardIds Json? field,
and correct the generated client import path.

Made-with: Cursor
2026-03-11 11:49:33 -05:00
Randall Stillwell
77b9fbea4c Fix AI test endpoint min token requirement
OpenAI requires maxOutputTokens >= 16; bump from 10 to 20.

Made-with: Cursor
2026-03-11 11:46:25 -05:00
Randall Stillwell
2db14b7336 Switch to Vercel AI Gateway for multi-provider OCR
Replace individual provider SDKs (@ai-sdk/openai, @ai-sdk/google,
@ai-sdk/anthropic) with the Vercel AI Gateway. Cloud models are now
accessed via a single AI_GATEWAY_API_KEY with provider/model strings
(e.g. openai/gpt-4o-mini, google/gemini-2.5-flash). Ollama remains
available as a local fallback via @ai-sdk/openai-compatible.

Made-with: Cursor
2026-03-11 11:05:38 -05:00