Files
DocuSighn/server.js

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}
╚═════════════════════════════════════════════════════════════
`);
});