Batch commit of the pending working tree on security/audit-fixes-2026-07. Major areas: - Outbound webhooks / Zapier: schema + signed delivery with retries, public v1 API (REST-hook subscribe/unsubscribe), settings UI, cron drain. - Deploy hardening: email via SMTP2GO (Resend fully removed), verified DB TLS (DATABASE_SSL=require + DATABASE_CA), storage fails loud in production when Spaces is unconfigured instead of silently using ephemeral disk. - Integrations & features (concurrent work): accounting (QuickBooks/Xero), e-signature (DocuSign/Dropbox Sign), PayPal, geocoding/maps, onboarding, expanded legal pages. - DB migrations 0006–0009. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
147 lines
6.5 KiB
Bash
147 lines
6.5 KiB
Bash
# ============================================================================
|
|
# PROPERTY MANAGEMENT NETWORK — Production Environment
|
|
# ============================================================================
|
|
# Set these in DigitalOcean App Platform (Environment Variables). Do NOT commit real values.
|
|
#
|
|
# Build-time vs runtime:
|
|
# NEXT_PUBLIC_* are inlined into the browser bundle at image build time, so
|
|
# they must be passed as --build-arg when building the image (see DIGITALOCEAN.md).
|
|
# ============================================================================
|
|
|
|
# === DATABASE (PostgreSQL) ===
|
|
# DO Managed Postgres (private host, direct port 25060): postgres://USER:PASSWORD@<private-host>:25060/dbname
|
|
DATABASE_URL=postgres://user:password@private-host:25060/dbname
|
|
|
|
# TLS policy (app + migrations). Default is encrypted + certificate-verified.
|
|
# disable -> no TLS (local / unix-socket development only).
|
|
# no-verify -> encrypted but unverified — use for DO Managed Postgres (or supply DATABASE_CA).
|
|
# require -> encrypted + verified (managed DBs with a public CA).
|
|
# DATABASE_CA -> optional custom CA cert (PEM) when verifying.
|
|
DATABASE_SSL=require
|
|
# DATABASE_CA=
|
|
|
|
# Run pending migrations automatically when the container starts.
|
|
# Set to "false" for multi-replica deploys and run migrations as a one-off job.
|
|
RUN_MIGRATIONS_ON_START=true
|
|
|
|
# === BETTER AUTH ===
|
|
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
BETTER_AUTH_SECRET=replace-with-a-64-char-hex-secret
|
|
# Public base URL of the app (no trailing slash).
|
|
BETTER_AUTH_URL=https://propertymanagement.network
|
|
|
|
# Google OAuth (optional). Redirect URI: <BETTER_AUTH_URL>/api/auth/callback/google
|
|
GOOGLE_CLIENT_ID=
|
|
GOOGLE_CLIENT_SECRET=
|
|
|
|
# === ADMIN BOOTSTRAP & AUTH POLICY ===
|
|
# Grant admin access to the /admin dashboard. Comma-separated Better Auth user
|
|
# IDs and/or emails. Set at least ONE on a fresh deploy or no one can administer
|
|
# the platform. There is no self-service path to become an admin (by design).
|
|
ADMIN_USER_IDS=
|
|
ADMIN_EMAILS=
|
|
# Require a verified email address before a user can sign in. Strongly
|
|
# recommended in production (defaults to false / disabled if unset).
|
|
REQUIRE_EMAIL_VERIFICATION=true
|
|
|
|
# === STORAGE ===
|
|
# Local-disk fallback (used only when Spaces below is NOT configured). If you use
|
|
# Spaces you no longer need the persistent volume, but keeping it is harmless.
|
|
STORAGE_DIR=/app/storage
|
|
|
|
# Object storage (DigitalOcean Spaces, S3-compatible) — recommended for prod.
|
|
# When KEY/SECRET/BUCKET are all set, uploads + serving use the bucket. Keep the
|
|
# bucket PRIVATE; files are served through the auth-gated /api/files route.
|
|
SPACES_KEY=
|
|
SPACES_SECRET=
|
|
SPACES_REGION=nyc3
|
|
SPACES_BUCKET=property-management-network
|
|
SPACES_ENDPOINT=https://nyc3.digitaloceanspaces.com
|
|
# Optional: if the Space's CDN is enabled, presigned URLs serve from the edge.
|
|
SPACES_CDN_ENDPOINT=https://nyc3.cdn.digitaloceanspaces.com
|
|
|
|
# === STRIPE ===
|
|
STRIPE_SECRET_KEY=sk_live_xxx
|
|
STRIPE_WEBHOOK_SECRET=whsec_xxx
|
|
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxx
|
|
# No price IDs needed — prices are resolved by lookup key and auto-created on
|
|
# first checkout, so going live is ONLY the three values above (live keys + live
|
|
# webhook secret). Optionally pre-create the catalog: node scripts/stripe-setup.mjs
|
|
|
|
# === PAYPAL (optional — alternative subscription checkout) ===
|
|
# Landlords can pay for their plan with PayPal alongside Stripe. Leave blank to
|
|
# hide the PayPal buttons. Create a REST app at https://developer.paypal.com and
|
|
# set PAYPAL_ENVIRONMENT=live for production. Create a webhook there pointing to
|
|
# <APP_URL>/api/paypal/webhook and put its id in PAYPAL_WEBHOOK_ID. Generate the
|
|
# plan IDs with `node scripts/paypal-setup-plans.mjs`.
|
|
PAYPAL_CLIENT_ID=
|
|
PAYPAL_SECRET=
|
|
PAYPAL_ENVIRONMENT=live
|
|
PAYPAL_WEBHOOK_ID=
|
|
PAYPAL_PRO_MONTHLY_PLAN_ID=
|
|
PAYPAL_PRO_YEARLY_PLAN_ID=
|
|
PAYPAL_LANDLORD_MONTHLY_PLAN_ID=
|
|
PAYPAL_LANDLORD_YEARLY_PLAN_ID=
|
|
|
|
# === AI (OpenAI) ===
|
|
OPENAI_API_KEY=sk-xxx
|
|
|
|
# === EMAIL (SMTP — SMTP2GO) ===
|
|
SMTP_HOST=mail.smtp2go.com
|
|
SMTP_PORT=2525
|
|
SMTP_USER=
|
|
SMTP_PASS=
|
|
EMAIL_FROM=postmaster@propertymanagement.network
|
|
|
|
# === ACCOUNTING SYNC (optional — QuickBooks / Xero OAuth) ===
|
|
# Redirect URIs: <APP_URL>/api/integrations/quickbooks/callback and .../xero/callback
|
|
# QBO_ENVIRONMENT=production for live QuickBooks. Tokens are encrypted at rest
|
|
# (AES-256-GCM) with a key derived from BETTER_AUTH_SECRET.
|
|
QBO_CLIENT_ID=
|
|
QBO_CLIENT_SECRET=
|
|
QBO_ENVIRONMENT=production
|
|
XERO_CLIENT_ID=
|
|
XERO_CLIENT_SECRET=
|
|
|
|
# === E-SIGNATURE (optional — DocuSign / Dropbox Sign) ===
|
|
# Leave blank to hide/disable a provider on the lease page. Configure the
|
|
# provider callbacks to point at this app:
|
|
# Dropbox Sign callback → <APP_URL>/api/esign/dropbox_sign/webhook
|
|
# DocuSign Connect → <APP_URL>/api/esign/docusign/webhook
|
|
# In production set DROPBOX_SIGN_TEST_MODE=false to send legally-binding docs.
|
|
DROPBOX_SIGN_API_KEY=
|
|
DROPBOX_SIGN_TEST_MODE=false
|
|
DOCUSIGN_ACCESS_TOKEN=
|
|
DOCUSIGN_ACCOUNT_ID=
|
|
DOCUSIGN_BASE_URI=https://www.docusign.net
|
|
|
|
# === APP (NEXT_PUBLIC_* — also set as Build Variables) ===
|
|
NEXT_PUBLIC_APP_URL=https://propertymanagement.network
|
|
NEXT_PUBLIC_APP_NAME=Property Management Network
|
|
# Google Search Console verification token (Search Console -> Settings -> HTML tag).
|
|
GOOGLE_SITE_VERIFICATION=
|
|
|
|
# === ANALYTICS (Umami) ===
|
|
# Cookieless page analytics, loaded only in production. These NEXT_PUBLIC_* vars
|
|
# are inlined at build time — set them as Build Variables in DO App Platform.
|
|
# Set the website id to empty to disable. Both values are public.
|
|
NEXT_PUBLIC_UMAMI_SRC=https://fickanalytics.phluit.net/script.js
|
|
NEXT_PUBLIC_UMAMI_WEBSITE_ID=4066c359-596f-4d0e-9636-c035c2adfbe8
|
|
|
|
# === MAPS / GEOCODING (OpenStreetMap — free, no key) ===
|
|
# Addresses are geocoded via OpenStreetMap Nominatim; the map uses Leaflet + OSM
|
|
# tiles. No API key or billing. Nominatim REQUIRES an identifying User-Agent —
|
|
# set a real contact URL/email so they can reach you if there's a usage issue.
|
|
GEOCODER_USER_AGENT=PropertyManagementNetwork/1.0 (https://propertymanagement.network)
|
|
|
|
# === CRON ===
|
|
# Bearer token required by the /api/cron/* and /api/follow-ups/run endpoints.
|
|
CRON_SECRET=replace-with-a-random-string
|
|
|
|
# === CLOUDFLARE TURNSTILE (bot protection on auth forms) ===
|
|
# Create a widget at https://dash.cloudflare.com/?to=/:account/turnstile
|
|
# NEXT_PUBLIC_TURNSTILE_SITE_KEY is inlined at build time — also set it as a
|
|
# Build Variable in Coolify. Leave both blank to disable the captcha.
|
|
NEXT_PUBLIC_TURNSTILE_SITE_KEY=
|
|
TURNSTILE_SECRET_KEY=
|