feat: add Zalo Personal channel integration via zca-js#32
Conversation
QR code login, credential persistence, WebSocket listener, message
send/receive, customer identity resolution, and dashboard card with
real-time status polling. Includes credential sanitization in API
responses and reconnect logic with backoff.
hsperus
left a comment
There was a problem hiding this comment.
Hey, I went through the entire PR in detail. Overall it's a well structured feature, things like exponential backoff reconnect, credential sanitization and cache eviction are nicely done. But there are a few important things that need to be addressed before merge.
The most critical issue is credential storage. The PR description mentions "encrypted" but looking at the code, imei, cookie and userAgent are written to the database as plain text. I couldn't find any encryption in the saveCredentials function. Anyone with database access can read these credentials. At minimum something like AES-256-GCM should be applied here, this is a blocker from a security standpoint.
The Prisma schema has been updated with the ZALO_PERSONAL enum and zalo field but there's no migration file in the PR. This will cause issues during production deploy. Please run prisma migrate dev and include the migration file.
The scope of the PR is also quite broad. On top of the Zalo integration there are data.data transformations across 10+ dashboard pages. These fixes aren't directly related to Zalo and they make the review harder to follow. I'd suggest moving those into a separate PR, it makes review easier and keeps rollbacks clean if anything goes wrong.
On the architecture side, there are global mutable variables like zaloApi, qrImageBase64 and connectionStatus defined at module level. This works fine with a single Node.js process but in serverless or multi instance environments they'll reset on every cold start. Not a blocker right now but this limitation should be documented and ideally moved to something like Redis down the road.
The startZaloQRLogin function wraps loginQR (which already returns a Promise) inside a new Promise constructor. If the callback never fires the Promise will hang forever. A timeout mechanism should be added there.
The sanitizeConfig function is duplicated across three files (channels/[type]/route.ts, channels/route.ts, channels/zalo-personal/route.ts). This could be extracted into a single shared utility.
Lastly the PR is opened from main to main. Using a feature branch like feat/zalo-personal would be better practice going forward.
In short: credential encryption and the migration file are must haves, and splitting out the data.data fixes into their own PR would really help. Happy to take another look once those are sorted, nice work overall.
…sensitive fields in API responses
… channels, and improved conversation handling
…ment - Add connection management for Zalo API with QR login and saved credentials. - Implement message handling for incoming messages, including self-messages and AI responses. - Introduce filtering mechanism for allowlist/denylist based on sender and thread IDs. - Enrich customer profiles with data from Zalo, including display names and phone numbers. - Create helper functions for thread metadata updates and user/group name resolution. - Set up a listener for Zalo API events to manage connection status and message processing. - Add validation schemas for Zalo actions and configuration updates. - Implement caching for user and group information to optimize API calls.
What does this PR do?
Adds Zalo Personal as a new messaging channel, allowing agents to receive and respond to Zalo messages directly from
the Owly dashboard. This is the 6th supported channel alongside WhatsApp, Email, Phone, SMS, and Telegram.
Changes
Zalo Personal channel (src/lib/channels/zalo-personal.ts)
API routes
Dashboard
Schema & config
Test plan
Type of change
Checklist
npm run buildpasses without errors