feat(whatsapp): auto-provision Docker bridge per tenant
- Add Dockerfile.whatsapp-bridge with Baileys + env var support - Modify whatsapp-bridge-server.js to accept PORT, TENANT_ID, WEBHOOK_BASE - Add internal_bp.py with endpoints to provision/destroy bridges via Docker - Register internal_bp in app.py - Each tenant gets isolated container, port, and volume
This commit is contained in:
@@ -6,19 +6,23 @@ const pino = require('pino');
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
const PORT = 21465;
|
||||
const API_KEY = 'nexus-wpp-secret-2026';
|
||||
const WEBHOOK_URL = 'http://localhost:5001/pos/api/whatsapp/webhook';
|
||||
// Configurable via environment variables
|
||||
const PORT = process.env.PORT || 21465;
|
||||
const API_KEY = process.env.API_KEY || 'nexus-wpp-secret-2026';
|
||||
const TENANT_ID = process.env.TENANT_ID || '';
|
||||
const WEBHOOK_BASE = process.env.WEBHOOK_BASE || 'http://localhost:5001/pos/api/whatsapp/webhook';
|
||||
const WEBHOOK_URL = TENANT_ID ? `${WEBHOOK_BASE}?tenant_id=${TENANT_ID}` : WEBHOOK_BASE;
|
||||
const AUTH_DIR = process.env.AUTH_DIR || '/app/auth';
|
||||
|
||||
let sock = null;
|
||||
let qrCode = null;
|
||||
let connectionState = 'disconnected';
|
||||
const logger = pino({ level: 'warn' });
|
||||
const logger = pino({ level: process.env.LOG_LEVEL || 'warn' });
|
||||
|
||||
async function connectWhatsApp() {
|
||||
const { state, saveCreds } = await useMultiFileAuthState('/opt/whatsapp-bridge/auth');
|
||||
const { state, saveCreds } = await useMultiFileAuthState(AUTH_DIR);
|
||||
const { version } = await fetchLatestBaileysVersion();
|
||||
console.log('Connecting with Baileys v' + version.join('.'));
|
||||
console.log(`[Tenant ${TENANT_ID}] Connecting with Baileys v` + version.join('.'));
|
||||
connectionState = 'connecting';
|
||||
|
||||
sock = makeWASocket({ version, auth: state, logger, printQRInTerminal: true, browser: ['Nexus POS', 'Chrome', '120.0'] });
|
||||
@@ -29,7 +33,7 @@ async function connectWhatsApp() {
|
||||
if (qr) {
|
||||
qrCode = await QRCode.toDataURL(qr);
|
||||
connectionState = 'qr';
|
||||
console.log('QR code generated!');
|
||||
console.log(`[Tenant ${TENANT_ID}] QR code generated!`);
|
||||
}
|
||||
if (connection === 'close') {
|
||||
connectionState = 'disconnected';
|
||||
@@ -37,7 +41,7 @@ async function connectWhatsApp() {
|
||||
const reason = lastDisconnect?.error?.output?.statusCode;
|
||||
if (reason !== DisconnectReason.loggedOut) { setTimeout(connectWhatsApp, 5000); }
|
||||
}
|
||||
if (connection === 'open') { connectionState = 'open'; qrCode = null; console.log('Connected!'); }
|
||||
if (connection === 'open') { connectionState = 'open'; qrCode = null; console.log(`[Tenant ${TENANT_ID}] Connected!`); }
|
||||
});
|
||||
|
||||
sock.ev.on('messages.upsert', async ({ messages }) => {
|
||||
@@ -45,16 +49,16 @@ async function connectWhatsApp() {
|
||||
if (msg.key.fromMe) continue;
|
||||
const phone = msg.key.remoteJid.replace('@s.whatsapp.net', '');
|
||||
const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text || '';
|
||||
console.log('From ' + phone + ': ' + text);
|
||||
console.log(`[Tenant ${TENANT_ID}] From ${phone}: ${text}`);
|
||||
try {
|
||||
await fetch(WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ event: 'messages.upsert', data: { key: msg.key, message: msg.message, messageTimestamp: msg.messageTimestamp } }) });
|
||||
} catch (e) { console.log('Webhook failed:', e.message); }
|
||||
} catch (e) { console.log(`[Tenant ${TENANT_ID}] Webhook failed:`, e.message); }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
app.get('/', (req, res) => res.json({ status: 'ok', service: 'Nexus WhatsApp Bridge', state: connectionState }));
|
||||
app.get('/', (req, res) => res.json({ status: 'ok', service: 'Nexus WhatsApp Bridge', tenant: TENANT_ID, state: connectionState }));
|
||||
app.get('/status', (req, res) => res.json({ state: connectionState, hasQr: !!qrCode }));
|
||||
app.get('/qr', (req, res) => {
|
||||
if (connectionState === 'open') return res.json({ state: 'open', message: 'Already connected' });
|
||||
@@ -71,4 +75,4 @@ app.post('/send', async (req, res) => {
|
||||
});
|
||||
app.post('/logout', async (req, res) => { if (sock) { await sock.logout(); sock = null; } qrCode = null; connectionState = 'disconnected'; res.json({ state: 'disconnected' }); });
|
||||
|
||||
app.listen(PORT, () => { console.log('WhatsApp Bridge on port ' + PORT); connectWhatsApp(); });
|
||||
app.listen(PORT, () => { console.log(`[Tenant ${TENANT_ID}] WhatsApp Bridge on port ${PORT}`); connectWhatsApp(); });
|
||||
|
||||
Reference in New Issue
Block a user