# Architecture Reference

## Express + Socket.IO Server (`backend/src/index.js`)

### Middleware Stack (order matters)

```javascript
app.use(cors())
app.use(express.json({ limit: '50mb' }))   // MUST be 50mb+ for base64 images
app.use('/api', apiRoutes)                  // REST API first
app.use(express.static(dashboardPath))       // Static dashboard
app.get('*', spaFallback)                   // SPA catch-all LAST
```

### Socket.IO Setup

```javascript
const io = new Server(server, {
  cors: { origin: '*', methods: ['GET', 'POST', 'PUT', 'DELETE'] }
})

io.on('connection', (socket) => {
  socket.emit('status', getStatus())  // Send current state on connect
})
```

### Event Flow: Server → Dashboard

| Event | Payload | When |
|-------|---------|------|
| `status` | `{ status, phone? }` | Connection changes |
| `qr` | `string` (QR data) | WhatsApp needs scan |
| `ready` | `{ status: 'connected', phone }` | WhatsApp ready |
| `disconnected` | `{}` | WhatsApp lost |
| `state_change` | `{ phone, state }` | User state changes |
| `generation_complete` | `{ phone, count }` | AI task done |

### SPA Fallback Pattern

```javascript
app.get('*', (req, res) => {
  if (!req.path.startsWith('/api') && !req.path.startsWith('/socket.io')) {
    res.sendFile(join(dashboardPath, 'index.html'))
  }
})
```

### Dashboard Path Resolution

```javascript
// Production: compiled React in dashboard/dist/
const dashboardPath = join(dirname(fileURLToPath(import.meta.url)), '../../dashboard/dist')
```

### Environment Variables

```env
PORT=3002              # Server port
GEMINI_API_KEY=AIza... # AI provider key (fallback/admin key)
```

### Package.json Essentials

```json
{
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "dev": "node --watch src/index.js"
  },
  "dependencies": {
    "express": "^4.21",
    "cors": "^2.8",
    "socket.io": "^4.8",
    "whatsapp-web.js": "^1.26",
    "better-sqlite3": "^11",
    "@google/genai": "^0.10",
    "dotenv": "^16"
  }
}
```
