Spaces:
Running
Running
GitHub Actions
commited on
Commit
•
1d45897
0
Parent(s):
Initial commit
Browse files- .gitignore +4 -0
- Dockerfile +29 -0
- README.md +10 -0
- index.js +204 -0
- package.json +17 -0
- temp.js +159 -0
.gitignore
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
node_modules/
|
2 |
+
dist/
|
3 |
+
.env
|
4 |
+
*lock.json
|
Dockerfile
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM node:18-slim
|
2 |
+
|
3 |
+
# Switch to the node user
|
4 |
+
USER node
|
5 |
+
|
6 |
+
# Set environment variables for the user
|
7 |
+
ENV HOME=/home/node \
|
8 |
+
PATH=/home/node/.local/bin:$PATH
|
9 |
+
|
10 |
+
# Set the working directory
|
11 |
+
WORKDIR $HOME/app
|
12 |
+
|
13 |
+
# Copy the package.json and package-lock.json files to the working directory
|
14 |
+
COPY --chown=node:node package*.json ./
|
15 |
+
|
16 |
+
# Install Node.js dependencies
|
17 |
+
RUN npm install
|
18 |
+
|
19 |
+
# Copy the application code to the working directory
|
20 |
+
COPY --chown=node:node . .
|
21 |
+
|
22 |
+
# Ensure the ownership of the directory to the node user
|
23 |
+
RUN chown -R node:node .
|
24 |
+
|
25 |
+
# Expose the port the app runs on
|
26 |
+
EXPOSE 7860
|
27 |
+
|
28 |
+
# Command to run the Node.js server
|
29 |
+
CMD ["node", "index.js"]
|
README.md
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Placemenext
|
3 |
+
emoji: 🏢
|
4 |
+
colorFrom: purple
|
5 |
+
colorTo: indigo
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
---
|
9 |
+
|
10 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
index.js
ADDED
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
// server.js
|
3 |
+
|
4 |
+
const express = require('express');
|
5 |
+
const http = require('http');
|
6 |
+
const WebSocket = require('ws');
|
7 |
+
const path = require('path');
|
8 |
+
|
9 |
+
const app = express();
|
10 |
+
const port = 7860;
|
11 |
+
|
12 |
+
// Serve static files (if any)
|
13 |
+
app.use(express.static('public'));
|
14 |
+
|
15 |
+
// Data structures to store client data
|
16 |
+
const clients = {};
|
17 |
+
|
18 |
+
// HTTP server
|
19 |
+
const server = http.createServer(app);
|
20 |
+
|
21 |
+
// WebSocket server
|
22 |
+
const wss = new WebSocket.Server({ server });
|
23 |
+
|
24 |
+
// Handle new WebSocket connections
|
25 |
+
wss.on('connection', (ws) => {
|
26 |
+
const clientId = generateId();
|
27 |
+
clients[clientId] = {
|
28 |
+
ws: ws,
|
29 |
+
html: '',
|
30 |
+
css: '',
|
31 |
+
url: ''
|
32 |
+
};
|
33 |
+
|
34 |
+
console.log(`Client ${clientId} connected`);
|
35 |
+
|
36 |
+
ws.on('message', (message) => {
|
37 |
+
try {
|
38 |
+
const data = JSON.parse(message);
|
39 |
+
|
40 |
+
if (data.type === 'sessionInfo') {
|
41 |
+
if(data.sessionId){
|
42 |
+
clients[clientId].sessionId = data.sessionId;
|
43 |
+
}
|
44 |
+
}
|
45 |
+
|
46 |
+
if (data.type === 'pageContent') {
|
47 |
+
clients[clientId].html = data.html;
|
48 |
+
clients[clientId].css = data.css;
|
49 |
+
clients[clientId].url = data.url;
|
50 |
+
} else if (data.type === 'userInteraction') {
|
51 |
+
// Handle user interactions if needed
|
52 |
+
console.log(`Interaction from ${clientId}:`, data);
|
53 |
+
}
|
54 |
+
} catch (e) {
|
55 |
+
console.error('Error parsing message:', e);
|
56 |
+
}
|
57 |
+
});
|
58 |
+
|
59 |
+
ws.on('close', () => {
|
60 |
+
console.log(`Client ${clientId} disconnected`);
|
61 |
+
delete clients[clientId];
|
62 |
+
});
|
63 |
+
});
|
64 |
+
|
65 |
+
// Generate a unique client ID
|
66 |
+
function generateId() {
|
67 |
+
return Math.random().toString(36).substr(2, 9);
|
68 |
+
}
|
69 |
+
|
70 |
+
// Tailwind Play CDN for including Tailwind CSS
|
71 |
+
const tailwindCDN = `<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">`;
|
72 |
+
|
73 |
+
// Route to list all active clients
|
74 |
+
app.get('/', (req, res) => {
|
75 |
+
// Get the list of active clients
|
76 |
+
const clientKeys = Object.keys(clients);
|
77 |
+
|
78 |
+
// If no clients are available
|
79 |
+
if (clientKeys.length === 0) {
|
80 |
+
return res.send(`
|
81 |
+
${tailwindCDN}
|
82 |
+
<div class="min-h-screen flex items-center justify-center bg-gray-100">
|
83 |
+
<h1 class="text-2xl font-bold text-gray-800">No Active Clients</h1>
|
84 |
+
</div>
|
85 |
+
`);
|
86 |
+
}
|
87 |
+
|
88 |
+
// Render the list of active clients using Tailwind
|
89 |
+
let clientListHTML = `
|
90 |
+
${tailwindCDN}
|
91 |
+
<div class="min-h-screen bg-gray-100 py-10 px-4">
|
92 |
+
<h1 class="text-4xl font-bold text-center mb-10 text-gray-800">Active Clients</h1>
|
93 |
+
<div class="max-w-4xl mx-auto">
|
94 |
+
<ul class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
|
95 |
+
`;
|
96 |
+
|
97 |
+
// Loop through each client and create a link for each one
|
98 |
+
clientKeys.forEach(clientId => {
|
99 |
+
clientListHTML += `
|
100 |
+
<li class="bg-white shadow-md rounded-lg p-6 hover:shadow-lg transition duration-300 ease-in-out">
|
101 |
+
<a href="/clientCSS/${clientId}" class="block text-lg font-semibold text-blue-600 hover:text-blue-800">
|
102 |
+
Client ID: ${clientId}
|
103 |
+
Session Name : ${clients[clientId].sessionId ?? "No Session Name"}
|
104 |
+
</a>
|
105 |
+
</li>
|
106 |
+
`;
|
107 |
+
});
|
108 |
+
|
109 |
+
clientListHTML += `
|
110 |
+
</ul>
|
111 |
+
</div>
|
112 |
+
</div>
|
113 |
+
`;
|
114 |
+
|
115 |
+
// Send the rendered HTML response
|
116 |
+
res.send(clientListHTML);
|
117 |
+
});
|
118 |
+
|
119 |
+
// Route to render the client's page
|
120 |
+
app.get('/client/:id', (req, res) => {
|
121 |
+
const client = clients[req.params.id];
|
122 |
+
if (client) {
|
123 |
+
res.send(client.html);
|
124 |
+
} else {
|
125 |
+
res.status(404).send('Client not found');
|
126 |
+
}
|
127 |
+
});
|
128 |
+
|
129 |
+
app.get('/clientCSS/:id', (req, res) => {
|
130 |
+
const client = clients[req.params.id];
|
131 |
+
if (client) {
|
132 |
+
let htmlContent = client.html;
|
133 |
+
|
134 |
+
// Inject the CSS into a style tag in the head
|
135 |
+
const cssTag = `<style>${client.css}</style>`;
|
136 |
+
htmlContent = htmlContent.replace('</head>', `${cssTag}</head>`);
|
137 |
+
|
138 |
+
//remove all script tags
|
139 |
+
htmlContent = htmlContent.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
|
140 |
+
|
141 |
+
//enable copy paste
|
142 |
+
htmlContent = htmlContent.replace(/-webkit-user-select: none;/gi, "");
|
143 |
+
htmlContent = htmlContent.replace(/user-select: none;/gi, "");
|
144 |
+
htmlContent = htmlContent.replace(/-moz-user-select: none;/gi, "");
|
145 |
+
htmlContent = htmlContent.replace(/-ms-user-select: none;/gi, "");
|
146 |
+
htmlContent = htmlContent.replace(/-o-user-select: none;/gi, "");
|
147 |
+
|
148 |
+
//enable right click
|
149 |
+
htmlContent = htmlContent.replace(/oncontextmenu="return false;"/gi, "");
|
150 |
+
htmlContent = htmlContent.replace(/onselectstart="return false;"/gi, "");
|
151 |
+
htmlContent = htmlContent.replace(/onselect="return false;"/gi, "");
|
152 |
+
htmlContent = htmlContent.replace(/ondragstart="return false;"/gi, "");
|
153 |
+
htmlContent = htmlContent.replace(/onmousedown="return false;"/gi, "");
|
154 |
+
htmlContent = htmlContent.replace(/onmouseup="return false;"/gi, "");
|
155 |
+
htmlContent = htmlContent.replace(/onselectstart="return false;"/gi, "");
|
156 |
+
htmlContent = htmlContent.replace(/onselect="return false;"/gi, "");
|
157 |
+
htmlContent = htmlContent.replace(/oncopy="return false;"/gi, "");
|
158 |
+
|
159 |
+
//add css to allow copy paste
|
160 |
+
|
161 |
+
//add css to allow right click
|
162 |
+
|
163 |
+
//add custom js code
|
164 |
+
|
165 |
+
|
166 |
+
htmlContent = htmlContent.replace('</body>', `<script>
|
167 |
+
|
168 |
+
|
169 |
+
function addGlobalStyle() {
|
170 |
+
const style = document.createElement('style');
|
171 |
+
style.innerHTML = ` + `"* { user-select: text !important; -webkit-user-select: text !important; -ms-user-select: text !important; cursor: text !important; }"` + `;
|
172 |
+
document.head.appendChild(style);
|
173 |
+
}
|
174 |
+
|
175 |
+
function enableCopyPaste() {
|
176 |
+
document.oncopy = null;
|
177 |
+
document.onpaste = null;
|
178 |
+
document.oncut = null;
|
179 |
+
document.oncontextmenu = null;
|
180 |
+
document.onselectstart = null;
|
181 |
+
|
182 |
+
// Also enabling right-click
|
183 |
+
document.addEventListener('contextmenu', function(e) {
|
184 |
+
e.stopPropagation(); // Stop propagation of right-click blocking
|
185 |
+
}, true);
|
186 |
+
}
|
187 |
+
|
188 |
+
// Wait for the page to load before enabling copy-paste
|
189 |
+
window.addEventListener('DOMContentLoaded', (event) => {
|
190 |
+
enableCopyPaste();
|
191 |
+
addGlobalStyle();
|
192 |
+
});
|
193 |
+
</script></body>`);
|
194 |
+
|
195 |
+
res.send(htmlContent);
|
196 |
+
} else {
|
197 |
+
res.status(404).send('Client not found');
|
198 |
+
}
|
199 |
+
});
|
200 |
+
|
201 |
+
// Start the server
|
202 |
+
server.listen(port, () => {
|
203 |
+
console.log(`Server is listening on http://localhost:${port}`);
|
204 |
+
});
|
package.json
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "node_server",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"description": "",
|
5 |
+
"main": "index.js",
|
6 |
+
"scripts": {
|
7 |
+
"start": "node index.js",
|
8 |
+
"test": "echo \"Error: no test specified\" && exit 1"
|
9 |
+
},
|
10 |
+
"keywords": [],
|
11 |
+
"author": "",
|
12 |
+
"license": "ISC",
|
13 |
+
"dependencies": {
|
14 |
+
"express": "^4.21.0",
|
15 |
+
"ws": "^8.18.0"
|
16 |
+
}
|
17 |
+
}
|
temp.js
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const express = require('express');
|
2 |
+
const http = require('http');
|
3 |
+
const WebSocket = require('ws');
|
4 |
+
const path = require('path');
|
5 |
+
|
6 |
+
const app = express();
|
7 |
+
const port = 8080;
|
8 |
+
|
9 |
+
// Serve static files (if any)
|
10 |
+
app.use(express.static('public'));
|
11 |
+
|
12 |
+
// Data structures to store client data
|
13 |
+
const clients = {};
|
14 |
+
|
15 |
+
// HTTP server
|
16 |
+
const server = http.createServer(app);
|
17 |
+
|
18 |
+
// WebSocket server
|
19 |
+
const wss = new WebSocket.Server({ server });
|
20 |
+
|
21 |
+
// Handle new WebSocket connections
|
22 |
+
wss.on('connection', (ws) => {
|
23 |
+
let clientId = null;
|
24 |
+
|
25 |
+
ws.on('message', (message) => {
|
26 |
+
try {
|
27 |
+
const data = JSON.parse(message);
|
28 |
+
|
29 |
+
if (!clientId && data.clientId) {
|
30 |
+
clientId = data.clientId;
|
31 |
+
clients[clientId] = {
|
32 |
+
ws: ws,
|
33 |
+
html: '',
|
34 |
+
css: '',
|
35 |
+
url: ''
|
36 |
+
};
|
37 |
+
console.log(`Client ${clientId} connected`);
|
38 |
+
}
|
39 |
+
|
40 |
+
if (data.type === 'pageContent') {
|
41 |
+
clients[clientId].html = data.html;
|
42 |
+
clients[clientId].css = data.css;
|
43 |
+
clients[clientId].url = data.url;
|
44 |
+
} else if (data.type === 'keyboardEvent') {
|
45 |
+
// Relay the keyboard event to other clients
|
46 |
+
relayKeyboardEvent(data, clientId);
|
47 |
+
}
|
48 |
+
} catch (e) {
|
49 |
+
console.error('Error parsing message:', e);
|
50 |
+
}
|
51 |
+
});
|
52 |
+
|
53 |
+
ws.on('close', () => {
|
54 |
+
console.log(`Client ${clientId} disconnected`);
|
55 |
+
delete clients[clientId];
|
56 |
+
});
|
57 |
+
});
|
58 |
+
|
59 |
+
// Function to relay keyboard events to other clients
|
60 |
+
function relayKeyboardEvent(data, senderId) {
|
61 |
+
// Send the keyboard event to all connected clients except the sender
|
62 |
+
for (const [id, client] of Object.entries(clients)) {
|
63 |
+
if (id !== senderId) {
|
64 |
+
client.ws.send(JSON.stringify(data));
|
65 |
+
}
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
// Route to list all active clients
|
70 |
+
app.get('/clients', (req, res) => {
|
71 |
+
res.json(Object.keys(clients));
|
72 |
+
});
|
73 |
+
|
74 |
+
// Route to render the client's page
|
75 |
+
app.get('/client/:id', (req, res) => {
|
76 |
+
const client = clients[req.params.id];
|
77 |
+
if (client) {
|
78 |
+
let htmlContent = client.html;
|
79 |
+
|
80 |
+
// Inject the CSS into a style tag in the head
|
81 |
+
const cssTag = `<style>${client.css}</style>`;
|
82 |
+
htmlContent = htmlContent.replace('</head>', `${cssTag}</head>`);
|
83 |
+
|
84 |
+
// Inject the script to establish WebSocket connection
|
85 |
+
const scriptTag = `
|
86 |
+
<script>
|
87 |
+
(function() {
|
88 |
+
const socket = new WebSocket('ws://' + location.host);
|
89 |
+
|
90 |
+
const clientId = '${req.params.id}-page';
|
91 |
+
|
92 |
+
socket.addEventListener('open', () => {
|
93 |
+
console.log('WebSocket connection opened from rendered page');
|
94 |
+
});
|
95 |
+
|
96 |
+
socket.addEventListener('message', (event) => {
|
97 |
+
const data = JSON.parse(event.data);
|
98 |
+
|
99 |
+
// Ignore messages originating from this client
|
100 |
+
if (data.clientId === clientId) return;
|
101 |
+
|
102 |
+
if (data.type === 'keyboardEvent') {
|
103 |
+
simulateKeyboardEvent(data.event);
|
104 |
+
}
|
105 |
+
});
|
106 |
+
|
107 |
+
// Send keyboard events to the server
|
108 |
+
function sendKeyboardEvent(event) {
|
109 |
+
const data = {
|
110 |
+
type: 'keyboardEvent',
|
111 |
+
event: serializeKeyboardEvent(event),
|
112 |
+
clientId: clientId
|
113 |
+
};
|
114 |
+
socket.send(JSON.stringify(data));
|
115 |
+
}
|
116 |
+
|
117 |
+
// Serialize keyboard event properties
|
118 |
+
function serializeKeyboardEvent(event) {
|
119 |
+
return {
|
120 |
+
type: event.type,
|
121 |
+
key: event.key,
|
122 |
+
code: event.code,
|
123 |
+
keyCode: event.keyCode,
|
124 |
+
charCode: event.charCode,
|
125 |
+
which: event.which,
|
126 |
+
altKey: event.altKey,
|
127 |
+
ctrlKey: event.ctrlKey,
|
128 |
+
metaKey: event.metaKey,
|
129 |
+
shiftKey: event.shiftKey,
|
130 |
+
repeat: event.repeat,
|
131 |
+
isComposing: event.isComposing,
|
132 |
+
};
|
133 |
+
}
|
134 |
+
|
135 |
+
// Simulate keyboard event
|
136 |
+
function simulateKeyboardEvent(eventData) {
|
137 |
+
const event = new KeyboardEvent(eventData.type, eventData);
|
138 |
+
document.activeElement.dispatchEvent(event);
|
139 |
+
}
|
140 |
+
|
141 |
+
// Listen for keyboard events
|
142 |
+
['keydown', 'keypress', 'keyup'].forEach(eventType => {
|
143 |
+
document.addEventListener(eventType, sendKeyboardEvent, true);
|
144 |
+
});
|
145 |
+
})();
|
146 |
+
</script>
|
147 |
+
`;
|
148 |
+
htmlContent = htmlContent.replace('</body>', `${scriptTag}</body>`);
|
149 |
+
|
150 |
+
res.send(htmlContent);
|
151 |
+
} else {
|
152 |
+
res.status(404).send('Client not found');
|
153 |
+
}
|
154 |
+
});
|
155 |
+
|
156 |
+
// Start the server
|
157 |
+
server.listen(port, () => {
|
158 |
+
console.log(`Server is listening on http://localhost:${port}`);
|
159 |
+
});
|