b433c79677
Covers: - Connection test (direct vs pooler, IPv4 vs IPv6) - pg_dump commands (schema-only and data-only) - Restore steps to local DB - Verification queries - Schema-per-brand restructure (future work)
177 lines
5.6 KiB
Markdown
177 lines
5.6 KiB
Markdown
# Supabase Dump & Restore Guide
|
|
|
|
This guide is the runbook for when your brother removes the Supabase
|
|
spend cap and we can finally connect to the live Supabase project to
|
|
pull the real schema + data.
|
|
|
|
## Prerequisites
|
|
|
|
- Brother has removed the Supabase spend cap (project status: ACTIVE_HEALTHY)
|
|
- Supabase project ref: `wnzkhezyhnfzhkhiflrp` (from CLAUDE.md)
|
|
- Supabase DB password: `YLKzP9jz2yqop7jr` (user-provided)
|
|
- Local Postgres running on `127.0.0.1:5432` (user: `routecommerce`,
|
|
password: `routecommerce_dev_password`, db: `route_commerce`)
|
|
|
|
## Connection Test
|
|
|
|
The Supabase hostname has NO IPv4 address from this dev box. You
|
|
must use the **Supabase Supavisor pooler** (port 6543) which resolves
|
|
over IPv4. From your home network, you can also use the direct
|
|
hostname on port 5432.
|
|
|
|
```bash
|
|
# Direct hostname (home network only — dev box has no IPv6)
|
|
psql "postgres://postgres.wnzkhezyhnfzhkhiflrp:YLKzP9jz2yqop7jr@db.wnzkhezyhnfzhkhiflrp.supabase.co:5432/postgres"
|
|
|
|
# Pooler (works from dev box — IPv4 only)
|
|
psql "postgres://postgres.wnzkhezyhnfzhkhiflrp:YLKzP9jz2yqop7jr@aws-0-us-east-1.pooler.supabase.com:6543/postgres"
|
|
```
|
|
|
|
If neither works after the cap is removed, check:
|
|
1. `getent hosts db.wnzkhezyhnfzhkhiflrp.supabase.co` — should return IPv4
|
|
2. Supabase dashboard → Settings → API → "Direct connection" string
|
|
|
|
## Capture Schema (no data)
|
|
|
|
```bash
|
|
# From your home network, with the password set:
|
|
PGPASSWORD="YLKzP9jz2yqop7jr" pg_dump \
|
|
--host=db.wnzkhezyhnfzhkhiflrp.supabase.co \
|
|
--port=5432 \
|
|
--username=postgres \
|
|
--dbname=postgres \
|
|
--schema-only \
|
|
--no-owner \
|
|
--no-privileges \
|
|
--no-acl \
|
|
--file=supabase/captured_schema.sql
|
|
```
|
|
|
|
This produces a SQL file with all CREATE TABLE, CREATE FUNCTION,
|
|
CREATE INDEX, etc. statements. May be 50-200 MB depending on the
|
|
function count. **Compress it before committing:**
|
|
|
|
```bash
|
|
gzip supabase/captured_schema.sql # → captured_schema.sql.gz
|
|
```
|
|
|
|
## Capture Data (no schema)
|
|
|
|
```bash
|
|
PGPASSWORD="YLKzP9jz2yqop7jr" pg_dump \
|
|
--host=db.wnzkhezyhnfzhkhiflrp.supabase.co \
|
|
--port=5432 \
|
|
--username=postgres \
|
|
--dbname=postgres \
|
|
--data-only \
|
|
--no-owner \
|
|
--no-privileges \
|
|
--no-acl \
|
|
--disable-triggers \
|
|
--file=supabase/captured_data.sql
|
|
```
|
|
|
|
Use `--disable-triggers` so the dump skips triggers that might fire
|
|
on INSERT and slow things down. Compress:
|
|
|
|
```bash
|
|
gzip supabase/captured_data.sql
|
|
```
|
|
|
|
## Restore to Local DB
|
|
|
|
```bash
|
|
cd /path/to/route-commerce
|
|
export PGPASSWORD=routecommerce_dev_password
|
|
|
|
# 1. Wipe the synthesized schema
|
|
psql -U routecommerce -h 127.0.0.1 -d route_commerce \
|
|
-c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
|
|
|
|
# 2. Apply the real schema
|
|
zcat supabase/captured_schema.sql.gz | \
|
|
psql -U routecommerce -h 127.0.0.1 -d route_commerce \
|
|
-v ON_ERROR_STOP=1 2>&1 | tee /tmp/schema_restore.log
|
|
|
|
# 3. Apply the data
|
|
zcat supabase/captured_data.sql.gz | \
|
|
psql -U routecommerce -h 127.0.0.1 -d route_commerce \
|
|
-v ON_ERROR_STOP=1 2>&1 | tee /tmp/data_restore.log
|
|
```
|
|
|
|
## Verify
|
|
|
|
```bash
|
|
# Should show ~70+ tables (not just the 70 from the synthesized schema)
|
|
psql -U routecommerce -h 127.0.0.1 -d route_commerce -c "\dt"
|
|
|
|
# Should show actual data, not 0 rows everywhere
|
|
psql -U routecommerce -h 127.0.0.1 -d route_commerce -c "SELECT count(*) FROM brands;"
|
|
psql -U routecommerce -h 127.0.0.1 -d route_commerce -c "SELECT count(*) FROM products;"
|
|
psql -U routecommerce -h 127.0.0.1 -d route_commerce -c "SELECT count(*) FROM orders;"
|
|
```
|
|
|
|
## Clean Up
|
|
|
|
```bash
|
|
# Once verified, drop the synthesized schema (no longer needed)
|
|
rm supabase/synthesized/000_base_schema.sql
|
|
git add -A
|
|
git commit -m "remove synthesized schema (real dump now in place)"
|
|
```
|
|
|
|
## Schema-Per-Brand Restructure (FUTURE, not in this dump)
|
|
|
|
The user wants each brand in its own Postgres schema:
|
|
- `brand_tuxedo` — products, orders, stops, etc. for Tuxedo
|
|
- `brand_indian_river_direct` — products, orders, stops, etc. for IRD
|
|
|
|
This is a major refactor and is **NOT** part of the dump-and-restore.
|
|
Plan for it as a follow-up phase once the data is loaded into shared
|
|
tables and the app is verified working.
|
|
|
|
The refactor will involve:
|
|
1. Creating schemas `brand_tuxedo`, `brand_indian_river_direct`
|
|
2. Moving brand-specific tables into the appropriate schema
|
|
3. Updating all RPC SECURITY DEFINER functions to set
|
|
`search_path` based on caller brand
|
|
4. Updating server actions to set `search_path` per request
|
|
5. Updating PostgREST config to expose schemas
|
|
6. Testing brand isolation thoroughly
|
|
|
|
## Troubleshooting
|
|
|
|
### "password authentication failed"
|
|
Password has been rotated. Check Supabase dashboard → Settings →
|
|
Database → Reset password. The user-provided password
|
|
`YLKzP9jz2yqop7jr` may need to be reset.
|
|
|
|
### "could not translate host name"
|
|
Dev box has no IPv6. Use the pooler hostname
|
|
`aws-0-us-east-1.pooler.supabase.com` or run from a machine with IPv6.
|
|
|
|
### "permission denied for schema auth"
|
|
The dump includes the `auth` schema which is Supabase-managed.
|
|
Filter it out with: `--exclude-schema=auth --exclude-schema=storage
|
|
--exclude-schema=realtime --exclude-schema=supabase_functions`.
|
|
|
|
### "relation already exists"
|
|
You forgot step 1 (DROP SCHEMA public CASCADE). The data restore
|
|
runs `INSERT` and may not recreate tables — only the schema dump
|
|
does that.
|
|
|
|
### "out of memory" during data restore
|
|
The data file is huge. Use `--single-transaction` to keep
|
|
Postgres from spilling to disk:
|
|
|
|
```bash
|
|
zcat supabase/captured_data.sql.gz | \
|
|
psql -U routecommerce -h 127.0.0.1 -d route_commerce \
|
|
--single-transaction \
|
|
-v ON_ERROR_STOP=0 2>&1 | tee /tmp/data_restore.log
|
|
```
|
|
|
|
### Data dump is too big to commit
|
|
Don't commit it. The data file is in `.gitignore` and gets
|
|
re-loaded on first run via a one-shot script.
|