const WebSocket = require('ws'); const http = require('http'); const fs = require('fs'); const path = require('path'); // Create HTTP server const server = http.createServer((req, res) => { // CORS headers res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } // Serve files let urlPath = req.url; // Remove query string if present urlPath = urlPath.split('?')[0]; // Handle root path if (urlPath === '/' || urlPath === '/index.html') { urlPath = '/html/signature.html'; } // Map URL paths to file system paths based on file type let cleanPath = urlPath.startsWith('/') ? urlPath.substring(1) : urlPath; // Route requests to appropriate directories // Only add directory prefix if path doesn't already have it if (cleanPath.endsWith('.html')) { // HTML files from html/ directory const fileName = path.basename(cleanPath); if (!cleanPath.startsWith('html/')) { cleanPath = 'html/' + fileName; } } else if (cleanPath.endsWith('.css')) { // CSS files from css/ directory const fileName = path.basename(cleanPath); if (!cleanPath.startsWith('css/')) { cleanPath = 'css/' + fileName; } } else if (cleanPath.endsWith('.js')) { // JS files from js/ directory const fileName = path.basename(cleanPath); if (!cleanPath.startsWith('js/')) { cleanPath = 'js/' + fileName; } } // Security: Prevent directory traversal attacks if (cleanPath.includes('..')) { res.writeHead(403, { 'Content-Type': 'text/html' }); res.end('

403 - Zugriff verweigert

', 'utf-8'); return; } // Convert to filesystem path const filePath = path.join(process.cwd(), cleanPath); // Security: Ensure resolved path is within current directory const currentDir = process.cwd(); const resolvedPath = path.resolve(filePath); if (!resolvedPath.startsWith(currentDir)) { res.writeHead(403, { 'Content-Type': 'text/html' }); res.end('

403 - Zugriff verweigert

', 'utf-8'); return; } const extname = String(path.extname(filePath)).toLowerCase(); const mimeTypes = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml', '.pdf': 'application/pdf', '.ico': 'image/x-icon' }; const contentType = mimeTypes[extname] || 'application/octet-stream'; // Log file requests for debugging console.log(`πŸ“„ Request: ${req.url} -> ${filePath} (${contentType})`); fs.readFile(filePath, (error, content) => { if (error) { if (error.code === 'ENOENT') { console.log(`❌ 404: Datei nicht gefunden: ${filePath}`); res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(`

404 - Datei nicht gefunden

Gesuchte Datei: ${filePath}

`, 'utf-8'); } else { console.error(`❌ Server Fehler bei ${filePath}:`, error); res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(`

500 - Server Fehler

${error.code}

