# WhatsApp Mirror — Full-Stack Real-Time Bridge A production-grade, highly-responsive WhatsApp Mirror application. This system allows you to remotely view and manage a WhatsApp account on a dark-themed Flutter mobile app (iOS + Android) by bridging commands and real-time events over a standalone WebSockets Node.js bridge server. --- ## 🏗️ Architecture ``` 📱 Mobile Client (Flutter) ⚡ [WebSockets Protocol] ⚡ 🖥️ standalone Backend Server (Node.js) ⚙️ [Puppeteer] ⚙️ 🟩 WhatsApp Web (LocalAuth Session) ``` --- ## 📂 Project Structure ``` whatsapp-app/ ├── README.md ← Full system configuration & documentation ├── whatsapp_bridge/ ← standalone Node.js server │ ├── package.json ← Package dependencies (whatsapp-web.js, ws, qrcode, express) │ ├── server.js ← Bridge server entrypoint (Port 3025) │ └── .wwebjs_auth/ ← Session persistence auth cache (git-ignored) └── whatsapp_app/ ← Dark-themed Flutter Client ├── pubspec.yaml ← Flutter configuration and plugins └── lib/ ├── main.dart ← Flutter app entry point (GetX service initializer) ├── config/ │ └── app_config.dart ← Server host & port variables ├── theme/ │ └── app_theme.dart ← WhatsApp-style immersive Dark Theme ├── services/ │ └── whatsapp_service.dart ← Websocket request/response manager ├── models/ │ ├── conversation_model.dart │ └── message_model.dart ├── controllers/ │ ├── conversations_controller.dart │ └── chat_controller.dart ├── screens/ │ ├── conversations_screen.dart │ ├── chat_screen.dart │ └── qr_screen.dart └── widgets/ ├── conversation_tile.dart └── message_bubble.dart ``` --- ## ⚡ WebSockets Protocol Specification To handle asynchronous requests and map them back to specific UI components or triggers, a strict **Request-Response ID matching mechanism** is implemented. ### 1. Client-to-Server Requests Every request from the Flutter client MUST contain a unique `requestId`. The server replies with the exact same `requestId`. #### A. Ping Check (`ping`) * **Request:** ```json { "type": "ping", "requestId": "1" } ``` * **Response:** ```json { "type": "pong", "ready": true, "requestId": "1" } ``` #### B. Get Conversation List (`get_conversations`) * **Request:** ```json { "type": "get_conversations", "limit": 50, "offset": 0, "requestId": "2" } ``` * **Response:** ```json { "type": "conversations", "data": [ { "id": "1234567890@c.us", "name": "Jane Doe", "isGroup": false, "unreadCount": 2, "avatar": "https://pps.whatsapp.net/v/...", "lastMessage": { "body": "Hello there!", "timestamp": 1716035000, "fromMe": false, "hasMedia": false }, "timestamp": 1716035000, "pinned": false, "isMuted": false } ], "total": 1, "requestId": "2" } ``` #### C. Get Message History (`get_messages`) * **Request:** ```json { "type": "get_messages", "chatId": "1234567890@c.us", "limit": 50, "requestId": "3" } ``` * **Response:** ```json { "type": "messages", "chatId": "1234567890@c.us", "data": [ { "id": "true_1234567890@c.us_ABC123", "body": "Hello there!", "fromMe": false, "timestamp": 1716035000, "type": "chat", "hasMedia": false, "isForwarded": false, "author": null, "ack": 4 } ], "requestId": "3" } ``` #### D. Send Message (`send_message`) * **Request:** ```json { "type": "send_message", "chatId": "1234567890@c.us", "text": "Hello!", "requestId": "4" } ``` * **Response:** ```json { "type": "message_sent", "chatId": "1234567890@c.us", "data": { "id": "true_1234567890@c.us_XYZ987", "body": "Hello!", "fromMe": true, "timestamp": 1716035005, "type": "chat", "hasMedia": false, "isForwarded": false, "author": null, "ack": 1 }, "requestId": "4" } ``` #### E. Mark Chat as Read (`mark_read`) * **Request:** ```json { "type": "mark_read", "chatId": "1234567890@c.us", "requestId": "5" } ``` * **Response:** ```json { "type": "marked_read", "chatId": "1234567890@c.us", "requestId": "5" } ``` #### F. Search Conversations (`search_conversations`) * **Request:** ```json { "type": "search_conversations", "query": "Jane", "requestId": "6" } ``` * **Response:** ```json { "type": "conversations", "data": [...], "search": true, "requestId": "6" } ``` --- ### 2. Server-to-Client Push Events The server broadcasts live events to all connected clients immediately. * **QR Code Broadcast:** ```json { "type": "qr", "qr": "data:image/png;base64,iVBORw0KGgo..." } ``` * **Authenticated:** ```json { "type": "authenticated" } ``` * **Client Ready:** ```json { "type": "ready" } ``` * **Status Updates:** ```json { "type": "status", "ready": true } ``` * **Client Disconnected:** ```json { "type": "disconnected", "reason": "Session expired or logged out" } ``` * **New Incoming Message:** ```json { "type": "new_message", "chatId": "1234567890@c.us", "data": { "id": "false_1234567890@c.us_DEF456", "body": "Live incoming text!", "fromMe": false, "timestamp": 1716035100, "type": "chat", "hasMedia": false, "isForwarded": false, "author": null, "ack": 2 } } ``` * **Message Delivery / Read Receipt (`message_ack`):** ```json { "type": "message_ack", "messageId": "true_1234567890@c.us_XYZ987", "chatId": "1234567890@c.us", "ack": 4 } ``` *(Ack codes: 0 = Error/None, 1 = Pending, 2 = Sent, 3 = Delivered, 4 = Read)* --- ## 🚀 Quick Setup & Installation ### 1. Server Setup (`whatsapp_bridge/`) Navigate into the backend project, install dependencies, and start the standalone service: ```bash cd whatsapp_bridge npm install node server.js ``` The server will boot up and bind to **Port 3025**. It will automatically print: `[SERVER] Standalone WhatsApp Bridge running on port 3025` ### 2. Flutter App Setup (`whatsapp_app/`) First, ensure that you have created the Flutter project structure using standard platform templates: ```bash # Run this inside the workspace directory flutter create whatsapp_app ``` Then, copy all files in `whatsapp_app/` into the newly created project folder. Open the folder and install Dart packages: ```bash cd whatsapp_app flutter pub get ``` #### Run on Simulator / Device ```bash flutter run ``` --- ## 🌐 Production Deployment (CloudPanel Server) Since this backend server acts as a standalone daemon and does not conflict with existing apps, it operates exclusively on **Port 3025**. ### 1. Create Node.js Site in CloudPanel 1. Navigate to **CloudPanel Admin Portal** -> **Sites** -> **Add Site** -> **Create a Node.js Application**. 2. Set Domain Name: `mywhatsappapp.interlap.com` (or your subdomain). 3. Set Port: `3025`. 4. Set Entry Point: `server.js`. 5. Select Node.js Version: `18+ LTS` (or Node 20 LTS). ### 2. Bypass Nginx Reverse Proxy By default, CloudPanel routes external traffic from Port 80/443 through an Nginx reverse proxy. For our standalone WebSockets configuration, we want direct access. Make sure to open **Port 3025** on your firewall (e.g. AWS Security Group or UFW): ```bash sudo ufw allow 3025/tcp ``` This isolates Node.js directly on port 3025. ### 3. Keep Server Alive with PM2 We highly recommend running your Node.js application inside PM2 to ensure it auto-reconnects, logs diagnostic crashes, and recovers seamlessly: ```bash # Install PM2 globally npm install -g pm2 # Start the bridge server pm2 start server.js --name "whatsapp-bridge" # Persist server launch on reboot pm2 save pm2 startup ``` --- ## 🔒 Security & Performance Features * **Session Persistence:** Configured using `whatsapp-web.js`'s built-in `LocalAuth` session strategies, meaning that once you scan the QR code once, you will not have to scan it again unless explicitly logged out. * **Resilience & Crash Prevention:** The server wraps critical events in structured `try/catch` clauses and binds events to `process.on('uncaughtException')`, protecting the server from unexpected Puppeteer execution crashes. * **Auto-Reconnection:** If WhatsApp Web loses connection or the user logs out, the server attempts re-initialization automatically after a 5-second backoff delay. * **Dark Mode Design:** Beautiful custom-tailored dark theme matching official WhatsApp guidelines (`#111B21`, `#1F2C34`, `#00A884`) with full custom ripple alerts, status badges, and tick indicators.