| # Slip Scanner Backend (FastAPI) |
|
|
| FastAPI service exposing a /scan endpoint to process receipt images: |
|
|
| - Validates Supabase user via Authorization: Bearer <supabase_jwt> |
| - Detects edges and crops the receipt (OpenCV, perspective transform) |
| - Performs OCR via Hugging Face Inference API (TrOCR) |
| - Uploads the cropped image to Supabase Storage |
| - Inserts a row into the slips table with extracted metadata |
|
|
| ## Prerequisites |
|
|
| - Python 3.11+ |
| - Supabase project with Storage and Postgres |
| - Hugging Face account and API token |
|
|
| ## Environment Variables |
|
|
| Create a .env file in backend/ with: |
|
|
| SUPABASE_URL=... |
| SUPABASE_ANON_KEY=... |
| SUPABASE_SERVICE_ROLE_KEY=... |
| SUPABASE_STORAGE_BUCKET=slips |
|
|
| HUGGINGFACE_API_TOKEN=... |
| HF_OCR_MODEL=microsoft/trocr-base-printed |
|
|
| FRONTEND_CORS_ORIGIN=http://localhost:3000 |
|
|
| Notes: |
|
|
| - Use the Service Role key server-side only. Never expose it to the frontend. |
| - If you prefer signed URLs, adjust upload_bytes_to_supabase_storage accordingly and do not make the bucket public. |
|
|
| ## Install & Run (Windows PowerShell) |
|
|
| cd backend |
| python -m venv .venv |
| . .venv\Scripts\Activate.ps1 |
| pip install -r requirements.txt |
| uvicorn backend.main:app --reload --host 0.0.0.0 --port 8000 |
|
|
| ## API |
|
|
| ### Health |
|
|
| GET http://localhost:8000/health |
|
|
| ### Scan |
|
|
| POST http://localhost:8000/scan |
| Authorization: Bearer <supabase_jwt> |
| Content-Type: multipart/form-data |
| file=@/path/to/receipt.jpg |
|
|
| cURL example: |
|
|
| curl -X POST http://localhost:8000/scan \ |
| -H "Authorization: Bearer $SUPABASE_JWT" \ |
| -F "file=@receipt.jpg" |
| |
| Response: |
| { |
| "ok": true, |
| "slip": { |
| "id": "...", |
| "user_id": "...", |
| "image_url": "https://...", |
| "amount": 250.0, |
| "category": "Food", |
| "raw_text": "...", |
| "created_at": "2025-09-15T10:00:00Z", |
| "crop_confidence": 0.8 |
| } |
| } |
|
|
| ## Supabase Setup |
|
|
| ### Storage Bucket (public or signed) |
|
|
| - Create bucket slips. |
| - For public previews, enable public read on the bucket or a specific folder per user. Alternatively, keep private and generate signed URLs server-side. |
|
|
| ### Table |
|
|
| Create table slips (adjust types as desired): |
|
|
| create table if not exists public.slips ( |
| id uuid primary key default gen_random_uuid(), |
| user_id uuid not null, |
| image_path text not null, |
| image_url text, |
| raw_text text, |
| amount numeric, |
| category text, |
| crop_confidence real, |
| created_at timestamptz not null default now() |
| ); |
|
|
| alter table public.slips enable row level security; |
|
|
| RLS policies (only owner reads/writes their slips): |
|
|
| create policy "slips select own" on public.slips |
| for select |
| using (auth.uid() = user_id); |
| |
| create policy "slips insert own" on public.slips |
| for insert |
| with check (auth.uid() = user_id); |
|
|
| create policy "slips update own" on public.slips |
| for update |
| using (auth.uid() = user_id); |
| |
| ### Frontend Config |
| |
| In Next.js .env.local: |
| |
| NEXT_PUBLIC_API_URL=http://localhost:8000 |
| NEXT_PUBLIC_SUPABASE_URL=... |
| NEXT_PUBLIC_SUPABASE_ANON_KEY=... |
| |
| From the client, include the Supabase session JWT as the Authorization header when calling /scan. |
| |
| ## Design Notes (rules.md alignment) |
| |
| - Mobile-first UX is handled in the Next.js app; backend is stateless and fast. |
| - No sensitive keys in frontend; backend uses Service Role for Storage/DB. |
| - Performance: edge detection and OCR happen server-side; images are resized and optimized. |
| - Security: RLS ensures users only see their own slips; Storage access can be scoped. |
| |
| ## Dev Tips |
| |
| - OpenAPI docs: http://localhost:8000/docs |
| - If HF model cold-starts and returns 503, retry the request after a few seconds. |
| |