<p align="center">
  <picture>
    <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>

<h1 align="center">🏠 Property Management Network</h1>

<p align="center">
  <strong>The all-in-one property-management platform for independent landlords.</strong><br>
  Properties, tenants, rent, maintenance, leases, expenses, AI insights, and integrations — in one clean dashboard.
</p>

<p align="center">
  <img alt="Next.js" src="https://img.shields.io/badge/Next.js-16-black?logo=nextdotjs">
  <img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=white">
  <img alt="PostgreSQL" src="https://img.shields.io/badge/PostgreSQL-Drizzle_ORM-4169E1?logo=postgresql&logoColor=white">
  <img alt="License" src="https://img.shields.io/badge/license-Proprietary-red">
</p>

---

## ✨ Overview

Property Management Network replaces the spreadsheet-and-WhatsApp chaos that most small landlords live with. It gives a solo landlord or a small team a single source of truth for their whole portfolio — and the automation, AI, and integrations to run it hands-off.

Everything is **multi-tenant and team-aware**: each landlord operates on their own isolated portfolio, and Landlord/Lifetime accounts can invite teammates with scoped roles.

### 🧰 What you can do

**Core operations**
- 🏢 **Properties & units** — manage your whole portfolio with live occupancy tracking and a map view (addresses are auto-geocoded).
- 👥 **Tenants** — profiles, lease history, payment records, and a private **tenant portal** (token-based, no login required).
- 💵 **Rent tracking** — log payments, send **Stripe** payment links, and auto-mark balances overdue with automatic late fees.
- 🔧 **Maintenance** — full status workflow (Open → In Progress → Resolved), with tenant-submitted requests from the portal.
- 📄 **Leases** — expiry countdowns, automated 60/30/7-day email alerts, and **e-signature** (DocuSign / Dropbox Sign).
- 🧾 **Expenses** — categorized logging with recurring-expense support.
- 🗂️ **Documents** — a per-property file vault stored in object storage and served through an auth-gated route.
- 🔎 **Inspections & vendors** — move-in/out/routine inspection checklists and a vendor directory.
- 📊 **Reports & exports** — portfolio analytics with CSV export.
- 📅 **Calendar** — an in-app calendar plus a read-only **iCal (ICS) feed** you can subscribe to.

**Automation & AI**
- 🤖 **AI features** — recommendations, predictions, impact tracking, and a portfolio assistant (OpenAI). *(Pro and up.)*
- ✉️ **Automated email** — rent reminders, overdue notices, and lease-expiry alerts, plus a configurable **follow-up engine**.
- 🎨 **White-label branding** — put your own brand on the tenant portal. *(Landlord / Lifetime.)*
- 🛡️ **Admin dashboard** — superadmin tools with a full audit log.

---

## 🔌 Integrations & developer platform

| Capability | Details |
|---|---|
| 🌐 **Public REST API** | Versioned `/api/v1` endpoints (properties, tenants, payments, maintenance, webhooks) authenticated with Bearer **API keys**. See `/api-docs`. |
| 🪝 **Outbound webhooks / Zapier** | Subscribe to events (`tenant.created`, `payment.paid`, `maintenance.updated`, …). Deliveries are **HMAC-signed**, retried with backoff, and Zapier-compatible via the REST-hook subscribe/unsubscribe pattern. |
| 💳 **Payments** | Stripe (subscriptions + rent payment links). |
| 📚 **Accounting sync** | One-way push of income & expenses to **QuickBooks Online** or **Xero** (OAuth). |
| ✍️ **E-signature** | Send leases for signature via **DocuSign** or **Dropbox Sign**. |
| 🔑 **Auth** | Email/password and Google OAuth (Better Auth). |

Every integration is env-gated: unconfigured providers show a clean “not configured” state instead of a broken button.

---

## 💳 Plans & pricing

| Plan | Price | Highlights |
|------|-------|------------|
| 🆓 **Starter** | Free | 1 property, 3 tenants, no AI |
| 🚀 **Pro** | $29/mo | 10 properties, unlimited tenants, AI (50 calls/mo) |
| 🏆 **Landlord** | $59/mo | Unlimited properties, team access, white-label, AI (200/mo) |
| ♾️ **Lifetime** | $199 once | Everything in Landlord, forever |

Billing runs through **Stripe**. Products/prices are resolved by stable lookup keys and auto-created on first checkout, so going live is just an API-key swap — no price IDs to wire up.

---

## 🧱 Tech stack

| Layer | Technology |
|-------|------------|
| Framework | Next.js 16.2 (App Router, TypeScript, React 19) |
| Styling | Tailwind CSS + Geist |
| Database | PostgreSQL via **Drizzle ORM** |
| Auth | Better Auth (email/password + Google OAuth) |
| Object storage | DigitalOcean Spaces (S3-compatible, CDN, auth-gated) |
| Payments | Stripe |
| AI | OpenAI (`gpt-4o-mini`) |
| Email | SMTP (SMTP2GO) |
| Maps | Leaflet + OpenStreetMap / Nominatim geocoding |
| Cron | DigitalOcean Functions (scheduled triggers) |
| Deploy | DigitalOcean App Platform (Docker image via DOCR) |

---

## 🚀 Getting started

### 1. Clone & install

```bash
git clone <your-repo>
cd property-management-network
npm install
```

### 2. ⚙️ Configure environment

Copy the template and fill in your own values:

```bash
cp .env.example .env.local
```

