diff --git a/app.js b/app.js index df61af5..dac2f97 100644 --- a/app.js +++ b/app.js @@ -1,23 +1,23 @@ -const express = require('express'); -const session = require('express-session'); -const fs = require('fs'); -const path = require('path'); -const morgan = require('morgan'); -const bodyParser = require('body-parser'); -const sqlite3 = require('sqlite3').verbose(); -const bole = require('bole'); +const express = require('express') +const session = require('express-session') +const fs = require('fs') +const path = require('path') +const morgan = require('morgan') +const bodyParser = require('body-parser') +const sqlite3 = require('sqlite3').verbose() +const bole = require('bole') -const config = require('./config'); +const config = require('./config') -const log = bole('app'); -const app = express(); +const log = bole('app') +const app = express() // Create an SQLite database and initialize tables const db = new sqlite3.Database(config.db_path, (err) => { if (err) { - log.error("Error opening SQLite database:", err.message); + log.error('Error opening SQLite database:', err.message) } else { - log.info("Connected to SQLite database", config.db_path); + log.info('Connected to SQLite database', config.db_path) db.run(` CREATE TABLE IF NOT EXISTS button_clicks ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -27,133 +27,133 @@ const db = new sqlite3.Database(config.db_path, (err) => { timestamp DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, time_difference INTEGER -- Add this column for time difference ) - `); + `) db.run(` CREATE TABLE IF NOT EXISTS animals ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL ) - `); + `) } -}); +}) -var accessLogStream = fs.createWriteStream( - path.join(__dirname, "log", "access.log"), - { flags: "a" }, -); +const accessLogStream = fs.createWriteStream( + path.join(__dirname, 'log', 'access.log'), + { flags: 'a' } +) -app.use(bodyParser.json()); -app.use(morgan("combined", { stream: accessLogStream })); +app.use(bodyParser.json()) +app.use(morgan('combined', { stream: accessLogStream })) app.use(session({ resave: false, saveUninitialized: false, - secret: config.session_token, -})); + secret: config.session_token +})) -var animals; +let animals // check and load animals into redis try { - fs.readFile("./animals.json", function (err, data) { + fs.readFile('./animals.json', function (err, data) { if (err) { - throw err; + throw err } - var jsondata = JSON.parse(data); - animals = jsondata.animals; + const jsondata = JSON.parse(data) + animals = jsondata.animals for (const animal of animals) { db.run(` INSERT INTO animals(name) SELECT ? WHERE NOT EXISTS(SELECT 1 FROM animals WHERE name = ?); `, - [animal, animal], - (err) => { - if (err) { - log.error(`Error inserting animal ${animal}: `, err.message); - } else { - log.info(`Success inserting animal ${animal}`); - } - }, - ); + [animal, animal], + (err) => { + if (err) { + log.error(`Error inserting animal ${animal}: `, err.message) + } else { + log.info(`Success inserting animal ${animal}`) + } + } + ) } - }); + }) } catch (error) { - log.error("Error loading animals:", error); - animals = ["Dog", "Cat", "Elephant", "Lion", "Giraffe"]; + log.error('Error loading animals:', error) + animals = ['Dog', 'Cat', 'Elephant', 'Lion', 'Giraffe'] } // Serve the HTML file -app.get("/", (req, res) => { - if (typeof req.session.cookie.expires == "undefined") { - var hour = 3600000; - req.session.cookie.expires = new Date(Date.now() + hour); - req.session.cookie.maxAge = hour; +app.get('/', (req, res) => { + if (typeof req.session.cookie.expires === 'undefined') { + const hour = 3600000 + req.session.cookie.expires = new Date(Date.now() + hour) + req.session.cookie.maxAge = hour } - res.sendFile(__dirname + "/index.html"); -}); -app.get("/asset/frontend.js", (req, res) => { - res.sendFile(__dirname + "/asset/frontend.js"); -}); + res.sendFile(path.join(__dirname, 'index.html')) +}) +app.get('/asset/frontend.js', (req, res) => { + res.sendFile(path.join(__dirname, 'asset/frontend.js')) +}) -app.get("/newSession", (req, res) => { +app.get('/newSession', (req, res) => { // XXX - log.info(req.session); - req.session.regenerate(); - log.info(req.session); -}); + log.info(req.session) + req.session.regenerate() + log.info(req.session) +}) // Route to get a random animal name -app.get("/getNextAnimal", async (req, res) => { +app.get('/getNextAnimal', async (req, res) => { try { // TODO this is currently random, and should have a bit of reasoning behind the next choice - const randomIndex = Math.floor(Math.random() * animals.length); - const randomAnimal = animals[randomIndex]; - res.json({ animalName: randomAnimal }); + const randomIndex = Math.floor(Math.random() * animals.length) + const randomAnimal = animals[randomIndex] + res.json({ animalName: randomAnimal }) } catch (error) { - log.error("Error fetching random animal:", error); - res.status(500).json({ error: "Internal server error" }); + log.error('Error fetching random animal:', error) + res.status(500).json({ error: 'Internal server error' }) } -}); +}) // Route to record button clicks along with session IDs in SQLite -app.post("/recordButtonClick", (req, res) => { +app.post('/recordButtonClick', (req, res) => { try { - //const { buttonName, sessionId } = req.body; - const result = req.body; - log.error(result); + // const { buttonName, sessionId } = req.body; + const result = req.body + log.error(result) db.run( - "INSERT INTO button_clicks (session_id, animal_name, button_name, timestamp, time_difference) VALUES (?, ?, ?, ?, ?)", + 'INSERT INTO button_clicks (session_id, animal_name, button_name, timestamp, time_difference) VALUES (?, ?, ?, ?, ?)', [ result.session, result.animal, result.button, result.time, - result.difference, + result.difference ], (err) => { if (err) { - log.error("Error recording button click:", err.message); - res.status(500).json({ error: "Internal server error" }); + log.error('Error recording button click:', err.message) + res.status(500).json({ error: 'Internal server error' }) } else { - res.sendStatus(200); + res.sendStatus(200) } - }, - ); + } + ) } catch (error) { - log.error("Error recording button click:", error); - res.status(500).json({ error: "Internal server error" }); + log.error('Error recording button click:', error) + res.status(500).json({ error: 'Internal server error' }) } -}); +}) // Route to show the current results from SQLite -app.get("/results", async (req, res) => { +app.get('/results', async (req, res) => { try { const results = { count: {}, - avgTimes: {}, - }; + avgTimes: {} + } const getCount = new Promise((resolve, reject) => { db.all( ` @@ -163,19 +163,19 @@ app.get("/results", async (req, res) => { `, (err, rows) => { if (err) { - reject("getCount: " + err.message); + reject(new Error('getCount: ' + err.message)) } else { rows.forEach((row) => { - if (typeof results.count[row.animal_name] == "undefined") { - results.count[row.animal_name] = {}; + if (typeof results.count[row.animal_name] === 'undefined') { + results.count[row.animal_name] = {} } - results.count[row.animal_name][row.button_name] = row.count; - }); - resolve(); + results.count[row.animal_name][row.button_name] = row.count + }) + resolve() } - }, - ); - }); + } + ) + }) const getAvgTime = new Promise((resolve, reject) => { db.all( ` @@ -185,19 +185,19 @@ app.get("/results", async (req, res) => { `, (err, rows) => { if (err) { - reject("getAvgTime: " + err.message); + reject(new Error('getAvgTime: ' + err.message)) } else { rows.forEach((row) => { - if (typeof results.avgTimes[row.animal_name] == "undefined") { - results.avgTimes[row.animal_name] = {}; + if (typeof results.avgTimes[row.animal_name] === 'undefined') { + results.avgTimes[row.animal_name] = {} } - results.avgTimes[row.animal_name][row.button_name] = row.time; - }); - resolve(); + results.avgTimes[row.animal_name][row.button_name] = row.time + }) + resolve() } - }, - ); - }); + } + ) + }) const getTotalAvgTime = new Promise((resolve, reject) => { db.all( ` @@ -207,27 +207,27 @@ app.get("/results", async (req, res) => { `, (err, rows) => { if (err) { - reject("getTotalAvgTime: " + err.message); + reject(new Error('getTotalAvgTime: ' + err.message)) } else { rows.forEach((row) => { - if (typeof results.avgTimes[row.animal_name] == "undefined") { - results.avgTimes[row.animal_name] = {}; + if (typeof results.avgTimes[row.animal_name] === 'undefined') { + results.avgTimes[row.animal_name] = {} } - results.avgTimes[row.animal_name]["total"] = row.time; - }); - resolve(); + results.avgTimes[row.animal_name].total = row.time + }) + resolve() } - }, - ); - }); - await Promise.all([getCount, getTotalAvgTime, getAvgTime]); - res.json(results); + } + ) + }) + await Promise.all([getCount, getTotalAvgTime, getAvgTime]) + res.json(results) } catch (error) { - log.error("Error fetching results:", error); - res.status(500).json({ error: "Internal server error" }); + log.error('Error fetching results:', error) + res.status(500).json({ error: 'Internal server error' }) } -}); +}) -module.exports = app; +module.exports = app // vim:set sts=2 sw=2 et: diff --git a/app.test.js b/app.test.js index 6450016..babd137 100644 --- a/app.test.js +++ b/app.test.js @@ -1,29 +1,29 @@ // app.test.js -const request = require('supertest'); -const app = require('./app'); +const request = require('supertest') +const app = require('./app') describe('GET /', () => { it('should respond with 200 status', async () => { - const response = await request(app).get('/'); - expect(response.statusCode).toBe(200); - //expect(response.body.message).toBe('Hello, World!'); - }); -}); + const response = await request(app).get('/') + expect(response.statusCode).toBe(200) + // expect(response.body.message).toBe('Hello, World!'); + }) +}) describe('GET /asset/frontend.js', () => { it('should respond with 200 status', async () => { - const response = await request(app).get('/asset/frontend.js'); - expect(response.statusCode).toBe(200); - //expect(response.body.message).toBe('Hello, World!'); - }); -}); + const response = await request(app).get('/asset/frontend.js') + expect(response.statusCode).toBe(200) + // expect(response.body.message).toBe('Hello, World!'); + }) +}) describe('GET /getNextAnimal', () => { it('should respond with 200 status', async () => { - const response = await request(app).get('/getNextAnimal'); - expect(response.statusCode).toBe(200); - //expect(response.body.message).toBe('Hello, World!'); - }); -}); + const response = await request(app).get('/getNextAnimal') + expect(response.statusCode).toBe(200) + // expect(response.body.message).toBe('Hello, World!'); + }) +}) // vim:set sts=2 sw=2 et: diff --git a/asset/frontend.js b/asset/frontend.js index 544eed3..eb26ab5 100644 --- a/asset/frontend.js +++ b/asset/frontend.js @@ -1,103 +1,103 @@ // Initialize session variables -let sessionStartTime; -let lastButtonClickTime; +let sessionStartTime +let lastButtonClickTime // Function to fetch a random animal name from the server -async function getNextAnimal() { +async function getNextAnimal () { try { - const response = await fetch("/getNextAnimal"); - const data = await response.json(); - document.getElementById("animal-name").textContent = data.animalName; + const response = await fetch('/getNextAnimal') + const data = await response.json() + document.getElementById('animal-name').textContent = data.animalName } catch (error) { - console.error("Error fetching data:", error); + console.error('Error fetching data:', error) } } // Function to set or retrieve the session ID cookie -function getSessionId() { +function getSessionId () { const sessionId = document.cookie.replace( /(?:(?:^|.*;\s*)sessionId\s*=\s*([^;]*).*$)|^.*$/, - "$1", - ); + '$1' + ) if (!sessionId) { - return newSession(); + return newSession() } - return sessionId; + return sessionId } -async function newSession() { - setSessionStartTime(); +async function newSession () { + setSessionStartTime() try { - const response = await fetch("/newSession"); - const data = await response.json(); - document.getElementById("animal-name").textContent = data.animalName; + const response = await fetch('/newSession') + const data = await response.json() + document.getElementById('animal-name').textContent = data.animalName } catch (error) { - console.error("Error fetching data:", error); + console.error('Error fetching data:', error) } - getSessionId(); + getSessionId() } // Function to set session start time -function setSessionStartTime() { - sessionStartTime = new Date(); +function setSessionStartTime () { + sessionStartTime = new Date() } // Function to calculate and display time difference -function displayTimeDifference() { +function displayTimeDifference () { if (sessionStartTime && lastButtonClickTime) { - const timeDifference = lastButtonClickTime - sessionStartTime; - console.log(`Time since session start: ${timeDifference} milliseconds`); + const timeDifference = lastButtonClickTime - sessionStartTime + console.log(`Time since session start: ${timeDifference} milliseconds`) // You can display the time difference on the page as needed } } // Function to record button clicks on the server -async function recordButtonClick(buttonName, sessionId) { +async function recordButtonClick (buttonName, sessionId) { try { - const currentTime = new Date(); + const currentTime = new Date() if (lastButtonClickTime) { - const timeDifference = currentTime - lastButtonClickTime; + const timeDifference = currentTime - lastButtonClickTime // Include the time difference in the POST request data - const animal = document.getElementById("animal-name").textContent; + const animal = document.getElementById('animal-name').textContent const bodyData = JSON.stringify({ - animal: animal, + animal, button: buttonName, session: sessionId, difference: timeDifference, - time: sessionStartTime, - }); - //console.log(bodyData); - await fetch("/recordButtonClick", { - method: "POST", + time: sessionStartTime + }) + // console.log(bodyData); + await fetch('/recordButtonClick', { + method: 'POST', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json' }, - body: bodyData, - }); + body: bodyData + }) } - lastButtonClickTime = currentTime; // Record the timestamp of the button click - displayTimeDifference(); // Calculate and display time difference + lastButtonClickTime = currentTime // Record the timestamp of the button click + displayTimeDifference() // Calculate and display time difference // TODO - slight delay before loading next animal, to show the user how long that decision took them - getNextAnimal(); // Load another random animal + getNextAnimal() // Load another random animal } catch (error) { - console.error("Error recording button click:", error); + console.error('Error recording button click:', error) } } // Add click event listeners to the buttons -document.getElementById("isCritterButton").addEventListener("click", () => { - recordButtonClick("is critter", getSessionId()); -}); +document.getElementById('isCritterButton').addEventListener('click', () => { + recordButtonClick('is critter', getSessionId()) +}) -document.getElementById("isNotCritterButton").addEventListener("click", () => { - recordButtonClick("is not critter", getSessionId()); -}); +document.getElementById('isNotCritterButton').addEventListener('click', () => { + recordButtonClick('is not critter', getSessionId()) +}) -document.getElementById("startOverButton").addEventListener("click", () => { - newSession(); - getNextAnimal(); -}); +document.getElementById('startOverButton').addEventListener('click', () => { + newSession() + getNextAnimal() +}) // Initial random animal load and session start time -getNextAnimal(); -setSessionStartTime(); +getNextAnimal() +setSessionStartTime() diff --git a/config.js b/config.js index 282a1d0..b3aa802 100644 --- a/config.js +++ b/config.js @@ -1,19 +1,19 @@ -const config = module.exports; -const PRODUCTION = process.env.NODE_ENV === 'production'; -const bole = require('bole'); +const config = module.exports +const PRODUCTION = process.env.NODE_ENV === 'production' +const bole = require('bole') config.express = { port: process.env.EXPRESS_PORT || 3000, - ip: '127.0.0.1', -}; + ip: '127.0.0.1' +} if (PRODUCTION) { - config.express.ip = '0.0.0.0'; - config.db_path = 'db/results.db'; - config.session_token = process.env.SESSION_TOKEN; + config.express.ip = '0.0.0.0' + config.db_path = 'db/results.db' + config.session_token = process.env.SESSION_TOKEN bole.output({ level: 'info', stream: process.stdout }) } else { - config.db_path = ':memory:'; - config.session_token = 'cat bag'; + config.db_path = ':memory:' + config.session_token = 'cat bag' bole.output({ level: 'debug', stream: process.stdout }) } diff --git a/package.json b/package.json index f1fdbea..80abd8d 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "server.js", "scripts": { "test": "./node_modules/.bin/jest", + "lint": "./node_modules/.bin/standard --ignore *.test.js .", "start": "node server.js", "act": "act -W ./.gitea/workflows/" }, diff --git a/server.js b/server.js index cc92c10..2ba47de 100644 --- a/server.js +++ b/server.js @@ -1,15 +1,15 @@ -const bole = require('bole'); -const config = require('./config'); -const app = require('./app'); -const log = bole('server'); +const bole = require('bole') +const config = require('./config') +const app = require('./app') +const log = bole('server') -app.listen(config.express.port, config.express.ip, function(error) { +app.listen(config.express.port, config.express.ip, function (error) { if (error) { - log.error('Unable to listen for connections', error); - process.exit(10); + log.error('Unable to listen for connections', error) + process.exit(10) } log.info('express is listening on http://' + config.express.ip + ':' + config.express.port) -}); +}) // vim:set sts=2 sw=2 et: