forked from bitembassy/trivia-game
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.js
120 lines (106 loc) · 3.79 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
const express = require('express')
, bodyParser = require('body-parser')
, cookieParser = require('cookie-parser')
, morgan = require('morgan')
, stylus = require('stylus')
, browserify = require('browserify-middleware')
, { EventEmitter } = require('events')
const SECRET_KEY = process.env.SECRET_KEY
// Init app
const app = express()
app.set('view engine', 'pug')
app.set('views', './views')
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser(SECRET_KEY))
app.use(morgan('combined'))
app.locals.pretty = true
app.use((req, res, next) => {
let username = req.signedCookies.username
if (username && scores.has(username)) {
req.username = res.locals.username = username
}
next()
})
// Web UI
app.use('/_', stylus.middleware({ src: 'www', serve: true }))
app.use('/_', express.static('www'))
app.get('/play.js', browserify('client/play.js'))
app.get('/game.js', browserify('client/game.js'))
app.get('/admin.js', browserify('client/admin.js'))
app.get('/', (req, res) => req.username
? res.redirect('/play')
: res.render('register'))
app.get('/play', (req, res) => req.username
? res.render('play')
: res.redirect('/'))
app.get('/join', (req, res) => res.render('register'))
app.get('/game', (req, res) => res.render('game'))
// Model
const buzzers = []
, scores = new Map
, state = { scores, buzzers }
, ev = new EventEmitter
, emitUpdate = _ => {
const json = encodeState(true)
console.log('state updated:', json)
ev.emit('update', json)
}
, encodeState = (asStr=false) =>
asStr ? JSON.stringify(encodeState()) : { buzzers, scores: [ ...scores ] }
// API
app.post('/join', (req, res) => {
const { name } = req.body
if (!name || typeof name != 'string' || name.length < 3 || name.length > 20) return res.sendStatus(400)
if (req.username === name) return res.redirect('play')
if (scores.has(name)) return res.sendStatus(409)
console.log(`joined: ${name}`)
scores.set(name, 0)
res.cookie('username', name, { signed: true, maxAge: 999999999999 })
res.redirect('play')
emitUpdate()
})
app.post('/buzz', (req, res) => {
if (!req.username) return res.sendStatus(403)
if (!scores.has(req.username)) return res.sendStatus(500)
console.log(`buzzed: ${req.username}`)
buzzers.includes(req.username) || buzzers.push(req.username)
res.send(encodeState())
emitUpdate()
})
app.get('/stream', (req, res) => {
res.set({
'X-Accel-Buffering': 'no'
, 'Cache-Control': 'no-cache'
, 'Content-Type': 'text/event-stream'
, 'Connection': 'keep-alive'
}).flushHeaders()
const keepAlive = setInterval(_ => res.write(': keepalive\n\n'), 15000)
const onUpdate = json => res.write(`data:${json}\n\n`)
ev.on('update', onUpdate)
onUpdate(encodeState(true))
req.on('close', _ => (ev.removeListener('update', onUpdate)
, clearInterval(keepAlive)))
})
// Admin
const auth = (req, res, next) =>
req.signedCookies.admin ? next() : res.sendStatus(401)
app.get('/admin', auth, (req, res) => res.render('admin'))
app.get(`/login-${SECRET_KEY}`, (req, res) =>
res.cookie('admin', 1, { signed: true, maxAge: 999999999999 }).redirect('admin'))
app.post('/reset', auth, (req, res) => {
console.log('admin: reset')
buzzers.splice(0, buzzers.length)
res.send(encodeState())
emitUpdate()
})
app.post('/score', auth, (req, res) => {
const { player, score } = req.body
console.log(`admin: update score for ${player}: ${score}`)
if (!scores.has(player)) return res.status(404).send('player not registered')
scores.set(player, +scores.get(player) + +score)
res.send(encodeState())
emitUpdate()
})
// Go!
app.listen(process.env.PORT || 26411, _ => console.log('Trivia server running'))