# Snapblox · Phase 2 Migration Guide

Migrate from browser-only localStorage → Supabase backend with authentication + Row Level Security.

**Time estimate:** 2-3 hours (first-time setup)  
**Prerequisites:** free Supabase account · Netlify site already deployed · admin email address

---

## Phase 2 · What changes

| Before (Phase 1) | After (Phase 2) |
|---|---|
| Data lives in browser localStorage | Data lives in Supabase Postgres |
| Any URL visitor sees all data | Login required · RLS enforces per-tenant view |
| Photos uploaded via free hosts (unreliable) | Photos uploaded directly to Supabase Storage |
| Passcode-only "auth" for Master CMS | Magic-link email auth for all admin surfaces |
| localStorage lost = data lost | Postgres = automatic backups (7d free tier, 30d Pro) |

**Backward compatibility:** if Supabase config is missing, all pages fall back to localStorage (Phase 1 behavior). This means you can migrate gradually.

---

## Step 1 · Create Supabase project

1. Go to <https://supabase.com> · Sign up (free)
2. **New Project** → pick a region close to your users (Singapore recommended for Thailand)
3. Set strong **database password** · save in 1Password / Bitwarden
4. Wait ~2 min for provisioning

---

## Step 2 · Run schema.sql

1. In Supabase Dashboard → **SQL Editor** → **New Query**
2. Open `supabase/schema.sql` from the repo · copy the entire content
3. Paste into the query editor → click **Run**
4. Should see: `Success. No rows returned` · confirms tables + RLS policies created

**Verify:** go to **Database** → **Tables** · you should see 8 tables (profiles, clients, sub_booths, templates, sub_booth_templates, sessions, photos, audit_log).

---

## Step 3 · Create your admin user

1. Supabase Dashboard → **Authentication** → **Providers** → make sure **Email** is enabled (default on)
2. **Authentication** → **Users** → **Add user** → enter your email + a temp password
3. Go back to **SQL Editor** · promote to platform admin:

```sql
UPDATE profiles SET role = 'platform_admin' 
WHERE email = 'hello.snapblox@gmail.com';
```

Confirm 1 row updated.

---

## Step 4 · Run seed.sql (initial data)

1. **SQL Editor** → **New Query** · paste `supabase/seed.sql`
2. Click **Run**

This creates:
- 8 code layout templates (STRIP, GRID, HERO, ...)
- 10 SAINT image templates (snap-01 through snap-10)
- 1 demo K.MEZ client + 1 sub booth for smoke test

The verify block at the end should show:
```
templates            18
clients               1
sub_booths            1
sub_booth_templates  18
```

---

## Step 5 · Create Storage buckets

Supabase Dashboard → **Storage** → **New bucket** · create 3 buckets:

| Bucket name | Public? | Purpose |
|---|---|---|
| `photos` | ✅ Yes | Guest photo uploads (QR downloads) |
| `templates` | ✅ Yes | Template PNG files (served to booth) |
| `hero-images` | ✅ Yes | Promo page hero images/videos |

For each bucket, add a policy allowing public read:
```sql
-- Run in SQL Editor · repeat for each bucket
CREATE POLICY "Public read photos"
  ON storage.objects FOR SELECT
  USING (bucket_id = 'photos');

CREATE POLICY "Authenticated insert photos"
  ON storage.objects FOR INSERT
  WITH CHECK (bucket_id = 'photos' AND auth.role() = 'authenticated');
```

Then upload the 10 SAINT template PNGs to `templates/saint/snap-01.png` … `snap-10.png` via the Storage UI (drag-and-drop).

Grab the public URL of one file (e.g. snap-01.png) and update the seed:
```sql
UPDATE templates 
SET image_url = 'https://YOUR-PROJECT.supabase.co/storage/v1/object/public/templates/saint/snap-01.png'
WHERE slug = 'snap-01';
-- Repeat for snap-02..snap-10 (or use a batch SQL)
```

Or just batch-update all at once:
```sql
UPDATE templates 
SET image_url = 'https://YOUR-PROJECT.supabase.co/storage/v1/object/public/' || image_url
WHERE kind = 'image' AND image_url NOT LIKE 'http%';
```

---

## Step 6 · Get your Supabase credentials

Supabase Dashboard → **Settings** → **API** · copy:

- **Project URL:** `https://xxxxxxx.supabase.co`
- **anon public key:** `eyJhbGc...` (long string)

Do NOT expose the `service_role` key — it bypasses RLS.

---

## Step 7 · Configure Snapblox to use Supabase

**Preferred method (site-wide, one-time):**

Edit **`assets/snapblox-config.js`** in the Netlify bundle:

