napoletana-99713: Supabase persistence layer (schema + RLS + driver) - #9
Merged
Conversation
Build the live persistence backbone so the feature-complete app can flip from the in-memory mock to real Supabase by setting env vars, with the mock staying the zero-env default (build + full Vitest suite pass with no env). - Migrations: 20260605000000_domain_core.sql (every domain table the mock driver implies — menu, orders/items/modifiers, payments, connect, customers, magic links, deliveries, inventory + movements + recipe links, staff/shifts/cash, business_day_closes, store/payment settings, subscriptions, onboarding, audit_log; enums mirror the TS unions; money in integer cents; jsonb for totals/fulfillment/report blobs; indexes on tenant_id/location_id) and 20260605000100_domain_rls.sql (RLS enabled+forced on all, memberships-keyed, platform-admin bypass; public storefront menu read for anon; customer can read their own orders via is_self_customer/can_read_order; explicit grants). - Supabase driver (src/lib/db/supabase.ts) implements the FULL PosDriver contract with the same idempotency/menu-fold/depletion/report semantics as the mock; wired into getPosDriver() (selected only when Supabase env is present). - Extended rls_isolation.sql to assert isolation on orders/menu/payments (the optional non-blocking Postgres CI job now covers them). - Full demo seed (Tony's Pizza), apply.sh + npm db:apply, README + PRODUCTION READINESS go-live steps, .env.example notes. Adds @supabase/supabase-js. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The final live-wiring step: a real Supabase driver + complete Postgres schema
so the feature-complete app can flip from the in-memory mock to live persistence
by setting env vars — without requiring env to build (the mock stays the
zero-env default; build + the full Vitest suite pass with no env).
getPosDriver()now selects the Supabase driver iffNEXT_PUBLIC_SUPABASE_URLSUPABASE_*/anon key are present (read lazily at call time), else the mock.No call sites changed — both implement the same
PosDriver.What's in it
Migrations (after the tenancy core):
20260605000000_domain_core.sql— every domain table the mock implies: menu(categories/items/sizes/modifier_groups/modifiers/item_modifier_groups/
location_menu_overrides),
store_settings/payment_settings, orders(
orders/order_items/order_item_modifiers),payments/connect_accounts,customers/magic_link_tokens/deliveries, inventory(
inventory_items/inventory_movements/item_inventory_links),staff/shifts/shift_cash_events/business_day_closes, and the SaaS layer(
subscriptions/tenant_onboarding/audit_log). Enums mirror the TS unions;money is integer cents; jsonb for
totals/fulfillment/report+drawersnapshots/rail
raw; FKs cascade from tenant/location; indexes ontenant_id/location_id+ common queries.20260605000100_domain_rls.sql— RLS enabled + FORCED on every table,memberships-keyed with a platform-admin bypass (same pattern as the tenancycore). Deliberate, least-privilege customer surfaces: public storefront menu
read for
anon(menu definition + overrides +store_settings; writes stayowner/manager); customer-owns-their-orders (
is_self_customer/can_read_orderlet a signed-in customer read their own order graph). Explicitgrants:
authenticatedfull DML (rows gated by policies),anonSELECT onlyon the storefront-public surface.
Driver (
src/lib/db/supabase.ts) — implements the entirePosDrivercontract over
@supabase/supabase-jswith the same idempotency, menu-fold + 86,inventory depletion, report, and idempotent end-of-day semantics as the mock.
Service-role key is read server-side only and every tenant-scoped query carries
an explicit
tenant_id/location_idfilter (never crosses tenants).Tests/seed/ops —
rls_isolation.sqlextended to assert isolation onorders, menu, and payments (the optional non-blocking Postgres CI job now
covers them); full demo seed (Tony's Pizza, 2 locations, menu, settings,
inventory, staff, onboarding + Pro sub);
supabase/apply.sh+npm run db:apply;README + PRODUCTION_READINESS go-live steps;
.env.examplenotes. Adds@supabase/supabase-js(lockfile committed).Verification (local, zero env)
typecheck✓ ·lint✓ (no warnings) ·test:run✓ (106/106) ·build✓non-blocking
rls-isolationCI job (native psql on Ubuntu); it now exercisesthe new orders/menu/payments tables.
Go-live (needs live credentials only — no code left)
Provision Supabase →
npm run db:apply(migrations + seed) → set the Supabaseenv vars (auto-flips
getPosDriver()) → wire Supabase Auth soauth.uid() == users.id→ runrun-rls-isolation.shgreen.🤖 Generated with Claude Code