karaoke-eternal 1.0.0 → 2.0.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +10 -10
  2. package/build/client/447.83b0127845c2fa8729fe.js +1 -0
  3. package/build/client/715.83b0127845c2fa8729fe.js +1 -0
  4. package/build/client/718.83b0127845c2fa8729fe.js +1 -0
  5. package/build/client/851.83b0127845c2fa8729fe.js +1 -0
  6. package/build/{845.4be526e3a94d53aeceae.css → client/958.83b0127845c2fa8729fe.css} +53 -6
  7. package/build/client/958.83b0127845c2fa8729fe.js +1 -0
  8. package/build/{index.html → client/index.html} +1 -1
  9. package/build/{licenses.txt → client/licenses.txt} +208 -496
  10. package/build/client/main.83b0127845c2fa8729fe.css +2341 -0
  11. package/build/client/main.83b0127845c2fa8729fe.js +1 -0
  12. package/build/server/Library/Library.js +297 -0
  13. package/build/server/Library/ipc.js +13 -0
  14. package/build/server/Library/router.js +20 -0
  15. package/build/server/Library/socket.js +35 -0
  16. package/build/server/Media/Media.js +170 -0
  17. package/build/server/Media/fileTypes.js +8 -0
  18. package/build/server/Media/ipc.js +13 -0
  19. package/build/server/Media/router.js +97 -0
  20. package/build/server/Player/socket.js +66 -0
  21. package/build/server/Prefs/Prefs.js +181 -0
  22. package/build/server/Prefs/router.js +151 -0
  23. package/build/server/Prefs/socket.js +52 -0
  24. package/build/server/Queue/Queue.js +203 -0
  25. package/build/server/Queue/socket.js +83 -0
  26. package/build/server/Rooms/Rooms.js +171 -0
  27. package/build/server/Rooms/router.js +97 -0
  28. package/build/server/Rooms/socket.js +23 -0
  29. package/build/server/Scanner/FileScanner/FileScanner.js +166 -0
  30. package/build/server/Scanner/FileScanner/getConfig.js +32 -0
  31. package/build/server/Scanner/FileScanner/getFiles.js +61 -0
  32. package/build/server/Scanner/MetaParser/MetaParser.js +77 -0
  33. package/build/server/Scanner/MetaParser/defaultMiddleware.js +170 -0
  34. package/build/server/Scanner/Scanner.js +26 -0
  35. package/build/server/Scanner/ScannerQueue.js +62 -0
  36. package/build/server/User/User.js +206 -0
  37. package/build/server/User/router.js +366 -0
  38. package/build/server/lib/Database.js +39 -0
  39. package/build/server/lib/Errors.js +6 -0
  40. package/build/server/lib/IPCBridge.js +128 -0
  41. package/build/server/lib/Log.js +31 -0
  42. package/build/server/lib/accumulatedThrottle.js +16 -0
  43. package/build/server/lib/bcrypt.js +23 -0
  44. package/build/server/lib/cli.js +131 -0
  45. package/build/server/lib/getCdgName.js +18 -0
  46. package/build/server/lib/getFolders.js +8 -0
  47. package/build/server/lib/getHotMiddleware.js +22 -0
  48. package/build/server/lib/getIPAddress.js +14 -0
  49. package/build/server/lib/getPermutations.js +17 -0
  50. package/build/server/lib/getWindowsDrives.js +17 -0
  51. package/build/server/lib/parseCookie.js +13 -0
  52. package/build/server/lib/pushQueuesAndLibrary.js +22 -0
  53. package/{server → build/server}/lib/schemas/001-initial-schema.sql +26 -26
  54. package/build/server/lib/schemas/004-paths-rooms-data.sql +7 -0
  55. package/build/server/lib/schemas/005-roles.sql +32 -0
  56. package/build/server/lib/util.js +39 -0
  57. package/build/server/main.js +124 -0
  58. package/build/server/scannerWorker.js +59 -0
  59. package/build/server/serverWorker.js +219 -0
  60. package/build/server/socket.js +134 -0
  61. package/build/server/watcherWorker.js +51 -0
  62. package/build/shared/actionTypes.js +113 -0
  63. package/build/shared/types.js +1 -0
  64. package/package.json +111 -86
  65. package/build/267.4be526e3a94d53aeceae.js +0 -1
  66. package/build/591.4be526e3a94d53aeceae.js +0 -1
  67. package/build/598.4be526e3a94d53aeceae.js +0 -1
  68. package/build/799.4be526e3a94d53aeceae.js +0 -1
  69. package/build/845.4be526e3a94d53aeceae.js +0 -1
  70. package/build/main.4be526e3a94d53aeceae.css +0 -2034
  71. package/build/main.4be526e3a94d53aeceae.js +0 -1
  72. package/server/Library/Library.js +0 -340
  73. package/server/Library/index.js +0 -3
  74. package/server/Library/ipc.js +0 -18
  75. package/server/Library/router.js +0 -27
  76. package/server/Library/socket.js +0 -47
  77. package/server/Media/Media.js +0 -207
  78. package/server/Media/index.js +0 -3
  79. package/server/Media/ipc.js +0 -19
  80. package/server/Media/router.js +0 -99
  81. package/server/Player/socket.js +0 -78
  82. package/server/Prefs/Prefs.js +0 -165
  83. package/server/Prefs/index.js +0 -3
  84. package/server/Prefs/router.js +0 -124
  85. package/server/Prefs/socket.js +0 -68
  86. package/server/Queue/Queue.js +0 -208
  87. package/server/Queue/index.js +0 -3
  88. package/server/Queue/socket.js +0 -99
  89. package/server/Rooms/Rooms.js +0 -114
  90. package/server/Rooms/index.js +0 -3
  91. package/server/Rooms/router.js +0 -146
  92. package/server/Scanner/FileScanner/FileScanner.js +0 -225
  93. package/server/Scanner/FileScanner/getConfig.js +0 -35
  94. package/server/Scanner/FileScanner/getFiles.js +0 -63
  95. package/server/Scanner/FileScanner/index.js +0 -3
  96. package/server/Scanner/MetaParser/MetaParser.js +0 -49
  97. package/server/Scanner/MetaParser/defaultMiddleware.js +0 -197
  98. package/server/Scanner/MetaParser/index.js +0 -3
  99. package/server/Scanner/Scanner.js +0 -33
  100. package/server/User/User.js +0 -139
  101. package/server/User/index.js +0 -3
  102. package/server/User/router.js +0 -442
  103. package/server/lib/Database.js +0 -55
  104. package/server/lib/IPCBridge.js +0 -115
  105. package/server/lib/Log.js +0 -71
  106. package/server/lib/bcrypt.js +0 -24
  107. package/server/lib/cli.js +0 -136
  108. package/server/lib/electron.js +0 -81
  109. package/server/lib/getCdgName.js +0 -20
  110. package/server/lib/getDevMiddleware.js +0 -51
  111. package/server/lib/getFolders.js +0 -10
  112. package/server/lib/getHotMiddleware.js +0 -27
  113. package/server/lib/getIPAddress.js +0 -16
  114. package/server/lib/getPermutations.js +0 -21
  115. package/server/lib/getWindowsDrives.js +0 -30
  116. package/server/lib/parseCookie.js +0 -12
  117. package/server/lib/pushQueuesAndLibrary.js +0 -29
  118. package/server/main.js +0 -135
  119. package/server/scannerWorker.js +0 -58
  120. package/server/serverWorker.js +0 -242
  121. package/server/socket.js +0 -173
  122. package/shared/actionTypes.js +0 -103
  123. /package/build/{7ce9eb3fe454f54745a4.woff2 → client/7ce9eb3fe454f54745a4.woff2} +0 -0
  124. /package/build/{598.4be526e3a94d53aeceae.css → client/851.83b0127845c2fa8729fe.css} +0 -0
  125. /package/build/{a35814dd9eb496e3d7cc.woff2 → client/a35814dd9eb496e3d7cc.woff2} +0 -0
  126. /package/build/{e419b95dccb58b362811.woff2 → client/e419b95dccb58b362811.woff2} +0 -0
  127. /package/{server → build/server}/lib/schemas/002-replaygain.sql +0 -0
  128. /package/{server → build/server}/lib/schemas/003-queue-linked-list.sql +0 -0