```js
window.SNAPBLOX_CONFIG = {
  supabaseUrl: 'https://YOUR-PROJECT.supabase.co',   // ← paste your URL
  supabaseKey: 'eyJhbGc...',                          // ← paste your anon key
  ...
};
```

Redeploy → **all pages** (signup, login, booth, dashboard) now use Supabase automatically. No per-browser configuration needed.

**Per-browser method (dev/testing):**

If you don't want to embed config in the bundle, expand the **⚙️ Supabase configuration** panel on the login page and paste values manually. These are stored in localStorage per-browser.

**Sign in:**
1. Enter your admin email (the one you promoted in Step 3)
2. Click **Send magic link →**
3. Check inbox · click the link · you'll be redirected to Master CMS · signed in

---

## Step 8 · Verify migration

Master CMS should now show data pulled from Supabase (not localStorage):

- **Overview** → 18 templates + 1 client + 1 sub booth (from seed)
- **Client Dashboard** → sign in with a client-owner user (see Step 9 for creating one)
- **Public Booth** → open `?client=k-mez&sub=main-cafe` → should render normally

Console should log: `[SnapStore] Backend: Supabase`

---

## Step 9 · Onboard K.MEZ manager

1. Supabase Dashboard → **Auth** → **Users** → **Add user** with their email
2. In SQL Editor:
```sql
-- Link user to K.MEZ client + give client_owner role
UPDATE profiles 
SET role = 'client_owner',
    client_id = (SELECT id FROM clients WHERE slug = 'k-mez')
WHERE email = 'manager@k-mez.co';
```
3. Send them the Client Dashboard URL: `https://YOUR-SITE.netlify.app/snapblox-admin-client/login.html`
4. They receive the magic link · sign in · see only K.MEZ data (RLS enforcement)

---

## Step 10 · Onboard booth staff (for iPad kiosk)

Booth staff don't need to log in — the booth reads its sub-booth config from the URL params (`?client=X&sub=Y`). No auth needed for guests.

However, if you want the booth to WRITE session/photo records to Supabase (for accurate analytics), the iPad needs to be signed in as a `booth_staff` user:

1. Create a shared staff account in Supabase Auth
2. Sign in on the iPad once (magic link) — session persists 1 year in localStorage
3. Set role:
```sql
UPDATE profiles 
SET role = 'booth_staff',
    client_id = (SELECT id FROM clients WHERE slug = 'k-mez')
WHERE email = 'booth-staff@k-mez.co';
```

Now every photo the booth prints creates a `sessions` + `photos` row · Analytics becomes real.

---

## Rollback plan

If anything goes wrong · Supabase config can be removed and Phase 1 (localStorage) behavior returns:

1. Open Master CMS → Settings → **Remove Supabase config**
2. OR in browser DevTools: `localStorage.removeItem('snapblox_supabase_url')` + `localStorage.removeItem('snapblox_supabase_key')`
3. Refresh page · console logs `[SnapStore] Backend: localStorage`

Your Supabase data stays untouched · you can retry later.

---

## Cost estimate (Supabase free tier)

Free tier covers:
- 500 MB database (fits ~50K photo records)
- 1 GB storage (fits ~5000 receipt PNGs @ 200 KB each)
- 50K monthly active users
- 500K DB API calls / month

**For K.MEZ scale:** ~1000 photos/month → well within free tier · $0/month

When to upgrade to Pro ($25/mo):
- \> 5000 photos/month
- Need daily backups > 7 days
- Custom SMTP for branded magic-link emails

---

## Troubleshooting

**"Failed: JWT expired"** → magic link expired · request new one via login page

**"401 Unauthorized"** → user has no profile row · run `handle_new_user` trigger manually or `INSERT INTO profiles ...`

**"Photo upload failed"** → bucket policies not set · re-run Step 5 SQL

**Data not appearing in Master CMS** → check console for `[SnapStore] Backend:` line · if "localStorage" then config didn't save → repeat Step 7

**Client Owner sees other clients' data** → RLS not enforced · check `SELECT * FROM auth.users` returns your own row only (with anon key)

---

## What's next (Phase 3)

- Custom SMTP for branded email (Amazon SES / SendGrid / Resend)
- Stripe billing integration for self-service client sign-up
- Onboarding wizard (no SQL needed)
- Error monitoring (Sentry)
- Automated backups to R2 / S3
- Multi-language booth UI
- Legal pages (ToS, Privacy Policy, DPA)

---

**Support:** odwnow@gmail.com · Phase 2 rollout typically takes ODW ~1 day for a new client from Supabase project creation to production.
