Floralé’s database lives in Supabase (PostgreSQL) and consists of two core tables —Documentation Index
Fetch the complete documentation index at: https://mintlify.com/dlampatricio/florale/llms.txt
Use this file to discover all available pages before exploring further.
categories and products — plus a public storage bucket for product images. Row Level Security is enabled on every table, allowing anonymous visitors to read data while restricting all write operations to authenticated admin users. This page documents every column, constraint, index, and policy defined in supabase-schema.sql.
Tables
categories
Stores the top-level product categories displayed in the Floralé storefront. Each category has a human-readable slug used as its primary key so that URLs remain stable and meaningful.
Primary key. A short, URL-safe slug that uniquely identifies the category (e.g.
cajas, desayunos). Chosen manually at insert time rather than auto-generated so that it can be referenced directly in URLs and foreign keys without an extra lookup.Display name shown to customers in the storefront (e.g.
Cajas de Regalo, Desayunos). Must not be null.A short prose description of the category. Defaults to an empty string if omitted.
products
The central table for all gift items sold in the store. Each product belongs to exactly one category and carries its own pricing, imagery, and timestamps.
Primary key. A human-readable slug (e.g.
corazon_red_oso, desayuno_premium) that doubles as a stable URL identifier for each product.Full product name displayed in listings and detail pages (e.g.
Box de Corazón con Peluche Oso).Long-form product description shown on the detail page. Defaults to an empty string if not provided.
Price in Uruguayan pesos (UYU), stored as a whole integer (no decimal). For example, a value of
1850 represents $1,850 UYU. The frontend is responsible for formatting this value with the appropriate locale and currency symbol.URL or relative path of the product’s primary image. Can be a Supabase Storage URL (e.g.
https://<project>.supabase.co/storage/v1/object/public/product-images/corazon_red_oso.jpg) or a local public path (e.g. /products/corazon_red_oso.jpg). Defaults to an empty string if omitted. Next.js <Image> is configured to allow remote patterns from *.supabase.co.Foreign key referencing
categories(id). Every product must belong to a category. Deleting a category that still has products will fail with a foreign-key constraint violation.Timestamp with time zone recording when the row was first inserted. Defaults to
NOW() so it is set automatically at insert time.Timestamp with time zone recording the last modification. Defaults to
NOW(). Update this column manually (or via a trigger) whenever a product record changes to keep it accurate.Index
A standard B-tree index on
products(category_id). Speeds up the common query pattern of fetching all products that belong to a given category — used on every category page in the storefront. Created with IF NOT EXISTS so the migration is safe to re-run.Row Level Security
RLS is enabled on bothcategories and products. The policies implement a simple public-read / authenticated-write pattern that suits a single-admin storefront.
Read policies (public)
Any visitor — authenticated or not — maySELECT from both tables. This is what powers the storefront’s product listings without requiring a login.
| Table | Policy name | Operation | Condition |
|---|---|---|---|
products | Public can read products | SELECT | true (always) |
categories | Public can read categories | SELECT | true (always) |
Write policies (authenticated only)
Insert, update, and delete operations are restricted to users whose JWT carries theauthenticated role — i.e. users who have signed in via Supabase Auth. This protects the admin panel data from unauthenticated writes.
| Table | Policy name | Operation | Condition |
|---|---|---|---|
products | Authenticated can insert products | INSERT | auth.role() = 'authenticated' |
products | Authenticated can update products | UPDATE | auth.role() = 'authenticated' |
products | Authenticated can delete products | DELETE | auth.role() = 'authenticated' |
categories | Authenticated can insert categories | INSERT | auth.role() = 'authenticated' |
categories | Authenticated can update categories | UPDATE | auth.role() = 'authenticated' |
categories | Authenticated can delete categories | DELETE | auth.role() = 'authenticated' |
Server-side code that uses
supabaseAdmin (the service role client from lib/supabase-service.ts) bypasses RLS entirely, regardless of these policies. That client should only be used for trusted admin operations in server-side contexts — never in browser code.Supabase Storage
product-images bucket
A single public Supabase Storage bucket holds all product images. It is created by the schema migration with public = true, which means Supabase generates publicly accessible URLs for every object — no signed URL required for reads.
| Setting | Value |
|---|---|
| Bucket ID | product-images |
| Public | true |
| Allowed origins | Any (governed by RLS policies below) |
Storage policies
| Policy name | Operation | Condition |
|---|---|---|
| Public can read product images | SELECT | bucket_id = 'product-images' |
| Authenticated can upload product images | INSERT | bucket_id = 'product-images' AND auth.role() = 'authenticated' |
| Authenticated can update product images | UPDATE | bucket_id = 'product-images' AND auth.role() = 'authenticated' |
| Authenticated can delete product images | DELETE | bucket_id = 'product-images' AND auth.role() = 'authenticated' |
next.config.ts to allow images from *.supabase.co so that Supabase Storage URLs work seamlessly with the <Image> component: