375 lines
13 KiB
JavaScript
375 lines
13 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) {
|
|
// 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}
|
|
║
|
|
╚═════════════════════════════════════════════════════════════
|
|
`);
|
|
}); |