Spaces:
Running
Running
const express = require('express'); | |
const http = require('http'); | |
const WebSocket = require('ws'); | |
const path = require('path'); | |
const app = express(); | |
const port = 8080; | |
// Serve static files (if any) | |
app.use(express.static('public')); | |
// Data structures to store client data | |
const clients = {}; | |
// HTTP server | |
const server = http.createServer(app); | |
// WebSocket server | |
const wss = new WebSocket.Server({ server }); | |
// Handle new WebSocket connections | |
wss.on('connection', (ws) => { | |
let clientId = null; | |
ws.on('message', (message) => { | |
try { | |
const data = JSON.parse(message); | |
if (!clientId && data.clientId) { | |
clientId = data.clientId; | |
clients[clientId] = { | |
ws: ws, | |
html: '', | |
css: '', | |
url: '' | |
}; | |
console.log(`Client ${clientId} connected`); | |
} | |
if (data.type === 'pageContent') { | |
clients[clientId].html = data.html; | |
clients[clientId].css = data.css; | |
clients[clientId].url = data.url; | |
} else if (data.type === 'keyboardEvent') { | |
// Relay the keyboard event to other clients | |
relayKeyboardEvent(data, clientId); | |
} | |
} catch (e) { | |
console.error('Error parsing message:', e); | |
} | |
}); | |
ws.on('close', () => { | |
console.log(`Client ${clientId} disconnected`); | |
delete clients[clientId]; | |
}); | |
}); | |
// Function to relay keyboard events to other clients | |
function relayKeyboardEvent(data, senderId) { | |
// Send the keyboard event to all connected clients except the sender | |
for (const [id, client] of Object.entries(clients)) { | |
if (id !== senderId) { | |
client.ws.send(JSON.stringify(data)); | |
} | |
} | |
} | |
// Route to list all active clients | |
app.get('/clients', (req, res) => { | |
res.json(Object.keys(clients)); | |
}); | |
// Route to render the client's page | |
app.get('/client/:id', (req, res) => { | |
const client = clients[req.params.id]; | |
if (client) { | |
let htmlContent = client.html; | |
// Inject the CSS into a style tag in the head | |
const cssTag = `<style>${client.css}</style>`; | |
htmlContent = htmlContent.replace('</head>', `${cssTag}</head>`); | |
// Inject the script to establish WebSocket connection | |
const scriptTag = ` | |
<script> | |
(function() { | |
const socket = new WebSocket('ws://' + location.host); | |
const clientId = '${req.params.id}-page'; | |
socket.addEventListener('open', () => { | |
console.log('WebSocket connection opened from rendered page'); | |
}); | |
socket.addEventListener('message', (event) => { | |
const data = JSON.parse(event.data); | |
// Ignore messages originating from this client | |
if (data.clientId === clientId) return; | |
if (data.type === 'keyboardEvent') { | |
simulateKeyboardEvent(data.event); | |
} | |
}); | |
// Send keyboard events to the server | |
function sendKeyboardEvent(event) { | |
const data = { | |
type: 'keyboardEvent', | |
event: serializeKeyboardEvent(event), | |
clientId: clientId | |
}; | |
socket.send(JSON.stringify(data)); | |
} | |
// Serialize keyboard event properties | |
function serializeKeyboardEvent(event) { | |
return { | |
type: event.type, | |
key: event.key, | |
code: event.code, | |
keyCode: event.keyCode, | |
charCode: event.charCode, | |
which: event.which, | |
altKey: event.altKey, | |
ctrlKey: event.ctrlKey, | |
metaKey: event.metaKey, | |
shiftKey: event.shiftKey, | |
repeat: event.repeat, | |
isComposing: event.isComposing, | |
}; | |
} | |
// Simulate keyboard event | |
function simulateKeyboardEvent(eventData) { | |
const event = new KeyboardEvent(eventData.type, eventData); | |
document.activeElement.dispatchEvent(event); | |
} | |
// Listen for keyboard events | |
['keydown', 'keypress', 'keyup'].forEach(eventType => { | |
document.addEventListener(eventType, sendKeyboardEvent, true); | |
}); | |
})(); | |
</script> | |
`; | |
htmlContent = htmlContent.replace('</body>', `${scriptTag}</body>`); | |
res.send(htmlContent); | |
} else { | |
res.status(404).send('Client not found'); | |
} | |
}); | |
// Start the server | |
server.listen(port, () => { | |
console.log(`Server is listening on http://localhost:${port}`); | |
}); | |