Consolidate audit-fixes branch: webhooks, integrations, and deploy hardening

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>
This commit is contained in:
Leon Serfaty
2026-07-02 13:42:34 -04:00
co-authored by Claude Opus 4.8
parent 969d5d4c8a
commit c9968531e4
282 changed files with 41530 additions and 4013 deletions
+22 -27
View File
@@ -1,7 +1,7 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="public/logo-light.svg">
<img alt="Property Management Network" src="public/logo-dark.svg" width="360">
<source media="(prefers-color-scheme: dark)" srcset="public/logo-light.png">
<img alt="Property Management Network" src="public/logo-dark.png" width="360">
</picture>
</p>
@@ -9,7 +9,7 @@
**Property management SaaS for independent landlords.** Track properties, tenants, rent, maintenance, leases, and expenses — all in one clean dashboard.
Built with Next.js 16, PostgreSQL (Drizzle ORM), Better Auth, Stripe, and OpenAI. Ready to deploy on Vercel in under 10 minutes.
Built with Next.js 16, PostgreSQL (Drizzle ORM), Better Auth, Stripe, and OpenAI. Deploys to DigitalOcean App Platform (see [DIGITALOCEAN.md](DIGITALOCEAN.md)).
---
@@ -25,7 +25,7 @@ Property Management Network replaces the spreadsheet + WhatsApp chaos that most
- **Expenses** — categorized logging with recurring expense support
- **Documents** — file vault per property with drag-and-drop upload to local disk, served through an auth-gated route
- **AI features** — AI-powered recommendations, predictions, and impact tracking (Pro+)
- **Automated emails** — rent reminders, overdue alerts, lease expiry notifications via Resend
- **Automated emails** — rent reminders, overdue alerts, lease expiry notifications via SMTP (SMTP2GO)
- **Tenant portal** — token-based (no login), tenants can view rent history and submit maintenance
---
@@ -51,12 +51,12 @@ Subscription billing via Stripe. Lifetime deal is ideal for Flippa buyers who wa
| Styling | Tailwind CSS + Geist font |
| Database | PostgreSQL (via Drizzle ORM) |
| Auth | Better Auth (email/password + Google OAuth) |
| Storage | Local disk (auth-gated file serving) |
| Storage | DigitalOcean Spaces (S3-compatible, CDN, auth-gated) |
| Payments | Stripe (subscriptions + payment links) |
| AI | OpenAI (gpt-4o-mini) |
| Email | Resend |
| Cron | Vercel Cron Jobs |
| Deploy | Vercel |
| Email | SMTP (SMTP2GO) |
| Cron | DigitalOcean Functions (scheduled triggers) |
| Deploy | DigitalOcean App Platform (Docker image via DOCR) |
---
@@ -91,20 +91,20 @@ GOOGLE_CLIENT_SECRET=
# File storage (local disk)
STORAGE_DIR=./storage
# Stripe
# Stripe (no price IDs needed — resolved by lookup key, auto-created on first checkout)
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
STRIPE_PRO_MONTHLY_PRICE_ID=
STRIPE_LANDLORD_MONTHLY_PRICE_ID=
STRIPE_LIFETIME_PRICE_ID=
# OpenAI
OPENAI_API_KEY=
# Resend
RESEND_API_KEY=
RESEND_FROM_EMAIL=Property Management Network <noreply@yourdomain.com>
# Email (SMTP — e.g. SMTP2GO)
SMTP_HOST=mail.smtp2go.com
SMTP_PORT=2525
SMTP_USER=
SMTP_PASS=
EMAIL_FROM=postmaster@yourdomain.com
# App
NEXT_PUBLIC_APP_URL=http://localhost:3000
@@ -123,10 +123,7 @@ To regenerate migrations after changing the schema, use `npm run db:generate`. F
### 4. Configure Stripe
Create three products in your Stripe dashboard:
- **Pro Monthly** — $29/mo recurring → copy Price ID to `STRIPE_PRO_MONTHLY_PRICE_ID`
- **Landlord Monthly** — $59/mo recurring → copy Price ID to `STRIPE_LANDLORD_MONTHLY_PRICE_ID`
- **Lifetime** — $199 one-time → copy Price ID to `STRIPE_LIFETIME_PRICE_ID`
Add your API keys (`STRIPE_SECRET_KEY`, `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`) — that's it. Products and prices are resolved by stable **lookup keys** and auto-created on first checkout (Pro $29/mo, Landlord $59/mo, Lifetime $199, plus annual), so there are **no price IDs to configure** and going live is just an API-key swap. To pre-create the catalog, optionally run `node scripts/stripe-setup.mjs`.
Set up a webhook at `https://yourdomain.com/api/stripe/webhook` listening to:
- `checkout.session.completed`
@@ -136,9 +133,9 @@ Set up a webhook at `https://yourdomain.com/api/stripe/webhook` listening to:
- `invoice.payment_failed`
- `payment_intent.succeeded`
### 5. Configure Resend
### 5. Configure email (SMTP)
Add a verified sending domain in your Resend dashboard. Update `RESEND_FROM_EMAIL` with your domain address.
Use any SMTP provider (e.g. SMTP2GO). Verify your sending domain with the provider, then set `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, and `EMAIL_FROM`.
### 6. (Optional) Google OAuth
@@ -152,11 +149,9 @@ npm run dev
Open [http://localhost:3000](http://localhost:3000).
### 8. Deploy to Vercel
### 8. Deploy (DigitalOcean App Platform)
Connect the repo in the Vercel dashboard and add all environment variables under **Settings → Environment Variables**.
Cron jobs are pre-configured in `vercel.json` and run automatically on Vercel.
The repo ships a production `Dockerfile` (Next.js standalone output), an App Platform spec at [`.do/app.yaml`](.do/app.yaml), DO Functions cron under [`functions/`](functions/), and a `/api/health` liveness probe. See **[DIGITALOCEAN.md](DIGITALOCEAN.md)** for the full walkthrough: build/push the image to DOCR, create the app, wire up Managed Postgres + Spaces, and deploy the scheduled cron functions.
---
@@ -184,7 +179,7 @@ app/
│ ├── expenses/ # CRUD
│ ├── documents/ # Document metadata (files on local disk)
│ ├── ai/ # Rent receipts + maintenance summaries
│ ├── notifications/ # Send emails via Resend
│ ├── notifications/ # Send emails via SMTP (SMTP2GO)
│ ├── stripe/ # Checkout, portal, webhook
│ └── cron/ # Rent reminders + lease expiry alerts
└── tenant-portal/[token]/ # Public tenant portal (no login)
@@ -195,7 +190,7 @@ lib/
├── storage.ts # Local-disk file storage helpers
├── stripe/ # Client, plans, payment links
├── ai/ # OpenAI client + prompts
├── email/ # Resend client + HTML templates
├── email/ # SMTP (SMTP2GO) client + HTML templates
└── validations/ # Zod schemas for all entities
drizzle.config.ts # Drizzle ORM config (DATABASE_URL, migrations dir)