390 lines
14 KiB
JavaScript
390 lines
14 KiB
JavaScript
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('<h1>403 - Zugriff verweigert</h1>', '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('<h1>403 - Zugriff verweigert</h1>', '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(`<h1>404 - Datei nicht gefunden</h1><p>Gesuchte Datei: ${filePath}</p>`, 'utf-8');
|
||
} else {
|
||
console.error(`❌ Server Fehler bei ${filePath}:`, error);
|
||
res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
|
||
res.end(`<h1>500 - Server Fehler</h1><p>${error.code}</p>`, '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) {
|
||
// Wenn bereits eine Signatur-Station verbunden ist, erhält die NEUE Station Priorität.
|
||
if (signatureConnection && signatureConnection.readyState === WebSocket.OPEN) {
|
||
console.log('⚠️ Neue Signatur-Station meldet sich an – bestehende Station wird abgemeldet');
|
||
|
||
// Informiere die bisher aktive Station, dass sie durch eine neue ersetzt wird
|
||
try {
|
||
signatureConnection.send(JSON.stringify({
|
||
type: 'replaced_by_new_station'
|
||
}));
|
||
console.log('Aktive Signatur-Station über neue Station informiert');
|
||
} catch (error) {
|
||
console.error('Fehler beim Informieren der alten Signatur-Station:', error);
|
||
}
|
||
|
||
// Alte Verbindung merken und neue sofort als aktive Station setzen
|
||
const oldConnection = signatureConnection;
|
||
signatureConnection = ws;
|
||
console.log('Signatur-Station gewechselt: neue Verbindung ist nun aktiv');
|
||
|
||
// Alte Verbindung sauber schließen (dieser Close-Event betrifft NICHT die neue Station)
|
||
try {
|
||
oldConnection.close();
|
||
console.log('Alte Signatur-Station Verbindung geschlossen');
|
||
} catch (error) {
|
||
console.error('Fehler beim Schließen der alten Signatur-Station:', error);
|
||
}
|
||
} else {
|
||
// Keine bestehende Station – neue Verbindung einfach registrieren
|
||
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}
|
||
║
|
||
╚═════════════════════════════════════════════════════════════
|
||
`);
|
||
}); |