Initial commit with Flutter and Node.js code
This commit is contained in:
307
README.md
Normal file
307
README.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user