diff --git a/package.json b/package.json index f15fec3..2c530a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nuke", - "version": "1.0.0", + "version": "1.1.9", "description": "APL Esports' Nuke Server", "main": "index.js", "scripts": { diff --git a/src/api/index.js b/src/api/index.js index d786458..68d8756 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -23,6 +23,7 @@ router.post('/', async (req, res) => { push(id) res.json({ session: id, + name: r.data.entries[0].name, }) } else { res.status(403).json({ diff --git a/src/index.js b/src/index.js index 9b7e7d7..de98a56 100644 --- a/src/index.js +++ b/src/index.js @@ -4,10 +4,12 @@ const url = require('url') const app = require('./app') const { get } = require('./uuids') const handler = require('./ws/handler') +const { subscribe } = require('./ws/streams') mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true, + useFindAndModify: false, }) const db = mongoose.connection @@ -15,15 +17,24 @@ db.once('open', () => console.log('Connected to mongodb instance!')) const port = process.env.PORT || 5000; -wss = new WebSocket.Server({ +wss1 = new WebSocket.Server({ + noServer: true, +}) + +wss2 = new WebSocket.Server({ noServer: true, }) -wss.on('connection', (ws, req) => { +wss1.on('connection', (ws, req) => { const id = req.id handler(ws, id) }) +wss2.on('connection', (ws, req) => { + const id = req.id + subscribe(ws, id) +}) + const server = app.listen(port, () => { console.log(`Express listening: http://localhost:${port}`) }) @@ -32,9 +43,14 @@ server.on('upgrade', (req, socket, head) => { const pathname = url.parse(req.url).pathname.split('/') const token = pathname[2] if (pathname[1] === 'ws' && get(token)) { - wss.handleUpgrade(req, socket, head, (socket) => { + wss1.handleUpgrade(req, socket, head, (socket) => { + req.id = token + wss1.emit('connection', socket, req); + }) + } else if (pathname[1] === 'stream') { + wss2.handleUpgrade(req, socket, head, (socket) => { req.id = token - wss.emit('connection', socket, req); + wss2.emit('connection', socket, req); }) } }) \ No newline at end of file diff --git a/src/models/casters.js b/src/models/casters.js index 112fa9d..f47ab3f 100644 --- a/src/models/casters.js +++ b/src/models/casters.js @@ -15,8 +15,7 @@ const casters = new Schema({ }, image: { type: String, - required: true, - }, + } }); const Caster = model('Casters', casters) diff --git a/src/models/events.js b/src/models/events.js index 6a3ae5c..a51457c 100644 --- a/src/models/events.js +++ b/src/models/events.js @@ -5,9 +5,9 @@ const events = new mongoose.Schema({ type: String, required: true, unique: true, - }, + } }); -const Event = mongoose.model('Events', events) +const Event = mongoose.model('Event', events) module.exports = Event \ No newline at end of file diff --git a/src/models/hosts.js b/src/models/hosts.js index 72a05a0..19e4ead 100644 --- a/src/models/hosts.js +++ b/src/models/hosts.js @@ -15,8 +15,7 @@ const hosts = new Schema({ }, image: { type: String, - required: true, - }, + } }); const Host = model('Hosts', hosts) diff --git a/src/models/matches.js b/src/models/matches.js index 0fcb5f1..919da0a 100644 --- a/src/models/matches.js +++ b/src/models/matches.js @@ -3,23 +3,23 @@ const { Schema, model } = require('mongoose') const gameScores = new Schema({ orange: { type: Number, - required: true, + default: 0, }, blue: { type: Number, - required: true, + default : 0, }, }) const matches = new Schema({ orange: { type: Schema.Types.ObjectId, - refs: 'Rosters', + ref: 'Rosters', required: true, }, blue: { type: Schema.Types.ObjectId, - refs: 'Rosters', + ref: 'Rosters', required: true, }, started: { @@ -41,17 +41,16 @@ const matches = new Schema({ series: { orange: { type: Number, - required: true, + default: 0, }, blue: { type: Number, - required: true, + default: 0, }, }, games: { type: [gameScores], - required: true, - }, + } }) const Match = model('Matches', matches) diff --git a/src/models/rosters.js b/src/models/rosters.js index 63c7210..6241d7b 100644 --- a/src/models/rosters.js +++ b/src/models/rosters.js @@ -3,23 +3,23 @@ const { Schema, model } = require('mongoose') const stats = new Schema({ goals: { type: Number, - required: true, + default: 0, }, assists: { type: Number, - required: true, + default: 0, }, saves: { type: Number, - required: true, + default: 0, }, shots: { type: Number, - required: true, + default: 0, }, demos: { type: Number, - required: true, + default: 0, }, }) @@ -32,18 +32,9 @@ const players = new Schema({ type: String, required: true, }, - stats: { - type: [stats], - required: true, - }, }) const rosters = new Schema({ - event: { - type: Schema.Types.ObjectId, - ref: 'Events', - required: true, - }, name: { type: String, required: true, @@ -54,8 +45,10 @@ const rosters = new Schema({ }, players: { type: [players], - required: true, }, + stats: { + type: stats, + } }) const Roster = model('Rosters', rosters) diff --git a/src/models/streams.js b/src/models/streams.js index 6bbd74d..2fbf63b 100644 --- a/src/models/streams.js +++ b/src/models/streams.js @@ -7,14 +7,20 @@ const streams = new Schema({ }, event: { type: Schema.Types.ObjectId, - refs: 'Events', - requried: true, - }, - matches: { - type: [Schema.Types.ObjectId], - refs: 'matches', - requried: true, + ref: 'Event', }, + matches: [{ + type: Schema.Types.ObjectId, + ref: 'Matches', + }], + casters: [{ + type: Schema.Types.ObjectId, + ref: 'Casters', + }], + hosts: [{ + type: Schema.Types.ObjectId, + ref: 'Hosts', + }], }) const Stream = model('Streams', streams) diff --git a/src/ws/channels.js b/src/ws/channels.js index 5fea53a..b4e94e3 100644 --- a/src/ws/channels.js +++ b/src/ws/channels.js @@ -14,7 +14,9 @@ const events = [ 'delete', ]; -const channelEvents = []; +const channelEvents = [ + 'streams:full', +]; channels.forEach((c) => { events.forEach((e) => { diff --git a/src/ws/crud.js b/src/ws/crud.js index d8becd2..671d057 100644 --- a/src/ws/crud.js +++ b/src/ws/crud.js @@ -7,10 +7,10 @@ const hosts = require('../models/hosts') const eventFns = { getAll: async () => { - return await events.find().exec() + return await events.find().populate('streams').exec() }, getById: async (id) => { - return await events.findById(id).exec() + return await events.findById(id).populate('streams').exec() }, update: async(id, data) => { return await events.findByIdAndUpdate(id, data).exec() @@ -23,10 +23,10 @@ const eventFns = { const ev = new events(data) ev.save((err) => { if (err) - throw err; + console.warn(err) }) } catch (e) { - throw e; + console.warn(e) } } } @@ -49,20 +49,20 @@ const rosterFns = { const ev = new rosters(data) ev.save((err) => { if (err) - throw err; + console.warn(err) }) } catch (e) { - throw e; + console.warn(e) } } } const matchFns = { getAll: async () => { - return await matches.find().exec() + return await matches.find().populate('orange').populate('blue').exec() }, getById: async (id) => { - return await matches.findById(id).exec() + return await matches.findById(id).populate('orange').populate('blue').exec() }, update: async(id, data) => { return await matches.findByIdAndUpdate(id, data).exec() @@ -75,10 +75,10 @@ const matchFns = { const ev = new matches(data) ev.save((err) => { if (err) - throw err; + console.warn(err) }) } catch (e) { - throw e; + console.warn(e) } } } @@ -87,8 +87,14 @@ const streamFns = { getAll: async () => { return await streams.find().exec() }, + getAllPop: async () => { + return await streams.find().populate('event').populate({ + path: 'matches', + populate: [{ path: 'orange' }, { path: 'blue' }], + }).populate('casters').populate('hosts').exec() + }, getById: async (id) => { - return await streams.findById(id).exec() + return await streams.findById(id).populate('event').populate('matches').populate('casters').populate('hosts').exec() }, update: async(id, data) => { return await streams.findByIdAndUpdate(id, data).exec() @@ -101,10 +107,10 @@ const streamFns = { const ev = new streams(data) ev.save((err) => { if (err) - throw err; + console.warn(err) }) } catch (e) { - throw e; + console.warn(e) } } } @@ -127,10 +133,10 @@ const casterFns = { const ev = new casters(data) ev.save((err) => { if (err) - throw err; + console.warn(err) }) } catch (e) { - throw e; + console.warn(e) } } } @@ -153,10 +159,10 @@ const hostFns = { const ev = new hosts(data) ev.save((err) => { if (err) - throw err; + console.warn(err) }) } catch (e) { - throw e; + console.warn(e) } } } diff --git a/src/ws/handler.js b/src/ws/handler.js index 339507b..4afe418 100644 --- a/src/ws/handler.js +++ b/src/ws/handler.js @@ -1,6 +1,7 @@ const { pop } = require('../uuids') const { channelEvents } = require('./channels') const crud = require('./crud') +const { recvUpdate } = require('./streams') const connections = {}; @@ -12,7 +13,7 @@ const handler = (ws, id) => { ws.send(JSON.stringify({ event: 'info', - data: 'Welcome to APL Nuke v1.0.0!' + data: 'Welcome to APL Nuke v1.1.0!' })) ws.on('message', (msg) => handleMsg(msg, id)) @@ -26,25 +27,38 @@ const handleMsg = async (msg, id) => { try { const d = JSON.parse(msg) if (d.hasOwnProperty('subscribe') && channelEvents.indexOf(d.subscribe) !== -1) { + console.log('received sub for ', d.subscribe) const channel = d.subscribe.split(':')[0] connections[id].events.push(d.subscribe) - connections[id].connection.send(JSON.stringify(await crud[channel].getAll())) + const dm = { + event: `${channel}:read`, + data: await crud[channel].getAll(), + } + if (channel === 'streams' && d.subscribe === 'streams:full') { + dm.event = 'streams:full' + dm.data = await crud[channel].getAllPop() + } + connections[id].connection.send(JSON.stringify(dm)) } else if (d.hasOwnProperty('event') && channelEvents.indexOf(d.event) !== -1) { const ev = d.event.split(':') const channel = ev[0] const event = ev[1] console.log(`received event for ${channel} with data %s`, d.data) + let dm; switch (event) { case 'create': - await crud[channel].create(d.data) + dm = await crud[channel].create(d.data) + recvUpdate(channel, dm) fanoutMsg(channel, await crud[channel].getAll()) break; case 'update': - await crud[channel].update(d.data.id, d.data.data) + dm = await crud[channel].update(d.data.id, d.data.data) + recvUpdate(channel, dm) fanoutMsg(channel, await crud[channel].getAll()) break; case 'delete': await crud[channel].delete(d.data.id) + recvUpdate(channel, 'delete') fanoutMsg(channel, await crud[channel].getAll()) break; } @@ -55,9 +69,21 @@ const handleMsg = async (msg, id) => { } const fanoutMsg = (channel, data) => { - Object.keys(connections).forEach((k) => { + Object.keys(connections).forEach( async (k) => { if (connections[k].events.indexOf(`${channel}:read`) !== -1) { - connections[k].connection.send(JSON.stringify(data)) + const d = { + event: `${channel}:read`, + data, + } + connections[k].connection.send(JSON.stringify(d)) + if (connections[k].events.indexOf('streams:full') !== -1) { + const m = await crud['streams'].getAllPop() + const d = { + event: 'streams:full', + data: m, + } + connections[k].connection.send(JSON.stringify(d)) + } } }) } diff --git a/src/ws/streams.js b/src/ws/streams.js new file mode 100644 index 0000000..a65464c --- /dev/null +++ b/src/ws/streams.js @@ -0,0 +1,50 @@ +const { v4 } = require('uuid') +const crud = require('./crud') + +const connections = {} + +const subscribe = (ws, sid) => { + const id = v4() + connections[id] = { + connection: ws, + streamid: sid, + } + + ws.send(JSON.stringify({ + event: 'info', + data: 'Welcome to APL Nuke v1.1.0!', + })) + + sendInitial(id) + + ws.on('close', () => { + delete connections[id] + }) +} + +const cModel = { + 'events': 'event', + 'matches': 'matches', + 'casters': 'casters', + 'hosts': 'hosts', +} + +const recvUpdate = async (channel, data) => { + Object.keys(connections).forEach((k) => { + sendInitial(k) + }) +} + +const sendInitial = async (id) => { + const c = connections[id] + const stream = await crud['streams'].getById(c.streamid) + c.connection.send(JSON.stringify({ + event: 'streams:read', + data: stream, + })) +} + +module.exports = { + subscribe, + recvUpdate, +} \ No newline at end of file