mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 17:29:45 +00:00
Per Meta's brand-use guidance for third-party products, the registered
trademark symbol (®) should appear on the FIRST mention of "WhatsApp"
on each user-facing page. Subsequent mentions on the same page are
left plain.
Surfaces touched (one-per-page first mention):
- Landing hero badge ("Built on the official WhatsApp® Business API").
- src/lib/seo/site-config.ts: SITE_TAGLINE and SITE_DESCRIPTION
(rendered into <title> and <meta description> on every page).
- src/app/opengraph-image.tsx: hero text in social-share image.
- Settings, Inbox connection banner, Automations page subtitle.
- README.md tagline.
- All public docs pages (docs/README.md, docs/whatsapp-setup.md,
docs/getting-started.md, docs/supabase-setup.md,
docs/environment-variables.md, docs/deployment-hostinger.md,
docs/automations-and-cron.md, docs/architecture.md,
docs/troubleshooting.md).
Out of scope: SITE_KEYWORDS (the meta-keywords array isn't
user-rendered text — putting ® on every keyword would look broken in
the meta tag).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
156 lines
8.1 KiB
Markdown
156 lines
8.1 KiB
Markdown
# 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` |
|