MCPcopy Index your code
hub / github.com/diendh/zca-bridge

github.com/diendh/zca-bridge @v1.0.4

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.0.4 ↗ · + Follow
677 symbols 1,943 edges 159 files 33 documented · 5%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

🇬🇧 English: README.en.md

Zalo-Chatwoot Bridge

Zalo-Chatwoot Bridge

CI License

zca-bridge là sidecar tự host để đồng bộ hội thoại Zalo hai chiều với Chatwoot. Bridge biến Zalo thành một inbox trong Chatwoot để nhân viên có thể nhận, gửi, ghi chú và theo dõi lịch sử hội thoại ngay trong helpdesk.

Mặc định tài liệu chính dùng tiếng Việt. Bản tiếng Anh nằm ở README.en.md.

Tóm tắt

  • Kênh Zalo OA: dùng API chính thức của Zalo, hỗ trợ OAuth, webhook, gửi/nhận tin, backfill và một số luồng xin thông tin khách hàng.
  • Kênh Zalo cá nhân: dùng zca-js và đăng nhập QR. Đây là API không chính thức, có rủi ro khóa hoặc cấm tài khoản.
  • Chatwoot: bridge nhận webhook outbound từ Chatwoot và đẩy inbound message vào Chatwoot qua API.
  • Hàng đợi bền: lưu job trong PostgreSQL, retry lỗi tạm thời và đưa lỗi vĩnh viễn vào dead-letter.
  • Media: archive file đính kèm cục bộ; file lớn có thể gửi qua link /media có token.

Node 24+ · TypeScript ESM · Fastify · PostgreSQL · Vitest · Docker

Dự án này độc lập, không thuộc sở hữu, không được tài trợ và không được xác nhận chính thức bởi Zalo, VNG, Chatwoot hoặc nhóm phát triển zca-js.

Cảnh báo quan trọng

Tài khoản Zalo cá nhân được kết nối qua zca-js, một thư viện không chính thức. Dùng API không chính thức có thể khiến tài khoản Zalo bị hạn chế, khóa hoặc cấm vĩnh viễn. Nên dùng tài khoản phụ, không dùng tài khoản kinh doanh chính hoặc tài khoản có dữ liệu nhạy cảm.

Kênh Zalo Official Account (OA) dùng API chính thức của Zalo nên không thuộc nhóm rủi ro từ zca-js. Xem thêm SECURITY.vi.md.

Tính năng

  • Nhắn tin hai chiều Zalo ↔ Chatwoot.
  • Hỗ trợ text, ảnh, file, ghi âm, video, sticker, vị trí và fallback cho nội dung chưa nhận diện.
  • Chống lặp/echo bằng message_map.
  • Archive media bền và phục vụ file lớn qua link token hóa.
  • Hỗ trợ quote/reply, reaction và thu hồi tin nhắn cho tài khoản cá nhân.
  • Hỗ trợ Zalo OA OAuth, webhook, gửi ảnh/file, nén ảnh OA quá lớn trước khi upload, backfill khi khởi động.
  • Tự động kết nối lại tài khoản cá nhân khi rớt phiên, với backoff lũy thừa (5s → 5 phút). Nếu phiên hết hạn thật, tài khoản chuyển sang trạng thái cần quét QR lại.
  • Quản lý proxy riêng cho từng tài khoản cá nhân (HTTP, HTTPS, SOCKS5, có thể kèm user/mật khẩu) để tách IP đầu ra cho mỗi tài khoản.
  • Admin dashboard tại /admin/ để tạo tài khoản admin, cấu hình Chatwoot/OA, thêm inbox, quản lý proxy, quét QR, xem/export log và ẩn cảnh báo đã xử lý.

Ảnh giao diện

Admin console là UI vận hành chính sau khi bridge chạy. Bộ ảnh dưới đây được chụp từ bản build local trên http://localhost:4100/admin/ với dữ liệu preview và không chứa secret thật.

Setup lần đầu Dashboard
Màn hình tạo tài khoản admin lần đầu Dashboard quản lý tài khoản Zalo
Proxy Cấu hình Logs Mobile
Màn hình quản lý proxy theo tài khoản Màn hình cấu hình Chatwoot, Zalo OA và webhook URL Màn hình nhật ký vận hành Dashboard admin trên viewport mobile

Kiến trúc

  • Inbound: Zalo cá nhân (zca-js) hoặc Zalo OA webhook/backfill → phân loại message → PostgreSQL job queue → worker → Chatwoot Application/Platform API.
  • Outbound: Chatwoot webhook → PostgreSQL job queue → worker → Zalo personal sender hoặc OA sender.
  • Media: attachment được tải về archive cục bộ; nếu vượt giới hạn upload Chatwoot thì bridge gửi link /media/:token.
  • Settings: cấu hình nhạy cảm trong admin UI được mã hóa bằng CREDENTIALS_KEY.

Module chính

  • src/zalo — adapter Zalo cá nhân, phân loại message, session, đăng nhập QR, supervisor tự kết nối lại và định tuyến proxy.
  • src/zalo-oa — OA OAuth, webhook, sender, backfill, verify chữ ký, nén ảnh.
  • src/chatwoot — client, Application API, webhook server, tạo inbox.
  • src/handlers — orchestration inbound/outbound, note lỗi, đồng bộ contact info.
  • src/workersrc/store — queue bền, repository (gồm proxy), migration.
  • src/admin — admin API, đăng nhập, settings, webhook info, proxy CRUD, log dashboard.
  • src/media — archive và link media token hóa.

Yêu cầu

  • Chatwoot đang chạy sẵn. Dự án này không đóng gói hoặc phân phối Chatwoot.
  • PostgreSQL riêng cho bridge, tách khỏi database của Chatwoot.
  • Node.js 24+ nếu chạy trực tiếp, hoặc Docker nếu chạy container.
  • PUBLIC_BASE_URL phải là URL bridge có thể truy cập từ bên ngoài khi dùng webhook/OA/iframe.

Cấu hình nhanh

Copy .env.example thành .env rồi điền giá trị thật:

cp .env.example .env

Các biến tối thiểu:

  • DATABASE_URL — PostgreSQL riêng của bridge.
  • CHATWOOT_BASE_URL — URL Chatwoot mà bridge truy cập được.
  • CREDENTIALS_KEY — khóa hex 32 byte để mã hóa secret, tạo bằng openssl rand -hex 32.
  • PUBLIC_BASE_URL — URL public của bridge, ví dụ https://bridge.example.com.

Biến nên cấu hình thêm:

  • CHATWOOT_API_ACCESS_TOKENCHATWOOT_ACCOUNT_ID — cần cho tạo inbox tự động, import tin tự gửi từ app Zalo và ghi private note khi outbound fail vĩnh viễn.
  • WEBHOOK_SECRET — thêm secret vào URL webhook Chatwoot.
  • MEDIA_ARCHIVE_ROOT, MEDIA_TOKEN_TTL_DAYS, CHATWOOT_MAX_ATTACHMENT_MB — kiểm soát lưu trữ media.
  • ZALO_OA_APP_ID, ZALO_OA_APP_SECRET, ZALO_OA_SECRET_KEY, ZALO_OA_OAUTH_REDIRECT — chỉ cần khi dùng OA.

Không commit .env, token thật, app secret, session Zalo hoặc database dump.

Chạy bằng Docker

Dùng image dựng sẵn

Tải docker-compose.full.yml, chuẩn bị .env, rồi chạy:

cp .env.example .env
docker compose -f docker-compose.full.yml up -d

Bridge chạy ở http://localhost:4000. File compose này chạy kèm PostgreSQL riêng cho bridge và pull image ghcr.io/diendh/zca-bridge:latest.

Build từ source

cp .env.example .env
docker compose -f docker-compose.example.yml up -d --build

File compose mẫu chỉ chạy bridge và PostgreSQL của bridge. Bạn vẫn cần Chatwoot riêng và trỏ CHATWOOT_BASE_URL tới Chatwoot đó.

Container đơn

