diff --git a/.env.example b/.env.example index bcce661c3..b284efa67 100644 --- a/.env.example +++ b/.env.example @@ -1,96 +1,186 @@ -# Database -# For local development, use SQLite: -# DATABASE_URL="file:./dev.db" -# For production, use PostgreSQL (Vercel Postgres, Railway, Supabase, etc.): +# ============================================================================= +# Vets Who Code — environment variables +# ============================================================================= +# Each entry is annotated with: +# [REQUIRED] — app fails or a critical surface breaks without it +# [OPTIONAL] — feature gracefully degrades or falls back +# [DEV-ONLY] — never set in production / preview +# [PUBLIC] — exposed to the browser (NEXT_PUBLIC_ prefix) +# Each entry includes the file(s) that read it so reviewers can trace usage. +# ============================================================================= + + +# ---- Database -------------------------------------------------------------- +# [REQUIRED] Postgres in prod (Neon via @prisma/adapter-neon), SQLite locally. +# Prisma tolerates SQLite for dev/push but migrations are Postgres-only. +# Read by: prisma/* and the generated client DATABASE_URL="postgresql://username:password@hostname:port/database" +# DATABASE_URL="file:./dev.db" -# LMS Development Access - Set to 'true' to bypass auth in development ONLY -# WARNING: Never enable in production! -LMS_DEV_BYPASS=false -# NextAuth.js +# ---- NextAuth.js ----------------------------------------------------------- +# [REQUIRED] Secret used to sign session JWTs / encryption. NEXTAUTH_SECRET="your-secret-key-here" +# [REQUIRED] Canonical site URL NextAuth uses for callback construction. NEXTAUTH_URL="http://localhost:3000" -# GitHub OAuth (for authentication) + +# ---- GitHub OAuth & API ---------------------------------------------------- +# [REQUIRED] GitHub OAuth app credentials (sign-in provider). GITHUB_CLIENT_ID="your-github-client-id" GITHUB_CLIENT_SECRET="your-github-client-secret" -# GitHub API Access (for fetching org data, repos, etc.) -GITHUB_PAT="your-github-personal-access-token" +# [REQUIRED] Personal access token used for org/repo/PR lookups +# (org-membership gate, profile GitHub stats, etc.). +# Read by: src/lib/github.ts, src/lib/git-api-client.ts GITHUB_TOKEN="your-github-token" + +# [REQUIRED] GitHub org users must belong to in order to sign in. +# Read by: src/pages/api/auth/options.ts GITHUB_ORG="Vets-Who-Code" -# Admin allowlist — comma-separated GitHub usernames that bypass org-membership -# check on sign-in. Keep this list minimal (founders/external admins only). +# [OPTIONAL] Comma-separated GitHub usernames that bypass the org-membership +# check on sign-in. Keep minimal (founders / external admins only). +# Read by: src/pages/api/auth/options.ts ADMIN_GITHUB_LOGINS="" -# Dev-only escape hatch. Set to "true" ONLY when running locally with -# NODE_ENV=development to allow any GitHub user to sign in. NEVER set in -# production or preview deployments. +# [DEV-ONLY] When "true" AND NODE_ENV=development, allows any GitHub user +# to sign in regardless of org membership. NEVER set in prod or preview. +# Read by: src/pages/api/auth/options.ts ALLOW_ANY_GITHUB_USER="false" -# Google Maps API (for location features) -NEXT_PUBLIC_GOOGLE_MAPS_KEY="your-google-maps-api-key" -# Optional: Other OAuth providers -# GOOGLE_CLIENT_ID="your-google-client-id" -# GOOGLE_CLIENT_SECRET="your-google-client-secret" - -# AI Teaching Assistant (J0d!e) - Phase 5 -# Primary AI provider: gemini | azure | openai | phi3 +# ---- AI providers ---------------------------------------------------------- +# [REQUIRED] Primary AI provider for the J0d!e teaching assistant. +# Options: gemini | azure | openai | phi3 +# Read by: src/lib/ai-provider.ts PRIMARY_AI_PROVIDER="gemini" -# Google Gemini (recommended for cost-effectiveness) +# [REQUIRED if PRIMARY_AI_PROVIDER=gemini] Gemini API key for the chat router. +# Read by: src/lib/ai-provider.ts GOOGLE_GENERATIVE_AI_API_KEY="your-google-ai-api-key" -# Contact Form Spam Filter (Gemini) +# [REQUIRED] Gemini key used by the contact-form spam classifier +# (separate from the chat router so quotas can be split). +# Read by: src/pages/api/contact.ts (classifier) GEMINI_API_KEY="your-gemini-api-key" + +# [OPTIONAL] Gemini model override for the contact classifier. +# Default: gemini-2.5-flash GEMINI_MODEL="gemini-2.5-flash" -# Azure OpenAI (GPT-4) -AZURE_OPENAI_API_KEY="your-azure-openai-key" -AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com" -AZURE_OPENAI_DEPLOYMENT="your-gpt4-deployment-name" +# [OPTIONAL] Azure OpenAI fallback for the chat router. +# Required as a set if PRIMARY_AI_PROVIDER=azure or if you want Azure failover. +# Read by: src/lib/ai-provider.ts +AZURE_OPENAI_API_KEY="" +AZURE_OPENAI_ENDPOINT="" +AZURE_OPENAI_DEPLOYMENT="" + +# [OPTIONAL] OpenAI fallback for the chat router. +# Required if PRIMARY_AI_PROVIDER=openai. +# Read by: src/lib/ai-provider.ts +OPENAI_API_KEY="" + +# [OPTIONAL] Phi-3 endpoint (self-hosted or Azure AI Studio). +# Required as a pair if PRIMARY_AI_PROVIDER=phi3. +# Read by: src/lib/ai-provider.ts +PHI3_ENDPOINT="" +PHI3_API_KEY="" + + +# ---- J0dI3 backend (Cloud Run) -------------------------------------------- +# [REQUIRED] J0dI3 API base URL and key. Server-side only — never prefix with +# NEXT_PUBLIC_. Read by: src/lib/j0di3-client.ts (proxied via src/pages/api/j0di3/*) +J0DI3_API_URL="" +J0DI3_API_KEY="" + + +# ---- Slack webhooks (form submissions → Slack notifications) -------------- +# [OPTIONAL] Each is the path component of a Slack incoming webhook URL +# (the part after https://hooks.slack.com/services/). If unset, the form +# still saves; only the Slack notification is skipped. +# Read by: src/pages/api/{apply,contact,mentor,mentee}.ts +APPLY_WEBHOOK_ID="" +CONTACT_WEBHOOK_ID="" +MENTOR_WEBHOOK_ID="" + + +# ---- Cloudinary (image hosting & CDN) ------------------------------------- +# [REQUIRED][PUBLIC] Cloud name used by browser URLs. +# Read by: src/lib/cloudinary.ts, src/lib/blog.ts, profile/blog image rendering +NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME="your-cloud-name" + +# [REQUIRED] Server-side credentials for uploads and signed URL generation. +# Read by: src/lib/cloudinary.ts, src/pages/api/upload/* +CLOUDINARY_API_KEY="your-api-key" +CLOUDINARY_API_SECRET="your-api-secret" -# OpenAI (GPT-4) -OPENAI_API_KEY="your-openai-api-key" -# Phi-3 (optional - for local/Azure AI Studio deployment) -PHI3_ENDPOINT="your-phi3-endpoint" -PHI3_API_KEY="your-phi3-api-key" +# ---- Email (Resend) -------------------------------------------------------- +# [REQUIRED] Resend API key. Required for certificate delivery, contact form +# notifications, mentor matching, etc. +# Read by: src/lib/email.ts +RESEND_API_KEY="your-resend-api-key" -# Lightcast (Emsi) - labor market data: salary by SOC, demand, top skills -# Both required for live data; without these, scripts fall back to Census/curated. -# Used by: src/lib/lightcast-client.ts, scripts/generate-tech-pathways.ts -LIGHTCAST_CLIENT_ID="your-lightcast-client-id" -LIGHTCAST_CLIENT_SECRET="your-lightcast-client-secret" +# [OPTIONAL] Custom "From" address. Requires the sender domain to be verified +# in Resend. Defaults to the Resend sandbox if unset. +EMAIL_FROM="noreply@vetswhocode.io" -# US Census ACS - fallback salary data when Lightcast is unavailable -CENSUS_API_KEY="your-census-api-key" -# Shopify Storefront API (for browsing products/cart) +# ---- Shopify (Storefront + Admin webhooks) -------------------------------- +# [REQUIRED for /store] Storefront API credentials. +# Read by: src/lib/shopify.ts SHOPIFY_STORE_DOMAIN="your-store.myshopify.com" SHOPIFY_STOREFRONT_ACCESS_TOKEN="your-storefront-access-token" -# Shopify Admin API (optional - for making API calls to Shopify) -SHOPIFY_ADMIN_ACCESS_TOKEN="your-admin-access-token" +# [REQUIRED for webhooks] Shopify webhook signing secret. The handler accepts +# any of the three names below for legacy compatibility — set ONE. +# Read by: src/pages/api/shopify/webhooks/* +SHOPIFY_WEBHOOK_SECRET="your-webhook-secret" +# SHOPIFY_API_SECRET="your-api-secret-key" +# SHOPIFY_CLIENT_SECRET="your-client-secret" -# Shopify Webhook/API Secrets (REQUIRED for order webhooks - the API Secret Key from API credentials) -# The webhook handler will check for any of these names: -SHOPIFY_WEBHOOK_SECRET="your-webhook-secret" # Primary option -SHOPIFY_API_SECRET="your-api-secret-key" # Alternative name -SHOPIFY_CLIENT_SECRET="your-client-secret" # Alternative name -# Cloudinary (for image uploads and management) -NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME="your-cloud-name" -CLOUDINARY_API_KEY="your-api-key" -CLOUDINARY_API_SECRET="your-api-secret" - -# Email Service (Resend - for sending certificate emails) -RESEND_API_KEY="your-resend-api-key" -EMAIL_FROM="noreply@vetswhocode.io" # Optional: Custom from address (requires domain verification) +# ---- Maps / Analytics / Sitemap (browser-public) -------------------------- +# [REQUIRED][PUBLIC] Google Maps JS key for location features (event maps). +NEXT_PUBLIC_GOOGLE_MAPS_KEY="your-google-maps-api-key" -# J0dI3 AI Backend (server-side only — never prefix with NEXT_PUBLIC_) -J0DI3_API_URL= -J0DI3_API_KEY= \ No newline at end of file +# [OPTIONAL][PUBLIC] Google Analytics 4 measurement ID. When unset, the GA +# snippet is not rendered. +# Read by: src/pages/_document.tsx +NEXT_PUBLIC_GOOGLE_ANALYTICS_ID="" + +# [OPTIONAL][PUBLIC] Canonical site URL for next-sitemap. Defaults to +# https://vetswhocode.io. +# Read by: next-sitemap.config.js +NEXT_PUBLIC_SITE_URL="https://vetswhocode.io" + + +# ---- Labor market data (military-to-tech translator) ---------------------- +# [OPTIONAL] Lightcast (Emsi) credentials for live salary / demand / top +# skills. Without these, src/lib/labor-market.ts falls back to Census + +# curated data. +# Read by: src/lib/lightcast-client.ts +LIGHTCAST_CLIENT_ID="" +LIGHTCAST_CLIENT_SECRET="" + +# [OPTIONAL] US Census ACS API key — fallback salary data when Lightcast is +# unavailable. +# Read by: src/lib/census-client.ts +CENSUS_API_KEY="" + + +# ---- Generator scripts (build-time / operator-run only) ------------------- +# These are only read by scripts/ under `npm run generate:*` — they are +# never required at runtime. +# +# [OPTIONAL] Alternate Google AI key accepted by the generator scripts as a +# fallback when GOOGLE_GENERATIVE_AI_API_KEY / GEMINI_API_KEY are absent. +# Read by: scripts/generate-* (blog audio, career-pathways, training-pipeline, +# cert-equivalencies, cognitive-skills, military-systems) +GOOGLE_PRIVATE_KEY="" + +# [OPTIONAL] Gemini model override for the tech-pathways generator only. +# Default: gemini-2.0-flash +# Read by: scripts/generate-tech-pathways.ts +TECH_PATHWAYS_MODEL=""