diff options
author | Tom Smeding <tom@tomsmeding.com> | 2022-08-22 15:28:10 +0200 |
---|---|---|
committer | Tom Smeding <tom@tomsmeding.com> | 2022-08-22 15:28:10 +0200 |
commit | d1c02019db9a6fb2ab053091c9a4a32dc00b8b50 (patch) | |
tree | dbe9b8846f14d0aa65bb34cdcb0cc8c4d7031d1f /server.js |
Initial
Diffstat (limited to 'server.js')
-rwxr-xr-x | server.js | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/server.js b/server.js new file mode 100755 index 0000000..21d9094 --- /dev/null +++ b/server.js @@ -0,0 +1,180 @@ +#!/usr/bin/env node +const express = require("express"); +const http = require("http"); +const socketio = require("socket.io"); +const fs = require("fs"); + +const [DBFILE, PORT] = (() => { + if (process.argv.length == 3) { + return [process.argv[2], 8000]; + } else if (process.argv.length == 4) { + return [process.argv[2], +process.argv[3]]; + } else { + console.log("Usage: ./server.js <dbfile.txt> [port]"); + process.exit(1); + } +})(); + +const app = express(); +const httpServer = http.Server(app); +const io = socketio(httpServer); + +const STATIC = __dirname + "/static"; + + +// ------------------------------ +// "database" +// ------------------------------ + +class Database { + #filename; + #list; + #lastbackup; + + constructor(filename) { + this.#filename = filename; + this.#list = JSON.parse(fs.readFileSync(this.#filename)); + this.#lastbackup = new Date(); + } + + add_new(string) { + for (let i = 0; i < this.#list.length; i++) { + if (this.#list[i][1] == string) { + return "already existed"; + } + } + + this.#list.push([0, string]); + this.#sort(); + this.#persist(); + + return null; + } + + set_votes(string, votes) { + for (let i = 0; i < this.#list.length; i++) { + if (this.#list[i][1] == string) { + this.#list[i][0] = votes; + this.#sort(); + this.#persist(); + return null; + } + } + + return `'${string}' does not exist`; + } + + remove(string) { + for (let i = 0; i < this.#list.length; i++) { + if (this.#list[i][1] == string) { + this.#list.splice(i, 1); + this.#persist(); + return null; + } + } + + return "does not exist"; + } + + get() { + return this.#list; + } + + #sort() { + this.#list.sort((a, b) => { + if (a[0] != b[0]) return b[0] - a[0]; + if (a[1] < b[1]) return -1; + if (a[1] > b[1]) return 1; + return 0; + }); + } + + #persist() { + fs.writeFile(this.#filename, JSON.stringify(this.#list), err => { + if (err != null) { + console.error("Error writing to file:", err); + console.log(JSON.stringify(this.#list)); + } + }); + + const now = new Date(); + if (now - this.#lastbackup > 60 * 1000) { + const dotidx = this.#filename.lastIndexOf("."); + const bakfname = + dotidx == -1 + ? this.#filename + now.getTime() + : this.#filename.slice(0, dotidx) + "." + now.getTime() + this.#filename.slice(dotidx); + fs.writeFile(bakfname, JSON.stringify(this.#list), err => { + if (err != null) { + console.error("Error copying to backup file:", err); + } else { + console.log(`Copied to ${bakfname}`); + } + }); + + this.#lastbackup = now; + } + } +}; + +const database = new Database(DBFILE); + + +// ------------------------------ +// Express routes +// ------------------------------ + +app.use(express.static(STATIC)); + +app.get("/", (req, res) => { + res.sendFile(STATIC + "/index.html"); +}); + + +// ------------------------------ +// socket.io bindings +// ------------------------------ + +let next_socket_id = 1; +let num_connected = 0; + +io.on("connection", socket => { + const socket_id = next_socket_id++; + if (++num_connected >= 2) { + socket.emit("alert", `You're user number ${num_connected}, multi-user settings will mess up!`); + } + console.log(`new socket connection id=${socket_id} (nc=${num_connected})`); + + socket.on("disconnect", () => { + num_connected--; + console.log(`disconnect id=${socket_id} (nc=${num_connected})`); + }); + + socket.on("new", string => { + const res = database.add_new(string); + if (res != null) socket.emit("error", res); + }); + + socket.on("set_votes", (string, votes) => { + const res = database.set_votes(string, votes); + if (res != null) socket.emit("error", res); + }); + + socket.on("delete", string =>{ + const res = database.remove(string); + if (res != null) socket.emit("error", res); + }); + + socket.emit("init", database.get()); +}); + + +// ------------------------------ +// setup +// ------------------------------ + +const server = httpServer.listen(PORT, () => { + const host = server.address().address; + const port = server.address().port; + console.log("Server listening at http://" + host + ":" + port); +}); |