Skip to content

Commit 3bfd30e

Browse files
Refactored code in app.js and altered UI in CSS files
1 parent 52f39dc commit 3bfd30e

20 files changed

+5534
-4435
lines changed

app.js

+46-47
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
require("dotenv").config();
22

3+
// ========== Part #1. A redis client is instantiated here to fetch all of its non-expired cached information
4+
// ========== This includes Bus services, Bus Routes, Bus Stops & Bus ETAs for pre-load rendering
5+
// ========== and mitigate excessive load time for the web app on start
36
const redis = require("redis");
47
const url = require("url");
5-
68
const redis_username=process.env.REDIS_USERNAME;
79
const redis_password=process.env.REDIS_PASSWORD;
8-
910
const redis_endpoint_uri=process.env.REDIS_ENDPOINT_URI;
1011
const redis_db=process.env.REDIS_DB;
11-
1212
const redisStr=`redis://${redis_username}:${redis_password}@${redis_endpoint_uri}/${redis_db}`;
1313
const redisURL = url.parse(redisStr);
1414

@@ -19,7 +19,7 @@ redisClient.on("connect", () => {
1919
console.log("Successfully connected to Redis instance.");
2020
});
2121

22-
22+
// ================== Part #2. Most variables and constants are declared here
2323
const PORT = process.env.PORT || 3000;
2424
const ORIGIN=process.env.ORIGIN || `http://localhost:${PORT}`;
2525
const LTA_API_KEY=process.env.LTA_API_KEY;
@@ -45,6 +45,8 @@ router.use((req, res, next) => { // router middleware
4545
next();
4646
});
4747

48+
// ================== Part #3. All server side API calls are called via the below functions and the redis Client updatse its data
49+
// ================== storage with the API outputs in event the cache of its storage has expired (to fetch only up-to-date data)
4850
function resolveAsyncCall(reqOptions) {
4951
return new Promise(resolve => {
5052
request(reqOptions, function(err, res, body) {
@@ -53,7 +55,6 @@ function resolveAsyncCall(reqOptions) {
5355
});
5456
});
5557
}
56-
5758
async function asyncCall(transportation) {
5859
var arr_result=[];
5960
var offset = 0;
@@ -84,7 +85,6 @@ async function asyncCall(transportation) {
8485
resolve(arr_result);
8586
});
8687
};
87-
8888
router.post("/ltaodataservice/all/:transportation", async (req, res) => {
8989
try {
9090
let params=req.params;
@@ -122,7 +122,6 @@ router.post("/ltaodataservice/all/:transportation", async (req, res) => {
122122
});
123123
}
124124
});
125-
126125
router.post("/ltaodataservice/:transportation/:client_offset", async(req, res) => {
127126
try {
128127
let params=req.params;
@@ -207,14 +206,15 @@ router.post("/ltaodataservice/:transportation/:client_offset", async(req, res) =
207206
});
208207
}
209208
});
210-
211209
router.get("/wake_up", (req, res) => {
212210
res.json({"status":"app_is_awake"});
213211
});
214212

215213
const app = express();
216214
app.use(compression()); //use compression
217215

216+
// ================== Part #4. Server side socket is set up via socketio and http server below. Connection with client side must be
217+
// ================== established before bilateral messages can be exchanged
218218
const http = require("http");
219219
const socketio = require("socket.io");
220220
const server = http.createServer(app);
@@ -236,16 +236,20 @@ app.use(express.static(path.join(__dirname, "public")))
236236
.set("view engine", "html")
237237
.get("/", (req, res) => res.render("index.html"))
238238

239-
const onlineClients = new Set();
240-
const previousBusCode = new Map();
241-
const updateInterval = new Map();
242239

240+
241+
const onlineClients = new Set(); // Used to track the no. of connected client sockets and ids
242+
const previousBusCode = new Map(); // Stores the latst bus stop no. ETAs requested by a client
243+
const updateInterval = new Map(); // Stores latest intervalID of the socket tagged to its client to stop fetching data when not needed
244+
245+
246+
// whenever a new user logs onto the web app a new client socket shall be established and connected
243247
function onNewWebsocketConnection(socket) {
244248
console.info(`Server side socket[${socket.id}] connection established.`);
245249

246250
// awaits for client-side to callback and confirm connection.
247251
// echoes on the terminal every "back_to_server" message this socket sends
248-
socket.on("back_to_server", msg => { // socket.id callback from client-side
252+
socket.on("back_to_server", msg => {
249253
console.info(`Client side socket id: ${msg}`);
250254
if(msg==socket.id) {
251255
onlineClients.add(socket.id);
@@ -254,46 +258,42 @@ function onNewWebsocketConnection(socket) {
254258
}
255259
});
256260

257-
// server side socket receives bus stop code from client side socket
261+
// server side receives bus stop code from client side socket
258262
socket.on("bus_arrivals", bus_stop_code => {
263+
let intervalID=updateInterval.get(socket.id);
259264
let prevBusCode=previousBusCode.get(socket.id);
260-
let prevUpdateInterval=updateInterval.get(socket.id);
261-
262-
console.log(`Requesting bus stop: ${bus_stop_code}`);
263-
console.log(`Prev bus code: ${prevBusCode}. Prev update interval: ${prevUpdateInterval}`);
264265

266+
// when bus_stop_code is undefined it means client side has no required information
267+
// to transfer to server side to fetch the Bus ETAs
265268
if(typeof bus_stop_code==="undefined") {
266-
if( (typeof prevUpdateInterval!=="undefined") ) {
267-
clearInterval(prevUpdateInterval);
268-
prevUpdateInterval=undefined;
269-
updateInterval.set(socket.id, undefined);
270-
271-
prevBusCode=undefined;
272-
previousBusCode.set(socket.id, undefined);
273-
}
274-
} else if( (typeof prevBusCode==="undefined") || (prevBusCode !== bus_stop_code) ) {
275-
prevBusCode=bus_stop_code;
276-
previousBusCode.set(socket.id, bus_stop_code);
269+
if( (typeof intervalID!=="undefined") ) { // When user had requested for another bus stop's ETAs previously
270+
clearInterval(intervalID);
271+
updateInterval.set(socket.id, undefined);
277272

278-
if( (typeof prevUpdateInterval!=="undefined") ) {
279-
clearInterval(prevUpdateInterval);
280-
updateInterval.set(socket.id, undefined);
281-
}
273+
prevBusCode=undefined; // When user selects another bus stop, the ETAs for the previous selection should be removed
274+
previousBusCode.set(socket.id, undefined);
275+
}
276+
} else if( (typeof prevBusCode==="undefined") || (prevBusCode !== bus_stop_code) ) { // User has selected another bus stop
277+
previousBusCode.set(socket.id, bus_stop_code);
282278

283-
prevUpdateInterval = setInterval(() => {
284-
request({
285-
url: `${API_ENDPOINT}/BusArrivalv2?BusStopCode=${bus_stop_code}`,
286-
method: "GET",
287-
json: true,
288-
headers: {
289-
"AccountKey" : LTA_API_KEY,
290-
"accept" : "application/json"
291-
}
292-
}, (err, res, body) => {
293-
socket.emit("get_bus_arrivals_info", JSON.stringify(body["Services"]));
294-
});
295-
}, 10000);
296-
updateInterval.set(socket.id, prevUpdateInterval);
279+
if( (typeof intervalID!=="undefined") ) { // To stop fetch ETAs for previous bus stop selected
280+
clearInterval(intervalID);
281+
updateInterval.set(socket.id, undefined);
282+
}
283+
intervalID = setInterval(() => {
284+
request({
285+
url: `${API_ENDPOINT}/BusArrivalv2?BusStopCode=${bus_stop_code}`,
286+
method: "GET",
287+
json: true,
288+
headers: {
289+
"AccountKey" : LTA_API_KEY,
290+
"accept" : "application/json"
291+
}
292+
}, (err, res, body) => {
293+
socket.emit("get_bus_arrivals_info", JSON.stringify(body["Services"]));
294+
});
295+
}, 10000);
296+
updateInterval.set(socket.id, intervalID); // update the stored interval ID
297297
}
298298
});
299299

@@ -305,7 +305,6 @@ function onNewWebsocketConnection(socket) {
305305
});
306306
}
307307

308-
309308
// will fire for every new socket connection: every user logs onto the web app
310309
io.on("connection", onNewWebsocketConnection);
311310
// broadcast here

package.json

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "sg_transportation_app",
33
"version": "1.0.0",
4-
"description": "A realtime application which shows Bus Routes and Bus ETAs via LTA's API.",
4+
"description": "A realtime application to monitor Bus ETAs via LTA's API. Uses Redis, Node.js and Socket.IO.",
55
"main": "app.js",
66
"scripts": {
77
"start": "node app.js",
@@ -20,20 +20,24 @@
2020
"api",
2121
"redis",
2222
"proxy",
23-
"realtime"
23+
"realtime",
24+
"socketio"
2425
],
2526
"author": "geek_cc",
2627
"license": "ISC",
2728
"dependencies": {
28-
"compression": "^1.7.4",
29+
"path": "^0.12.7",
30+
"serve-favicon": "^2.5.0",
31+
2932
"consolidate": "^0.16.0",
30-
"express": "^4.17.1",
3133
"mustache": "^4.0.1",
3234
"openurl": "^1.1.1",
33-
"path": "^0.12.7",
34-
"redis": "^3.1.2",
35+
3536
"request": "^2.88.2",
36-
"serve-favicon": "^2.5.0",
37+
"compression": "^1.7.4",
38+
39+
"express": "^4.17.1",
40+
"redis": "^3.1.2",
3741
"socket.io": "^4.1.3",
3842
"dotenv": "^10.0.0"
3943
},

0 commit comments

Comments
 (0)