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
+5 -39
View File
@@ -4,7 +4,7 @@ import { nextCookies } from "better-auth/next-js"
import { admin } from "better-auth/plugins"
import { db } from "@/lib/db"
import { user, session, account, verification, profiles } from "@/lib/db/schema"
import { sendEmail } from "@/lib/email/send"
import { sendEmail, resetPasswordHtml, verifyEmailHtml } from "@/lib/email/send"
// Bootstrap superadmins from env — no API path lets a user self-promote.
const ADMIN_USER_IDS = (process.env.ADMIN_USER_IDS ?? "")
@@ -22,7 +22,7 @@ export const auth = betterAuth({
emailAndPassword: {
enabled: true,
// Env-gated so production can require a verified email without breaking
// local dev (where RESEND is typically unconfigured). Set
// local dev (where SMTP is typically unconfigured). Set
// REQUIRE_EMAIL_VERIFICATION=true in production to enforce.
requireEmailVerification: process.env.REQUIRE_EMAIL_VERIFICATION === "true",
minPasswordLength: 8,
@@ -38,6 +38,9 @@ export const auth = betterAuth({
// is gated by REQUIRE_EMAIL_VERIFICATION (see emailAndPassword above).
emailVerification: {
sendOnSignUp: true,
// After the user clicks the verification link, sign them in and send them
// to the callbackURL (set to /dashboard on sign-up).
autoSignInAfterVerification: true,
sendVerificationEmail: async ({ user: u, url }) => {
await sendEmail({
to: u.email,
@@ -88,40 +91,3 @@ export const auth = betterAuth({
],
})
function resetPasswordHtml(url: string) {
return `
<!DOCTYPE html>
<html>
<body style="font-family: sans-serif; background: #09090b; color: #fff; padding: 40px 20px; max-width: 560px; margin: 0 auto;">
<div style="background: #16161f; border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; padding: 32px;">
<h1 style="font-size: 20px; margin: 0 0 8px; color: #fff;">Reset your password</h1>
<p style="color: rgba(255,255,255,0.6); margin: 0 0 24px;">
Click the button below to choose a new password. If you didn't request this, you can ignore this email.
</p>
<a href="${url}" style="display: inline-block; background: #6366f1; color: #fff; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600;">
Reset Password
</a>
<p style="color: rgba(255,255,255,0.4); font-size: 12px; margin: 24px 0 0;">Property Management Network</p>
</div>
</body>
</html>`
}
function verifyEmailHtml(url: string) {
return `
<!DOCTYPE html>
<html>
<body style="font-family: sans-serif; background: #09090b; color: #fff; padding: 40px 20px; max-width: 560px; margin: 0 auto;">
<div style="background: #16161f; border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; padding: 32px;">
<h1 style="font-size: 20px; margin: 0 0 8px; color: #fff;">Verify your email</h1>
<p style="color: rgba(255,255,255,0.6); margin: 0 0 24px;">
Confirm your email address to finish setting up your account. If you didn't create an account, you can ignore this email.
</p>
<a href="${url}" style="display: inline-block; background: #6366f1; color: #fff; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600;">
Verify Email
</a>
<p style="color: rgba(255,255,255,0.4); font-size: 12px; margin: 24px 0 0;">Property Management Network</p>
</div>
</body>
</html>`
}