Skip to content

Commit 7227388

Browse files
author
Brian Genisio
committed
added server.js with the ability to send a validation request to the client for view purposes
1 parent a9c5c5b commit 7227388

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
1. Clone the repository
66
2. Add questions to the `questions.json` file
7-
3. Run `python server.py` to start the server
7+
3. Run `node server.js` to start the server
88
4. Open `localhost:3000` in your browser
99

10+
Note: the legacy way of doing it was `python server.py` but python does not support websockets out of the box where Node.js does. So `server.py` is deprecated in favor of `server.js`.
11+
1012
## How to add questions
1113

1214
Questions are stored in the `questions.json` file. The questions are stored in an array of objects. Each object represents a question and has the following properties:
@@ -27,3 +29,11 @@ As the user interacts with the quiz, the answers are stored in the `answers.json
2729

2830
To run the solution, run `python format_answers.py`. This will read the `questions.json` and `answers.json` files and format the answers into a human readable format.
2931

32+
## How to display the correct/incorrect state to the user
33+
First, you must use the `node` version of the server (`server.js`). It exposes a websocket and a `/validate` endpoint you can POST to. It will request the HTML to annotate the UX.
34+
35+
If you want the Web UX to display the correct/incorrect state to the user, you can make a request to the quiz server:
36+
```
37+
curl -X POST localhost:3000/validate &> /dev/null
38+
```
39+

index.html

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
</style>
2121
</head>
2222
<body>
23-
<button onClick="validateAnswers()">Validate Answers</button>
2423
<div id="surveyContainer"></div>
2524

2625

@@ -31,6 +30,39 @@
3130
let saveTimeout;
3231
let answers = {};
3332
let validating = false;
33+
let socket = null;
34+
35+
// Initialize WebSocket connection
36+
function connectWebSocket() {
37+
try {
38+
socket = new WebSocket(`ws://${window.location.host}`);
39+
40+
socket.onopen = () => {
41+
console.log('WebSocket connected');
42+
};
43+
44+
socket.onmessage = (event) => {
45+
validateAnswers();
46+
};
47+
48+
socket.onclose = () => {
49+
console.log('WebSocket disconnected');
50+
51+
// Try to reconnect after a few seconds
52+
setTimeout(connectWebSocket, 5000);
53+
socket = null;
54+
};
55+
56+
socket.onerror = (error) => {
57+
console.error('WebSocket error:', error);
58+
};
59+
} catch (e) {
60+
console.error('Failed to connect WebSocket:', e);
61+
62+
// Try to reconnect after a few seconds
63+
setTimeout(connectWebSocket, 5000);
64+
}
65+
}
3466

3567
Promise.all([
3668
fetch('/questions.json')
@@ -53,6 +85,9 @@
5385
survey.onValueChanged.add(onValueChanged);
5486
survey.onTextMarkdown.add(onTextMarkdown);
5587
survey.render(document.getElementById("surveyContainer"));
88+
89+
// Connect to the WebSocket server after survey is initialized
90+
connectWebSocket();
5691
})
5792
.catch(error => console.error('Error loading data:', error));
5893

server.js

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
const http = require('http');
22
const fs = require('fs');
33
const path = require('path');
4+
const WebSocket = require('ws');
5+
6+
// Store active WebSocket connections
7+
const clients = new Set();
48

59
class RequestHandler {
610
constructor(req, res) {
@@ -19,7 +23,8 @@ class RequestHandler {
1923

2024
handleGet() {
2125
// Serve static files like SimpleHTTPRequestHandler
22-
const filePath = path.join(process.cwd(), this.req.url === '/' ? 'index.html' : this.req.url);
26+
const url = new URL(this.req.url, `http://${this.req.headers.host}`);
27+
const filePath = path.join(process.cwd(), url.pathname === '/' ? 'index.html' : url.pathname);
2328

2429
fs.readFile(filePath, (err, data) => {
2530
if (err) {
@@ -82,6 +87,19 @@ class RequestHandler {
8287
this.sendJsonResponse(400, { error: 'Invalid JSON' });
8388
}
8489
});
90+
} else if (this.req.url === '/validate') {
91+
// Send message to all connected WebSocket clients
92+
clients.forEach(client => {
93+
if (client.readyState === WebSocket.OPEN) {
94+
client.send(JSON.stringify({ type: 'validate' }));
95+
}
96+
});
97+
98+
this.sendJsonResponse(200, {
99+
status: 'success',
100+
message: 'Validation message sent to all connected clients',
101+
clientCount: clients.size
102+
});
85103
} else {
86104
this.sendJsonResponse(404, { error: 'Not Found' });
87105
}
@@ -110,8 +128,30 @@ function runServer(port = 3000) {
110128
}
111129
});
112130

131+
// Create WebSocket server using the ws module
132+
const wss = new WebSocket.Server({ server });
133+
134+
wss.on('connection', (ws, req) => {
135+
// Add new client to the Set
136+
clients.add(ws);
137+
console.log('WebSocket connection established, total clients:', clients.size);
138+
139+
// Handle WebSocket connection close
140+
ws.on('close', () => {
141+
clients.delete(ws);
142+
console.log('WebSocket connection closed, remaining clients:', clients.size);
143+
});
144+
145+
// Handle errors
146+
ws.on('error', (error) => {
147+
console.error('WebSocket error:', error);
148+
clients.delete(ws);
149+
});
150+
});
151+
113152
server.listen(port, () => {
114153
console.log(`Server running on port ${port}...`);
154+
console.log(`WebSocket server available at ws://localhost:${port}`);
115155
});
116156

117157
server.on('error', (e) => {

0 commit comments

Comments
 (0)