- 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
8.9 KiB
Environment Setup
This guide documents every environment variable the platform uses, what each one does, where to get it, and any gotchas.
Copy .env.example → .env.local and fill in the values.
Public Variables
These are safe to commit and can be used in client bundles. Prefix with NEXT_PUBLIC_.
NEXT_PUBLIC_SUPABASE_URL
Your Supabase project URL.
- Where to get: Supabase Dashboard → Project Settings → API → Project URL
- Example:
https://abc123.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY
Supabase anonymous (anon) key — safe for client-side use.
- Where to get: Supabase Dashboard → Project Settings → API → anon key
NEXT_PUBLIC_BASE_URL
The base URL of your deployment. Used for OAuth redirects and webhook URLs.
- Local:
http://localhost:3000 - Production:
https://yourdomain.com
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
Stripe publishable key for Stripe.js in the client.
- Where to get: Stripe Dashboard → Developers → API Keys → Publishable key
- Format:
pk_live_...orpk_test_...
NEXT_PUBLIC_SINGLE_BRAND
Set to a brand slug to hide other brand routes in single-brand mode.
- Example:
tuxedo - Default: empty (multi-brand mode)
Server-Only Variables
Never prefix with NEXT_PUBLIC_. These must only be accessed in Server Components, Server Actions, or API Routes.
Supabase
SUPABASE_SERVICE_ROLE_KEY
Full service role key — grants DB admin access bypassing RLS.
- Where to get: Supabase Dashboard → Project Settings → API → service_role key
- ⚠️ Critical: Never expose this to the client. Used only in server-side code.
Stripe
STRIPE_SECRET_KEY
Stripe secret key for backend API calls (creating sessions, webhooks, etc.).
- Where to get: Stripe Dashboard → Developers → API Keys → Secret key
- Format:
sk_live_...orsk_test_...
STRIPE_WEBHOOK_SECRET
Signing secret for Stripe webhook payloads.
- Where to get: Stripe Dashboard → Developers → Webhooks → select endpoint → Signing secret
- Format:
whsec_...
Stripe Price IDs
Create these as recurring prices in your Stripe Dashboard first. Each maps to a plan tier or add-on.
| Variable | What it is | Stripe Dashboard |
|---|---|---|
STRIPE_PRICE_STARTER |
$49/mo Starter plan | Create a Product → Price at $49/mo |
STRIPE_PRICE_FARM |
$149/mo Farm plan | Create a Product → Price at $149/mo |
STRIPE_PRICE_ENTERPRISE |
$399/mo Enterprise plan | Create a Product → Price at $399/mo |
STRIPE_PRICE_HARVEST_REACH |
$79/mo Harvest Reach add-on | Create a Product → Price at $79/mo recurring |
STRIPE_PRICE_WHOLESALE_PORTAL |
$99/mo Wholesale Portal add-on | Create a Product → Price at $99/mo recurring |
STRIPE_PRICE_WATER_LOG |
$39/mo Water Log add-on | Create a Product → Price at $39/mo recurring |
STRIPE_PRICE_AI_TOOLS |
$59/mo AI Intelligence add-on | Create a Product → Price at $59/mo recurring |
STRIPE_PRICE_SQUARE_SYNC |
$39/mo Square Sync add-on | Create a Product → Price at $39/mo recurring |
STRIPE_PRICE_SMS_CAMPAIGNS |
$29/mo SMS Campaigns add-on | Create a Product → Price at $29/mo recurring |
Note: Annual pricing requires separate annual prices (e.g., $441/yr for Starter = $49 × 12 × 0.75). Use different price IDs and pass annual=true to checkout actions.
Email (Resend)
RESEND_API_KEY
API key for sending transactional email via Resend.
- Where to get: Resend.com → API Keys → Create API key
- Format:
re_...
RESEND_WEBHOOK_SECRET
Signing secret for Resend webhook payloads.
- Where to get: Resend Dashboard → Webhooks → select endpoint → Signing secret
- Format:
whsec_...
AI
The platform supports five AI providers. Each brand can override per-provider keys in the admin AI Provider panel (/admin/settings/ai). Env vars here are fallbacks for when no per-brand key is configured.
OPENAI_API_KEY
OpenAI API key for AI features (AI Intelligence Pack add-on).
- Where to get: OpenAI Platform → API Keys → Create secret key
- Format:
sk-...
OPENAI_ORG_ID
OpenAI organization ID (optional — for workspace-level usage tracking).
- Where to get: OpenAI Platform → Settings → Organization ID
- Format:
org-...
MINIMAX_API_KEY
MiniMax (MiniMax) API key — OpenAI-compatible. Pre-launch default provider.
- Where to get: MiniMax Platform → API Keys
- Format:
ey...(JWT-style) - Used by:
getAIClient()falls back to this when no per-brand key is set.ai-import.tsImport Center also prefers this overOPENAI_API_KEYwhen both are set.
MINIMAX_BASE_URL (optional)
Override the MiniMax API base URL.
- Default:
https://api.minimax.io/v1(global) - China:
https://api.minimaxi.com/v1(mainland endpoint, lower latency inside China)
Other providers
The admin panel also accepts per-brand keys for Anthropic (sk-ant-...), Google Gemini (AIza...), xAI (xai-...), and custom OpenAI-compatible endpoints. These can also be set globally via ANTHROPIC_API_KEY, GOOGLE_API_KEY, XAI_API_KEY.
Square
SQUARE_APP_SECRET
Square app secret for Square API authentication.
- Where to get: Square Developer Dashboard → Your App → Credentials → Application Secret
- Format:
sq0...
SQUARE_ENVIRONMENT
Controls whether to use Square sandbox or production.
- Values:
sandbox(default for dev) orproduction - ⚠️ Note: Production credentials and sandbox credentials are separate. Make sure this matches which credentials you're using.
Webhook Setup
Stripe
Dashboard: Stripe Dashboard → Developers → Webhooks → Add endpoint
- URL:
https://yourdomain.com/api/stripe/webhook - Events to listen for:
checkout.session.completedcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_succeededinvoice.payment_failed
Resend
Dashboard: Resend → Webhooks → Add webhook
- URL:
https://yourdomain.com/api/resend/webhook - Events:
email_delivered,email_bounced,email_complained
Local vs Production
| Variable | Local (.env.local) |
Production (hosting dashboard) |
|---|---|---|
NEXT_PUBLIC_BASE_URL |
http://localhost:3000 |
https://yourdomain.com |
STRIPE_SECRET_KEY |
sk_test_... |
sk_live_... |
STRIPE_PUBLISHABLE_KEY |
pk_test_... |
pk_live_... |
SQUARE_ENVIRONMENT |
sandbox |
production |
Gotchas
- Wrong Stripe key type: Using
sk_live_in development orsk_test_in production will silently fail. Always match key type to environment. - Stripe price IDs drift: If you create new prices in Stripe Dashboard but forget to update
.env.local, billing will fail. Keep them in sync. - SQUARE_ENVIRONMENT mismatch: Production Square credentials won't work with
sandbox. Match it to your app secret type. - SUPABASE_SERVICE_ROLE_KEY in client: If you ever see
SUPABASE_SERVICE_ROLE_KEYin a browser bundle, it's a critical security incident. The key was exposed server-side. Rotate it immediately in Supabase Dashboard. - OPENAI_API_KEY for AI features: AI features won't work without this. The AI Intelligence Pack add-on requires a valid OpenAI key.
Authentication
Production (HTTPS Required)
Neon Auth session cookies use the __Secure- prefix, which requires HTTPS. In production, the auth flow works as follows:
- User submits credentials to
/api/auth/sign-in - Neon Auth server sets session cookie with
secure: true - Browser stores the cookie and sends it with subsequent requests
- Middleware validates the session cookie
Local Development (HTTP)
For local development over HTTP (e.g., http://localhost:4000), the platform provides a dev_session bypass:
- Via Login Page: Visit
/login- you'll see "Dev Mode — Quick Access" buttons for Platform Admin, Brand Admin, and Store Employee roles. - Via Browser Console:
document.cookie = 'dev_session=platform_admin; path=/; max-age=86400' - Via curl:
curl -b "dev_session=platform_admin" http://localhost:4000/admin
The dev_session cookie is automatically recognized by:
- The middleware (
src/proxy.ts) - The
getAdminUser()function (src/lib/admin-permissions.ts)
Note: The dev_session bypass only works when NODE_ENV !== "production". In production, only Neon Auth session cookies are accepted.
HTTPS for Local Development
If you prefer to test with real auth over HTTPS locally, you can use a tool like mkcert:
# Install mkcert
brew install mkcert
# Create local CA and install it
mkcert -install
# Generate certificate for localhost
mkcert localhost 127.0.0.1 ::1
# Update next.config.ts to use HTTPS
For most development work, the dev_session bypass is sufficient.