This commit is contained in:
Tour
2025-12-06 19:24:10 +01:00
parent b418912a1e
commit 48965d4c50
5 changed files with 518 additions and 24 deletions

263
server.js
View File

@@ -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