Repository Structure
Integrated System (4 repos, tightly coupled)
These repos share a common Supabase backend and must be considered together:
~/code/sv-portal # Customer-facing React app
~/code/sv-edge # Supabase Edge Functions (Deno/TypeScript)
~/code/sv-db # Database migrations (SQL)
~/code/sv-docs # System documentation
Standalone Projects
| Repo | Purpose | URL |
sv-website | Marketing site, signup flow | www.mystoragevalet.com |
sv-wiki | Internal docs (AI + human reference) | wiki.mystoragevalet.com |
These repos have NO Supabase integration, NO shared code with the portal.
sv-portal (Customer Portal)
Overview
- Tech: Vite + React + TypeScript + Tailwind CSS
- Auth: Supabase magic links (email-based, no passwords)
- State: React Query for server state
- Routing: React Router (4 routes only)
Routes (Non-Negotiable)
| Route | Purpose |
/login | Magic link authentication |
/dashboard | Item inventory + booking management |
/schedule | Service scheduling flow |
/account | User settings |
Key Components
src/
├── components/
│ ├── ItemCard.tsx # Individual item display
│ ├── BookingsList.tsx # Booking management
│ ├── AddItemModal.tsx # Item creation
│ └── EditItemModal.tsx # Item editing
├── pages/
│ ├── Login.tsx # Auth page
│ ├── Dashboard.tsx # Main inventory view
│ ├── Schedule.tsx # Booking flow
│ └── Account.tsx # User settings
└── lib/
└── supabase.ts # Supabase client
Commands
cd ~/code/sv-portal
npm run dev # Start development server (localhost:5173)
npm run build # Build for production
npm run typecheck # TypeScript validation
vercel --prod # Deploy to production
sv-edge (Edge Functions)
Overview
- Runtime: Deno (Supabase Edge Functions)
- Auth: JWT verification + RLS
- Deployment:
supabase functions deploy
Functions
| Function | Purpose | Trigger |
stripe-webhook | Process Stripe events | Webhook |
calendly-webhook | Process Calendly events | Webhook |
create-checkout-session | Initialize Stripe checkout | Portal |
create-portal-session | Stripe billing portal | Portal |
bookings-list | Fetch user bookings | Portal |
update-booking-items | Update booking items | Portal |
Deployment (CRITICAL)
⚠️ ALWAYS use --no-verify-jwt flag for webhooks
cd ~/code/sv-edge
# Individual function
supabase functions deploy stripe-webhook --no-verify-jwt
supabase functions deploy calendly-webhook --no-verify-jwt
# Or use the deploy script (recommended)
./deploy-and-test.sh
Webhook URLs
| Webhook | URL |
| Stripe | https://gmjucacmbrumncfnnhua.supabase.co/functions/v1/stripe-webhook |
| Calendly | https://gmjucacmbrumncfnnhua.supabase.co/functions/v1/calendly-webhook |
Security
Authentication
- Method: Magic links (email-based, no passwords)
- Provider: Supabase Auth
- Token: JWT with user ID
- Session: Managed by Supabase client
Row Level Security (RLS)
All customer tables are protected by RLS:
- Users can only see/modify their own data
auth.uid() used in all policies
- Service role bypasses RLS (edge functions only)
Billing Protection
Billing fields on customer_profile are protected:
- Users CANNOT update
subscription_status, stripe_customer_id, subscription_id
- Updates only via SECURITY DEFINER functions from webhooks
Secrets Management
Policy
Never paste secrets into chat, docs, or commits. 1Password CLI (op) is the source of truth.
Deployment
Portal (Vercel)
cd ~/code/sv-portal
npm run build # Verify build passes
vercel --prod # Deploy to production
Auto-deploys from GitHub main branch.
Edge Functions
cd ~/code/sv-edge
./deploy-and-test.sh
# OR manually:
supabase functions deploy <function-name> --no-verify-jwt
Database Migrations
cd ~/code/sv-db
supabase db push --linked
Requires: Docker Desktop running
Git Configuration
| Setting | Value |
| Author Email | zach@mystoragevalet.com |
| Author Name | Zachary Brown |
Use only real, verified email addresses. Fabricated emails cause Vercel permission failures.
Architecture Constraints (Non-Negotiable)
- 4 routes only:
/login, /dashboard, /schedule, /account
- Supabase backend only — no custom API servers
- Stripe Hosted flows only — no custom card UI
- Single pricing tier: $299/month
- Magic links only — no password auth
- RLS on all tables — zero cross-tenant access
- Private storage bucket — signed URLs, 1h expiry