`.env.local` holds your database URL, auth secret, and credentials for Stripe, OpenAI, SMTP, and object storage. **Every variable is documented inline in `.env.example`**, and the full production reference lives in **[DIGITALOCEAN.md](DIGITALOCEAN.md)**. Never commit real secrets.

### 3. 🗄️ Run migrations

The schema is managed by Drizzle (see `drizzle.config.ts`). Point `DATABASE_URL` at your PostgreSQL instance, then:

```bash
npm run db:migrate      # apply migrations
npm run db:generate     # regenerate after schema changes
npm run db:push         # push schema directly (quick local prototyping)
```

### 4. 🔌 Wire up services (as needed)

- **Stripe** — set the API keys, then add a webhook at `https://yourdomain.com/api/stripe/webhook` for `checkout.session.completed`, the `customer.subscription.*` events, `invoice.payment_failed`, and `payment_intent.succeeded`.
- **Email** — verify a sending domain with your SMTP provider (e.g. SMTP2GO) and set the `SMTP_*` + `EMAIL_FROM` vars.
- **Google / OpenAI / accounting / e-sign** — each is optional and activates once its env vars are present.

### 5. ▶️ Run locally

```bash
npm run dev
```

Open **[http://localhost:3000](http://localhost:3000)**.

### 6. 🚢 Deploy

The repo ships a production `Dockerfile` (Next.js standalone), an App Platform spec at [`.do/app.yaml`](.do/app.yaml), DO Functions cron under [`functions/`](functions/), and a `/api/health` probe. Follow **[DIGITALOCEAN.md](DIGITALOCEAN.md)** for the full walkthrough.

---

## 🗂️ Project structure

```
app/
├── (marketing)/         # Landing page, pricing, legal, API docs
├── (auth)/              # Login, signup, password reset
├── (dashboard)/         # Auth-gated app (properties, tenants, rent, maintenance,
│                        #   leases, expenses, inspections, vendors, reports,
│                        #   calendar, AI, onboarding, settings)
├── (admin)/             # Superadmin dashboard
├── api/
│   ├── v1/              # 🌐 Public REST API (Bearer API keys)
│   ├── webhooks + cron/ # 🪝 Outbound webhook delivery + scheduled jobs
│   ├── stripe/          # 💳 Billing + payment links + provider webhooks
│   ├── integrations/    # 📚 QuickBooks / Xero OAuth
│   ├── esign/           # ✍️ DocuSign / Dropbox Sign
│   └── …                # Properties, tenants, rent, maintenance, documents, AI
└── tenant-portal/[token]/  # Public tenant portal (no login)

lib/
├── db/                  # Drizzle schema, queries, migrations
├── auth.ts  account.ts  # Better Auth + team/account scoping
├── storage.ts           # Object storage (Spaces) with local-disk dev fallback
├── webhooks/            # Event catalog, HMAC signing, SSRF guard, delivery
├── stripe/              # Billing clients & plans
├── accounting/  esign/  # QuickBooks/Xero & DocuSign/Dropbox Sign
├── ai/                  # OpenAI client + prompts
├── email/               # SMTP (SMTP2GO) client + HTML templates
└── validations/         # Zod schemas for all entities
```

---

## 🗄️ Data model & isolation

The schema spans **~30 tables** managed via Drizzle ORM, grouped roughly as:

- **Core** — `profiles`, `properties`, `units`, `tenants`, `rent_payments`, `maintenance_requests`, `leases`, `expenses`, `documents`, `inspections`, `vendors`
- **Automation & AI** — `notifications`, `follow_up_rules`, `follow_up_log`, `ai_recommendations`, `ai_predictions`, `activity_log`, `usage_events`
- **Accounts & platform** — `account_members`, `api_keys`, `app_settings`, `admin_audit_log`, `accounting_connections`, `signature_requests`, `webhook_endpoints`, `webhook_deliveries`
- **Auth (Better Auth)** — `user`, `session`, `account`, `verification`

> 🔐 **Tenancy is enforced in the application layer.** Every query scopes by the resolved **account owner id** (team-aware), never the raw session user. There is no database RLS, so this scoping must be preserved on every new route — see `lib/account.ts` (`getEffectiveOwnerId`).

---

## ⏰ Scheduled jobs

Cron is driven by DigitalOcean Functions hitting `CRON_SECRET`-protected endpoints (`functions/project.yml`):

| Job | Schedule (UTC) | What it does |
|-----|----------------|--------------|
| `daily` | 09:00 | Rent reminders, overdue marking, 60/30/7-day lease-expiry alerts |
| `late-fees` | 08:00 | Applies late fees past the grace period |
| `follow-ups` | 10:00 | Runs each account's active follow-up rules |
| `webhooks` | every 5 min | Retries pending outbound webhook deliveries |

---

## 🔒 Security highlights

- 🔑 API keys are stored as SHA-256 hashes; the plaintext is shown once.
- 🪝 Webhook payloads are **HMAC-SHA256 signed** (`X-PMN-Signature`); endpoint URLs are **SSRF-guarded** (private/loopback/metadata ranges blocked).
- 📁 Uploaded files are served only through an auth-gated route; object storage is required in production (uploads **fail loud** rather than silently hit ephemeral disk).
- 🛢️ Verified TLS to Postgres in production (`DATABASE_SSL=require` + CA).
- ⏱️ Cron endpoints use a constant-time bearer check and fail closed.

---

## 📜 License

**Proprietary — © 2026 Property Management Network. All rights reserved.**

This source code is proprietary and confidential. No license or permission is granted to use, copy, modify, merge, publish, distribute, sublicense, or sell any part of it without the prior written consent of the copyright holder. See [LICENSE](LICENSE).