package/server/main.js DELETED
@@ -1,135 +0,0 @@
1
- #!/usr/bin/env node
2
- const childProcess = require('child_process')
3
- const path = require('path')
4
- const env = require('./lib/cli')
5
- const { Log } = require('./lib/Log')
6
-
7
- const log = new Log('server', {
8
- console: Log.resolve(env.KES_CONSOLE_LEVEL, env.NODE_ENV === 'development' ? 5 : 4),
9
- file: Log.resolve(env.KES_LOG_LEVEL, env.NODE_ENV === 'development' ? 0 : 3),
10
- }).setDefaultInstance().logger.scope(`main[${process.pid}]`)
11
-
12
- const scannerLog = new Log('scanner', {
13
- console: Log.resolve(process.env.KES_SCAN_CONSOLE_LEVEL, process.env.NODE_ENV === 'development' ? 5 : 4),
14
- file: Log.resolve(process.env.KES_SCAN_LOG_LEVEL, process.env.NODE_ENV === 'development' ? 0 : 3),
15
- }).logger.scope('scanner')
16
-
17
- const Database = require('./lib/Database')
18
- const IPC = require('./lib/IPCBridge')
19
- const refs = {}
20
- const {
21
- SCANNER_CMD_START,
22
- SCANNER_CMD_STOP,
23
- SERVER_WORKER_ERROR,
24
- SCANNER_WORKER_LOG,
25
- SERVER_WORKER_STATUS,
26
- } = require('../shared/actionTypes')
27
-
28
- // handle scanner logs
29
- // @todo: this doesn't need to be async
30
- IPC.use({
31
- [SCANNER_WORKER_LOG]: async (action) => {
32
- scannerLog[action.payload.level](action.payload.msg)
33
- }
34
- })
35
-
36
- // log non-default settings
37
- for (const key in env) {
38
- if (process.env[key]) log.verbose(`${key}=${process.env[key]}`)
39
- }
40
-
41
- // support PUID/PGID convention (group MUST be set before user!)
42
- if (Number.isInteger(env.KES_PGID)) {
43
- log.verbose(`PGID=${env.KES_PGID}`)
44
- process.setgid(env.KES_PGID)
45
- }
46
-
47
- if (Number.isInteger(env.KES_PUID)) {
48
- log.verbose(`PUID=${env.KES_PUID}`)
49
- process.setuid(env.KES_PUID)
50
- }
51
-
52
- // close db before exiting (can't do async in the 'exit' handler)
53
- process.on('SIGTERM', shutdown)
54
- process.on('SIGINT', shutdown)
55
-
56
- // make sure child processes don't hang around
57
- process.on('exit', function () {
58
- if (refs.server) refs.server.kill()
59
- if (refs.scanner) refs.scanner.kill()
60
- })
61
-
62
- // debug: log stack trace for unhandled promise rejections
63
- process.on('unhandledRejection', (reason, p) => {
64
- log.error('Unhandled Rejection:', p, 'reason:', reason)
65
- })
66
-
67
- // detect electron
68
- if (process.versions.electron) {
69
- refs.electron = require('./lib/electron.js')({ env })
70
- env.KES_PATH_DATA = refs.electron.app.getPath('userData')
71
- }
72
-
73
- Database.open({
74
- file: path.join(env.KES_PATH_DATA, 'database.sqlite3'),
75
- ro: false,
76
- }).then(db => {
77
- if (refs.electron) {
78
- process.on('serverWorker', action => {
79
- const { type, payload } = action
80
-
81
- if (type === SERVER_WORKER_STATUS) {
82
- return refs.electron.setStatus('url', payload.url)
83
- } else if (type === SERVER_WORKER_ERROR) {
84
- return refs.electron.setError(action.error)
85
- }
86
- })
87
- }
88
-
89
- // start web server
90
- require('./serverWorker.js')({ env, startScanner, stopScanner })
91
- }).catch(err => {
92
- log.error(err.message)
93
- process.exit(1)
94
- })
95
-
96
- function startScanner (onExit) {
97
- if (refs.scanner === undefined) {
98
- log.info('Starting media scanner process')
99
- refs.scanner = childProcess.fork(path.join(__dirname, 'scannerWorker.js'), [], {
100
- env: { ...env, KES_CHILD_PROCESS: 'scanner' },
101
- gid: Number.isInteger(env.KES_PGID) ? env.KES_PGID : undefined,
102
- uid: Number.isInteger(env.KES_PUID) ? env.KES_PUID : undefined,
103
- })
104
-
105
- refs.scanner.on('exit', (code, signal) => {
106
- log.info(`Media scanner exited (${signal || code})`)
107
- IPC.removeChild(refs.scanner)
108
- delete refs.scanner
109
-
110
- if (typeof onExit === 'function') onExit()
111
- })
112
-
113
- IPC.addChild(refs.scanner)
114
- } else {
115
- IPC.send({ type: SCANNER_CMD_START })
116
- }
117
- }
118
-
119
- function stopScanner () {
120
- if (refs.scanner) {
121
- IPC.send({ type: SCANNER_CMD_STOP })
122
- }
123
- }
124
-
125
- function shutdown (signal) {
126
- log.info('Received %s', signal)
127
-
128
- Database.close().then(() => {
129
- log.info('Goodbye for now...')
130
- process.exit(0)
131
- }).catch(err => {
132
- log.error(err.message)
133
- process.exit(1)
134
- })
135
- }
@@ -1,58 +0,0 @@
1
- const path = require('path')
2
- const log = require('./lib/Log')(`scanner[${process.pid}]`)
3
- const Database = require('./lib/Database')
4
- const IPC = require('./lib/IPCBridge')
5
- const {
6
- SCANNER_CMD_START,
7
- SCANNER_CMD_STOP,
8
- } = require('../shared/actionTypes')
9
-
10
- let FileScanner, Prefs
11
- let _Scanner
12
- let _isScanQueued = true
13
-
14
- Database.open({
15
- file: path.join(process.env.KES_PATH_DATA, 'database.sqlite3'),
16
- ro: true,
17
- }).then(db => {
18
- Prefs = require('./Prefs')
19
- FileScanner = require('./Scanner/FileScanner')
20
-
21
- IPC.use({
22
- [SCANNER_CMD_START]: async () => {
23
- log.info('Media scan requested (restarting)')
24
- _isScanQueued = true
25
- cancelScan()
26
- },
27
- [SCANNER_CMD_STOP]: async () => {
28
- log.info('Stopping media scan (user requested)')
29
- _isScanQueued = false
30
- cancelScan()
31
- }
32
- })
33
-
34
- startScan()
35
- }).catch(err => {
36
- log.error(err.message)
37
- process.exit(1)
38
- })
39
-
40
- async function startScan () {
41
- log.info('Starting media scan')
42
-
43
- while (_isScanQueued) {
44
- _isScanQueued = false
45
-
46
- const prefs = await Prefs.get()
47
- _Scanner = new FileScanner(prefs)
48
- await _Scanner.scan()
49
- } // end while
50
-
51
- process.exit(0)
52
- }
53
-
54
- function cancelScan () {
55
- if (_Scanner) {
56
- _Scanner.cancel()
57
- }
58
- }
@@ -1,242 +0,0 @@
1
- const log = require('./lib/Log')('server')
2
- const path = require('path')
3
- const getIPAddress = require('./lib/getIPAddress')
4
- const http = require('http')
5
- const fs = require('fs')
6
- const { promisify } = require('util')
7
- const parseCookie = require('./lib/parseCookie')
8
- const jwtVerify = require('jsonwebtoken').verify
9
- const Koa = require('koa')
10
- const koaRouter = require('koa-router')
11
- const koaBody = require('koa-body')
12
- const koaFavicon = require('koa-favicon')
13
- const koaLogger = require('koa-logger')
14
- const koaMount = require('koa-mount')
15
- const koaRange = require('koa-range')
16
- const koaStatic = require('koa-static')
17
-
18
- const Prefs = require('./Prefs')
19
- const libraryRouter = require('./Library/router')
20
- const mediaRouter = require('./Media/router')
21
- const prefsRouter = require('./Prefs/router')
22
- const roomsRouter = require('./Rooms/router')
23
- const userRouter = require('./User/router')
24
- const pushQueuesAndLibrary = require('./lib/pushQueuesAndLibrary')
25
- const SocketIO = require('socket.io')
26
- const socketActions = require('./socket')
27
- const IPC = require('./lib/IPCBridge')
28
- const IPCLibraryActions = require('./Library/ipc')
29
- const IPCMediaActions = require('./Media/ipc')
30
- const {
31
- SERVER_WORKER_STATUS,
32
- SERVER_WORKER_ERROR,
33
- } = require('../shared/actionTypes')
34
-
35
- async function serverWorker ({ env, startScanner, stopScanner }) {
36
- const indexFile = path.join(env.KES_PATH_WEBROOT, 'index.html')
37
- const urlPath = env.KES_URL_PATH.replace(/\/?$/, '/') // force trailing slash
38
- const jwtKey = await Prefs.getJwtKey(env.KES_ROTATE_KEY)
39
- const app = new Koa()
40
- let server, io
41
-
42
- // called when middleware is finalized
43
- function createServer () {
44
- server = http.createServer(app.callback())
45
-
46
- // http server error handler
47
- server.on('error', function (err) {
48
- log.error(err)
49
-
50
- process.emit('serverWorker', {
51
- type: SERVER_WORKER_ERROR,
52
- error: err.message,
53
- })
54
-
55
- // not much we can do without a working server
56
- process.exit(1)
57
- })
58
-
59
- // create socket.io server
60
- io = SocketIO(server, {
61
- path: urlPath + 'socket.io',
62
- serveClient: false,
63
- })
64
-
65
- // attach socket.io handlers
66
- socketActions(io, jwtKey)
67
-
68
- // attach IPC action handlers
69
- IPC.use(IPCLibraryActions(io))
70
- IPC.use(IPCMediaActions(io))
71
-
72
- // success callback in 3rd arg
73
- server.listen(env.KES_PORT, () => {
74
- const port = server.address().port
75
- const url = `http://${getIPAddress()}${port === 80 ? '' : ':' + port}${urlPath}`
76
- log.info(`Web server running at ${url}`)
77
-
78
- process.emit('serverWorker', {
79
- type: SERVER_WORKER_STATUS,
80
- payload: { url },
81
- })
82
-
83
- // scanning on startup?
84
- if (env.KES_SCAN) startScanner(() => pushQueuesAndLibrary(io))
85
- })
86
- }
87
-
88
- // --------------------
89
- // Begin Koa middleware
90
- // --------------------
91
-
92
- // server error handler
93
- app.on('error', (err, ctx) => {
94
- if (err.code === 'EPIPE') {
95
- // these are common since browsers make multiple requests for media files
96
- log.verbose(err.message)
97
- return
98
- }
99
-
100
- // silence 4xx response "errors" (koa-logger should show these anyway)
101
- if (ctx.response && ctx.response.status.toString().startsWith('4')) {
102
- return
103
- }
104
-
105
- if (err.stack) log.error(err.stack)
106
- else log.error(err)
107
- })
108
-
109
- // middleware error handler
110
- app.use(async (ctx, next) => {
111
- try {
112
- await next()
113
- } catch (err) {
114
- ctx.status = err.status || 500
115
- ctx.body = err.message
116
- ctx.app.emit('error', err, ctx)
117
- }
118
- })
119
-
120
- // http request/response logging
121
- app.use(koaLogger((str, args) => (args.length === 6 && args[3] >= 500) ? log.error(str) : log.debug(str)))
122
-
123
- app.use(koaFavicon(path.join(env.KES_PATH_ASSETS, 'favicon.ico')))
124
- app.use(koaRange)
125
- app.use(koaBody({ multipart: true }))
126
-
127
- // all http requests
128
- app.use(async (ctx, next) => {
129
- ctx.jwtKey = jwtKey // used by login route
130
-
131
- // skip JWT/session validation if non-API request or logging in/out
132
- if (!ctx.request.path.startsWith(`${urlPath}api/`) ||
133
- ctx.request.path === `${urlPath}api/login` ||
134
- ctx.request.path === `${urlPath}api/logout`) {
135
- return next()
136
- }
137
-
138
- // verify JWT
139
- try {
140
- const { keToken } = parseCookie(ctx.request.header.cookie)
141
- ctx.user = jwtVerify(keToken, jwtKey)
142
- } catch (err) {
143
- ctx.user = {
144
- dateUpdated: null,
145
- isAdmin: false,
146
- name: null,
147
- roomId: null,
148
- userId: null,
149
- username: null,
150
- }
151
- }
152
-
153
- // validated
154
- ctx.io = io
155
- ctx.startScanner = () => startScanner(() => pushQueuesAndLibrary(io))
156
- ctx.stopScanner = stopScanner
157
-
158
- await next()
159
- })
160
-
161
- // http api endpoints
162
- const baseRouter = koaRouter({
163
- prefix: urlPath.replace(/\/$/, '') // avoid double slashes with /api prefix
164
- })
165
-
166
- baseRouter.use(libraryRouter.routes())
167
- baseRouter.use(mediaRouter.routes())
168
- baseRouter.use(prefsRouter.routes())
169
- baseRouter.use(roomsRouter.routes())
170
- baseRouter.use(userRouter.routes())
171
- app.use(baseRouter.routes())
172
-
173
- // serve index.html with dynamic base tag at the main SPA routes
174
- const createIndexMiddleware = content => {
175
- const indexRoutes = [
176
- urlPath,
177
- ...['account', 'library', 'queue', 'player'].map(r => urlPath + r + '/')
178
- ]
179
-
180
- content = content.replace('<base href="/">', `<base href="${urlPath}">`)
181
-
182
- return async (ctx, next) => {
183
- // use a trailing slash for matching purposes
184
- const reqPath = ctx.request.path.replace(/\/?$/, '/')
185
-
186
- if (!indexRoutes.includes(reqPath)) {
187
- return next()
188
- }
189
-
190
- ctx.set('content-type', 'text/html')
191
- ctx.body = content
192
- ctx.status = 200
193
- }
194
- }
195
-
196
- if (env.NODE_ENV !== 'development') {
197
- // make sure we handle index.html before koaStatic,
198
- // otherwise it'll be served without dynamic base tag
199
- app.use(createIndexMiddleware(await promisify(fs.readFile)(indexFile, 'utf8')))
200
-
201
- // serve build and asset folders
202
- app.use(koaMount(urlPath, koaStatic(env.KES_PATH_WEBROOT)))
203
- app.use(koaMount(`${urlPath}assets`, koaStatic(env.KES_PATH_ASSETS)))
204
-
205
- createServer()
206
- return
207
- }
208
-
209
- // ----------------------
210
- // Development middleware
211
- // ----------------------
212
- log.info('Enabling webpack dev and HMR middleware')
213
- const webpack = require('webpack')
214
- const webpackConfig = require('../config/webpack.config')
215
- const compiler = webpack(webpackConfig)
216
-
217
- compiler.hooks.done.tap('indexPlugin', async (params) => {
218
- const indexContent = await new Promise((resolve, reject) => {
219
- compiler.outputFileSystem.readFile(indexFile, 'utf8', (err, result) => {
220
- if (err) return reject(err)
221
- return resolve(result)
222
- })
223
- })
224
-
225
- // @todo make this less hacky
226
- if (!server) {
227
- app.use(createIndexMiddleware(indexContent))
228
- createServer()
229
- }
230
- })
231
-
232
- const devMiddleware = require('./lib/getDevMiddleware')(compiler, { publicPath: urlPath })
233
- app.use(devMiddleware)
234
-
235
- const hotMiddleware = require('./lib/getHotMiddleware')(compiler)
236
- app.use(hotMiddleware)
237
-
238
- // serve assets since webpack-dev-server is unaware of this folder
239
- app.use(koaMount(`${urlPath}assets`, koaStatic(env.KES_PATH_ASSETS)))
240
- }
241
-
242
- module.exports = serverWorker
package/server/socket.js DELETED
@@ -1,173 +0,0 @@
1
- const log = require('./lib/Log')('server')
2
- const jwtVerify = require('jsonwebtoken').verify
3
-
4
- const parseCookie = require('./lib/parseCookie')
5
- const Library = require('./Library')
6
- const LibrarySocket = require('./Library/socket')
7
- const PlayerSocket = require('./Player/socket')
8
- const Prefs = require('./Prefs')
9
- const PrefsSocket = require('./Prefs/socket')
10
- const Rooms = require('./Rooms')
11
- const Queue = require('./Queue')
12
- const QueueSocket = require('./Queue/socket')
13
- const {
14
- LIBRARY_PUSH,
15
- QUEUE_PUSH,
16
- STARS_PUSH,
17
- STAR_COUNTS_PUSH,
18
- PLAYER_STATUS,
19
- PLAYER_LEAVE,
20
- PREFS_PUSH,
21
- SOCKET_AUTH_ERROR,
22
- _ERROR,
23
- } = require('../shared/actionTypes')
24
-
25
- const handlers = {
26
- ...LibrarySocket,
27
- ...QueueSocket,
28
- ...PlayerSocket,
29
- ...PrefsSocket,
30
- }
31
-
32
- module.exports = function (io, jwtKey) {
33
- io.on('connection', async (sock) => {
34
- const { keToken } = parseCookie(sock.handshake.headers.cookie)
35
- const clientLibraryVersion = parseInt(sock.handshake.query.library, 10)
36
- const clientStarsVersion = parseInt(sock.handshake.query.stars, 10)
37
-
38
- // authenticate the JWT sent via cookie in http handshake
39
- try {
40
- sock.user = jwtVerify(keToken, jwtKey)
41
-
42
- // success
43
- log.verbose('%s (%s) connected from %s', sock.user.name, sock.id, sock.handshake.address)
44
- } catch (err) {
45
- io.to(sock.id).emit('action', {
46
- type: SOCKET_AUTH_ERROR,
47
- })
48
-
49
- sock.user = null
50
- sock.disconnect()
51
- log.verbose('disconnected %s (%s)', sock.handshake.address, err.message)
52
- return
53
- }
54
-
55
- // attach disconnect handler
56
- sock.on('disconnect', reason => {
57
- log.verbose('%s (%s) disconnected (%s)',
58
- sock.user.name, sock.id, reason
59
- )
60
-
61
- if (typeof sock.user.roomId !== 'number') return
62
-
63
- // beyond this point assumes there is a room
64
-
65
- log.verbose('%s (%s) left room %s (%s; %s in room)',
66
- sock.user.name, sock.id, sock.user.roomId, reason, sock.adapter.rooms.size
67
- )
68
-
69
- // any players left in room?
70
- if (!Rooms.isPlayerPresent(io, sock.user.roomId)) {
71
- io.to(Rooms.prefix(sock.user.roomId)).emit('action', {
72
- type: PLAYER_LEAVE,
73
- payload: { socketId: sock.id },
74
- })
75
- }
76
- })
77
-
78
- // attach action handler
79
- sock.on('action', async (action, acknowledge) => {
80
- const { type } = action
81
-
82
- if (!sock.user) {
83
- return acknowledge({
84
- type: SOCKET_AUTH_ERROR,
85
- })
86
- }
87
-
88
- if (typeof handlers[type] !== 'function') {
89
- log.error('No handler for socket action: %s', type)
90
- return
91
- }
92
-
93
- try {
94
- await handlers[type](sock, action, acknowledge)
95
- } catch (err) {
96
- log.error(err)
97
-
98
- return acknowledge({
99
- type: type + _ERROR,
100
- error: `Error in ${type}: ${err.message}`,
101
- })
102
- }
103
- })
104
-
105
- // push prefs (admin only)
106
- if (sock.user.isAdmin) {
107
- log.verbose('pushing prefs to %s (%s)', sock.user.name, sock.id)
108
- io.to(sock.id).emit('action', {
109
- type: PREFS_PUSH,
110
- payload: await Prefs.get(),
111
- })
112
- }
113
-
114
- // push library (only if client's is outdated)
115
- if (clientLibraryVersion !== Library.cache.version) {
116
- log.verbose('pushing library to %s (%s) (client=%s, server=%s)',
117
- sock.user.name, sock.id, clientLibraryVersion, Library.cache.version)
118
-
119
- io.to(sock.id).emit('action', {
120
- type: LIBRARY_PUSH,
121
- payload: await Library.get(),
122
- })
123
- }
124
-
125
- // push user's stars
126
- io.to(sock.id).emit('action', {
127
- type: STARS_PUSH,
128
- payload: await Library.getUserStars(sock.user.userId),
129
- })
130
-
131
- // push star counts (only if client's is outdated)
132
- if (clientStarsVersion !== Library.starCountsCache.version) {
133
- log.verbose('pushing star counts to %s (%s) (client=%s, server=%s)',
134
- sock.user.name, sock.id, clientStarsVersion, Library.starCountsCache.version)
135
-
136
- io.to(sock.id).emit('action', {
137
- type: STAR_COUNTS_PUSH,
138
- payload: await Library.getStarCounts(),
139
- })
140
- }
141
-
142
- // it's possible for an admin to not be in a room
143
- if (typeof sock.user.roomId !== 'number') return
144
-
145
- // beyond this point assumes there is a room
146
-
147
- // add user to room
148
- sock.join(Rooms.prefix(sock.user.roomId))
149
-
150
- // if there's a player in room, emit its last known status
151
- // @todo this just emits the first status found
152
- for (const s of io.of('/').sockets.values()) {
153
- if (s.user && s.user.roomId === sock.user.roomId && s._lastPlayerStatus) {
154
- io.to(sock.id).emit('action', {
155
- type: PLAYER_STATUS,
156
- payload: s._lastPlayerStatus,
157
- })
158
-
159
- break
160
- }
161
- }
162
-
163
- log.verbose('%s (%s) joined room %s (%s in room)',
164
- sock.user.name, sock.id, sock.user.roomId, sock.adapter.rooms.size
165
- )
166
-
167
- // send room's queue
168
- io.to(sock.id).emit('action', {
169
- type: QUEUE_PUSH,
170
- payload: await Queue.get(sock.user.roomId),
171
- })
172
- })
173
- }