# ============================================================ # OnlyPets — Environment Variables # Copy this file to .env.local and fill in your values. # NEVER commit .env.local to version control. # ============================================================ # ── Supabase ───────────────────────────────────────────────── # Supabase Dashboard -> Project Settings -> Database -> Connection string # DATABASE_URL: POOLED connection (port 6543, pgbouncer) — used by Prisma at runtime # DIRECT_URL: DIRECT connection (port 5432) — used by Prisma for migrations DATABASE_URL="postgresql://postgres.xxxxxxxxxxxx:password@aws-0-eu-central-1.pooler.supabase.com:6543/postgres?pgbouncer=true" DIRECT_URL="postgresql://postgres.xxxxxxxxxxxx:password@aws-0-eu-central-1.pooler.supabase.com:5432/postgres" # Supabase Dashboard -> Project Settings -> API # NEXT_PUBLIC_SUPABASE_URL: Project URL (e.g. https://xxxx.supabase.co) # !! This is NOT the database URL — it is the Supabase REST/Storage base URL !! # Used for: avatar CDN links, signed upload URLs, Storage client. # Baked into the client bundle at build time (NEXT_PUBLIC_ prefix). # SUPABASE_SERVICE_ROLE_KEY: service_role key (secret! — server-only, never expose to client) NEXT_PUBLIC_SUPABASE_URL="https://xxxxxxxxxxxxxxxxxxxx.supabase.co" SUPABASE_SERVICE_ROLE_KEY="eyJxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # ── Clerk (Authentication) ─────────────────────────────────── # Clerk Dashboard -> API Keys NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_xxxxxxxxxxxxxxxxxxxx" CLERK_SECRET_KEY="sk_test_xxxxxxxxxxxxxxxxxxxx" # ── Stripe (Sponsoring, Gitea #22) ────────────────────────── # Stripe Dashboard → Developers → API keys STRIPE_SECRET_KEY="sk_test_xxxxxxxxxxxxxxxxxxxxxxxx" # Stripe Dashboard → Developers → Webhooks → Add endpoint → copy Signing Secret # Endpoint must point at https:///api/webhooks/stripe # Subscribe to: checkout.session.completed, invoice.paid, # customer.subscription.updated, customer.subscription.deleted STRIPE_WEBHOOK_SECRET="whsec_xxxxxxxxxxxxxxxxxxxxxxxx" # ── Mux (Video) ────────────────────────────────────────────── # Mux Dashboard → Settings → Access Tokens → Create Token (Mux Video) MUX_TOKEN_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" MUX_TOKEN_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Mux Dashboard → Webhooks → Add endpoint → copy Signing Secret # Endpoint must point at https:///api/webhooks/mux MUX_WEBHOOK_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Your public app URL — currently informational only. Mux's cors_origin # (videos.ts) is intentionally "*" for now; restrict it to this value if # you lock down direct-upload origins later. NEXT_PUBLIC_APP_URL="http://localhost:3000" # ── Cron ────────────────────────────────────────────────────── # Bearer token required by /api/cron/trim-feeds and /api/cron/anniversaries. # vercel.json's crons array is inert on this self-hosted deployment — an # external trigger (e.g. a NAS-side crontab) must call these routes with # this token in the Authorization header. CRON_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # ── Admin Panel ─────────────────────────────────────────────── # NEVER use NEXT_PUBLIC_ prefix — these MUST stay server-only! # ADMIN_SECRET: random UUID path segment (/p/[secret]/) — generate with: # node -e "console.log(require('crypto').randomUUID())" ADMIN_SECRET="your-random-uuid-here" # ADMIN_OWNER_ID: Your Clerk user ID (Clerk dashboard). Bootstraps the super-admin. ADMIN_OWNER_ID="user_xxxxxxxxxxxxxxxxxxxx" # ── Redis ───────────────────────────────────────────────────── # Self-hosted: use the Docker Redis service (docker-compose sets this automatically). # Local dev: run `docker run -d -p 6379:6379 redis:7-alpine` or install Redis locally. REDIS_URL="redis://localhost:6379" # ── Error Monitoring (Sentry) ─────────────────────────────────── # Sentry Dashboard -> Settings -> Projects -> -> Client Keys (DSN) # Same DSN value in both — server-side (src/instrumentation.ts) and # client-side (src/instrumentation-client.ts) report to the same project. # Leave unset to run with Sentry fully inert (no build-time requirement). SENTRY_DSN="https://xxxxxxxxxxxxxxxxxxxx@o0.ingest.sentry.io/0" NEXT_PUBLIC_SENTRY_DSN="https://xxxxxxxxxxxxxxxxxxxx@o0.ingest.sentry.io/0" # Optional — only needed to upload source maps for readable stack traces. # Sentry Dashboard -> Settings -> Auth Tokens (needs project:releases scope). SENTRY_ORG="your-sentry-org" SENTRY_PROJECT="your-sentry-project" SENTRY_AUTH_TOKEN="" # ── SMTP (Team mailbox) ────────────────────────────────────────── # Use a mailbox hosted by an established provider (e.g. your domain host's # mail hosting) — NOT a self-hosted relay from a home/dynamic IP, which gets # spam-flagged by most receiving servers regardless of DKIM/SPF. SMTP_HOST="smtp.example.com" SMTP_PORT="465" SMTP_USER="team@example.com" SMTP_PASSWORD="" MAIL_FROM="team@example.com"