docker run --env-file .env -p 4000:4000 ghcr.io/diendh/zca-bridge:latest

Khi chạy container đơn, DATABASE_URLCHATWOOT_BASE_URL phải trỏ tới dịch vụ truy cập được từ container.

Chạy trực tiếp

npm ci
npm run build
mkdir -p dist/store/migrations dist/admin/public
cp src/store/migrations/*.sql dist/store/migrations/
cp -r src/admin/public/. dist/admin/public/
npm start

npm run build chỉ biên dịch TypeScript; các lệnh cp đưa SQL migration và admin UI tĩnh vào dist giống Dockerfile. Migration tự chạy khi khởi động. Bridge lắng nghe trên $PORT, mặc định là 4000.

Chạy dev mode:

npm run dev

Thiết lập sau khi chạy

  1. Mở PUBLIC_BASE_URL/admin/ hoặc http://localhost:4000/admin/.
  2. Tạo tài khoản admin lần đầu. Mật khẩu tối thiểu 8 ký tự.
  3. Vào phần Settings để kiểm tra CHATWOOT_BASE_URL, Chatwoot account id/token và cấu hình OA nếu dùng.
  4. (Tùy chọn) Vào tab Proxy để khai báo proxy nếu muốn định tuyến tài khoản cá nhân qua IP riêng (xem mục Proxy và tự động kết nối lại).
  5. Tạo tài khoản bridge:
  6. Với Zalo cá nhân: thêm account, tạo hoặc gắn Chatwoot inbox, chọn proxy (nếu có), bấm đăng nhập và quét QR.
  7. Với Zalo OA: thêm OA account, bấm kết nối OA và hoàn tất OAuth.
  8. Copy webhook URL trong admin dashboard:
  9. Chatwoot webhook: dùng cho inbox Chatwoot tương ứng.
  10. Zalo OA webhook: dùng trong Zalo developer console nếu bật OA.
  11. Gửi thử một tin inbound và một tin outbound để kiểm tra mapping và chống echo.

Proxy và tự động kết nối lại

Hai tính năng này chỉ áp dụng cho tài khoản Zalo cá nhân (zca-js); kênh OA không bị ảnh hưởng.

Proxy theo từng tài khoản. Trong tab Proxy của admin, bạn khai báo danh sách proxy (giao thức http, https hoặc socks5, kèm host, port và tùy chọn user/mật khẩu). Mật khẩu proxy được mã hóa khi lưu và API không trả mật khẩu thô. Mỗi tài khoản cá nhân có thể gắn một proxy để toàn bộ lưu lượng websocket và HTTP của tài khoản đó đi qua IP riêng — hữu ích khi muốn tách IP cho mỗi tài khoản. Khi bạn đổi proxy của một tài khoản đang chạy, tài khoản hiển thị nhãn "Cần áp dụng proxy"; bấm "Áp dụng proxy" để bridge kết nối lại qua proxy mới. Xóa một proxy đang được dùng sẽ yêu cầu xác nhận và tự gỡ proxy khỏi các tài khoản liên quan.

Tự động kết nối lại. Khi phiên zca-js của một tài khoản cá nhân bị rớt, supervisor sẽ tự tạo lại phiên với backoff lũy thừa (5s, 15s, 45s, 2 phút, tối đa 5 phút). Trong lúc này tài khoản ở trạng thái "Đang kết nối lại…". Lỗi mạng hoặc lỗi proxy được coi là tạm thời và sẽ retry. Chỉ khi lỗi cho thấy phiên/đăng nhập hết hạn thật, tài khoản mới chuyển sang trạng thái cần quét QR lại thủ công.

Log vận hành. Dashboard hiển thị cảnh báo/lỗi gần đây; operator có thể đánh dấu cảnh báo là đã xử lý để ẩn khỏi summary. Tab Logs vẫn cho phép lọc, copy hoặc export JSON để debug.

Kiểm thử

npm test

Bộ test dùng Vitest. Nếu test liên quan sharp báo không load được module, chạy lại npm ci để cài dependency native/optional đúng nền tảng rồi chạy test lại.

Một số test repository cần TEST_DATABASE_URL; khi biến này không có, các test đó được skip có chủ ý. Kiểm tra local gần nhất trên code hiện tại: 71 test file pass, 7 test file skip có chủ ý; 440 test pass, 29 test skip. Xem ROADMAP.vi.md để biết lộ trình hiện tại.

Cách Codex được sử dụng

Codex được dùng như một trợ lý maintainer cho các việc có thể kiểm chứng bằng code và test:

  • rà soát luồng webhook, media, queue và admin có rủi ro bảo mật;
  • mở rộng test coverage cho worker, OA sender, retry/dead-letter và Chatwoot provisioning;
  • cập nhật tài liệu vận hành production, release notes và checklist bảo mật;
  • tự động hóa các việc bảo trì lặp lại như review PR, kiểm tra dependency và chuẩn bị release.

Bảo mật và kiểm tra lộ thông tin

  • Không đưa secret thật vào README, issue, PR, log hoặc screenshot.
  • CREDENTIALS_KEY phải là 64 ký tự hex và được giữ riêng theo môi trường.
  • Admin UI dùng tài khoản admin tạo lần đầu và cookie phiên ký bằng secret dẫn xuất từ CREDENTIALS_KEY.
  • Dùng HTTPS cho PUBLIC_BASE_URL, đặc biệt khi expose /admin/, /webhooks/*/media/*.
  • Dùng WEBHOOK_SECRET cho Chatwoot webhook và ZALO_OA_SECRET_KEY để xác thực webhook OA.
  • Trước khi commit tài liệu, nên scan bằng rg hoặc secret scanner để đảm bảo chỉ có placeholder/tên biến.

Tài liệu liên quan

Bên thứ ba

Dự án tích hợp với Chatwoot, Zalozca-js. Tất cả tên thương hiệu, logo, nhãn hiệu và tên sản phẩm thuộc về chủ sở hữu tương ứng; việc nhắc đến chỉ để mô tả tích hợp kỹ thuật.

Giấy phép

Copyright 2026 Tom.

Phát hành theo Apache License 2.0.

Extension points exported contracts — how you extend this code

AlertNotifier (Interface)
(no doc) [5 implementers]
src/alerting/types.ts
Sender (Interface)
(no doc) [4 implementers]
src/zalo/types.ts
MediaArchive (Interface)
(no doc) [2 implementers]
src/media/archive.ts
InfoRequestHook (Interface)
(no doc) [2 implementers]
src/handlers/inbound.ts
QrLoginService (Interface)
(no doc) [2 implementers]
src/admin/routes.ts
SettingsSource (Interface)
(no doc) [1 implementers]
src/config/resolve.ts
HistoryClient (Interface)
(no doc)
src/zalo-oa/oaBackfill.ts
EventLog (Interface)
(no doc)
src/logging/eventLog.ts

Core symbols most depended-on inside this repo

get
called by 123
src/store/proxyRepo.ts
$
called by 76
src/admin/public/admin.js
send
called by 72
src/alerting/types.ts
query
called by 60
src/store/logsRepo.ts
handle
called by 56
src/handlers/events.ts
push
called by 46
src/store/proxyRepo.ts
escapeHtml
called by 44
src/admin/public/admin.js
msg
called by 37
src/admin/public/admin.js

Shape

Function 273
Method 242
Interface 84
Class 76
Enum 2

Languages

TypeScript100%

Modules by API surface

src/admin/public/admin.js56 symbols
src/zalo/reconnectSupervisor.ts32 symbols
src/zalo/types.ts31 symbols
src/handlers/inbound.ts26 symbols
src/store/accountRepo.ts21 symbols
src/zalo-oa/sender.ts19 symbols
src/zalo/sessionManager.ts16 symbols
src/zalo/zcaAdapter.ts15 symbols
src/main.ts15 symbols
src/handlers/outbound.ts15 symbols
src/store/proxyRepo.ts14 symbols
src/extension/registry.ts14 symbols

Datastores touched

zcaDatabase · 1 repos

For agents

$ claude mcp add zca-bridge \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page