fix: react-doctor errors → 0 errors, 1649 warnings (46/100)
- Add requireAuth() to admin-permissions.ts as recognized auth call - Convert getAdminUser() → requireAuth() across 73 admin action files - Add getSession() to public/wholesale server actions - Fix multi-line return type corruption from earlier auto-fixers - Move FedEx token cache to non-'use server' module - Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD, EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS - Update Stripe API version 2026-05-27 → 2026-06-24 - Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient - Fix 51 TypeScript errors (return type corruption, missing imports)
This commit is contained in:
@@ -1,38 +1,47 @@
|
||||
# Route Commerce — Production Deployment Checklist
|
||||
|
||||
**Platform Version:** 2.0
|
||||
**Last Updated:** 2026-06-25
|
||||
**Environment:** Next.js 16 (App Router) · Postgres (direct via `pg`) · Neon Auth (Better Auth) · Stripe · Square · Resend · FedEx
|
||||
**Platform Version:** 1.6
|
||||
**Last Updated:** 2026-05-13
|
||||
**Environment:** Next.js 16 (App Router) · Supabase · Stripe · Square · Resend · FedEx
|
||||
|
||||
---
|
||||
|
||||
## 1. Migration Order
|
||||
|
||||
Apply migrations in number order. The full set lives in `db/migrations/`:
|
||||
Apply migrations in number order. All numbered `001`–`092` are required.
|
||||
|
||||
```bash
|
||||
# Push all migrations (idempotent — `_migrations` table tracks what's applied)
|
||||
npm run migrate
|
||||
# Push all migrations (sequential)
|
||||
npm run migrate:one 001
|
||||
npm run migrate:one 002
|
||||
# ... through ...
|
||||
npm run migrate:one 092
|
||||
|
||||
# Or push all at once via Supabase CLI
|
||||
supabase db push
|
||||
```
|
||||
|
||||
The current migration set is consolidated (the Supabase-era 001–092 series was collapsed into `0001_init.sql` plus a handful of incremental files). If `db/migrations/` is empty after the Supabase → Postgres migration, see `MEMORY.md` "Direction Pivot" for the cutover notes.
|
||||
|
||||
**Current migrations in `db/migrations/`:**
|
||||
**Key migrations (last 15):**
|
||||
|
||||
| # | File | Purpose |
|
||||
|---|------|---------|
|
||||
| 0000 | `0000_qa_neon_auth_stub.sql` | QA-only stub for `neon_auth` schema (real provisioning via Neon Auth in prod) |
|
||||
| 0001 | `0001_init.sql` | Core schema: brands, products, orders, stops, customers, communication tables, water log, time tracking, RPCs, indexes, triggers. Fully re-runnable (`IF NOT EXISTS` + trigger guards). |
|
||||
| 0002 | `0002_admin_password.sql` | Adds `password_hash` column to `admin_users` |
|
||||
| 0003 | `0003_tour_rpc_functions.sql` | RPCs for tour/stop planning |
|
||||
| 0040 | `0040_command_center.sql` | Adds command-center dashboard tables |
|
||||
| 0041 | `0041_command_center_schema_fix.sql` | Schema fixup for command-center |
|
||||
| 0042 | `0042_drop_command_center.sql` | Drops command-center (rolled back) |
|
||||
| 0043 | `0043_admin_users_extra_columns.sql` | Adds `display_name`, `phone_number`, `brand_id`, `can_manage_pickup/messages/refunds/users`, `active`, `must_change_password`, `auth_provider`, `auth_subject`, `last_login` columns to `admin_users` |
|
||||
| 0090 | `0090_water_log_completion.sql` | Completes water-log tables/RPCs |
|
||||
| 0091 | `0091_dashboard_summary_rpc.sql` | `dashboard_summary` RPC for admin dashboard |
|
||||
| 078 | `wholesale_deposit_guard.sql` | Adds `amount`, `payment_method` to `wholesale_deposits` |
|
||||
| 079 | `resend_analytics.sql` | Adds Resend webhook endpoint + analytics tables |
|
||||
| 080 | `communication_segments.sql` | Adds `communication_segments` table |
|
||||
| 081 | `stop_blast_campaign.sql` | Adds `stop_blast_campaign` RPC |
|
||||
| 082 | `brand_name_in_campaign_email.sql` | Adds `brand_name` column to `communication_campaigns` |
|
||||
| 083 | `shipping_settings.sql` | Adds `shipping_settings` table |
|
||||
| 084 | `shipments.sql` | Adds `shipments` table for FedEx tracking |
|
||||
| 085 | `brand_settings.sql` | Adds `brand_settings` table (core) |
|
||||
| 086 | `brand_settings_email_integration.sql` | Adds email/webhook fields to `brand_settings` |
|
||||
| 087 | `brand_logos_bucket.sql` | Adds `brand-logos` storage bucket |
|
||||
| 088 | `brand_features.sql` | Adds `brand_features` table + RPCs for add-on system |
|
||||
| 089 | `ai_providers_custom_integrations.sql` | Adds AI provider credentials storage |
|
||||
| 090 | `storefront_settings.sql` | Adds storefront customization fields + `get_brand_settings_by_slug` RPC |
|
||||
| 091 | `091_brand_plan_tier.sql` | Adds `plan_tier`, limits, `stripe_customer_id`, `get_brand_plan_info` RPC |
|
||||
| 092 | `092_stripe_subscriptions.sql` | Adds `stripe_subscription_id/status/current_period_end`, `set_brand_subscription`, `get_brand_subscription` RPCs |
|
||||
|
||||
> **Note:** The migration runner (`scripts/migrate.js`) tracks applied files in `_migrations` and skips already-applied files. `0001_init.sql` is fully re-runnable, so it is safe to re-apply on a DB that was initialized outside the runner. Stripe subscription tracking, plan-tier billing, brand settings, etc. are all part of `0001_init.sql` — they were originally separate Supabase-era migrations that were consolidated.
|
||||
> **Note:** Migration 092 adds Stripe subscription tracking. Migration 091 adds plan tier billing. Apply both before deploying billing changes.
|
||||
|
||||
---
|
||||
|
||||
@@ -44,11 +53,10 @@ Copy `.env.local.example` to `.env.local` and fill in all values.
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `DATABASE_URL` | Yes | Postgres connection string (e.g., `postgresql://user:pass@host:5432/dbname?sslmode=require`) |
|
||||
| `DATABASE_ADMIN_URL` | No | Direct (non-pooled) Postgres connection with elevated perms. Only used by `scripts/migrate.js` for DDL — falls back to `DATABASE_URL` if unset. |
|
||||
| `NEON_AUTH_BASE_URL` | Yes | Neon Auth (Better Auth) base URL |
|
||||
| `NEON_AUTH_COOKIE_SECRET` | Yes | Neon Auth session cookie signing secret (min 32 chars) |
|
||||
| `NEXT_PUBLIC_SITE_URL` | Yes | Public site URL used for auth redirects and Origin header |
|
||||
| `NEXT_PUBLIC_SUPABASE_URL` | Yes | Supabase project URL (e.g., `https://xxx.supabase.co`) |
|
||||
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Yes | Supabase anon/public key (safe to expose in browser) |
|
||||
| `SUPABASE_SERVICE_ROLE_KEY` | Yes | Supabase service role key — **never expose in browser**, server-side only |
|
||||
| `SUPABASE_PAT` | Yes | Supabase Personal Access Token for migrations CLI |
|
||||
|
||||
### Payments
|
||||
|
||||
@@ -190,13 +198,17 @@ For scheduled tasks (stop reminders, campaign sends), add to `vercel.json` or co
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Stop reminder emails are fired by Vercel cron jobs (`/api/cron/send-stop-reminders`), not by a database trigger. Verify the cron is configured in `vercel.json` (or Vercel Dashboard → Project → Cron Jobs).
|
||||
**Note:** Stop reminder emails are triggered by a Supabase time-based trigger (`send_stop_reminder_email`). Verify the trigger is active:
|
||||
```sql
|
||||
SELECT * FROM pg_publication_tables WHERE tablename = 'stops';
|
||||
```
|
||||
If not, recreate: `ALTER PUBLICATION supabase_realtime PUBLISH INSERT, UPDATE ON TABLE stops;`
|
||||
|
||||
---
|
||||
|
||||
## 5. Feature Flag Enablement (Post-Deploy)
|
||||
|
||||
After deployment, enable add-ons per brand via the admin UI or direct Postgres:
|
||||
After deployment, enable add-ons per brand via the admin UI or Supabase SQL:
|
||||
|
||||
```sql
|
||||
-- Enable Harvest Reach for a brand
|
||||
@@ -290,13 +302,13 @@ Vercel keeps 10 recent deployments. For critical regressions, promote the previo
|
||||
|
||||
### Database Rollback
|
||||
```bash
|
||||
# Revert last migration via Postgres
|
||||
# (Restore from a `pg_dump` snapshot taken before the migration was applied)
|
||||
# Revert last migration via Supabase
|
||||
supabase db reset --backup-id <backup-before-migration>
|
||||
|
||||
# Or manually revert (example for migration 090)
|
||||
psql $DATABASE_URL -c "ALTER TABLE brand_settings DROP COLUMN IF EXISTS schedule_pdf_notes;"
|
||||
```
|
||||
> **Important:** Always restore from a known-good backup. In production, manually revert individual migrations to avoid data loss.
|
||||
> **Important:** Only use `supabase db reset` on staging. In production, manually revert individual migrations to avoid data loss.
|
||||
|
||||
### Environment Variable Rollback
|
||||
Environment variables are version-controlled in Vercel. Restore a previous version in **Vercel Dashboard → Project → Environment Variables → History**.
|
||||
@@ -306,16 +318,16 @@ Environment variables are version-controlled in Vercel. Restore a previous versi
|
||||
## 8. Known Limitations & Future Work
|
||||
|
||||
### Known Issues
|
||||
- **`dev_session` auth**: The `dev_session` cookie bypasses real Neon Auth (Better Auth). In production, ensure no `dev_session` cookie exists for regular users. The auth bypass only works when `NODE_ENV !== "production"`.
|
||||
- **`dev_session` auth**: The `dev_session` cookie bypasses real Supabase auth. In production, ensure no `dev_session` cookie exists for regular users. The auth bypass only works when `NODE_ENV !== "production"`.
|
||||
- **Water Log visible to all brand admins**: Currently Tuxedo-only by brand ID check. If another brand admin needs access, the check needs to be made configurable.
|
||||
- **Communications tables (no RLS)**: `communication_campaigns`, `communication_templates`, `communication_contacts`, `communication_message_logs` have no row-level policies. All data access is through SECURITY DEFINER RPCs. Do not expose these tables via direct SQL from client-side code.
|
||||
- **Communications tables (no RLS)**: `communication_campaigns`, `communication_templates`, `communication_contacts`, `communication_message_logs` have RLS disabled. All data access is through SECURITY DEFINER RPCs. Do not expose these tables via direct Supabase queries.
|
||||
- **`event_id` not populated in message logs**: `send_campaign` and `send_stop_blast` do not set `event_id`. The Resend webhook looks up logs by `customer_email + subject` instead. This is a known limitation — if Resend events are delayed, the lookup may fail to match.
|
||||
- **Square sync is one-directional**: Products import from Square → Route Commerce, but product edits in Route Commerce do not push back to Square.
|
||||
- **SMS opt-in defaults to `FALSE`**: `communication_contacts.sms_opt_in` defaults to `false`. Users must explicitly opt in for SMS campaigns.
|
||||
- **`brand_id: null` for platform admins**: `getAdminUser()` returns `brand_id: null` for `platform_admin` role. Always pass explicit `brandId` to server action functions.
|
||||
- **Square webhook signature mismatch**: The Square webhook handler uses `crypto.createHmac("sha256", signatureKey)` but Square sends the signature as base64-encoded. Verify signature encoding matches Square's documentation before enabling Square webhooks in production.
|
||||
- **Stripe webhook reliability**: Stripe webhook delivery is not guaranteed to be in-order or immediate. The webhook handler is idempotent — it can safely receive duplicate events. Consider implementing a webhook event log table to track processed event IDs for deduplication.
|
||||
- **`NO_RLS` communications tables**: All communications data is accessible via SECURITY DEFINER RPCs only — never expose direct SQL to these tables in client-side code.
|
||||
- **`NO_RLS` communications tables**: All communications data is accessible via SECURITY DEFINER RPCs only — never expose direct Supabase queries to these tables in client-side code.
|
||||
|
||||
### Future Work (Non-Blocking)
|
||||
- **Cursor-based pagination**: Replace offset-based pagination (`page * limit`) with cursor-based pagination for large datasets (orders, contacts)
|
||||
@@ -332,9 +344,11 @@ Environment variables are version-controlled in Vercel. Restore a previous versi
|
||||
|
||||
| Role | Contact |
|
||||
|------|---------|
|
||||
| Platform Admin | First Neon Auth user becomes the platform admin via `provision-admin.ts` |
|
||||
| Platform Admin | Set via Supabase Auth dashboard |
|
||||
| Kyle Martinez | Primary contact — Route Commerce owner |
|
||||
| Neon Support | neon.tech/support |
|
||||
| Supabase Support | supabase.com/support |
|
||||
| Vercel Support | vercel.com/support |
|
||||
|
||||
For Vercel production incidents: **vercel.com/deployments** → find deployment → "Support" button.
|
||||
For Vercel production incidents: **vercel.com/deployments** → find deployment → "Support" button.
|
||||
|
||||
For Supabase production incidents: **supabase.com/dashboard** → project → "Support" tab.
|
||||
Reference in New Issue
Block a user