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
+144 -127
View File
@@ -1,145 +1,162 @@
import Link from "next/link"
import { LegalPage, Section, LegalContact } from "@/components/marketing/legal"
import { LEGAL } from "@/lib/legal"
export const metadata = {
title: "GDPR Compliance — Property Management Network",
description: "Property Management Network's commitment to GDPR compliance and your data rights as a data subject.",
title: "GDPR & Data Rights",
description: `How ${LEGAL.entity} complies with the GDPR and UK GDPR, and the data rights available to you as a data subject.`,
alternates: { canonical: "/gdpr" },
}
const RIGHTS = [
{ right: "Right of access", desc: "You can request a full export of all data we hold about you at any time from your account settings." },
{ right: "Right to rectification", desc: "You can update your personal information directly in your account settings, or contact us to correct inaccurate data." },
{ right: "Right to erasure", desc: "You can permanently delete your account and all associated data from the settings page. Deletion is irreversible and processed within 30 days." },
{ right: "Right to data portability", desc: "You can export your data in machine-readable JSON or CSV format from the dashboard at any time." },
{ right: "Right to restriction", desc: "You may request that we restrict processing of your data while a dispute is being resolved." },
{ right: "Right to object", desc: "You may object to processing where we rely on legitimate interest. You can opt out of analytics tracking by enabling Do Not Track in your browser." },
]
export default function GdprPage() {
return (
<div className="bg-[#09090b] text-white min-h-screen">
<div className="mx-auto max-w-3xl px-6 pt-32 pb-24">
<div className="mb-10">
<div className="inline-flex items-center gap-2 rounded-full border border-emerald-500/30 bg-emerald-500/10 px-3 py-1 text-xs font-medium text-emerald-300 mb-4">
EU GDPR Compliant
</div>
<h1 className="text-3xl font-bold text-white mb-2">GDPR Compliance</h1>
<p className="text-xs text-white/30">Last updated: April 2026</p>
</div>
<LegalPage
title="GDPR & Data Rights"
subtitle={`Our commitment to the General Data Protection Regulation and the UK GDPR, and the rights available to you when ${LEGAL.entity} processes your personal data.`}
>
<Section id="introduction" heading="1. Introduction">
<p>
{LEGAL.entity} is committed to protecting personal data and to complying with the
General Data Protection Regulation (Regulation (EU) 2016/679) (the{" "}
<strong>GDPR</strong>) and the United Kingdom General Data Protection Regulation
(the <strong>UK GDPR</strong>) where applicable. This page explains the rights
available to individuals whose personal data we process and how those rights may
be exercised in connection with {LEGAL.service}.
</p>
</Section>
<div className="space-y-8 text-sm text-white/50 leading-relaxed">
<div>
<h2 className="text-base font-semibold text-white mb-2">Who we are</h2>
<p>
Property Management Network (&quot;we&quot;, &quot;us&quot;, &quot;our&quot;) is the data controller for personal data collected through our platform.
We are committed to complying with the General Data Protection Regulation (EU) 2016/679 (GDPR)
and the UK GDPR where applicable.
</p>
</div>
<Section id="rights" heading="2. Your rights under the GDPR and UK GDPR">
<p>
Subject to the conditions in Applicable Data Protection Law, you have the
following rights:
</p>
<ul>
<li>
<strong>Right of access</strong> &mdash; to obtain confirmation of whether we
process your personal data and to receive a copy of it.
</li>
<li>
<strong>Right to rectification</strong> &mdash; to have inaccurate personal
data corrected and incomplete data completed.
</li>
<li>
<strong>Right to erasure</strong> &mdash; to have your personal data deleted in
certain circumstances.
</li>
<li>
<strong>Right to restriction of processing</strong> &mdash; to limit how we
process your personal data in certain circumstances.
</li>
<li>
<strong>Right to data portability</strong> &mdash; to receive your personal
data in a structured, commonly used, machine-readable format.
</li>
<li>
<strong>Right to object</strong> &mdash; to object to processing that relies on
our legitimate interests.
</li>
<li>
<strong>Right to withdraw consent</strong> &mdash; where processing is based on
consent, to withdraw that consent at any time.
</li>
<li>
<strong>Right to lodge a complaint</strong> &mdash; to lodge a complaint with a
supervisory authority.
</li>
</ul>
</Section>
<div>
<h2 className="text-base font-semibold text-white mb-2">What data we process</h2>
<p className="mb-3">We process the following categories of personal data:</p>
<ul className="space-y-2">
{[
"Account data: name, email address, password hash",
"Property data: addresses, rental amounts, lease terms you enter",
"Tenant data: names, emails, phone numbers you provide as a landlord",
"Payment data: payment amounts, dates, and status (card details handled by Stripe, not us)",
"Usage data: pages visited, features used — anonymised via Vercel Analytics",
].map((item) => (
<li key={item} className="flex items-start gap-2">
<span className="mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full bg-indigo-400" />
{item}
</li>
))}
</ul>
</div>
<Section id="legal-bases" heading="3. Legal bases for processing">
<p>
We process personal data on one or more of the following legal bases, depending
on the context:
</p>
<ul>
<li>
<strong>Contract</strong> &mdash; where processing is necessary to provide the
Service you have requested.
</li>
<li>
<strong>Legitimate interests</strong> &mdash; where processing is necessary for
our legitimate interests, such as securing and improving the Service, provided
those interests are not overridden by your rights.
</li>
<li>
<strong>Consent</strong> &mdash; where you have given consent for a specific
purpose.
</li>
<li>
<strong>Legal obligation</strong> &mdash; where processing is necessary to
comply with a legal obligation to which we are subject.
</li>
</ul>
</Section>
<div>
<h2 className="text-base font-semibold text-white mb-2">Legal basis for processing</h2>
<p>
We process personal data on the following legal bases: (1) Contract data necessary to provide the service you signed up for.
(2) Legitimate interest anonymous analytics to improve the product. (3) Legal obligation where required by applicable law.
We do not process data on the basis of consent for core functionality.
</p>
</div>
<Section id="storage" heading="4. Data storage and international transfers">
<p>
Personal data is stored in a managed PostgreSQL database hosted on DigitalOcean,
with an EU region available. Uploaded files are stored privately in DigitalOcean
Spaces. Access isolation is enforced at the application layer: every request is
authenticated and scoped to the relevant account so that data is not accessible
to other users. Where personal data is transferred to a country that has not been
recognized as providing an adequate level of protection, the transfer is protected
by Standard Contractual Clauses or another lawful transfer mechanism.
</p>
</Section>
<div>
<h2 className="text-base font-semibold text-white mb-2">Data storage and transfers</h2>
<p>
Your data is stored in Supabase (PostgreSQL), with servers located in the EU (Frankfurt, Germany) by default.
Row-level security (RLS) policies ensure only you can access your data. We do not transfer personal data outside
the EEA except where strictly necessary for integrated services (e.g. Stripe for payment processing,
which is covered by Standard Contractual Clauses).
</p>
</div>
<Section id="retention" heading="5. Data retention">
<p>
We retain personal data for as long as it is needed to provide the Service. Upon
deletion of an account, associated personal data is deleted within{" "}
{LEGAL.dataDeletionDays} days, except where a longer retention period is required
by law (for example, certain financial records that must be kept for tax
purposes).
</p>
</Section>
<div>
<h2 className="text-base font-semibold text-white mb-2">Your rights under GDPR</h2>
<p className="mb-5">As a data subject, you have the following rights:</p>
<div className="space-y-4">
{RIGHTS.map((r) => (
<div key={r.right} className="rounded-xl border border-white/[0.06] bg-[#111118] p-4">
<p className="text-sm font-semibold text-white mb-1">{r.right}</p>
<p className="text-xs text-white/50 leading-relaxed">{r.desc}</p>
</div>
))}
</div>
</div>
<Section id="breach" heading="6. Personal-data breach notification">
<p>
In the event of a personal-data breach, we notify affected users and, where
required, the relevant supervisory authority within 72 hours of becoming aware of
the breach, consistent with Article 33 of the GDPR.
</p>
</Section>
<div>
<h2 className="text-base font-semibold text-white mb-2">Data retention</h2>
<p>
We retain account data for as long as your account is active. Upon deletion, all personal data is purged within 30 days,
except where retention is required by law (e.g. financial records may be retained for up to 7 years for tax compliance).
</p>
</div>
<Section id="subprocessors" heading="7. Sub-processors">
<p>
We use vetted third-party sub-processors to help operate the Service. Our current
sub-processors are listed on our{" "}
<Link href="/subprocessors">Sub-processors</Link> page, and the terms governing
their engagement are set out in our{" "}
<Link href="/dpa">Data Processing Addendum</Link>.
</p>
</Section>
<div>
<h2 className="text-base font-semibold text-white mb-2">Data breach notification</h2>
<p>
In the event of a data breach affecting your personal data, we will notify affected users within 72 hours of becoming aware,
in accordance with GDPR Article 33 obligations.
</p>
</div>
<Section id="exercise" heading="8. How to exercise your rights">
<p>
To exercise any of the rights described above, contact us at{" "}
<a href={`mailto:${LEGAL.privacyEmail}`}>{LEGAL.privacyEmail}</a>. For
data-protection matters, you may also contact our data-protection team at{" "}
<a href={`mailto:${LEGAL.dpoEmail}`}>{LEGAL.dpoEmail}</a>. You also have the right
to lodge a complaint with your local supervisory authority (for example, the
Information Commissioner&rsquo;s Office in the United Kingdom, or your national
data-protection authority in the European Union).
</p>
</Section>
<div>
<h2 className="text-base font-semibold text-white mb-2">Sub-processors</h2>
<div className="rounded-2xl border border-white/[0.06] bg-[#111118] overflow-hidden">
{[
{ name: "Supabase", purpose: "Database & file storage", location: "EU (Frankfurt)" },
{ name: "Stripe", purpose: "Payment processing", location: "US (SCCs in place)" },
{ name: "Resend", purpose: "Transactional email", location: "US (SCCs in place)" },
{ name: "Vercel", purpose: "Hosting & edge network", location: "Global (anonymised data only)" },
].map((sp, i, arr) => (
<div key={sp.name} className={`grid grid-cols-3 gap-4 px-5 py-3.5 text-xs ${i !== arr.length - 1 ? "border-b border-white/[0.04]" : ""}`}>
<span className="font-semibold text-white">{sp.name}</span>
<span className="text-white/50">{sp.purpose}</span>
<span className="text-white/40">{sp.location}</span>
</div>
))}
</div>
</div>
<Section id="roles" heading="9. Roles: controller versus processor">
<p>
Where you use the Service to manage the personal data of your Tenants, you act as
the data <strong>controller</strong> and we act as the data{" "}
<strong>processor</strong>, processing that personal data on your documented
instructions under our{" "}
<Link href="/dpa">Data Processing Addendum</Link>. Where we process your own
account data, we act as a <strong>controller</strong>, as described in our{" "}
<Link href="/privacy">Privacy Policy</Link>.
</p>
</Section>
<div>
<h2 className="text-base font-semibold text-white mb-2">Contact & complaints</h2>
<p>
To exercise any of your rights or to raise a data protection concern, contact our Data Protection lead at{" "}
<a href="mailto:privacy@propertymanagement.network" className="text-indigo-400 hover:text-indigo-300">
privacy@propertymanagement.network
</a>
. You also have the right to lodge a complaint with your local supervisory authority (e.g. the ICO in the UK,
or your national DPA in the EU).
</p>
<p className="mt-4">
See also our{" "}
<Link href="/privacy" className="text-indigo-400 hover:text-indigo-300">Privacy Policy</Link>{" "}
and{" "}
<Link href="/cookie-policy" className="text-indigo-400 hover:text-indigo-300">Cookie Policy</Link>.
</p>
</div>
</div>
</div>
</div>
<LegalContact email={LEGAL.dpoEmail} />
</LegalPage>
)
}