`, 'utf-8'); } } else { res.writeHead(200, { 'Content-Type': contentType }); res.end(content, 'utf-8'); } }); }); // Create WebSocket server const wss = new WebSocket.Server({ server }); // Store connections let masterConnection = null; let signatureConnection = null; wss.on('connection', (ws) => { console.log('Neue WebSocket Verbindung'); ws.on('message', (message) => { try { const data = JSON.parse(message); console.log('πŸ“¨ Nachricht empfangen:', data.type); console.log(' Von:', ws === masterConnection ? 'Master' : ws === signatureConnection ? 'Signatur-Station' : 'Unbekannt'); switch (data.type) { case 'register_master': handleMasterRegister(ws); break; case 'register_signature': handleSignatureRegister(ws); break; case 'pdf': handlePdfUpload(data); break; case 'signature': handleSignature(data); break; case 'page_change': handlePageChange(data); break; case 'signature_placed': console.log('πŸ”„ signature_placed Nachricht empfangen, leite weiter...'); handleSignaturePlaced(); break; case 'discard': console.log('πŸ”„ discard Nachricht empfangen, leite weiter...'); handleDiscard(); break; default: console.log('⚠️ Unbekannter Nachrichtentyp:', data.type); } } catch (error) { console.error('❌ Fehler beim Verarbeiten der Nachricht:', error); } }); ws.on('close', () => { console.log('WebSocket Verbindung geschlossen'); if (ws === masterConnection) { masterConnection = null; console.log('Master getrennt'); } if (ws === signatureConnection) { signatureConnection = null; console.log('Signatur-Station getrennt'); // Notify master that signature station disconnected if (masterConnection && masterConnection.readyState === WebSocket.OPEN) { masterConnection.send(JSON.stringify({ type: 'signature_station_disconnected' })); console.log('Master ΓΌber Signatur-Station Trennung informiert'); } } }); }); function handleMasterRegister(ws) { masterConnection = ws; console.log('Master registriert'); // Notify master about current signature station connection status if (signatureConnection && signatureConnection.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'signature_station_connected' })); console.log('Master ΓΌber Signatur-Station Verbindung informiert'); } else { ws.send(JSON.stringify({ type: 'signature_station_disconnected' })); console.log('Master ΓΌber fehlende Signatur-Station informiert'); } } function handleSignatureRegister(ws) { // Check if another signature station is already connected if (signatureConnection && signatureConnection.readyState === WebSocket.OPEN) { // Inform the new station that another station is already connected ws.send(JSON.stringify({ type: 'already_connected' })); console.log('⚠️ Neue Signatur-Station versucht sich zu verbinden, aber bereits eine Station verbunden'); console.log('⚠️ Neue Station wurde informiert, alte Station bleibt aktiv'); // Keep the old connection, don't replace it // Don't register the new connection as signatureConnection return; // Exit without registering the new connection } // No existing connection, register this new one signatureConnection = ws; console.log('Signatur-Station registriert'); // Notify master that signature station is now connected if (masterConnection && masterConnection.readyState === WebSocket.OPEN) { masterConnection.send(JSON.stringify({ type: 'signature_station_connected' })); console.log('Master ΓΌber Signatur-Station Verbindung informiert'); } } function handlePdfUpload(data) { const { pdf } = data; if (!signatureConnection || signatureConnection.readyState !== WebSocket.OPEN) { console.log('Keine Signatur-Station verbunden'); return; } // Forward PDF to signature station signatureConnection.send(JSON.stringify({ type: 'pdf', pdf: pdf })); console.log('PDF an Signatur-Station weitergeleitet'); } function handleSignature(data) { const { signature } = data; if (!masterConnection || masterConnection.readyState !== WebSocket.OPEN) { console.log('Keine Master-Verbindung'); return; } // Send signature to master masterConnection.send(JSON.stringify({ type: 'signature', signature: signature })); console.log('Unterschrift an Master gesendet'); } function handlePageChange(data) { const { pageNum, totalPages } = data; if (!signatureConnection || signatureConnection.readyState !== WebSocket.OPEN) { console.log('Keine Signatur-Station verbunden'); return; } // Forward page change to signature station signatureConnection.send(JSON.stringify({ type: 'page_change', pageNum: pageNum, totalPages: totalPages })); console.log('Seitenwechsel an Signatur-Station weitergeleitet: Seite', pageNum, 'von', totalPages); } function handleSignaturePlaced() { console.log('handleSignaturePlaced() aufgerufen'); console.log('signatureConnection vorhanden:', signatureConnection ? 'Ja' : 'Nein'); console.log('signatureConnection.readyState:', signatureConnection ? signatureConnection.readyState : 'N/A'); if (!signatureConnection || signatureConnection.readyState !== WebSocket.OPEN) { console.log('⚠️ Keine Signatur-Station verbunden - BestΓ€tigung kann nicht gesendet werden'); return; } try { // Forward signature placed confirmation to signature station const message = JSON.stringify({ type: 'signature_placed' }); signatureConnection.send(message); console.log('βœ… Unterschrift-Platzierung-BestΓ€tigung an Signatur-Station gesendet'); } catch (error) { console.error('❌ Fehler beim Senden der BestΓ€tigung:', error); } } function handleDiscard() { console.log('handleDiscard() aufgerufen'); console.log('signatureConnection vorhanden:', signatureConnection ? 'Ja' : 'Nein'); console.log('signatureConnection.readyState:', signatureConnection ? signatureConnection.readyState : 'N/A'); if (!signatureConnection || signatureConnection.readyState !== WebSocket.OPEN) { console.log('⚠️ Keine Signatur-Station verbunden - Verwerfen-Nachricht kann nicht gesendet werden'); return; } try { // Forward discard message to signature station const message = JSON.stringify({ type: 'discard' }); signatureConnection.send(message); console.log('βœ… Verwerfen-Nachricht an Signatur-Station gesendet'); } catch (error) { console.error('❌ Fehler beim Senden der Verwerfen-Nachricht:', error); } } const PORT = 8080; const HOST = '0.0.0.0'; // Listen on all network interfaces server.listen(PORT, HOST, () => { const os = require('os'); const networkInterfaces = os.networkInterfaces(); let ipAddresses = []; // Get all IPv4 addresses Object.keys(networkInterfaces).forEach((interfaceName) => { networkInterfaces[interfaceName].forEach((iface) => { if (iface.family === 'IPv4' && !iface.internal) { ipAddresses.push(iface.address); } }); }); console.log(` ╔════════════════════════════════════════════════════════════╗ β•‘ PDF Unterschrift System gestartet! β•‘ ╠════════════════════════════════════════════════════════════╝ β•‘ β•‘ Lokaler Zugriff: β•‘ Master-Seite: http://localhost:${PORT}/master.html β•‘ Signatur-Seite: http://localhost:${PORT}/signature.html β•‘ `); if (ipAddresses.length > 0) { console.log(`β•‘ β•‘ Netzwerk-Zugriff (fΓΌr iPhone/Tablet): `); ipAddresses.forEach(ip => { console.log(`β•‘ Master-Seite: http://${ip}:${PORT}/master.html `); console.log(`β•‘ Signatur-Seite: http://${ip}:${PORT}/signature.html `); }); } console.log(`β•‘ β•‘ WICHTIG: Γ–ffne die Signatur-Seite einmal und lasse β•‘ sie offen. Jede neue PDF wird dort automatisch β•‘ angezeigt! β•‘ β•‘ WebSocket Server lΓ€uft auf Port ${PORT} β•‘ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• `); });