whatsapp_bridge/server.js aktualisiert

Fix Error session end, fix error manual reconnect after crash.
This commit is contained in:
2026-06-15 07:10:52 +00:00
parent a05db84163
commit 332f386b10

View File

@@ -6,23 +6,73 @@ const path = require('path');
const app = express();
app.use(express.json());
// Statische Dateien aus dem "ui" Ordner servieren
app.use(express.static(path.join(__dirname, 'ui')));
let lastQr = "";
let isReady = false;
let isInitializing = true;
// Fester Speicherort für die Session außerhalb flüchtiger Docker-Verzeichnisse
const SESSION_DATA = '/data/auth';
// Sauberes Herunterfahren bei Abstürzen, um Dateikorruption der Session zu verhindern
async function handleCrash(errorContext) {
console.error(`💥 CRITICAL ERROR CAUGHT: ${errorContext}`);
isReady = false;
try {
console.log('Versuche Browser sauber zu schließen, um Session zu retten...');
if (client && client.pupBrowser) {
await client.pupBrowser.close().catch(() => {});
}
} catch (e) {
console.error('Browser konnte nicht sauber geschlossen werden:', e);
}
console.log('Add-on wird beendet. HA Watchdog startet es sauber neu...');
process.exit(1);
}
// Sicherheitsnetz für ungefangene Puppeteer/Browser-Fehler
process.on('uncaughtException', async (err) => {
if (err.message.includes('ProtocolError') || err.message.includes('Runtime.callFunctionOn') || err.message.includes('Target closed')) {
await handleCrash('Puppeteer Protocol Timeout / Crash');
} else {
console.error('Uncaught Exception:', err);
}
});
process.on('unhandledRejection', async (reason, promise) => {
if (reason && reason.message && reason.message.includes('Session closed')) {
await handleCrash('Unhandled Puppeteer Session Rejection');
} else {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
}
});
// Client-Definition mit fixierter Client-ID gegen Session-Verlust
const client = new Client({
authStrategy: new LocalAuth({ dataPath: SESSION_DATA }),
authStrategy: new LocalAuth({
dataPath: SESSION_DATA,
clientId: "ha_bridge_session"
}),
authTimeoutMs: 90000,
qrMaxRetries: 5,
puppeteer: {
executablePath: '/usr/bin/chromium-browser',
protocolTimeout: 0,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu'
'--disable-gpu',
'--no-first-run',
'--no-zygote',
'--single-process',
'--disable-extensions',
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding'
]
}
});
@@ -31,57 +81,71 @@ const client = new Client({
client.on('qr', (qr) => {
lastQr = qr;
isReady = false;
isInitializing = false;
console.log('Neuer QR Code generiert.');
});
client.on('ready', () => {
isReady = true;
lastQr = "";
console.log('WhatsApp ist bereit!');
isInitializing = false;
console.log('WhatsApp ist bereit! Session wurde erfolgreich geladen/gespeichert.');
});
client.on('disconnected', async (reason) => {
console.log(`WhatsApp Verbindung manuell oder extern getrennt (Grund: ${reason}).`);
await handleCrash('Externer Disconnect');
});
// --- ROUTES ---
// Hauptseite für Ingress
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'ui', 'index.html'));
});
// Status API
app.get('/api/status', async (req, res) => {
try {
let qrData = null;
if (lastQr) {
qrData = await qrcode.toDataURL(lastQr);
}
let clientState = 'DISCONNECTED';
if (isReady) {
clientState = await client.getState().catch(() => 'ERROR');
}
res.json({
isReady: isReady,
hasQr: lastQr !== "",
qrCode: qrData
qrCode: qrData,
isInitializing: isInitializing,
clientState: clientState
});
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// Logout
// Logout & Session-Löschung (Nur bei explizitem Klick auf "Account wechseln")
app.post('/auth/logout', async (req, res) => {
try {
await client.logout();
await fs.remove(SESSION_DATA);
res.json({ success: true, message: "Erfolgreich abgemeldet. Addon startet neu..." });
console.log("Manueller Logout über Web-UI...");
await client.logout().catch(() => {});
await fs.remove(path.join(SESSION_DATA, 'session-ha_bridge_session')).catch(() => {});
res.json({ success: true, message: "Erfolgreich abgemeldet. Ordner gelöscht. Addon startet neu..." });
setTimeout(() => process.exit(0), 2000);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Reset
// Factory Reset
app.delete('/system/reset', async (req, res) => {
try {
await client.destroy();
await fs.emptyDir('/data');
res.json({ success: true, message: "Werkseinstellungen wiederhergestellt. Addon startet neu..." });
await client.destroy().catch(() => {});
await fs.emptyDir('/data').catch(() => {});
res.json({ success: true, message: "Werkseinstellungen aktiv. Alles gelöscht. Neustart..." });
setTimeout(() => process.exit(0), 2000);
} catch (err) {
res.status(500).json({ error: err.message });
@@ -101,5 +165,9 @@ app.post('/send', async (req, res) => {
}
});
client.initialize();
console.log('Initialisiere WhatsApp Client...');
client.initialize().catch(async (err) => {
await handleCrash(`Initialisierungsfehler: ${err.message}`);
});
app.listen(3000, () => console.log('WhatsApp Bridge Server läuft auf Port 3000'));