Skip to content

napoletana-99713: Supabase persistence layer (schema + RLS + driver) - #9

Merged
snackman merged 1 commit into
mainfrom
napoletana-99713-supabase
Jun 5, 2026
Merged

napoletana-99713: Supabase persistence layer (schema + RLS + driver)#9
snackman merged 1 commit into
mainfrom
napoletana-99713-supabase

Conversation

@snackman

@snackman snackman commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

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 iff NEXT_PUBLIC_SUPABASE_URL

  • a SUPABASE_*/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+drawer
    snapshots/rail raw; FKs cascade from tenant/location; indexes on
    tenant_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 tenancy
    core). Deliberate, least-privilege customer surfaces: public storefront menu
    read
    for anon (menu definition + overrides + store_settings; writes stay
    owner/manager); customer-owns-their-orders (is_self_customer /
    can_read_order let a signed-in customer read their own order graph). Explicit
    grants: authenticated full DML (rows gated by policies), anon SELECT only
    on the storefront-public surface.

Driver (src/lib/db/supabase.ts) — implements the entire PosDriver
contract over @supabase/supabase-js with 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_id filter (never crosses tenants).

Tests/seed/opsrls_isolation.sql extended to assert isolation on
orders, 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.example notes. Adds
@supabase/supabase-js (lockfile committed).

Verification (local, zero env)

  • typecheck ✓ · lint ✓ (no warnings) · test:run ✓ (106/106) · build
  • The Postgres RLS isolation harness is intended to run in the optional
    non-blocking rls-isolation CI job (native psql on Ubuntu); it now exercises
    the new orders/menu/payments tables.

Go-live (needs live credentials only — no code left)

Provision Supabase → npm run db:apply (migrations + seed) → set the Supabase
env vars (auto-flips getPosDriver()) → wire Supabase Auth so
auth.uid() == users.id → run run-rls-isolation.sh green.

🤖 Generated with Claude Code

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>
@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pos Ready Ready Preview, Comment Jun 5, 2026 4:36am

Request Review

@snackman
snackman marked this pull request as ready for review June 5, 2026 08:00
@snackman
snackman merged commit 86c7656 into main Jun 5, 2026
5 checks passed
@snackman
snackman deleted the napoletana-99713-supabase branch June 5, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant