11 Commits

Author SHA1 Message Date
Tyler edf3989ef2 fix(deploy): move inline node -e scripts into separate files to fix shell quoting errors
Deploy to route.crispygoat.com / deploy (push) Successful in 4m16s
The inline node -e '...' blocks inside the YAML run: block had shell
quoting conflicts (nested parens, single-quote escapes) that caused
act/local tooling to fail with 'syntax error near unexpected token ('.

Split into two dedicated scripts:
- scripts/preflight-check.js  — neon_auth schema check + 0001_init.sql tracking repair
- scripts/postflight-check.js — admin_users table verification after migrations

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:02:08 -06:00
Tyler 1d4300d505 fix(deploy): make 0001_init.sql re-runnable and repair historical _migrations tracking
Deploy to route.crispygoat.com / deploy (push) Failing after 1m12s
The "Run migrations" job (and thus the whole deploy) was failing on every push after the first successful bootstrap with:

  ✗ 0001_init.sql failed: relation "admin_users" already exists

Root causes:
- 0001_init.sql used plain CREATE TABLE/INDEX/TRIGGER despite the header comment claiming "CREATE TABLE IF NOT EXISTS".
- The _migrations tracking row for 0001_init.sql was never recorded on the prod DATABASE_URL (tracking logic landed after initial apply, or apply happened outside the runner).

Fix (3 layers of defense):
* db/migrations/0001_init.sql: every CREATE TABLE now uses IF NOT EXISTS (63 tables), CREATE INDEX uses IF NOT EXISTS (49), all CREATE TRIGGER wrapped in safe DO $$ IF NOT EXISTS (pg_trigger join check) THEN ... END IF; $$, removed the file's own BEGIN/COMMIT so the runner's transaction is authoritative. RLS drop+create and CREATE OR REPLACE FUNCTION were already safe.
* scripts/migrate.js: added ensureTracked() right after loading the applied set. For 0001/0002, if the core objects (admin_users table, password_hash column) already exist in information_schema but the tracking row is absent, INSERT the filename (ON CONFLICT DO NOTHING) and log "repaired tracking". Then the normal skip path handles it.
* .gitea/workflows/deploy.yml: added an inline node -e pre-repair (same admin_users existence check → force _migrations row) immediately before `npm run migrate:one`. This protects even on an older checkout of the runner. The neon_auth preflight and post-apply "admin_users must exist" hard gate are untouched.
* Minor: cleaned 0002_admin_password.sql tx wrapper for consistency; updated MEMORY.md with the incident + resolution.

Result: deploys (and any server-side `node scripts/migrate.js` recovery runs) on an already-initialized prod DB will repair/skip 0001 cleanly, pass the verification query, and continue to build/deploy. The scp of scripts/ + db/migrations/ in the Deploy step now carries the fixed versions.

See the plan note in deploy.yml and MEMORY.md for background.
2026-06-09 17:55:42 -06:00
openclaw d312783f3a fix: admin auth for prod Neon Auth deployment
Deploy to route.crispygoat.com / deploy (push) Successful in 3m59s
- getAdminUser now properly supports platform_admin with 0 brand links and loads full brand_ids
- createAdminUser action now inserts into admin_user_brands join table
- Admin layout surfaces the signed-in email on Access Denied
- AdminAccessDenied links to /login instead of dead-end /admin
- Main dashboard uses direct pool query instead of dead supabase shim
- Improved provision-admin.ts script for prod bootstrap (loads .env.production too)
2026-06-09 15:30:23 -06:00
openclaw 2db6c0149b docs: add DB schema reference and Tuxedo seed script 2026-06-09 13:38:34 -06:00
openclaw 916ad39176 feat(storage): MinIO object storage, Neon Auth, Supabase removal
Deploy to route.crispygoat.com / deploy (push) Failing after 3m1s
- Add MinIO/S3-compatible storage client (src/lib/storage.ts) with uploadObject,
  deleteObject, presigned URL helpers, and BUCKETS constant
- Wire product images, brand logos, and water log photos to MinIO via the
  new storage client
- Migrate forgot-password to Neon Auth (remove Supabase /auth/v1/recover call)
- Migrate send-scheduled cron to direct Postgres + Resend (remove Supabase Edge
  Function proxy)
- Add logoUrl to email types (OrderReceipt, Welcome, PasswordReset) and pass
  brand_settings.logo_url from all call sites
- Update email templates to use dynamic logoUrl instead of hardcoded Supabase
  bucket URLs
- Remove hardcoded Supabase URLs from TuxedoVideoHero, TuxedoAboutPage,
  TimeTrackingFieldClient; use brand_settings props + local public/ fallback
- Download brand logos (3) and tuxedo-hero.mp4 (36MB) from Supabase bucket to
  public/ for local development
- Add MinIO env vars to .env.example (endpoint, access key, secret, buckets)
- Fix TimeTrackingFieldClient to destructure logoUrl and brandAccent props
- Fix admin/users.ts logoUrl type (null → undefined for optional string)
- Remove stale sb- cookie from wholesale-auth
- Migrate tuxedo/about page to remove supabase import and use pool query for
  wholesale_settings lookup
2026-06-09 12:23:37 -06:00
tyler 6e71596daf Merge branch 'feature/drizzle-rls-real-auth'
# Conflicts:
#	CLAUDE.md
#	package.json
#	src/app/api/auth/[...nextauth]/route.ts
#	src/app/login/LoginClient.tsx
#	src/auth.config.ts
#	src/components/admin/AdminSidebar.tsx
#	src/lib/admin-permissions-types.ts
#	src/lib/admin-permissions.ts
#	src/lib/auth.ts
#	src/middleware.ts
2026-06-07 01:55:06 +00:00
tyler 7cd0603cfb feat: remove dev_session, add Drizzle schema + RLS + real auth
BREAKING: dev_session cookie bypass removed. Admin access now requires
a real Auth.js v5 session (Google OAuth in production). Provision users
by inserting into users + tenant_users tables.

New in this commit:
- db/migrations/0001_init.sql: 18-table SaaS schema with RLS (tenants,
  users, tenant_users, plans, add_ons, subscriptions, tenant_add_ons,
  products, product_images, stops, customers, orders, order_items,
  brand_settings, email_templates, campaigns, files, audit_log)
- db/schema/: Drizzle TypeScript mirror of every table
- db/client.ts: withTenant() / withPlatformAdmin() query wrappers that
  set Postgres GUCs (app.current_tenant_id, app.platform_admin) for
  RLS enforcement. Never query a tenant-scoped table without one.
- db/seed.ts: seeds 3 plans, 6 add-ons, 2 tenants (Tuxedo, Indian River
  Direct), brand_settings, sample products/stops/customers
- scripts/migrate.js: applies migrations in lexical order with tracking
- scripts/db-reset.js: drops + recreates DB, runs migrate + seed
- DATABASE_URL now uses rc_app (non-superuser, NOBYPASSRLS). RLS is
  enforced even for the app user. DATABASE_ADMIN_URL for migrations.
- src/lib/admin-permissions.ts: getAdminUser() reads Auth.js session,
  looks up user + tenant in Postgres. brand_id kept as alias for
  backward compat.
- src/middleware.ts: Auth.js-only route protection, dev_session gone
- src/app/login/LoginClient.tsx: Google OAuth only, no demo mode
- src/components/admin/AdminSidebar.tsx + AdminHeader.tsx: signOutAction
  replaces supabase signout
- @/db/* path aliases in tsconfig.json + vitest.config.ts
- drizzle.config.ts added
- db/auth_schema.sql removed (was a stub; replaced by real schema)
- src/app/api/dev-login/route.ts deleted
- tests: updated to remove dev_session coverage
2026-06-07 01:23:44 +00:00
tyler 858ca0d64d test(scripts): add end-to-end validation script for local Postgres + PostgREST + MinIO + Next.js stack
Validates the full local development stack:
- Postgres connectivity
- PostgREST API on :3001
- MinIO object storage on :9000
- Next.js dev server on :4000
- Brand isolation via SECURITY DEFINER RPCs
- Auth via dev_session cookie (3 roles)
2026-06-05 22:49:52 +00:00
tyler 1feba25ced chore: commit tour-seed scripts and delete_stop 400 fix from prior work
* inspect_xlsx.py — one-off xlsx dumper used to discover the Stop
  Directory sheet structure (14 lines, kept for reference when the
  tour schedule xlsx is updated next season).
* scripts/seed_tuxedo_tour.py — Python+openpyxl seed script that
  parsed the 3-week Tuxedo Corn 2026 tour schedule and inserted
  269 stops via admin_create_stops_batch. Also linked each stop to
  its Stop Directory venue (address/phone/contact) and self-published
  by flipping status='draft' -> 'active'.
* supabase/migrations/202_delete_stop_orders_guard_fix.sql — fixes a
  400 error on the admin Stop Table delete button. The delete_stop
  RPC referenced o.deleted_at on the orders table, but that column
  doesn't exist, so the SELECT raised 'column does not exist' and
  PostgREST surfaced it as HTTP 400. The fix drops the redundant
  o.deleted_at IS NULL clause (pickup_complete = false is sufficient
  on its own); re-add it here if/when orders grows a deleted_at
  column.
2026-06-04 15:32:16 +00:00
tyler 03ae372509 fix(home): resilient homepage animations + anchors + counters
Subagent 1 (Codex review pass):

LandingPageClient.tsx
- Guard GSAP .scroll-reveal loop to prevent 'missing target' warnings when no targets exist

brands/page.tsx
- Comment cleanup for selector scoping

tuxedo/page.tsx
- Add pageScopeRef + use as explicit GSAP context scope (fixes 'Invalid scope' warnings)
- Reduced-motion guard for counter animations
- Comments noting scope is now resilient

HeroSection.tsx
- Wrap sections in containerRef for proper GSAP context
- Reduced-motion support (final states shown immediately, no heavy anims)
- Counter animation: switched from textContent tween (unreliable, left values at 0) to proxy object pattern (val -> display)
- Added IO + rAF fallback if GSAP is unavailable
- Added section ids: #features, #stats, #reviews (matches nav anchors)
- Reduced blank scroll region (300vh -> 140vh on story section)
- Removed duplicate inline footer (LandingPageWrapper <Footer /> now sole source)

LandingPageWrapper.tsx
- Anchor comments noting ids added in HeroSection

Docs + scripts:
- docs/ADMIN_FUNCTIONALITY_CHECKLIST.md (admin app coverage notes)
- docs/pricing-assessment.md (pricing model notes)
- scripts/apply-admin-create-stop.js, check-stop-fns*.js, verify-stop-fns.js (admin stop creation tooling)
2026-06-03 15:36:40 +00:00
tyler 53a9671461 Initial commit - Route Commerce platform 2026-06-01 19:41:12 +00:00