From 7cd0f46369191f4bb7b379ceb6c40a3349f2d833 Mon Sep 17 00:00:00 2001 From: Arnas Donauskas Date: Mon, 20 Apr 2026 21:43:42 +0300 Subject: [PATCH] docs: README polish + architecture + alt hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finishes the template-readiness sequence. All docs — nothing runtime. README.md Rewrites the landing page of the repo to actually sell what it is to someone finding the repo cold on GitHub search. - Shields.io badges: MIT, CI status, Next.js 16, Supabase, GitHub stars. - "What you get out of the box" bullet list — inbox, contacts, pipelines, broadcasts, automations, dashboard, account management — so the feature surface is visible before the reader scrolls. - "Why fork this?" section: full ownership, full customisation, zero ops to start, real security primitives. Frames WaCRM as a template, not an SDK. - Docs section reorganised into Setup / Deploy / Reference to match the in-app /docs viewer and docs/README.md. - Contributing section rewrites the stale "Pull requests welcome" line to match the fork-first framing from PR #73. docs/architecture.md (new) One-page tour for forkers: stack table, full folder layout, two ASCII request-lifecycle diagrams (inbound webhook + outbound send), security primitives rundown, and a "where to change things" index that maps common customisation tasks to source files. This is the thing I wish I'd had the first time I opened a forked template. docs/deployment-alternatives.md (new) Short blocks for Vercel, Railway, raw VPS, and Docker. Each covers env-var setup, cron strategy (Vercel cron without custom headers is a footgun worth calling out), and trade-offs. Keeps Hostinger as the primary recommendation — this page is for people whose constraints rule it out. docs/README.md - Reading order grouped as Setup / Deploy / Reference, adding the two new pages. - Stack-at-a-glance updated: encryption is AES-256-GCM, not CBC (the PR #71 change that earlier docs hadn't caught up to). src/lib/docs/content.ts Registers the two new markdown files in the ORDER map so they appear in the in-app /docs sidebar under the Reference section, with prev/next pagination wiring. Verified: /docs, /docs/architecture, /docs/deployment-alternatives, and /docs/getting-started all return 200 from the dev server. Sidebar shows 9 pages grouped Setup / Deploy / Reference with the new entries. Typecheck clean. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 79 +++++++++++++--- docs/README.md | 19 +++- docs/architecture.md | 160 ++++++++++++++++++++++++++++++++ docs/deployment-alternatives.md | 145 +++++++++++++++++++++++++++++ src/lib/docs/content.ts | 16 +++- 5 files changed, 402 insertions(+), 17 deletions(-) create mode 100644 docs/architecture.md create mode 100644 docs/deployment-alternatives.md diff --git a/README.md b/README.md index 06f5d10..1be48d1 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,100 @@ # 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**: +[![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//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 . -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 on Hostinger](./docs/deployment-hostinger.md) + +**Deploy** +- [Deploy on Hostinger](./docs/deployment-hostinger.md) *(recommended)* +- [Alternative hosts](./docs/deployment-alternatives.md) — Vercel, + Railway, raw VPS - [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. diff --git a/docs/README.md b/docs/README.md index ef60bc5..d767dd5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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,21 @@ 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. + +**Deploy** + 5. **[Deploy on Hostinger](./deployment-hostinger.md)** — recommended hosting walkthrough (Managed Node.js Hosting). -6. **[Automations cron](./automations-and-cron.md)** — schedule the pending +6. **[Alternative hosts](./deployment-alternatives.md)** — Vercel, Railway, + raw VPS, Docker / Kubernetes. +7. **[Automations cron](./automations-and-cron.md)** — schedule the pending executions drain so waits and delays fire. -7. **[Troubleshooting](./troubleshooting.md)** — the usual suspects. + +**Reference** + +8. **[Architecture](./architecture.md)** — stack, folder layout, request + lifecycle, where to change things. +9. **[Troubleshooting](./troubleshooting.md)** — the usual suspects. ## What you will need before starting @@ -34,7 +46,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 diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..e6e4599 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,160 @@ +# 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:', 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: `. 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//route.ts`, pattern after existing routes | +| Tweak rate limits | `RATE_LIMITS` in `src/lib/rate-limit.ts` | + +## Next step + +- [Alternative hosts](./deployment-alternatives.md) — Vercel, Railway, + raw VPS. diff --git a/docs/deployment-alternatives.md b/docs/deployment-alternatives.md new file mode 100644 index 0000000..a51ec4d --- /dev/null +++ b/docs/deployment-alternatives.md @@ -0,0 +1,145 @@ +# Alternative hosts + +[Hostinger Managed Node.js Hosting](./deployment-hostinger.md) is the +recommended default — zero-ops, Git deploy from hPanel, AutoSSL. But +WaCRM is a plain Next.js app; any host that runs long-running +Node.js 20+ will work. This page is a quick sketch for the common +alternatives. Each block assumes you've already completed +[Supabase setup](./supabase-setup.md) and [WhatsApp setup](./whatsapp-setup.md). + +## Vercel + +1. Push your fork to GitHub. +2. → **Import Git Repository** → pick the fork. +3. **Environment Variables**: paste every value from + [`environment-variables.md`](./environment-variables.md). Mark the + `NEXT_PUBLIC_*` ones as available at build time (Vercel does this + automatically). +4. Deploy. Vercel handles HTTPS, preview URLs, and edge routing. +5. Attach your domain (Vercel → Project → Settings → Domains). +6. Update Meta → WhatsApp → Configuration → Webhook to + `https:///api/whatsapp/webhook`. + +**Automations cron**: Vercel has built-in cron (Pro plan). Add to +[`vercel.json`](https://vercel.com/docs/cron-jobs): + +```json +{ + "crons": [ + { "path": "/api/automations/cron", "schedule": "* * * * *" } + ] +} +``` + +Vercel cron hits the endpoint server-to-server but **doesn't let you +send custom headers**. The simplest path is to skip `x-cron-secret` +and gate on Vercel's `x-vercel-cron` header instead: + +```ts +// adjust src/app/api/automations/cron/route.ts +if (request.headers.get('x-vercel-cron') !== '1' && + request.headers.get('x-cron-secret') !== process.env.AUTOMATION_CRON_SECRET) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); +} +``` + +**Trade-offs**: + +- ✅ Fastest onboarding, preview URLs per PR, global CDN. +- ⚠️ Serverless functions have cold starts — inbound webhooks from + Meta may occasionally take an extra second on the first hit. +- ⚠️ The in-memory [rate limiter](./architecture.md#security-primitives) + is per-function-instance, so effective limits multiply across + regions. Swap for Upstash Redis if that matters. + +## Railway + +1. → **Deploy from GitHub repo** → pick your + fork. +2. Railway detects Next.js automatically. Default build + + start commands are correct. +3. **Variables**: add every value from + [`environment-variables.md`](./environment-variables.md). +4. **Generate Domain** under Settings, or attach a custom domain. +5. Update Meta webhook URL, same as above. + +**Automations cron**: Railway has built-in cron. Services → **+ Cron**: + +- Schedule: `* * * * *` +- Command: + ``` + curl -s -H "x-cron-secret: $AUTOMATION_CRON_SECRET" https:///api/automations/cron + ``` + +**Trade-offs**: + +- ✅ Long-running processes (no cold starts), built-in Redis if you + need one, usage-based pricing. +- ⚠️ No preview deploys per PR out of the box (paid feature). + +## Raw VPS (any provider) + +Any Ubuntu 22/24 or Debian 12 VPS with 2 GB+ RAM works. The +Hostinger VPS walkthrough in +[`deployment-hostinger.md`](./deployment-hostinger.md) (at the bottom, +"When to reach for a VPS instead") gives the exact commands — PM2 + +nginx + certbot. Same commands apply on DigitalOcean, Hetzner, Linode, +AWS Lightsail, OVH, etc. Only the billing dashboard changes. + +Systemd cron is the obvious scheduler: + +```cron +* * * * * curl -s -H "x-cron-secret: $AUTOMATION_CRON_SECRET" https:///api/automations/cron > /dev/null +``` + +**Trade-offs**: + +- ✅ Most control (custom binaries, private networking, your own + observability stack), cheapest at scale. +- ⚠️ You are the ops team. Patching, TLS renewal, log rotation, backup + to S3 — all you. + +## Self-hosted Docker / Kubernetes + +No official image is shipped. If you containerise, the app is a +plain Next.js standalone build: + +```Dockerfile +FROM node:20-alpine AS deps +WORKDIR /app +COPY package*.json ./ +RUN npm ci + +FROM node:20-alpine AS build +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN npm run build + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +COPY --from=build /app/.next ./.next +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/package.json ./ +COPY --from=build /app/public ./public +EXPOSE 3000 +CMD ["npm", "start"] +``` + +Pass env vars via `--env-file` or a Kubernetes Secret. The cron ping +stays an external concern (K8s CronJob, a sidecar, or an external +uptime monitor). + +## Which should I pick? + +| If you want… | Pick | +|---|---| +| Zero-ops, lowest-friction deploy | **Hostinger Managed Node.js** | +| Preview URLs per PR, global edge | **Vercel** | +| Long-running processes, simple cron | **Railway** | +| Full control, cheapest at scale, you're OK on ops | **VPS / self-host** | +| Air-gapped or multi-region with your own K8s | **Docker / Kubernetes** | + +None of these are one-way doors — they all run the same fork of the +same Next.js app. diff --git a/src/lib/docs/content.ts b/src/lib/docs/content.ts index e8d52f3..3242e0f 100644 --- a/src/lib/docs/content.ts +++ b/src/lib/docs/content.ts @@ -44,14 +44,24 @@ const ORDER: Record