Selfhost/migrate #1

Open
tyler wants to merge 21 commits from TOC/route-commerce:selfhost/migrate into main
6 changed files with 37 additions and 698 deletions
Showing only changes of commit be226d3430 - Show all commits
+3 -3
View File
@@ -1,9 +1,9 @@
# --- Self-hosted Postgres (Docker) --- # --- Self-hosted Postgres (Docker) ---
POSTGRES_USER=routecommerce POSTGRES_USER=routecommerce
POSTGRES_PASSWORD=lay7yqM3fHNvwpfjb4tvz7M3kimopyrSHQF24vsuGUM POSTGRES_PASSWORD=routecommerce_dev_password
POSTGRES_DB=route_commerce POSTGRES_DB=route_commerce
# Used by the app and push-migrations.js to talk to the local DB # Used by the app and push-migrations.js to talk to the local DB
DATABASE_URL=postgresql://routecommerce:lay7yqM3fHNvwpfjb4tvz7M3kimopyrSHQF24vsuGUM@127.0.0.1:5432/route_commerce?schema=public DATABASE_URL=postgresql://routecommerce:routecommerce_dev_password@127.0.0.1:5432/route_commerce?schema=public
# --- PostgREST (REST API — replaces Supabase REST) --- # --- PostgREST (REST API — replaces Supabase REST) ---
# Server actions call `${NEXT_PUBLIC_SUPABASE_URL}/rest/v1/rpc/...` # Server actions call `${NEXT_PUBLIC_SUPABASE_URL}/rest/v1/rpc/...`
@@ -25,7 +25,7 @@ STORAGE_BUCKET_PREFIX=
NEXT_PUBLIC_STORAGE_BASE_URL=http://localhost:9000 NEXT_PUBLIC_STORAGE_BASE_URL=http://localhost:9000
# --- Better Auth --- # --- Better Auth ---
BETTER_AUTH_SECRET=change-me-32-char-random-string-here-pls-32chars BETTER_AUTH_SECRET=REPLACE_ME_WITH_OPENSSL_RAND_HEX_32
BETTER_AUTH_URL=http://localhost:3000 BETTER_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_BETTER_AUTH_URL=http://localhost:3000 NEXT_PUBLIC_BETTER_AUTH_URL=http://localhost:3000
+14
View File
@@ -44,3 +44,17 @@ supabase/.temp/
# Docker / self-hosted Postgres # Docker / self-hosted Postgres
db_data/ db_data/
# Captured Supabase data (re-pull from Supabase as needed)
supabase/captured/captured_data.sql.gz
supabase/captured/captured_schema.sql
# Local data and binaries
.data/
bin/postgrest
bin/minio
bin/mc
bin/postgrest-proxy.js
bin/postgrest.tar.xz
# Captured Supabase dump (regenerate from Supabase with the guide in docs/SUPABASE_DUMP_GUIDE.md)
supabase/captured/
+18
View File
@@ -19,6 +19,24 @@ const nextConfig: NextConfig = {
protocol: "https", protocol: "https",
hostname: "picsum.photos", hostname: "picsum.photos",
}, },
// Local self-hosted MinIO (replaces Supabase Storage)
{
protocol: "http",
hostname: "localhost",
port: "9000",
pathname: "/**",
},
{
protocol: "http",
hostname: "127.0.0.1",
port: "9000",
pathname: "/**",
},
// Production MinIO behind route.crispygoat.com
{
protocol: "https",
hostname: "storage.route.crispygoat.com",
},
], ],
formats: ["image/avif", "image/webp"], formats: ["image/avif", "image/webp"],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
+1 -1
View File
@@ -1,4 +1,4 @@
import { betterAuth } from "better-auth/minimal"; import { betterAuth } from "better-auth";
import { Kysely, PostgresDialect } from "kysely"; import { Kysely, PostgresDialect } from "kysely";
import { Pool } from "pg"; import { Pool } from "pg";
import { nextCookies } from "better-auth/next-js"; import { nextCookies } from "better-auth/next-js";
+1
View File
@@ -0,0 +1 @@
Captured Supabase dumps go here. Regenerate with docs/SUPABASE_DUMP_GUIDE.md
-694
View File
@@ -1,694 +0,0 @@
-- =====================================================================
-- SYNTHESIZED BASE SCHEMA - TEMPORARY
-- =====================================================================
-- This is a synthesized base schema derived from migration column
-- references. It exists to give the local DB a workable structure
-- BEFORE the real Supabase data dump arrives.
--
-- WHEN the brother removes the Supabase spend cap and the user gets
-- the real pg_dump, this entire file should be DROPPED and replaced
-- with the real schema from the dump. Do NOT rely on this for prod.
--
-- Pattern: every table has id (UUID PK), created_at, updated_at.
-- Columns confirmed by migrations are added with proper types.
-- For data we don't know about, a payload JSONB column holds it
-- so the dump can be loaded without constraint failures.
-- =====================================================================
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- ============== BRANDS ==============
CREATE TABLE IF NOT EXISTS brands (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug TEXT UNIQUE,
name TEXT,
owner_user_id UUID,
stripe_customer_id TEXT,
stripe_subscription_id TEXT,
stripe_subscription_status TEXT DEFAULT 'inactive',
stripe_current_period_end TIMESTAMPTZ,
plan_tier TEXT DEFAULT 'starter',
max_users INT DEFAULT 1,
max_stops_monthly INT DEFAULT 25,
max_products INT DEFAULT 50,
logo_url TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== BRAND SETTINGS ==============
CREATE TABLE IF NOT EXISTS brand_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
hero_tagline TEXT,
hero_image_url TEXT,
about_headline TEXT,
about_subheadline TEXT,
custom_footer_text TEXT,
show_wholesale_link BOOLEAN DEFAULT true,
show_zip_search BOOLEAN DEFAULT true,
show_schedule_pdf BOOLEAN DEFAULT true,
show_text_alerts BOOLEAN DEFAULT false,
schedule_pdf_notes TEXT,
brand_primary_color TEXT DEFAULT '#16a34a',
brand_secondary_color TEXT DEFAULT '#f5f5f4',
brand_bg_color TEXT DEFAULT '#fafaf9',
brand_text_color TEXT DEFAULT '#1c1917',
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== BRAND FEATURES (add-ons) ==============
CREATE TABLE IF NOT EXISTS brand_features (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
feature_key TEXT,
enabled BOOLEAN DEFAULT false,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== PRODUCTS ==============
CREATE TABLE IF NOT EXISTS products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
name TEXT,
description TEXT,
price NUMERIC(12,2),
image_url TEXT,
is_taxable BOOLEAN DEFAULT true,
is_active BOOLEAN DEFAULT true,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== STOPS ==============
CREATE TABLE IF NOT EXISTS stops (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
address TEXT,
zip TEXT,
city TEXT,
state TEXT,
datetime TIMESTAMPTZ,
cutoff_time TIMESTAMPTZ,
status TEXT DEFAULT 'active',
deleted_at TIMESTAMPTZ,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== ORDERS ==============
CREATE TABLE IF NOT EXISTS orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
stop_id UUID,
customer_email TEXT,
customer_name TEXT,
customer_phone TEXT,
customer_address TEXT,
total_amount NUMERIC(12,2),
status TEXT DEFAULT 'pending',
fulfillment_type TEXT,
shipping_status TEXT DEFAULT 'pending',
tracking_number TEXT,
idempotency_key UUID UNIQUE,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== ORDER ITEMS ==============
CREATE TABLE IF NOT EXISTS order_items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
order_id UUID,
product_id UUID,
quantity INT DEFAULT 1,
price NUMERIC(12,2),
fulfillment TEXT DEFAULT 'pickup',
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== PRODUCT STOPS (junction) ==============
CREATE TABLE IF NOT EXISTS product_stops (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
product_id UUID,
stop_id UUID,
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== CUSTOMERS ==============
CREATE TABLE IF NOT EXISTS customers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
email TEXT,
name TEXT,
phone TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== ADMIN USERS ==============
CREATE TABLE IF NOT EXISTS admin_users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID,
brand_id UUID,
email TEXT,
role TEXT,
is_active BOOLEAN DEFAULT true,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== WHOLESALE TABLES ==============
CREATE TABLE IF NOT EXISTS wholesale_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
wholesale_enabled BOOLEAN DEFAULT true,
require_approval BOOLEAN DEFAULT true,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS wholesale_products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
name TEXT,
description TEXT,
price NUMERIC(12,2),
rc_product_id UUID,
is_active BOOLEAN DEFAULT true,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS wholesale_customers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
email TEXT,
name TEXT,
phone TEXT,
status TEXT DEFAULT 'pending',
approved_at TIMESTAMPTZ,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS wholesale_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
customer_id UUID,
status TEXT DEFAULT 'pending',
total_amount NUMERIC(12,2),
cart_snapshot JSONB,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS wholesale_order_items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
wholesale_order_id UUID,
product_id UUID,
customer_id UUID,
quantity INT,
price NUMERIC(12,2),
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS wholesale_deposits (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
customer_id UUID,
order_id UUID,
amount NUMERIC(12,2),
payment_method TEXT,
status TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS wholesale_notifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
type TEXT,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS wholesale_customer_product_pricing (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
customer_id UUID,
product_id UUID,
price NUMERIC(12,2),
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS wholesale_sync_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
-- ============== COMMUNICATIONS ==============
CREATE TABLE IF NOT EXISTS communication_campaigns (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
name TEXT,
subject TEXT,
body TEXT,
status TEXT DEFAULT 'draft',
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS communication_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
name TEXT,
subject TEXT,
body TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS communication_contacts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
email TEXT,
name TEXT,
sms_opt_in BOOLEAN DEFAULT false,
email_opt_in BOOLEAN DEFAULT true,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS communication_message_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
campaign_id UUID,
contact_id UUID,
channel TEXT,
customer_email TEXT,
subject TEXT,
body TEXT,
event_id TEXT,
status TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS communication_segments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
name TEXT,
definition JSONB,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS communication_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== HARVEST LOTS ==============
CREATE TABLE IF NOT EXISTS harvest_lots (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
product_id UUID,
lot_number TEXT,
quantity_lbs NUMERIC(12,2),
quantity_used_lbs NUMERIC(12,2) DEFAULT 0,
harvest_date DATE,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS harvest_lot_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
lot_id UUID,
event_type TEXT,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
-- ============== WATER LOG ==============
CREATE TABLE IF NOT EXISTS water_users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
email TEXT,
name TEXT,
pin TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS water_headgates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
name TEXT,
token TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS water_irrigators (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
name TEXT,
pin TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS water_log_entries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
headgate_id UUID,
irrigator_id UUID,
logged_by UUID,
gallons NUMERIC(12,2),
started_at TIMESTAMPTZ,
ended_at TIMESTAMPTZ,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS water_admin_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS water_alert_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS water_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== TIME TRACKING ==============
CREATE TABLE IF NOT EXISTS time_tracking_workers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
name TEXT,
pin TEXT,
is_active BOOLEAN DEFAULT true,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS time_tracking_tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
name TEXT,
description TEXT,
is_active BOOLEAN DEFAULT true,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS time_tracking_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
worker_id UUID,
task_id UUID,
clock_in TIMESTAMPTZ,
clock_out TIMESTAMPTZ,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS time_tracking_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS time_tracking_notification_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS worker_time_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
worker_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== REFERRALS ==============
CREATE TABLE IF NOT EXISTS referral_codes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
code TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS referral_redemptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
referral_code_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== PAYMENTS / SETTINGS ==============
CREATE TABLE IF NOT EXISTS payment_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS refunds (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
order_id UUID,
amount NUMERIC(12,2),
reason TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS square_sync_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS square_sync_queue (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
-- ============== API KEYS ==============
CREATE TABLE IF NOT EXISTS api_keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
key TEXT,
name TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== ONBOARDING / ACTIVITY ==============
CREATE TABLE IF NOT EXISTS onboarding_progress (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
user_id UUID,
step TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS notification_preferences (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
user_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS user_carts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
user_email TEXT,
cart JSONB,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS user_activity_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
user_id UUID,
action TEXT,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS admin_action_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
admin_user_id UUID,
action TEXT,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS audit_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
user_id UUID,
action TEXT,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS operational_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
event_type TEXT,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
-- ============== WELCOME / ABANDONED CART ==============
CREATE TABLE IF NOT EXISTS welcome_email_sequence (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS abandoned_cart_recovery (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- ============== CHANGELOGS ==============
CREATE TABLE IF NOT EXISTS changelogs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
title TEXT,
body TEXT,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS changelog_reads (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
changelog_id UUID,
user_id UUID,
read_at TIMESTAMPTZ DEFAULT now()
);
-- ============== FOUNDER / MISC ==============
CREATE TABLE IF NOT EXISTS founder_pain_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
-- ============== VIEWS (used in some RPCs) ==============
CREATE TABLE IF NOT EXISTS completed_pickups (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS pending_pickups (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS pickup_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE IF NOT EXISTS shipping_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
brand_id UUID,
payload JSONB DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ DEFAULT now()
);
-- Done. 65 base tables synthesized.