Files
DocuSighn/server.js

390 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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}
╚═════════════════════════════════════════════════════════════
`);
});