# ============================================================================= # ENVIRONMENT CONFIGURATION # ============================================================================= # Copy this file to .env and update values according to your needs # Never commit .env file to version control - it contains sensitive data! # This file is read by `pnpm run dev` and `pnpm run build` (vite) and by `pnpm preview` (wrangler dev). # # PUBLIC_ENVIRONMENT, PUBLIC_TIMEZONE, PRODUCTION_DOMAIN and ALLOW_ROBOTS are # BUILD-TIME values (inlined by `pnpm run build`) and all four are REQUIRED: # without one, the build and the dev server fail with # "Invalid environment variables ... Value is missing". # - Cloudflare Workers Builds: set them under Settings > Build > Variables and Secrets # - Locally: after changing one, run `pnpm run build` again before `pnpm preview` # CRON_SECRET and ADMIN_PASSWORD are RUNTIME secrets (see their sections below). # ----------------------------------------------------------------------------- # APPLICATION ENVIRONMENT # ----------------------------------------------------------------------------- # Build-time variable (required, see top). Controls demo mode and the # environment tag in the header - nothing else: it doesn't change logging, and # 'dev' does NOT make ADMIN_PASSWORD optional (only `pnpm run dev` does) # # Options: # - 'dev' : Shows a "Local" tag in the header # - 'demo' : Shows a "Demo" tag. Public read-only showcase: forms/buttons # disabled, no data changes, ADMIN_PASSWORD ignored, cron # runs are skipped # - 'staging' : Shows a "Staging" tag in the header # - 'production' : No tag # # Examples: # PUBLIC_ENVIRONMENT = 'dev' # PUBLIC_ENVIRONMENT = 'demo' # PUBLIC_ENVIRONMENT = 'staging' PUBLIC_ENVIRONMENT = 'production' # ----------------------------------------------------------------------------- # TIMEZONE CONFIGURATION # ----------------------------------------------------------------------------- # Build-time variable (required, see top) # Sets the default timezone for the application # Used for date/time formatting, scheduling, logging timestamps, etc. # Must be a valid IANA timezone identifier (an invalid value falls back to UTC) # # Common options: # - 'UTC' : Coordinated Universal Time (recommended for servers) # - 'Europe/Ljubljana' : Slovenia timezone (CET/CEST) # - 'America/New_York' : US Eastern timezone (EST/EDT) # - 'America/Los_Angeles': US Pacific timezone (PST/PDT) # - 'Europe/London' : UK timezone (GMT/BST) # - 'Asia/Tokyo' : Japan timezone (JST) # - 'Australia/Sydney' : Australian Eastern timezone (AEST/AEDT) # # Examples: # PUBLIC_TIMEZONE = 'UTC' # PUBLIC_TIMEZONE = 'America/Chicago' PUBLIC_TIMEZONE = 'Europe/Ljubljana' # ----------------------------------------------------------------------------- # CRON JOB SECURITY # ----------------------------------------------------------------------------- # Secret key used to authenticate cron job endpoints # This prevents unauthorized access to scheduled tasks/jobs # # Runtime secret (read when the app starts, NOT inlined at build time): # - Local: set it here - `pnpm run dev` (vite) and `pnpm preview` (wrangler dev) # both read this .env file, so the cron handler (src/lib/cloudflare/cron.js) # and the /api/cron endpoint always use the same value # - Production: npx wrangler secret put CRON_SECRET --env production # (do NOT put it in wrangler.jsonc or in the Cloudflare build variables) # - Optional: without it the app still starts, but /api/cron rejects every # request with 503 (fail closed) # - /api/cron is not behind ADMIN_PASSWORD - this secret is its only protection # # Security requirements: # - Use a strong, random string (minimum 32 characters recommended) # - Never use predictable values like 'password', 'secret', etc. # - Rotate this key periodically for better security # # Generate a secure secret: # - Online: https://www.random.org/strings/ # - Node.js: crypto.randomBytes(32).toString('hex') # - OpenSSL: openssl rand -hex 32 # - UUID: Use any UUID generator for a unique identifier # # API Usage: # Endpoint: POST http://localhost:5173/api/cron (8787 with `pnpm preview`) # Header: x-cron-secret: {CRON_SECRET} # Header: Content-Type: application/json (required - SvelteKit rejects # cross-site POST requests without a Content-Type as CSRF) # # Scheduled execution (default): # - No body required or empty body {} # - Follows normal cron timing/conditions # # Manual trigger (immediate): # - Body: {"manual": true} # - Bypasses timing checks and sends notifications immediately # - Useful for testing and debugging # # cURL examples: # # Scheduled execution (use port 8787 with `pnpm preview`) # curl -X POST http://localhost:5173/api/cron \ # -H "x-cron-secret: your-secret-here" \ # -H "Content-Type: application/json" # # # Manual immediate trigger # curl -X POST http://localhost:5173/api/cron \ # -H "x-cron-secret: your-secret-here" \ # -H "Content-Type: application/json" \ # -d '{"manual": true}' # # Examples: # CRON_SECRET = 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6' # CRON_SECRET = '550e8400-e29b-41d4-a716-446655440000' CRON_SECRET = 'your-secret-here' # ----------------------------------------------------------------------------- # ADMIN PASSWORD (APP PROTECTION) # ----------------------------------------------------------------------------- # Protects the whole app with HTTP Basic Auth: the browser shows a login prompt, # any username is accepted and the password must match ADMIN_PASSWORD # # Runtime secret (read when the app starts, NOT inlined at build time): # - `pnpm run dev`: optional - when unset the app is open and a warning is logged # - `pnpm preview` (wrangler dev): required - wrangler dev reads this .env file, # without it every page responds 503 # - Production: npx wrangler secret put ADMIN_PASSWORD --env production # Without it every request gets a 503, so a deployment is never left open # - Demo mode (PUBLIC_ENVIRONMENT = 'demo') ignores it - the demo is public # - /api/cron (protected by CRON_SECRET) and /robots.txt are always reachable # # Security requirements: # - Use a long random password - there is no rate limiting on login attempts # Generate one: openssl rand -base64 24 # - Only open the app over HTTPS - Basic Auth sends the password with every request # # Example (uncomment to test the login prompt locally / for `pnpm preview`): # ADMIN_PASSWORD = 'your-local-password' # ----------------------------------------------------------------------------- # ROBOTS.TXT AND EMAIL ASSET DOMAIN CONFIGURATION # ----------------------------------------------------------------------------- # Build-time variables (both required, see top) # Production domain URL used for robots.txt environment detection and email template assets # This determines which robots.txt file to serve and provides the origin URL for email logos # # Purpose: # - Always provides origin URL for email template logos and assets in notifications # - Controls search engine crawling behavior with dual-layer protection # - Prevents accidental indexing of non-production environments # - Allows granular SEO control per deployment # # How it works: # - Email notifications ALWAYS use PRODUCTION_DOMAIN as origin parameter for logo URLs # - Production robots.txt is served ONLY when BOTH conditions are met: # 1. Request origin contains PRODUCTION_DOMAIN (flexible matching) # 2. ALLOW_ROBOTS is set to true # - If either condition fails → serve dev_robots.txt (blocks crawling) # - This provides dual-layer protection with flexible domain matching # - Every page is behind ADMIN_PASSWORD (crawlers get 401) - only /robots.txt, # /api/cron and static files are public, so ALLOW_ROBOTS=true only matters # for a public demo (PUBLIC_ENVIRONMENT = "demo") # # Environment scenarios: # Public demo (ALLOW_ROBOTS=true): https://your-app.com → Email logos ✓, Allow crawling ✓ # Production (ALLOW_ROBOTS=false): https://your-app.com → Email logos ✓, Block crawling ✗ # Staging: https://staging.app → Email logos ✓, Block crawling ✗ # Development: http://localhost:5173 → Email logos ✓, Block crawling ✗ # # Email template integration: # - Email HTML templates reference logo as: {PRODUCTION_DOMAIN}/img_logo.png # - Logo loading works consistently across ALL environments # - PRODUCTION_DOMAIN is used regardless of ALLOW_ROBOTS setting # # SEO and crawling control: # - Dual protection: domain matching + explicit robot allowance # - Perfect for testing production domain with blocked crawling # - Prevents accidental indexing during deployment or testing # - Controls what robots.txt tells crawlers (outside demo mode pages still need the password) # # Use cases: # - Public demo: ALLOW_ROBOTS=true + correct domain # - Testing phase: ALLOW_ROBOTS=false (blocks crawling even on prod domain) # - Development: Always blocked regardless of ALLOW_ROBOTS value # # Full origin including https:// and WITHOUT a trailing slash - with one, the # robots.txt check never matches and email logo URLs get a double slash PRODUCTION_DOMAIN = "https://your-app.com" ALLOW_ROBOTS = false # true only for a public demo - other deployments are behind ADMIN_PASSWORD