Merge pull request #81 from ArnasDon/docs/readme-arch-alt-hosts

docs: README polish + architecture
This commit is contained in:
Arnas Donauskas 2026-04-20 21:52:48 +03:00 committed by GitHub
commit 93158f4b82
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 242 additions and 16 deletions

View File

@ -1,43 +1,98 @@
# WaCRM
Self-hostable WhatsApp CRM — shared inbox, contacts, sales pipelines,
broadcasts, and no-code automations — built on Next.js 16 and Supabase.
> Self-hostable WhatsApp CRM template — shared inbox, contacts, sales
> pipelines, broadcasts, and no-code automations. Fork it, brand it,
> host it.
- **Live demo / marketing site**: [`/`](./src/app/page.tsx)
- **Source**: <https://github.com/ArnasDon/wacrm>
[![License: MIT](https://img.shields.io/badge/License-MIT-emerald.svg)](./LICENSE)
[![CI](https://github.com/ArnasDon/wacrm/actions/workflows/ci.yml/badge.svg)](https://github.com/ArnasDon/wacrm/actions/workflows/ci.yml)
[![Next.js 16](https://img.shields.io/badge/Next.js-16-black?logo=nextdotjs)](https://nextjs.org)
[![Supabase](https://img.shields.io/badge/Supabase-Postgres%20%2B%20Auth-3ecf8e?logo=supabase)](https://supabase.com)
[![Stars](https://img.shields.io/github/stars/ArnasDon/wacrm?style=social)](https://github.com/ArnasDon/wacrm/stargazers)
## What you get out of the box
- **Shared inbox** on the official WhatsApp Business API — multiple
agents working one number, per-conversation assignment, status, and
notes.
- **Contacts + tags + custom fields**, CSV import, deduplication.
- **Sales pipelines** (Kanban) with deals linked to conversations.
- **Broadcasts** with Meta-approved templates, delivery + read
tracking, per-recipient variable substitution.
- **No-code automations** — triggers on inbound messages, new
contacts, keywords, or schedule; conditional branches, waits,
tags, webhooks. Visual builder.
- **Real-time dashboard** — response times, daily volume, pipeline
value, cross-module activity feed.
- **Account management** — email, password, avatar, global sign-out.
## Why fork this?
WaCRM is a **template**, not a product. Forking means you get:
- **Full ownership** — your code, your Supabase project, your domain,
your data. No SaaS lock-in, no seat pricing, no trust dance.
- **Full customisation** — add the fields your team needs, remove the
modules you don't, redesign anything. The stack is boring on
purpose (Next.js + Supabase + Tailwind) so the learning curve is
short.
- **Zero ops to start** — Hostinger Managed Node.js deploys a fork in
a few clicks. No Docker, no Kubernetes, no infra team needed.
- **Real security primitives** — token encryption (AES-256-GCM), RLS
on every table, HMAC-verified webhooks, CSP, rate limiting, CI
typecheck/build on every PR.
Not a framework. Not an SDK. A concrete, working CRM you can stand up
in an afternoon and make yours.
## Quick start
```bash
# Fork on GitHub first: https://github.com/ArnasDon/wacrm → Fork
git clone https://github.com/<your-username>/wacrm.git
cd wacrm
npm install
cp .env.local.example .env.local
# Fill in Supabase keys + ENCRYPTION_KEY, then:
cp .env.local.example .env.local # fill in Supabase + Meta creds
npm run dev
```
Open <http://localhost:3000>.
For the full setup — Supabase migrations, WhatsApp Business API config,
production deploy on Hostinger — see [`docs/`](./docs/README.md).
Full setup — Supabase migrations, WhatsApp Business API config, and
production deploy — is in [`docs/`](./docs/README.md).
## Documentation
**Setup**
- [Getting started](./docs/getting-started.md)
- [Supabase setup](./docs/supabase-setup.md)
- [WhatsApp setup](./docs/whatsapp-setup.md)
- [Environment variables](./docs/environment-variables.md)
**Deploy**
- [Deploy on Hostinger](./docs/deployment-hostinger.md)
- [Automations cron](./docs/automations-and-cron.md)
**Reference**
- [Architecture](./docs/architecture.md) — stack, folder layout,
request lifecycle
- [Troubleshooting](./docs/troubleshooting.md)
## Stack
- **App** — Next.js 16 (App Router), React 19, TypeScript, Tailwind v4.
- **Data** — Supabase (Postgres + Auth + RLS).
- **Data** — Supabase (Postgres + Auth + Storage + RLS).
- **WhatsApp** — Meta Cloud API (official WhatsApp Business API).
## Contributing
This is a template, not a collaborative product — the expected flow is
fork → customise → deploy, **not** upstream contribution. Bug reports
and security issues are welcome; feature PRs often belong in your fork
rather than here. Details in
[`CONTRIBUTING.md`](./CONTRIBUTING.md) and
[`.github/SECURITY.md`](./.github/SECURITY.md).
## License
MIT — fork it, brand it, host it. Pull requests welcome.
[MIT](./LICENSE). Fork it, brand it, host it.

View File

@ -5,6 +5,8 @@ docs walk you from a freshly forked repo to a production deploy.
## Reading order
**Setup**
1. **[Getting started](./getting-started.md)** — fork the repo, install, run
locally.
2. **[Supabase setup](./supabase-setup.md)** — create the database, run the
@ -12,11 +14,19 @@ docs walk you from a freshly forked repo to a production deploy.
3. **[WhatsApp setup](./whatsapp-setup.md)** — create a Meta app, connect a
phone number, wire the webhook.
4. **[Environment variables](./environment-variables.md)** — the full reference.
5. **[Deploy on Hostinger](./deployment-hostinger.md)** — recommended hosting
walkthrough (Managed Node.js Hosting).
**Deploy**
5. **[Deploy on Hostinger](./deployment-hostinger.md)** — Managed Node.js
Hosting walkthrough, the recommended way to ship the template.
6. **[Automations cron](./automations-and-cron.md)** — schedule the pending
executions drain so waits and delays fire.
7. **[Troubleshooting](./troubleshooting.md)** — the usual suspects.
**Reference**
7. **[Architecture](./architecture.md)** — stack, folder layout, request
lifecycle, where to change things.
8. **[Troubleshooting](./troubleshooting.md)** — the usual suspects.
## What you will need before starting
@ -34,7 +44,8 @@ docs walk you from a freshly forked repo to a production deploy.
- **Frontend / API** — Next.js 16 (App Router), React 19, Tailwind v4.
- **Database + auth + storage** — Supabase (Postgres + RLS).
- **WhatsApp transport** — Meta Cloud API (official WhatsApp Business API).
- **Encryption** — AES-256-CBC for per-user access tokens.
- **Encryption** — AES-256-GCM for per-user WhatsApp access + verify
tokens at rest. Legacy CBC-encrypted rows self-heal on next use.
- **Scheduler** — cron (or any pinger) that hits `GET /api/automations/cron`.
## Getting help

155
docs/architecture.md Normal file
View File

@ -0,0 +1,155 @@
# Architecture
One-page tour of the codebase — so you know where to look when you
want to change something in your fork.
## Stack
| Layer | Tool | Why |
|---|---|---|
| Rendering | **Next.js 16** (App Router) | Server components for data-fetch pages; client components where interactivity's needed. React 19. |
| UI | **Tailwind v4** + **shadcn/base-ui** primitives | Tailwind's zero-runtime styling, shadcn patterns for composable, dark-theme-first components. |
| Data + Auth | **Supabase** | Postgres with Row-Level Security, built-in email/password auth, Storage for avatars, Realtime for the inbox. |
| WhatsApp | **Meta Cloud API** | Official Business API. No third-party gateway. |
| Encryption | `node:crypto` AES-256-GCM | Per-user WhatsApp access + verify tokens at rest. |
| Scheduler | External HTTP pinger | Hits `GET /api/automations/cron` to drain Wait-step executions. |
No ORM, no GraphQL layer, no dedicated backend. The Next server-side
routes read and write Supabase directly via `@supabase/ssr`.
## Folder layout
```
wacrm/
├─ src/
│ ├─ app/ Next.js App Router routes
│ │ ├─ (auth)/ login, signup, forgot-password
│ │ ├─ (dashboard)/ authenticated UI
│ │ │ ├─ dashboard/ home / metrics / activity feed
│ │ │ ├─ inbox/ shared inbox
│ │ │ ├─ contacts/ contacts + tags
│ │ │ ├─ pipelines/ Kanban deals
│ │ │ ├─ broadcasts/ campaign list + builder
│ │ │ ├─ automations/ flow builder + logs
│ │ │ └─ settings/ Profile / WhatsApp / Templates / Tags
│ │ ├─ docs/ this documentation, rendered
│ │ ├─ api/ JSON endpoints (server-only)
│ │ │ ├─ whatsapp/webhook/ inbound from Meta
│ │ │ ├─ whatsapp/send/ outbound message
│ │ │ ├─ whatsapp/broadcast/ bulk dispatch
│ │ │ ├─ whatsapp/config/ settings read/write
│ │ │ ├─ whatsapp/templates/ template sync
│ │ │ ├─ whatsapp/media/[id]/ media relay
│ │ │ └─ automations/ engine + cron + CRUD
│ │ ├─ page.tsx marketing landing
│ │ ├─ layout.tsx root layout, metadata, icon
│ │ ├─ opengraph-image.tsx dynamic OG image
│ │ └─ icon.tsx dynamic favicon
│ ├─ components/
│ │ ├─ landing/ marketing page sections
│ │ ├─ dashboard/ dashboard widgets
│ │ ├─ inbox/ conversation list, thread, composer
│ │ ├─ contacts/, pipelines/, broadcasts/, automations/, settings/
│ │ ├─ docs/ docs viewer shell
│ │ ├─ seo/ JSON-LD helpers
│ │ ├─ layout/ header, sidebar, shell
│ │ └─ ui/ shadcn/base-ui primitives
│ ├─ hooks/ use-auth, use-realtime, etc.
│ ├─ lib/
│ │ ├─ supabase/ client, server, middleware factories
│ │ ├─ whatsapp/ Meta API client, encryption, phone utils
│ │ ├─ automations/ engine, steps, validation, templates
│ │ ├─ dashboard/ queries + date utils
│ │ ├─ docs/ markdown loader for /docs/
│ │ ├─ seo/ site config, FAQ data, structured data
│ │ └─ rate-limit.ts per-user token bucket
│ ├─ types/ shared TypeScript types
│ └─ middleware.ts session-refresh middleware
├─ supabase/
│ └─ migrations/ idempotent SQL files, run in order
├─ docs/ you are here
├─ public/ static assets
└─ next.config.ts Cache-Control + security headers
```
## Request lifecycle: inbound WhatsApp message
```
Meta Cloud API ──POST──▶ /api/whatsapp/webhook
│
├─ verifyMetaWebhookSignature (HMAC-SHA256)
│ └─ rejects 401 if META_APP_SECRET unset or wrong
│
├─ supabaseAdmin (service-role, lazy init)
│
├─ find contact by phone (create if new)
├─ find or create conversation
├─ insert message row
│
├─ fire runAutomationsForTrigger(...)
│ └─ matches automations on new_message / keyword
│
└─ 200 OK (Meta retries otherwise)
Realtime fan-out:
messages INSERT ──▶ supabase/realtime ──▶ inbox page subscription
└─ appends to thread without refetch
```
## Request lifecycle: outbound message from the inbox
```
Composer ──fetch──▶ /api/whatsapp/send
│
├─ createClient() — user-scoped Supabase client
├─ auth.getUser() — 401 if unauthed
├─ checkRateLimit('send:<uid>', 60/min)
│
├─ select whatsapp_config row (RLS-scoped)
├─ decrypt(access_token)
│ └─ self-heal: upgrade CBC → GCM (fire-and-forget)
│
├─ sendTextMessage / sendTemplateMessage (Meta API)
├─ retry with phone-number variants on "not allowed"
│
├─ insert message row with status = 'sent'
└─ 200 + wamid
UI:
Optimistic temp row shown immediately.
Real row arrives via Supabase realtime → replaces temp.
If 429 / 5xx: temp row flips to status = 'failed'.
```
## Security primitives
- **Row-Level Security** on every table. Users see only their own
rows via `auth.uid() = user_id`. Service-role bypasses RLS; only
server-only modules use it.
- **Encryption** (`src/lib/whatsapp/encryption.ts`) — AES-256-GCM for
WhatsApp tokens at rest. Backward-compatible decrypt for legacy
CBC rows; self-heals on first use.
- **Webhook signatures** (`src/lib/whatsapp/webhook-signature.ts`) —
HMAC-SHA256 with `META_APP_SECRET`. Fail-closed if the env var
isn't set.
- **Cron secret** — `GET /api/automations/cron` requires
`x-cron-secret: <AUTOMATION_CRON_SECRET>`. Constant-ish string
comparison.
- **Rate limiting** (`src/lib/rate-limit.ts`) — per-user fixed window,
process-local `Map`. Swap for Redis/Upstash at horizontal scale.
- **HTTP headers** (`next.config.ts`) — HSTS, nosniff,
X-Frame-Options: DENY, Referrer-Policy, Permissions-Policy, and a
report-only CSP.
## Where to change things
| Want to change… | Start here |
|---|---|
| Marketing copy / landing page | `src/app/page.tsx` + `src/components/landing/*` |
| Dashboard metrics | `src/lib/dashboard/queries.ts` + `src/components/dashboard/*` |
| Inbox behaviour | `src/app/(dashboard)/inbox/page.tsx` + `src/components/inbox/*` |
| Automation triggers / actions | `src/lib/automations/engine.ts`, `steps-tree.ts`, `meta-send.ts` |
| Add a DB column | new migration in `supabase/migrations/NNN_*.sql`, then update the matching `src/types/*.ts` |
| Change auth provider | `src/lib/supabase/{client,server}.ts` + `src/hooks/use-auth.tsx` + `middleware.ts` |
| Add a new API route | `src/app/api/<name>/route.ts`, pattern after existing routes |
| Tweak rate limits | `RATE_LIMITS` in `src/lib/rate-limit.ts` |

View File

@ -49,9 +49,14 @@ const ORDER: Record<string, { order: number; section: string; description: strin
section: 'Deploy',
description: 'Schedule the drain so Wait steps resume on time.',
},
troubleshooting: {
architecture: {
order: 7,
section: 'Operate',
section: 'Reference',
description: 'Stack, folder layout, request lifecycle.',
},
troubleshooting: {
order: 8,
section: 'Reference',
description: 'The usual suspects when something breaks.',
},
}