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 -1
View File
@@ -3,9 +3,11 @@ import { eq, desc } from "drizzle-orm"
import { db } from "@/lib/db"
import { properties as propertiesTable } from "@/lib/db/schema"
import { getSessionUser } from "@/lib/session"
import { getEffectiveOwnerId } from "@/lib/account"
import Link from "next/link"
import { Building2, MapPin, BedDouble, ArrowRight, TrendingUp } from "lucide-react"
import { EmptyState } from "@/components/shared/empty-state"
import { PropertyMap } from "@/components/maps/property-map"
import { formatCurrency, getOccupancyRate } from "@/lib/utils"
export const metadata = { title: "Properties" }
@@ -14,8 +16,10 @@ export default async function PropertiesPage() {
const user = await getSessionUser()
if (!user) redirect("/login")
const ownerId = await getEffectiveOwnerId(user.id)
const properties = await db.query.properties.findMany({
where: eq(propertiesTable.user_id, user.id),
where: eq(propertiesTable.user_id, ownerId),
with: {
units: { columns: { id: true, status: true, rent_amount: true } },
},
@@ -26,6 +30,17 @@ export default async function PropertiesPage() {
return sum + (p.units ?? []).filter((u: any) => u.status === "occupied").reduce((s: number, u: any) => s + Number(u.rent_amount), 0)
}, 0)
const mapMarkers = (properties ?? [])
.filter((p: any) => p.latitude != null && p.longitude != null)
.map((p: any) => ({
id: p.id,
name: p.name,
lat: p.latitude as number,
lng: p.longitude as number,
subtitle: `${p.address_line1 ? `${p.address_line1}, ` : ""}${p.city}${p.state ? `, ${p.state}` : ""}`,
href: `/properties/${p.id}`,
}))
return (
<div className="space-y-6">
{/* Page header */}
@@ -39,6 +54,12 @@ export default async function PropertiesPage() {
</div>
</div>
{mapMarkers.length > 0 && (
<div className="rounded-2xl border border-white/[0.06] bg-[#16161f] overflow-hidden">
<PropertyMap className="h-80 w-full" markers={mapMarkers} />
</div>
)}
{!properties?.length ? (
<EmptyState
icon={Building2}