Storage
Supabase Storage for AI-generated images and user file uploads.
Overview
File storage uses Supabase Storage via the @supabase/supabase-js client. The module lives in lib/supabase/storage.ts and is server-side only — it uses the service role key which bypasses Row Level Security.
Functions
uploadImageFromBase64(base64, mimeType?)
Called by the generate_image tool after OpenAI returns a b64_json response:
import { uploadImageFromBase64 } from "@/lib/supabase/storage";
const publicUrl = await uploadImageFromBase64(base64String, "image/png");
// Returns: "https://xxx.supabase.co/storage/v1/object/public/generated-images/abc123.png"- Bucket:
generated-images - Filename:
nanoid()(random, collision-safe) - Returns the public URL stored in the
assetstable
uploadFile(buffer, fileName, mimeType, bucket)
Generic upload for user-submitted files. Used for profile images, chat attachments (receipt images, PDFs, etc.):
const url = await uploadFile(pdfBuffer, "contract.pdf", "application/pdf", "uploads");- Bucket:
uploads - Accepts any file type — images, PDFs, documents
- Returns the public URL
Supabase Setup
Create a Supabase project
Sign up at supabase.com and create a new project. See the Supabase Storage docs for a full overview of buckets and access policies.
Create the storage buckets
In the Supabase dashboard, go to Storage → New bucket and create both buckets:
| Bucket | Public | Purpose |
|---|---|---|
generated-images | Yes | AI-generated images from the generate_image tool |
uploads | Yes | User file uploads — profile images, chat attachments (receipt images, PDFs, etc.) |
Copy credentials to .env
SUPABASE_URL=https://xxxx.supabase.co
SUPABASE_SERVICE_ROLE_KEY=eyJ...SUPABASE_SERVICE_ROLE_KEY bypasses Supabase Row Level Security. It must never be exposed to the browser or included in any NEXT_PUBLIC_ variable. All uploads happen server-side via the lib/supabase/storage.ts module.