├── .gitignore ├── Dockerfile ├── index.html ├── package-lock.json ├── package.json ├── script.js └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official Node.js image 2 | FROM node:18 3 | 4 | # Create and set the working directory 5 | WORKDIR /app 6 | 7 | # Copy the package.json and package-lock.json 8 | COPY package*.json ./ 9 | 10 | # Install the dependencies 11 | RUN npm install 12 | 13 | # Copy the rest of the application code 14 | COPY . . 15 | 16 | # Expose the port the application runs on 17 | EXPOSE 8080 18 | 19 | # Run the application 20 | CMD ["npm", "start"] 21 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebSocket Checkboxes 7 | 119 | 120 | 121 | 132 | 133 |

WebSocket Checkboxes Visit chaicode

134 |
135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "websocker-checkboxes", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "websocker-checkboxes", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "ws": "^8.18.0", 13 | "zlib": "^1.0.5" 14 | } 15 | }, 16 | "node_modules/ws": { 17 | "version": "8.18.0", 18 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 19 | "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 20 | "engines": { 21 | "node": ">=10.0.0" 22 | }, 23 | "peerDependencies": { 24 | "bufferutil": "^4.0.1", 25 | "utf-8-validate": ">=5.0.2" 26 | }, 27 | "peerDependenciesMeta": { 28 | "bufferutil": { 29 | "optional": true 30 | }, 31 | "utf-8-validate": { 32 | "optional": true 33 | } 34 | } 35 | }, 36 | "node_modules/zlib": { 37 | "version": "1.0.5", 38 | "resolved": "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz", 39 | "integrity": "sha512-40fpE2II+Cd3k8HWTWONfeKE2jL+P42iWJ1zzps5W51qcTsOUKM5Q5m2PFb0CLxlmFAaUuUdJGc3OfZy947v0w==", 40 | "hasInstallScript": true, 41 | "engines": { 42 | "node": ">=0.2.0" 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "websocker-checkboxes", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "ws": "^8.18.0", 15 | "zlib": "^1.0.5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | const grid = document.getElementById('checkbox-grid'); 2 | const totalConnectionsElement = document.getElementById('total-connections'); 3 | const activeUsersElement = document.getElementById('active-users'); 4 | const connectBtn = document.getElementById('connect-btn'); 5 | const usernameInput = document.getElementById('username-input'); 6 | const usernameDisplay = document.getElementById('username-display'); 7 | const timerElement = document.createElement('div'); // Timer display 8 | 9 | let checkboxes = new Array(100000).fill(false); 10 | let ws; 11 | let countdownInterval; 12 | 13 | const CHECKBOX_BATCH_SIZE = 500; // Number of checkboxes to load at a time 14 | let loadedCheckboxes = 0; // Track how many checkboxes have been loaded 15 | let isConnected = false; // Track connection status 16 | 17 | // Automatically connect to WebSocket to get initial stats and checkbox state 18 | connectToWebSocket(); 19 | 20 | function connectToWebSocket() { 21 | const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; 22 | const host = window.location.host; 23 | const wsUrl = `${protocol}://${host}/`; 24 | ws = new WebSocket(wsUrl); 25 | 26 | ws.onopen = () => { 27 | console.log('WebSocket connection opened'); 28 | }; 29 | 30 | ws.onmessage = (event) => { 31 | const data = JSON.parse(event.data); 32 | if (Array.isArray(data)) { 33 | // Received checkbox state array 34 | checkboxes = data; 35 | renderCheckboxes(); 36 | } else if (data.totalConnections !== undefined && data.activeUsers !== undefined) { 37 | // Received stats update 38 | totalConnectionsElement.textContent = data.totalConnections; 39 | activeUsersElement.textContent = data.activeUsers; 40 | } else if (data.index !== undefined && data.checked !== undefined) { 41 | // Received individual checkbox update 42 | checkboxes[data.index] = data.checked; 43 | updateCheckbox(data.index, data.checked); 44 | } 45 | }; 46 | 47 | ws.onclose = () => { 48 | console.log('WebSocket connection closed'); 49 | connectBtn.style.display = 'inline-block'; 50 | grid.style.display = 'none'; 51 | timerElement.textContent = ''; // Clear the timer display 52 | isConnected = false; // Reset connection status 53 | disableCheckboxes(); // Disable checkboxes on disconnect 54 | }; 55 | 56 | ws.onerror = (error) => { 57 | console.error('WebSocket error:', error); 58 | }; 59 | } 60 | 61 | function startConnection() { 62 | grid.style.display = 'grid'; 63 | connectBtn.style.display = 'none'; 64 | usernameDisplay.appendChild(timerElement); // Add the timer to the username display 65 | startTimer(); 66 | isConnected = true; // Set connection status to true 67 | enableCheckboxes(); // Enable checkboxes after connection 68 | } 69 | 70 | function handleCheckboxChange(index) { 71 | if (!isConnected) return; // Prevent interaction if not connected 72 | const newCheckedState = !checkboxes[index]; 73 | checkboxes[index] = newCheckedState; 74 | ws.send(JSON.stringify({ index, checked: newCheckedState })); 75 | } 76 | 77 | function renderCheckboxes() { 78 | const fragment = document.createDocumentFragment(); 79 | for (let i = loadedCheckboxes; i < Math.min(checkboxes.length, loadedCheckboxes + CHECKBOX_BATCH_SIZE); i++) { 80 | const checkbox = document.createElement('input'); 81 | checkbox.type = 'checkbox'; 82 | checkbox.id = `checkbox-${i}`; 83 | checkbox.className = 'checkbox'; 84 | checkbox.checked = checkboxes[i]; 85 | checkbox.disabled = !isConnected; // Disable or enable based on connection status 86 | checkbox.onchange = () => handleCheckboxChange(i); 87 | fragment.appendChild(checkbox); 88 | } 89 | grid.appendChild(fragment); 90 | loadedCheckboxes += CHECKBOX_BATCH_SIZE; 91 | } 92 | 93 | function updateCheckbox(index, checked) { 94 | const checkbox = document.getElementById(`checkbox-${index}`); 95 | if (checkbox) { 96 | checkbox.checked = checked; 97 | } 98 | } 99 | 100 | function startTimer() { 101 | let timeLeft = 60; 102 | timerElement.textContent = `Time remaining: ${timeLeft}s`; 103 | countdownInterval = setInterval(() => { 104 | timeLeft--; 105 | timerElement.textContent = `Time remaining: ${timeLeft}s`; 106 | if (timeLeft <= 0) { 107 | clearInterval(countdownInterval); 108 | ws.close(); 109 | disableCheckboxes(); // Disable all checkboxes when the timer ends 110 | } 111 | }, 1000); 112 | } 113 | 114 | function enableCheckboxes() { 115 | document.querySelectorAll('.checkbox').forEach(checkbox => { 116 | checkbox.disabled = false; 117 | }); 118 | } 119 | 120 | function disableCheckboxes() { 121 | document.querySelectorAll('.checkbox').forEach(checkbox => { 122 | checkbox.disabled = true; 123 | }); 124 | grid.style.display = 'none'; // Hide the grid after disconnection 125 | connectBtn.style.display = 'inline-block'; // Show the connect button again 126 | } 127 | 128 | // Lazy loading: load more checkboxes as the user scrolls 129 | window.addEventListener('scroll', () => { 130 | if (window.innerHeight + window.scrollY >= document.body.offsetHeight) { 131 | renderCheckboxes(); 132 | } 133 | }); 134 | 135 | usernameInput.addEventListener('input', () => { 136 | const username = usernameInput.value.trim(); 137 | connectBtn.disabled = username === ''; 138 | usernameDisplay.textContent = username ? `Welcome, ${username}` : ''; 139 | }); 140 | 141 | connectBtn.addEventListener('click', () => { 142 | startConnection(); 143 | }); 144 | 145 | // Initial rendering of checkboxes 146 | renderCheckboxes(); 147 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const WebSocket = require('ws'); 2 | const http = require('http'); 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const zlib = require('zlib'); 6 | 7 | // Create HTTP server to serve the static files (HTML, JS, CSS) 8 | const server = http.createServer((req, res) => { 9 | let filePath = '.' + req.url; 10 | if (filePath === './') { 11 | filePath = './index.html'; 12 | } 13 | 14 | const extname = String(path.extname(filePath)).toLowerCase(); 15 | const mimeTypes = { 16 | '.html': 'text/html', 17 | '.js': 'application/javascript', 18 | '.css': 'text/css', 19 | '.json': 'application/json', 20 | '.png': 'image/png', 21 | '.jpg': 'image/jpg', 22 | '.gif': 'image/gif', 23 | '.svg': 'image/svg+xml', 24 | '.wav': 'audio/wav', 25 | '.mp4': 'video/mp4', 26 | '.woff': 'application/font-woff', 27 | '.ttf': 'application/font-ttf', 28 | '.eot': 'application/vnd.ms-fontobject', 29 | '.otf': 'application/font-otf', 30 | '.wasm': 'application/wasm' 31 | }; 32 | 33 | const contentType = mimeTypes[extname] || 'application/octet-stream'; 34 | 35 | fs.readFile(filePath, (error, content) => { 36 | if (error) { 37 | if (error.code == 'ENOENT') { 38 | fs.readFile('./404.html', (error, content) => { 39 | res.writeHead(404, { 'Content-Type': 'text/html' }); 40 | res.end(content, 'utf-8'); 41 | }); 42 | } else { 43 | res.writeHead(500); 44 | res.end('Sorry, there was an error: ' + error.code + ' ..\n'); 45 | } 46 | } else { 47 | res.writeHead(200, { 'Content-Type': contentType }); 48 | res.end(content, 'utf-8'); 49 | } 50 | }); 51 | }); 52 | 53 | // Create WebSocket server on top of HTTP server 54 | const wss = new WebSocket.Server({ server }); 55 | 56 | let totalConnections = 0; 57 | let activeUsers = 0; 58 | let checkboxState = new Array(100000).fill(false); 59 | 60 | const userActionLimits = {}; // Store user actions count and timestamps 61 | 62 | wss.on('connection', (ws) => { 63 | totalConnections++; 64 | activeUsers++; 65 | broadcastStats(); 66 | 67 | // Send initial checkbox state to new connection 68 | ws.send(JSON.stringify(checkboxState)); 69 | ws.send(JSON.stringify({ totalConnections, activeUsers })); 70 | 71 | ws.on('message', (message) => { 72 | const data = JSON.parse(message); 73 | const userId = ws._socket.remoteAddress; // Example user identifier 74 | const currentTime = Date.now(); 75 | 76 | if (!userActionLimits[userId]) { 77 | userActionLimits[userId] = { count: 0, lastActionTime: currentTime }; 78 | } 79 | 80 | const timeSinceLastAction = currentTime - userActionLimits[userId].lastActionTime; 81 | 82 | if (timeSinceLastAction > 1000) { // Reset count after 1 second 83 | userActionLimits[userId].count = 0; 84 | userActionLimits[userId].lastActionTime = currentTime; 85 | } 86 | 87 | if (userActionLimits[userId].count >= 5) { 88 | ws.send(JSON.stringify({ error: "Rate limit exceeded" })); 89 | return; 90 | } 91 | 92 | userActionLimits[userId].count += 1; 93 | 94 | if (data.index !== undefined && data.checked !== undefined) { 95 | checkboxState[data.index] = data.checked; 96 | broadcastCheckboxState(data); 97 | } 98 | }); 99 | 100 | ws.on('close', () => { 101 | activeUsers--; 102 | broadcastStats(); 103 | }); 104 | }); 105 | 106 | function broadcastStats() { 107 | const stats = { totalConnections, activeUsers }; 108 | wss.clients.forEach((client) => { 109 | if (client.readyState === WebSocket.OPEN) { 110 | client.send(JSON.stringify(stats)); 111 | } 112 | }); 113 | } 114 | 115 | function broadcastCheckboxState(data) { 116 | wss.clients.forEach((client) => { 117 | if (client.readyState === WebSocket.OPEN) { 118 | client.send(JSON.stringify(data)); 119 | } 120 | }); 121 | } 122 | 123 | // Listen on port 8080 124 | const PORT = 8080; 125 | server.listen(PORT, () => { 126 | console.log(`Server is listening on port ${PORT}`); 127 | }); 128 | --------------------------------------------------------------------------------