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.
- package/README.md +10 -10
- package/build/client/447.83b0127845c2fa8729fe.js +1 -0
- package/build/client/715.83b0127845c2fa8729fe.js +1 -0
- package/build/client/718.83b0127845c2fa8729fe.js +1 -0
- package/build/client/851.83b0127845c2fa8729fe.js +1 -0
- package/build/{845.4be526e3a94d53aeceae.css → client/958.83b0127845c2fa8729fe.css} +53 -6
- package/build/client/958.83b0127845c2fa8729fe.js +1 -0
- package/build/{index.html → client/index.html} +1 -1
- package/build/{licenses.txt → client/licenses.txt} +208 -496
- package/build/client/main.83b0127845c2fa8729fe.css +2341 -0
- package/build/client/main.83b0127845c2fa8729fe.js +1 -0
- package/build/server/Library/Library.js +297 -0
- package/build/server/Library/ipc.js +13 -0
- package/build/server/Library/router.js +20 -0
- package/build/server/Library/socket.js +35 -0
- package/build/server/Media/Media.js +170 -0
- package/build/server/Media/fileTypes.js +8 -0
- package/build/server/Media/ipc.js +13 -0
- package/build/server/Media/router.js +97 -0
- package/build/server/Player/socket.js +66 -0
- package/build/server/Prefs/Prefs.js +181 -0
- package/build/server/Prefs/router.js +151 -0
- package/build/server/Prefs/socket.js +52 -0
- package/build/server/Queue/Queue.js +203 -0
- package/build/server/Queue/socket.js +83 -0
- package/build/server/Rooms/Rooms.js +171 -0
- package/build/server/Rooms/router.js +97 -0
- package/build/server/Rooms/socket.js +23 -0
- package/build/server/Scanner/FileScanner/FileScanner.js +166 -0
- package/build/server/Scanner/FileScanner/getConfig.js +32 -0
- package/build/server/Scanner/FileScanner/getFiles.js +61 -0
- package/build/server/Scanner/MetaParser/MetaParser.js +77 -0
- package/build/server/Scanner/MetaParser/defaultMiddleware.js +170 -0
- package/build/server/Scanner/Scanner.js +26 -0
- package/build/server/Scanner/ScannerQueue.js +62 -0
- package/build/server/User/User.js +206 -0
- package/build/server/User/router.js +366 -0
- package/build/server/lib/Database.js +39 -0
- package/build/server/lib/Errors.js +6 -0
- package/build/server/lib/IPCBridge.js +128 -0
- package/build/server/lib/Log.js +31 -0
- package/build/server/lib/accumulatedThrottle.js +16 -0
- package/build/server/lib/bcrypt.js +23 -0
- package/build/server/lib/cli.js +131 -0
- package/build/server/lib/getCdgName.js +18 -0
- package/build/server/lib/getFolders.js +8 -0
- package/build/server/lib/getHotMiddleware.js +22 -0
- package/build/server/lib/getIPAddress.js +14 -0
- package/build/server/lib/getPermutations.js +17 -0
- package/build/server/lib/getWindowsDrives.js +17 -0
- package/build/server/lib/parseCookie.js +13 -0
- package/build/server/lib/pushQueuesAndLibrary.js +22 -0
- package/{server → build/server}/lib/schemas/001-initial-schema.sql +26 -26
- package/build/server/lib/schemas/004-paths-rooms-data.sql +7 -0
- package/build/server/lib/schemas/005-roles.sql +32 -0
- package/build/server/lib/util.js +39 -0
- package/build/server/main.js +124 -0
- package/build/server/scannerWorker.js +59 -0
- package/build/server/serverWorker.js +219 -0
- package/build/server/socket.js +134 -0
- package/build/server/watcherWorker.js +51 -0
- package/build/shared/actionTypes.js +113 -0
- package/build/shared/types.js +1 -0
- package/package.json +111 -86
- package/build/267.4be526e3a94d53aeceae.js +0 -1
- package/build/591.4be526e3a94d53aeceae.js +0 -1
- package/build/598.4be526e3a94d53aeceae.js +0 -1
- package/build/799.4be526e3a94d53aeceae.js +0 -1
- package/build/845.4be526e3a94d53aeceae.js +0 -1
- package/build/main.4be526e3a94d53aeceae.css +0 -2034
- package/build/main.4be526e3a94d53aeceae.js +0 -1
- package/server/Library/Library.js +0 -340
- package/server/Library/index.js +0 -3
- package/server/Library/ipc.js +0 -18
- package/server/Library/router.js +0 -27
- package/server/Library/socket.js +0 -47
- package/server/Media/Media.js +0 -207
- package/server/Media/index.js +0 -3
- package/server/Media/ipc.js +0 -19
- package/server/Media/router.js +0 -99
- package/server/Player/socket.js +0 -78
- package/server/Prefs/Prefs.js +0 -165
- package/server/Prefs/index.js +0 -3
- package/server/Prefs/router.js +0 -124
- package/server/Prefs/socket.js +0 -68
- package/server/Queue/Queue.js +0 -208
- package/server/Queue/index.js +0 -3
- package/server/Queue/socket.js +0 -99
- package/server/Rooms/Rooms.js +0 -114
- package/server/Rooms/index.js +0 -3
- package/server/Rooms/router.js +0 -146
- package/server/Scanner/FileScanner/FileScanner.js +0 -225
- package/server/Scanner/FileScanner/getConfig.js +0 -35
- package/server/Scanner/FileScanner/getFiles.js +0 -63
- package/server/Scanner/FileScanner/index.js +0 -3
- package/server/Scanner/MetaParser/MetaParser.js +0 -49
- package/server/Scanner/MetaParser/defaultMiddleware.js +0 -197
- package/server/Scanner/MetaParser/index.js +0 -3
- package/server/Scanner/Scanner.js +0 -33
- package/server/User/User.js +0 -139
- package/server/User/index.js +0 -3
- package/server/User/router.js +0 -442
- package/server/lib/Database.js +0 -55
- package/server/lib/IPCBridge.js +0 -115
- package/server/lib/Log.js +0 -71
- package/server/lib/bcrypt.js +0 -24
- package/server/lib/cli.js +0 -136
- package/server/lib/electron.js +0 -81
- package/server/lib/getCdgName.js +0 -20
- package/server/lib/getDevMiddleware.js +0 -51
- package/server/lib/getFolders.js +0 -10
- package/server/lib/getHotMiddleware.js +0 -27
- package/server/lib/getIPAddress.js +0 -16
- package/server/lib/getPermutations.js +0 -21
- package/server/lib/getWindowsDrives.js +0 -30
- package/server/lib/parseCookie.js +0 -12
- package/server/lib/pushQueuesAndLibrary.js +0 -29
- package/server/main.js +0 -135
- package/server/scannerWorker.js +0 -58
- package/server/serverWorker.js +0 -242
- package/server/socket.js +0 -173
- package/shared/actionTypes.js +0 -103
- /package/build/{7ce9eb3fe454f54745a4.woff2 → client/7ce9eb3fe454f54745a4.woff2} +0 -0
- /package/build/{598.4be526e3a94d53aeceae.css → client/851.83b0127845c2fa8729fe.css} +0 -0
- /package/build/{a35814dd9eb496e3d7cc.woff2 → client/a35814dd9eb496e3d7cc.woff2} +0 -0
- /package/build/{e419b95dccb58b362811.woff2 → client/e419b95dccb58b362811.woff2} +0 -0
- /package/{server → build/server}/lib/schemas/002-replaygain.sql +0 -0
- /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
|
-
}
|
package/server/scannerWorker.js
DELETED
|
@@ -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
|
-
}
|
package/server/serverWorker.js
DELETED
|
@@ -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
|
-
}
|