# EKS Support Relay Integration hub between **Chatwoot** (support inbox), **Flowise** (AI agent), **WooCommerce** and the **WordPress `eksrelay/v1` Store API** for the EasyKlima support system. Version 2 is a Node.js/TypeScript rewrite of the original PHP relay. The PHP implementation remains reachable in git history — see [Migration](#migration-from-the-php-relay). ## What it does ``` customer e-mail │ ▼ Chatwoot ──webhook message_created──▶ EKS Relay ──▶ Flowise (Tool Agent) ▲ │ ▲ │ │ │ └── /tools/* ───┘ └────────── outgoing reply ───────────┘ or ticket handoff ``` 1. Chatwoot posts `message_created` to `POST /webhooks/chatwoot`. 2. The relay validates, deduplicates and queues the event, then answers `202`. 3. A worker checks ticket mode, runs the spam gate, and calls Flowise. 4. The agent may call back into `/tools/*` for live shop data. 5. The answer is posted back to Chatwoot, or the conversation is handed off to a human with a ticket number. ## Endpoints | Method | Path | Auth | Purpose | |---|---|---|---| | GET | `/health` | — | Liveness. No dependencies touched. | | GET | `/ready` | — | Readiness plus per-dependency status. No secrets. | | POST | `/webhooks/chatwoot` | — | Chatwoot `message_created` intake. | | POST | `/tools/get_order_data` | Bearer | Order lookup with e-mail ownership check. | | POST | `/tools/get_product_data` | Bearer | Product by id / SKU / search. | | POST | `/tools/get_shipping_data` | Bearer | Zones, or per-zone methods and costs. | | POST | `/tools/get_payment_methods` | Bearer | Enabled payment gateways. | | POST | `/tools/get_product_compatibility` | Bearer | Car ↔ product compatibility. | | POST | `/tools/get_car_data` | Bearer | Brands / models / engines. | | POST | `/tools/new_ticket` | Bearer | Create (or reuse) a ticket, hand off. | | GET | `/admin/events` | Bearer (admin) | Recent audit trail. | | GET | `/admin/jobs` | Bearer (admin) | Queue stats and dead jobs. | | GET | `/admin/messages` | Bearer (admin) | Processed-message records. | `/tools/*` uses `Authorization: Bearer `. `/admin/*` uses `Authorization: Bearer `; when `ADMIN_TOKEN` is empty the admin routes are **closed**, not open. ## Architecture ``` src/ config.ts zod-validated environment contract logger.ts JSON logger + secret/PII redactor errors.ts RelayError (http code + stable error code) clients/ httpClient.ts fetch wrapper: timeouts, safe labels chatwootClient.ts conversations, labels, attributes, messages flowiseClient.ts prediction call + response normalisation wooClient.ts WooCommerce REST v3, WPML search fallbacks wpStoreClient.ts eksrelay/v1 mu-plugin endpoints domain/ messageNormalizer.ts Chatwoot payload → SupportMessageEvent spamGate.ts bounce / autoreply / newsletter filtering ticketService.ts ticket numbering + idempotent handoff conversationPipeline.ts the single decision path per message storeLocales.ts language → currency with shop-aware fallback formatters.ts curated order/product views for the agent queue/ jobQueue.ts DB-backed queue with retry/backoff worker.ts in-process poller store/ db.ts Prisma client idempotencyStore.ts ProcessedMessage claim/status auditLog.ts redacted audit events http/ express app, routes, middleware ``` Channel adapters are the extension point: any new channel only has to produce a `SupportMessageEvent`, and the whole AI/ticket/spam pipeline applies unchanged. ## Persistence SQLite via Prisma (`prisma/schema.prisma`): - `ProcessedMessage` — idempotency guard, unique on `(source, messageId)`. - `Job` — queued work with `attempts`, `lastError` (redacted), backoff. - `Ticket` — one row per conversation, unique on `conversationId`. - `AuditEvent` — redacted decision trail. ### Browsing the database ```bash # On the server: start Prisma Studio bound to localhost only, # then reach it through an SSH tunnel. ssh -i -L 5555:127.0.0.1:5555 ubuntu@ docker compose -f docker-compose.yml -f docker-compose.studio.yml up -d studio # open http://127.0.0.1:5555 — stop it again when done docker compose -f docker-compose.yml -f docker-compose.studio.yml down ``` Locally: `npm run db:studio`. Prisma Studio has full read/write access and **must never be exposed publicly**. ## Local development ```bash npm install cp .env.example .env # fill in real values, chmod 600 export DATABASE_URL="file:../data/eks_relay.db" npx prisma migrate deploy npm run dev # tsx watch ``` ```bash npm run lint # tsc type-check over src, tests and scripts npm test # node:test suite npm run build # compile to dist/ npm run events # CLI audit-trail viewer ``` ## Docker ```bash docker compose config # validate docker compose up -d --build docker compose ps docker compose logs -f relay ``` The repository is bind-mounted into the container and the entrypoint installs, generates and migrates on start, so **a code change needs a restart, not a rebuild**: ```bash git pull && docker compose restart relay ``` With `RELAY_MODE=dev` the container runs `tsx watch`, so edits are picked up without even a restart. `RELAY_MODE=prod` compiles once and runs `dist/`. A rebuild is only needed when the base image or system packages change. ## Configuration See [`.env.example`](.env.example) for every variable with inline notes. Boot fails loudly with the offending variable names (never their values) when the environment is incomplete. ## Logging Structured JSON on stdout/stderr. Secrets (tokens, bearer headers, Woo consumer keys, credentialed query strings) are **always** redacted. PII (customer e-mail, names, message bodies) is redacted unless `LOG_PII=true`, which is meant for temporary debugging only. ## Documentation - [`docs/DEPLOY.md`](docs/DEPLOY.md) — server layout, Traefik, Cloudflare, updates. - [`docs/SMOKE_TEST.md`](docs/SMOKE_TEST.md) — the verification runbook. - [`docs/MIGRATION.md`](docs/MIGRATION.md) — PHP → Node differences and fallback. ## Migration from the PHP relay The PHP implementation lived in `src/` and `public/` until the v2 rewrite. It is preserved at the `php-legacy` tag and stays deployed on the EasyKlima web server as a fallback until the Node relay has passed a real e-mail test. See [`docs/MIGRATION.md`](docs/MIGRATION.md). ## Flowise tools `flowise-tools/*.json` holds the exported custom-tool definitions. The relay's response contracts are built to match them exactly — changing a response shape means updating the corresponding tool in Flowise as well. ## WordPress plugin `wp-plugins/eksrelay_api.php` is the mu-plugin providing `eksrelay/v1`. It is deployed to the shop's WordPress, not to this service.