web
This commit is contained in:
263
server.js
263
server.js
@@ -8,6 +8,7 @@ const path = require('path')
|
||||
const fs = require('fs').promises
|
||||
const crypto = require('crypto')
|
||||
const qrcode = require('qrcode-terminal')
|
||||
const QRCode = require('qrcode')
|
||||
|
||||
// --- CONFIGURATION ---
|
||||
const config = {
|
||||
@@ -201,6 +202,255 @@ app.get('/api/status', [validateApiKey], async (req, res) => {
|
||||
|
||||
app.get('/health', (req, res) => res.json({ status: 'OK', timestamp: Date.now() }))
|
||||
|
||||
// QR Code storage
|
||||
let currentQR = null
|
||||
let qrTimestamp = null
|
||||
|
||||
// QR Code HTML page
|
||||
app.get('/qr', (req, res) => {
|
||||
const html = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WhatsApp QR Code</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
font-size: 28px;
|
||||
}
|
||||
.subtitle {
|
||||
color: #666;
|
||||
margin-bottom: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
#qr-container {
|
||||
background: #f5f5f5;
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
min-height: 300px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
#qr-code {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.status {
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
margin: 20px 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
.status.waiting {
|
||||
background: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
.status.ready {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
.status.error {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
.info {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
line-items: 1.6;
|
||||
}
|
||||
.loader {
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #667eea;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
.timestamp {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🔗 WhatsApp Connection</h1>
|
||||
<p class="subtitle">Scan the QR code with your WhatsApp mobile app</p>
|
||||
|
||||
<div id="status" class="status waiting">
|
||||
Waiting for QR code...
|
||||
</div>
|
||||
|
||||
<div id="qr-container">
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p><strong>How to scan:</strong></p>
|
||||
<p>1. Open WhatsApp on your phone</p>
|
||||
<p>2. Tap Menu (⋮) > Linked Devices</p>
|
||||
<p>3. Tap "Link a Device"</p>
|
||||
<p>4. Point your phone at this screen</p>
|
||||
</div>
|
||||
|
||||
<div class="timestamp" id="timestamp"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const statusEl = document.getElementById('status');
|
||||
const qrContainer = document.getElementById('qr-container');
|
||||
const timestampEl = document.getElementById('timestamp');
|
||||
let socket;
|
||||
|
||||
function connectWebSocket() {
|
||||
socket = io('${process.env.WS_URL || ''}');
|
||||
|
||||
socket.on('connect', () => {
|
||||
console.log('WebSocket connected');
|
||||
});
|
||||
|
||||
socket.on('qr', (data) => {
|
||||
console.log('QR code received');
|
||||
statusEl.textContent = '📱 Scan this QR code with WhatsApp';
|
||||
statusEl.className = 'status waiting';
|
||||
qrContainer.innerHTML = '<img id="qr-code" src="/qr/image" alt="QR Code">';
|
||||
timestampEl.textContent = 'Generated: ' + new Date(data.timestamp).toLocaleString();
|
||||
});
|
||||
|
||||
socket.on('ready', (data) => {
|
||||
console.log('WhatsApp ready');
|
||||
statusEl.textContent = '✅ Connected successfully!';
|
||||
statusEl.className = 'status ready';
|
||||
qrContainer.innerHTML = '<div style="font-size: 48px;">✅</div><p style="margin-top: 10px; color: #155724;">WhatsApp is connected and ready</p>';
|
||||
timestampEl.textContent = '';
|
||||
});
|
||||
|
||||
socket.on('status', (data) => {
|
||||
if (data.connected) {
|
||||
statusEl.textContent = '✅ Already connected';
|
||||
statusEl.className = 'status ready';
|
||||
qrContainer.innerHTML = '<div style="font-size: 48px;">✅</div><p style="margin-top: 10px; color: #155724;">WhatsApp is already connected</p>';
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
console.log('WebSocket disconnected');
|
||||
statusEl.textContent = '⚠️ Connection lost - Reconnecting...';
|
||||
statusEl.className = 'status error';
|
||||
});
|
||||
|
||||
socket.on('error', (error) => {
|
||||
console.error('Socket error:', error);
|
||||
statusEl.textContent = '❌ Error: ' + (error.message || 'Unknown error');
|
||||
statusEl.className = 'status error';
|
||||
});
|
||||
}
|
||||
|
||||
// Initial check
|
||||
fetch('/qr/check')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.hasQR) {
|
||||
statusEl.textContent = '📱 Scan this QR code with WhatsApp';
|
||||
statusEl.className = 'status waiting';
|
||||
qrContainer.innerHTML = '<img id="qr-code" src="/qr/image" alt="QR Code">';
|
||||
timestampEl.textContent = 'Generated: ' + new Date(data.timestamp).toLocaleString();
|
||||
} else if (data.connected) {
|
||||
statusEl.textContent = '✅ Already connected';
|
||||
statusEl.className = 'status ready';
|
||||
qrContainer.innerHTML = '<div style="font-size: 48px;">✅</div><p style="margin-top: 10px; color: #155724;">WhatsApp is already connected</p>';
|
||||
timestampEl.textContent = '';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Check failed:', err);
|
||||
});
|
||||
|
||||
// Connect WebSocket for live updates
|
||||
connectWebSocket();
|
||||
|
||||
// Auto-refresh QR image every 30 seconds if waiting
|
||||
setInterval(() => {
|
||||
const img = document.getElementById('qr-code');
|
||||
if (img && statusEl.classList.contains('waiting')) {
|
||||
img.src = '/qr/image?t=' + Date.now();
|
||||
}
|
||||
}, 30000);
|
||||
</script>
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
res.send(html)
|
||||
})
|
||||
|
||||
// QR Code check endpoint
|
||||
app.get('/qr/check', (req, res) => {
|
||||
res.json({
|
||||
hasQR: !!currentQR,
|
||||
connected: !!client.info,
|
||||
timestamp: qrTimestamp
|
||||
})
|
||||
})
|
||||
|
||||
// QR Code image endpoint
|
||||
app.get('/qr/image', async (req, res) => {
|
||||
try {
|
||||
if (!currentQR) {
|
||||
// Generate a placeholder
|
||||
res.setHeader('Content-Type', 'image/png')
|
||||
const placeholder = await QRCode.toBuffer('Waiting for QR code...', {
|
||||
width: 300,
|
||||
color: { dark: '#ccc', light: '#fff' }
|
||||
})
|
||||
return res.send(placeholder)
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'image/png')
|
||||
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
const qrImage = await QRCode.toBuffer(currentQR, {
|
||||
width: 300,
|
||||
margin: 2,
|
||||
color: { dark: '#000', light: '#fff' }
|
||||
})
|
||||
res.send(qrImage)
|
||||
} catch (error) {
|
||||
logger.error('Error generating QR image', error)
|
||||
res.status(500).send('Error generating QR code')
|
||||
}
|
||||
})
|
||||
|
||||
// --- WEBSOCKET ---
|
||||
io.on('connection', (socket) => {
|
||||
logger.info(`WebSocket client connected: ${ socket.id }`)
|
||||
@@ -222,12 +472,23 @@ const client = new Client({
|
||||
|
||||
client.on('qr', (qr) => {
|
||||
logger.info('QR received - scan to authenticate')
|
||||
logger.info('QR Code available at: http://localhost:' + config.port + '/qr')
|
||||
qrcode.generate(qr, { small: true })
|
||||
io.emit('qr', { qr, timestamp: Date.now() })
|
||||
|
||||
// Store QR for web display
|
||||
currentQR = qr
|
||||
qrTimestamp = Date.now()
|
||||
|
||||
io.emit('qr', { qr, timestamp: qrTimestamp })
|
||||
})
|
||||
|
||||
client.on('ready', async () => {
|
||||
logger.info('WhatsApp client ready');
|
||||
|
||||
// Clear QR code once connected
|
||||
currentQR = null
|
||||
qrTimestamp = null
|
||||
|
||||
const wwebVersion = await client.getWWebVersion();
|
||||
|
||||
// Sync contacts with proper type detection
|
||||
|
||||
Reference in New Issue
Block a user