Compare commits

..

13 Commits
v1.3.0 ... main

@ -0,0 +1,2 @@
$env:NODE_OPTIONS = "--openssl-legacy-provider"
npm run electron:build

34385
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,9 +1,9 @@
{
"name": "slapdash",
"version": "1.3.0",
"version": "1.6.0",
"author": "Ayush Mukherjee",
"description": "Dashboard for APL-Nuke cum APL-Boost data manager",
"private": true,
"private": false,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
@ -14,27 +14,26 @@
},
"main": "background.js",
"dependencies": {
"@tailwindcss/postcss7-compat": "^2.0.2",
"@vue/devtools": "^6.0.0-beta.6",
"autoprefixer": "^9",
"axios": "^0.21.1",
"autoprefixer": "^10",
"axios": "^1",
"core-js": "^3.6.5",
"postcss": "^7",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.2",
"postcss": "^8",
"tailwindcss": "3.3.5",
"vue": "^3.0.0",
"vue-router": "^4.0.0-0",
"ws": "^6.2.1"
"ws": "^8"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/cli-plugin-babel": "~5",
"@vue/cli-plugin-router": "~5",
"@vue/cli-service": "~5",
"@vue/compiler-sfc": "^3.0.0",
"electron": "^11.0.0",
"electron": "^27.0.4",
"electron-devtools-installer": "^3.1.0",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"vue-cli-plugin-electron-builder": "~2.0.0-rc.6",
"vue-cli-plugin-tailwind": "~2.0.6"
"sass-loader": "^13",
"vue-cli-plugin-electron-builder": "~2",
"vue-cli-plugin-tailwind": "~3"
}
}

@ -7,6 +7,7 @@ import path from 'path'
const isDevelopment = process.env.NODE_ENV !== 'production'
import { start, status, stop, state, sub, sendAll } from './utils/server'
import { start as relayStart, status as relayStatus, stop as relayStop, state as relayState } from './utils/relay'
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
@ -96,6 +97,10 @@ ipcMain.handle('status', async () => {
return status()
})
ipcMain.handle('relayStatus', async () => {
return relayStatus()
})
ipcMain.handle('server', async (e, arg) => {
if (arg === true) {
start()
@ -104,6 +109,14 @@ ipcMain.handle('server', async (e, arg) => {
}
})
ipcMain.handle('relay', async (e, arg) => {
if (arg === true) {
relayStart()
} else {
relayStop()
}
})
ipcMain.handle('massSend', async () => {
console.log('Sending refreshed data')
sendAll()

@ -0,0 +1,155 @@
<template>
<h1 class="text-2xl font-bold uppercase mb-10">streams management</h1>
<div class="grid grid-cols-2 gap-x-8">
<div class="overflow-x-auto">
<table class="table-auto">
<thead>
<tr>
<th class="border border-gray-400 text-left px-8 py-4">ID</th>
<th class="border border-gray-400 text-left px-8 py-4">Name</th>
<th class="border border-gray-400 text-left px-8 py-4">Name Arabic</th>
<th class="border border-gray-400 text-left px-8 py-4">Name Japanese</th>
<th class="border border-gray-400 text-left px-8 py-4">Event</th>
<th class="border border-gray-400 text-left px-8 py-4">Matches</th>
</tr>
</thead>
<tbody>
<tr class="item" v-for="e in store.state.brackets" :key="e._id" :class="item === e._id ? 'active' : ''" @click="setItem(e)">
<td class="border border-gray-400 text-left px-8 py-4">{{ e._id }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name }}</td>
<td class="border border-gray-400 text-left px-8 py-4" dir="rtl">{{ e.name_arabic ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name_jpn ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ store.state.events.filter(x => x._id === e.event)[0].name }}</td>
<td class="border border-gray-400 text-left">
<table v-if="e.matches" class="border border-gray-400 border-collapse mx-auto w-full h-full">
<tbody>
<tr v-for="p in e.matches" :key="p">
<td class="border border-gray-400">{{ store.state.matches.filter((x) => x._id === p)[0]?.type || '' }}: {{ store.state.matches.filter((x) => x._id === p)[0]?.blue?.name || '' }} VS {{ store.state.matches.filter((x) => x._id === p)[0]?.orange?.name || '' }}</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<h2 class="text-lg font-bold overflow-auto">Create / Update</h2>
<form name="form" @submit.prevent="form">
<p class="mt-4">Bracket name</p>
<input v-model="name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" required />
<p class="mt-4">Bracket name in arabic (optional)</p>
<input v-model="name_arabic" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" dir="rtl" />
<p class="mt-4">Bracket name in japanese (optional)</p>
<input v-model="name_jpn" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Event</p>
<select v-model="event" class="block my-4 border border-gray-500 rounded-lg bg-white w-64 h-8 px-4">
<option v-for="r in store.state.events" :key="r._id" :value="r._id">{{ r.name }}</option>
</select>
<p class="mt-4">Matches</p>
<select v-for="(n, i) in matches.length" :key="i" v-model="matches[n - 1]" class="block my-4 border border-gray-500 rounded-lg bg-white w-64 h-8 px-4">
<option v-for="r in store.state.matches" :key="r._id" :value="r._id"><span v-if="r">{{ r.type }}: {{ r.blue.name }} VS {{ r.orange.name }}</span></option>
</select>
<button @click.prevent="addMatch()" class="block w-64 h-10 my-4 text-white bg-red-700 rounded-lg hover:bg-red-600">Add match</button>
<button class="block w-64 h-10 text-white bg-red-700 rounded-lg hover:bg-red-600">Submit</button>
<button @click.prevent="delForm" v-if="item" class="block mt-4 w-64 h-10 text-white bg-red-700 rounded-lg hover:bg-red-600">Delete</button>
</form>
</div>
</div>
</template>
<script>
import { inject, ref } from 'vue'
import { create, update, del } from '../utils/websocket'
export default {
setup() {
const store = inject('store')
const item = ref('')
const name = ref('')
const name_arabic = ref('')
const name_jpn = ref('')
const event = ref('')
const matches = ref([])
const addMatch = () => {
matches.value.push('')
}
const setItem = (e) => {
reset()
item.value = e._id
name.value = e.name
name_arabic.value = e.name_arabic
name_jpn.value = e.name_jpn
event.value = e.event
e.matches.forEach(m => {
if (matches.value.length !== e.matches.length) {
matches.value.push(m)
}
})
}
const reset = () => {
item.value = ''
name.value = ''
name_arabic.value = ''
name_jpn.value = ''
event.value = ''
matches.value = []
}
const form = () => {
if (item.value !== '') {
update('brackets', item.value, {
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
event: event.value,
matches: matches.value,
})
} else if (name.value !== '') {
create('brackets', {
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
event: event.value,
matches: matches.value,
})
}
reset()
}
const delForm = () => {
if (item.value) {
del('brackets', item.value)
reset()
}
}
return {
store,
item,
name,
name_arabic,
name_jpn,
event,
matches,
setItem,
addMatch,
form,
delForm,
}
},
}
</script>
<style>
.item:hover {
@apply bg-gray-300 cursor-pointer;
}
.item.active {
@apply bg-gray-400;
}
</style>

@ -1,11 +1,14 @@
<template>
<h1 class="text-2xl font-bold uppercase mb-10">casters management</h1>
<div class="grid grid-cols-2 gap-x-8">
<div class="overflow-x-auto">
<table class="table-auto">
<thead>
<tr>
<th class="border border-gray-400 text-left px-8 py-4">Name Tag</th>
<th class="border border-gray-400 text-left px-8 py-4">Real Name</th>
<th class="border border-gray-400 text-left px-8 py-4">Real Name Arabic</th>
<th class="border border-gray-400 text-left px-8 py-4">Real Name Japanese</th>
<th class="border border-gray-400 text-left px-8 py-4">Twitter</th>
<th class="border border-gray-400 text-left px-8 py-4">Image</th>
</tr>
@ -14,11 +17,14 @@
<tr class="item" :class="item === e._id ? 'active' : ''" @click="setItem(e)" v-for="e in store.state.casters" :key="e._id">
<td class="border border-gray-400 text-left px-8 py-4">{{ e.username }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name_arabic ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name_jpn ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.twitter }}</td>
<td class="border border-gray-400 text-left px-8 py-4"><img :src="e.image" :alt="e.name"></td>
</tr>
</tbody>
</table>
</div>
<div>
<h2 class="text-lg font-bold">Create / Update</h2>
<form name="form" @submit.prevent="form">
@ -26,6 +32,10 @@
<input v-model="username" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" required />
<p class="mt-4">Caster real name</p>
<input v-model="name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" required />
<p class="mt-4">Caster real name arabic (optional)</p>
<input v-model="name_arabic" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Caster real name japanese (optional)</p>
<input v-model="name_jpn" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Caster twitter</p>
<input v-model="twitter" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" required />
<p class="mt-4">Caster image URL</p>
@ -48,6 +58,8 @@ export default {
const item = ref('')
const username = ref('')
const name = ref('')
const name_arabic = ref('')
const name_jpn = ref('')
const twitter = ref('')
const image = ref('')
@ -55,6 +67,8 @@ export default {
item.value = ''
username.value = ''
name.value = ''
name_arabic.value = ''
name_jpn.value = ''
twitter.value = ''
image.value = ''
}
@ -63,6 +77,8 @@ export default {
item.value = e._id
username.value = e.username
name.value = e.name
name_arabic.value = e.name_arabic
name_jpn.value = e.name_jpn
twitter.value = e.twitter
image.value = e.image
}
@ -72,6 +88,8 @@ export default {
update('casters', item.value, {
username: username.value,
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
twitter: twitter.value,
image: image.value,
})
@ -79,6 +97,8 @@ export default {
create('casters', {
username: username.value,
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
twitter: twitter.value,
image: image.value,
})
@ -98,6 +118,8 @@ export default {
item,
username,
name,
name_arabic,
name_jpn,
twitter,
image,
setItem,

@ -1,25 +1,35 @@
<template>
<h1 class="text-2xl font-bold uppercase mb-10">events management</h1>
<div class="grid grid-cols-2 gap-x-8">
<div class="overflow-x-auto">
<table class="table-auto">
<thead>
<tr>
<th class="border border-gray-400 text-left px-8 py-4">ID</th>
<th class="border border-gray-400 text-left px-8 py-4">Name</th>
<th class="border border-gray-400 text-left px-8 py-4">Arabic Name</th>
<th class="border border-gray-400 text-left px-8 py-4">Japanese Name</th>
</tr>
</thead>
<tbody>
<tr class="item" :class="item === e._id ? 'active' : ''" @click="setItem(e)" v-for="e in store.state.events" :key="e._id">
<td class="border border-gray-400 text-left px-8 py-4">{{ e._id }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name }}</td>
<td class="border border-gray-400 text-left px-8 py-4 rtl" dir="rtl">{{ e.name_arabic ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name_jpn ?? '' }}</td>
</tr>
</tbody>
</table>
</div>
<div>
<h2 class="text-lg font-bold">Create / Update</h2>
<form name="form" @submit.prevent="form">
<p class="mt-4">Event name</p>
<input v-model="name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" required />
<p class="mt-4">Event name in arabic (optional)</p>
<input v-model="name_arabic" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" dir="rtl" />
<p class="mt-4">Event name in japanese (optional)</p>
<input v-model="name_jpn" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<button class="block w-64 h-10 text-white bg-red-700 rounded-lg hover:bg-red-600">Submit</button>
<button @click.prevent="delForm" v-if="item" class="block mt-4 w-64 h-10 text-white bg-red-700 rounded-lg hover:bg-red-600">Delete</button>
</form>
@ -37,10 +47,14 @@ export default {
const item = ref('')
const name = ref('')
const name_arabic = ref('')
const name_jpn = ref('')
const setItem = (e) => {
item.value = e._id
name.value = e.name
name_arabic.value = e.name_arabic
name_jpn.value = e.name_jpn
}
const reset = () => {
@ -52,10 +66,14 @@ export default {
if (item.value !== '') {
update('events', item.value, {
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
})
} else if (name.value !== '') {
} else if (name.value !== '' && name_arabic.value !== '' && name_jpn.value !== '') {
create('events', {
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
})
}
reset()
@ -72,6 +90,8 @@ export default {
store,
item,
name,
name_arabic,
name_jpn,
setItem,
form,
delForm,

@ -6,6 +6,8 @@
<thead>
<tr>
<th class="border border-gray-400 text-left px-8 py-4">Round name</th>
<th class="border border-gray-400 text-left px-8 py-4">Round arabic</th>
<th class="border border-gray-400 text-left px-8 py-4">Round japanese</th>
<th class="border border-gray-400 text-left px-8 py-4">Blue</th>
<th class="border border-gray-400 text-left px-8 py-4">Orange</th>
<th class="border border-gray-400 text-left px-8 py-4">Current</th>
@ -20,6 +22,8 @@
<tbody>
<tr class="item" :class="item === e._id ? 'active' : ''" @click="setItem(e)" v-for="e in store.state.matches" :key="e._id">
<td class="border border-gray-400 text-left px-8 py-4">{{ e.type }}</td>
<td class="border border-gray-400 text-left px-8 py-4" dir="rtl">{{ e.type_arabic ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.type_jpn ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.blue.name }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.orange.name }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.current }}</td>
@ -47,6 +51,10 @@
<form name="form" @submit.prevent="form">
<p class="mt-4">Round name</p>
<input v-model="type" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" required />
<p class="mt-4">Round name in arabic (optional)</p>
<input v-model="type_arabic" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" dir="rtl" />
<p class="mt-4">Round name in japanese (optional)</p>
<input v-model="type_jpn" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Blue team</p>
<select v-model="blue" class="block my-4 border border-gray-500 rounded-lg bg-white w-64 h-8 px-4">
<option v-for="r in store.state.rosters" :key="r._id" :value="r._id">{{ r.name }}</option>
@ -96,6 +104,8 @@ export default {
const item = ref('')
const type = ref('')
const type_arabic = ref('')
const type_jpn = ref('')
const blue = ref('')
const orange = ref('')
const current = ref(false)
@ -111,6 +121,8 @@ export default {
const setItem = (e) => {
item.value = e._id
type.value = e.type
type_arabic.value = e.type_arabic
type_jpn.value = e.type_jpn
blue.value = e.blue._id
orange.value = e.orange
current.value = e.current
@ -127,6 +139,8 @@ export default {
const resetf = () => {
item.value = ''
type.value = ''
type_arabic.value = ''
type_jpn.value = ''
blue.value = ''
orange.value = ''
current.value = false
@ -144,6 +158,8 @@ export default {
if (item.value !== '') {
update('matches', item.value, {
type: type.value,
type_arabic: type_arabic.value,
type_jpn: type_jpn.value,
blue: blue.value,
orange: orange.value,
current: current.value,
@ -163,6 +179,8 @@ export default {
} else if (type.value !== '') {
create('matches', {
type: type.value,
type_arabic: type_arabic.value,
type_jpn: type_jpn.value,
blue: blue.value,
orange: orange.value,
current: current.value,
@ -185,6 +203,8 @@ export default {
store,
item,
type,
type_arabic,
type_jpn,
blue,
orange,
blueSeries,

@ -1,10 +1,13 @@
<template>
<h1 class="text-2xl font-bold uppercase mb-10">rosters management</h1>
<div class="grid grid-cols-2 gap-x-8">
<div class="overflow-x-auto">
<table class="table-auto">
<thead>
<tr>
<th class="border border-gray-400 text-left px-8 py-4">Name</th>
<th class="border border-gray-400 text-left px-8 py-4">Name Arabic</th>
<th class="border border-gray-400 text-left px-8 py-4">Name Japanese</th>
<th class="border border-gray-400 text-left px-8 py-4">Logo</th>
<th class="border border-gray-400 text-left px-8 py-4">Players</th>
</tr>
@ -12,6 +15,8 @@
<tbody>
<tr class="item" :class="item === e._id ? 'active' : ''" @click="setItem(e)" v-for="e in store.state.rosters" :key="e._id">
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name }}</td>
<td class="border border-gray-400 text-left px-8 py-4" dir="rtl">{{ e.name_arabic ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name_jpn ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4"><img class="w-12 h-12" :src="e.logo" :alt="e.name"></td>
<td class="border border-gray-400 text-left px-8 py-4">
<table v-if="e.players">
@ -25,11 +30,16 @@
</tr>
</tbody>
</table>
</div>
<div>
<h2 class="text-lg font-bold">Create / Update</h2>
<form name="form" @submit.prevent="form">
<p class="mt-4">Roster name</p>
<input v-model="name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" required />
<p class="mt-4">Roster name in arabic (optional)</p>
<input v-model="name_arabic" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" dir="rtl" />
<p class="mt-4">Roster name in japanese (optional)</p>
<input v-model="name_jpn" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Roster logo</p>
<input v-model="logo" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" required />
<fieldset v-for="(n, i) in playersLocal.length" :key="i" class="border border-gray-400 rounded px-6 my-4">
@ -57,6 +67,8 @@ export default {
const item = ref('')
const name = ref('')
const name_arabic = ref('')
const name_jpn = ref('')
const logo = ref('')
const playersLocal = ref([])
@ -65,6 +77,8 @@ export default {
const setItem = (e) => {
item.value = e._id
name.value = e.name
name_arabic.value = e.name_arabic
name_jpn.value = e.name_jpn
logo.value = e.logo
e.players.forEach(p => {
if (playersLocal.value.length !== e.players.length) {
@ -91,6 +105,8 @@ export default {
const reset = () => {
item.value = ''
name.value = ''
name_arabic.value = ''
name_jpn.value = ''
logo.value = ''
playersLocal.value = []
}
@ -99,12 +115,16 @@ export default {
if (item.value !== '') {
update('rosters', item.value, {
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
logo: logo.value,
players: playersLocal.value,
})
} else if (name.value !== '') {
create('rosters', {
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
logo: logo.value,
players: playersLocal.value,
})
@ -123,6 +143,8 @@ export default {
store,
item,
name,
name_arabic,
name_jpn,
logo,
playersLocal,
setItem,

@ -0,0 +1,231 @@
<template>
<h1 class="text-2xl font-bold uppercase mb-10">stats management</h1>
<div class="grid grid-cols-2 gap-x-8">
<div class="overflow-x-auto">
<table class="table-auto">
<thead>
<tr>
<th class="border border-gray-400 text-left px-8 py-4">Player Name</th>
<th class="border border-gray-400 text-left px-8 py-4">Player Name Arabic</th>
<th class="border border-gray-400 text-left px-8 py-4">Player Name Japanese</th>
<th class="border border-gray-400 text-left px-8 py-4">Image</th>
<th class="border border-gray-400 text-left px-8 py-4">Image Full?</th>
<th class="border border-gray-400 text-left px-8 py-4">Win Title</th>
<th class="border border-gray-400 text-left px-8 py-4">Win %</th>
<th class="border border-gray-400 text-left px-8 py-4">Goals Title</th>
<th class="border border-gray-400 text-left px-8 py-4">Goals</th>
<th class="border border-gray-400 text-left px-8 py-4">Shots Title</th>
<th class="border border-gray-400 text-left px-8 py-4">Shots</th>
<th class="border border-gray-400 text-left px-8 py-4">Saves Title</th>
<th class="border border-gray-400 text-left px-8 py-4">Saves</th>
<th class="border border-gray-400 text-left px-8 py-4">Assists Title</th>
<th class="border border-gray-400 text-left px-8 py-4">Assists</th>
</tr>
</thead>
<tbody>
<tr class="item" :class="item === e._id ? 'active' : ''" @click="setItem(e)" v-for="e in store.state.stats" :key="e._id">
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name }}</td>
<td class="border border-gray-400 text-left px-8 py-4" dir="rtl">{{ e.name_arabic ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name_jpn ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4"><img :src="e.image" :alt="e.name"></td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.image_full }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.win_name }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.win }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.goals_name }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.goals }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.shots_name }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.shots }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.saves_name }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.saves }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.assists_name }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.assists }}</td>
</tr>
</tbody>
</table>
</div>
<div>
<h2 class="text-lg font-bold">Create / Update</h2>
<form name="form" @submit.prevent="form">
<p class="mt-4">Player Name</p>
<input v-model="name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" required />
<p class="mt-4">Player name arabic (optional)</p>
<input v-model="name_arabic" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Player name japanese (optional)</p>
<input v-model="name_jpn" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Image URL</p>
<input v-model="image" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Image Full Size?</p>
<input v-model="image_full" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="checkbox" />
<p class="mt-4">Win Title</p>
<input v-model="win_name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Win %</p>
<input v-model="win" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Goals Title</p>
<input v-model="goals_name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Goals</p>
<input v-model="goals" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Shots Title</p>
<input v-model="shots_name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Shots</p>
<input v-model="shots" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Saves Title</p>
<input v-model="saves_name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Saves</p>
<input v-model="saves" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Assists Title</p>
<input v-model="assists_name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Assists</p>
<input v-model="assists" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<button class="block w-64 h-10 text-white bg-red-700 rounded-lg hover:bg-red-600">Submit</button>
<button @click.prevent="delForm" v-if="item" class="block mt-4 w-64 h-10 text-white bg-red-700 rounded-lg hover:bg-red-600">Delete</button>
</form>
</div>
</div>
</template>
<script>
import { inject, ref } from 'vue'
import { create, update, del } from '../utils/websocket'
export default {
setup() {
const store = inject('store')
const item = ref('')
const name = ref('')
const name_arabic = ref('')
const name_jpn = ref('')
const image = ref('')
const image_full = ref(false)
const win_name = ref('')
const win = ref('')
const goals_name = ref('')
const goals = ref('')
const shots_name = ref('')
const shots = ref('')
const saves_name = ref('')
const saves = ref('')
const assists_name = ref('')
const assists = ref('')
const reset = () => {
item.value = ''
name.value = ''
name_arabic.value = ''
name_jpn.value = ''
image.value = ''
image_full.value = false
win_name.value = ''
win.value = ''
goals_name.value = ''
goals.value = ''
shots_name.value = ''
shots.value = ''
saves_name.value = ''
saves.value = ''
assists_name.value = ''
assists.value = ''
}
const setItem = (e) => {
item.value = e._id
name.value = e.name
name_arabic.value = e.name_arabic
name_jpn.value = e.name_jpn
image.value = e.image
image_full.value = e.image_full
win_name.value = e.win_name
win.value = e.win
goals_name.value = e.goals_name
goals.value = e.goals
shots_name.value = e.shots
shots.value = e.shots
saves_name.value = e.saves_name
saves.value = e.saves
assists_name.value = e.assists_name
assists.value = e.assists
}
const form = () => {
if (item.value !== '') {
update('stats', item.value, {
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
image: image.value,
image_full: image_full.value,
win_name: win_name.value,
win: win.value,
goals_name: goals_name.value,
goals: goals.value,
shots_name: shots_name.value,
shots: shots.value,
saves_name: saves_name.value,
saves: saves.value,
assists_name: assists_name.value,
assists: assists.value,
})
} else if (name.value !== '') {
create('stats', {
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
image: image.value,
image_full: image_full.value,
win_name: win_name.value,
win: win.value,
goals_name: goals_name.value,
goals: goals.value,
shots_name: shots_name.value,
shots: shots.value,
saves_name: saves_name.value,
saves: saves.value,
assists_name: assists_name.value,
assists: assists.value,
})
}
reset()
}
const delForm = () => {
if (item.value) {
del('stats', item.value)
reset()
}
}
return {
store,
item,
name,
name_arabic,
name_jpn,
image,
image_full,
win_name,
win,
goals_name,
goals,
shots_name,
shots,
saves_name,
saves,
assists_name,
assists,
setItem,
form,
delForm,
}
},
}
</script>
<style>
.item:hover {
@apply bg-gray-300 cursor-pointer;
}
.item.active {
@apply bg-gray-400;
}
</style>

@ -7,16 +7,20 @@
<tr>
<th class="border border-gray-400 text-left px-8 py-4">ID</th>
<th class="border border-gray-400 text-left px-8 py-4">Name</th>
<th class="border border-gray-400 text-left px-8 py-4">Arabic Name</th>
<th class="border border-gray-400 text-left px-8 py-4">Japanese Name</th>
<th class="border border-gray-400 text-left px-8 py-4">Event</th>
<th class="border border-gray-400 text-left px-8 py-4">Matches</th>
<th class="border border-gray-400 text-left px-8 py-4">Casters</th>
<th class="border border-gray-400 text-left px-8 py-4">Hosts</th>
<th class="border border-gray-400 text-left px-8 py-4">Stats</th>
</tr>
</thead>
<tbody>
<tr class="item" v-for="e in store.state.streams" :key="e._id" :class="item === e._id ? 'active' : ''" @click="setItem(e)">
<td class="border border-gray-400 text-left px-8 py-4">{{ e._id }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name_arabic ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ e.name_jpn ?? '' }}</td>
<td class="border border-gray-400 text-left px-8 py-4">{{ store.state.events.filter(x => x._id === e.event)[0].name }}</td>
<td class="border border-gray-400 text-left">
<table v-if="e.matches" class="border border-gray-400 border-collapse mx-auto w-full h-full">
@ -37,10 +41,10 @@
</table>
</td>
<td class="border border-gray-400 text-left px-8 py-4">
<table v-if="e.hosts">
<table v-if="e.stats">
<tbody>
<tr v-for="p in e.hosts" :key="p">
<td>{{ store.state.hosts.filter(x => x._id === p)[0]?.name }}</td>
<tr v-for="p in e.stats" :key="p">
<td class="border border-gray-400">{{ store.state.stats.filter(x => x._id === p)[0]?.name }}</td>
</tr>
</tbody>
</table>
@ -54,6 +58,10 @@
<form name="form" @submit.prevent="form">
<p class="mt-4">Stream name</p>
<input v-model="name" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" required />
<p class="mt-4">Stream name in arabic (optional)</p>
<input v-model="name_arabic" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" dir="rtl" />
<p class="mt-4">Stream name in japanese (optional)</p>
<input v-model="name_jpn" class="block my-4 border border-gray-500 rounded-lg w-64 h-8 px-4" type="text" />
<p class="mt-4">Event</p>
<select v-model="event" class="block my-4 border border-gray-500 rounded-lg bg-white w-64 h-8 px-4">
<option v-for="r in store.state.events" :key="r._id" :value="r._id">{{ r.name }}</option>
@ -68,11 +76,11 @@
<option v-for="r in store.state.casters" :key="r._id" :value="r._id">{{ r.name }}</option>
</select>
<button @click.prevent="addCaster()" class="block w-64 h-10 my-4 text-white bg-red-700 rounded-lg hover:bg-red-600">Add caster</button>
<p class="mt-4">Hosts</p>
<p class="mt-4">Stats</p>
<select v-for="(n, i) in hosts.length" :key="i" v-model="hosts[n - 1]" class="block my-4 border border-gray-500 rounded-lg bg-white w-64 h-8 px-4">
<option v-for="r in store.state.hosts" :key="r._id" :value="r._id">{{ r.name }}</option>
<option v-for="r in store.state.stats" :key="r._id" :value="r._id" :selected="hosts[i] == r._id">{{ r.name }}</option>
</select>
<button @click.prevent="addHost()" class="block w-64 h-10 my-4 text-white bg-red-700 rounded-lg hover:bg-red-600">Add host</button>
<button @click.prevent="addHost()" class="block w-64 h-10 my-4 text-white bg-red-700 rounded-lg hover:bg-red-600">Add Stat</button>
<button class="block w-64 h-10 text-white bg-red-700 rounded-lg hover:bg-red-600">Submit</button>
<button @click.prevent="delForm" v-if="item" class="block mt-4 w-64 h-10 text-white bg-red-700 rounded-lg hover:bg-red-600">Delete</button>
</form>
@ -90,6 +98,8 @@ export default {
const item = ref('')
const name = ref('')
const name_arabic = ref('')
const name_jpn = ref('')
const event = ref('')
const matches = ref([])
const casters = ref([])
@ -108,8 +118,11 @@ export default {
}
const setItem = (e) => {
reset()
item.value = e._id
name.value = e.name
name_arabic.value = e.name_arabic
name_jpn.value = e.name_jpn
event.value = e.event
e.matches.forEach(m => {
if (matches.value.length !== e.matches.length) {
@ -121,8 +134,8 @@ export default {
casters.value.push(m)
}
})
e.hosts.forEach(m => {
if (hosts.value.length !== e.hosts.length) {
e.stats.forEach(m => {
if (hosts.value.length !== e.stats.length) {
hosts.value.push(m)
}
})
@ -131,6 +144,8 @@ export default {
const reset = () => {
item.value = ''
name.value = ''
name_arabic.value = ''
name_jpn.value = ''
event.value = ''
matches.value = []
casters.value = []
@ -141,18 +156,22 @@ export default {
if (item.value !== '') {
update('streams', item.value, {
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
event: event.value,
matches: matches.value,
casters: casters.value,
hosts: hosts.value,
stats: hosts.value,
})
} else if (name.value !== '') {
create('streams', {
name: name.value,
name_arabic: name_arabic.value,
name_jpn: name_jpn.value,
event: event.value,
matches: matches.value,
casters: casters.value,
hosts: hosts.value,
stats: hosts.value,
})
}
reset()
@ -169,6 +188,8 @@ export default {
store,
item,
name,
name_arabic,
name_jpn,
event,
matches,
casters,

@ -1,3 +1,7 @@
import { ipcRenderer } from 'electron'
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('ipcRenderer', {
invoke: (a, b = null) => ipcRenderer.invoke(a, b),
on: (a, b = null) => ipcRenderer.on(a, b),
})
window.ipcRenderer = ipcRenderer

@ -4,6 +4,10 @@ import { handleMsg, sendSub } from './utils/websocket'
const token = window.localStorage.getItem('token')
const server = process.env.VUE_APP_NUKE_URI
const api_proto = process.env.VUE_APP_NUKE_API_PROTO
const ws_proto = process.env.VUE_APP_NUKE_WS_PROTO
const state = reactive({
name: '',
token: token || '',
@ -15,16 +19,22 @@ const state = reactive({
matches: [],
casters: [],
hosts: [],
stats: [],
streams: [],
streamsFull: [],
brackets: [],
overlayStream: null,
server: false,
relay: false,
})
let interval = 0
const methods = {
authenticate: async (token) => {
state.session = null
window.localStorage.setItem('token', token)
return axios.post('http://178.79.128.135:5000/api/v1', {
return axios.post(`${api_proto}://${server}/api/v1`, {
token,
})
.then((res) => {
@ -34,16 +44,31 @@ const methods = {
return true
})
},
connect: (uri) => {
state.ws = new WebSocket(`ws://178.79.128.135:5000/ws/${state.session}`)
connect: () => {
if (state.session !== null) {
state.ws = new WebSocket(`${ws_proto}://${server}/ws/${state.session}`)
state.ws.onopen = () => {
clearInterval(interval)
sendSub(state.ws)
state.ws.onclose = () => {
console.warn('Lost connection to nuke. Attempting to reconnect every 5 seconds...')
interval = setInterval(() => {
methods.authenticate(state.token)
.then(() => {
methods.connect()
})
.catch((_) => {
console.warn('Error reconnecting! Trying again')
})
}, 5000)
}
}
state.ws.onmessage = (e) => {
const data = JSON.parse(e.data)
handleMsg(data)
}
}
}
}
export default {

@ -0,0 +1,108 @@
import ws from 'ws'
import { v4 } from 'uuid'
let relay = null
let rl = null
const connections = {}
export const sub = []
let overlayStream = null
export const state = (data) => {
overlayStream = data
}
export const start = () => {
console.warn('RELAY SERVER STARTED.')
relay = new ws.Server({
port: 49322,
})
relay.on('connection', handle)
rl = new ws.WebSocket('ws://localhost:49122')
rl.onopen = () => {
console.warn('RL connected at ws://localhost:49122');
}
rl.onclose = () => {
console.warn('Lost connection to RL, not retrying');
if (relay) {
stop()
}
rl = null
}
rl.on('message', (e) => {
console.warn('Received event from RL')
Object.values(connections).forEach(v => {
const d = JSON.parse(e)
v.send(JSON.stringify(d));
})
})
}
export const stop = () => {
console.log('Received relay stop')
Object.keys(connections).forEach((k) => {
delete connections[k]
})
relay.close(() => {
relay = null
})
console.warn('RELAY SERVER CLOSED')
}
export const status = () => {
if (relay !== null)
return true
return false
}
export const sendAll = () => {
Object.values(connections).forEach(v => {
sendData(v)
})
}
const handle = (w) => {
console.warn('NEW RELAY CONNECTION')
const id = v4()
connections[id] = w
sendData(w)
w.on('message', msg => console.log(msg))
w.on('close', () => {
delete connections[id]
})
}
const onMsg = (msg, id) => {
const d = JSON.parse(msg)
if (d.hasOwnProperty('event')) {
if (d.event === 'read') {
sendData(connections[id])
} else if (d.event === 'update') {
console.warn('RECEIVED UPDATE')
sub.forEach(x => {
x(d.data)
})
}
}
}
const sendData = (w) => {
console.log('SENDING DATA')
if (overlayStream !== null) {
console.log(overlayStream)
w.send(JSON.stringify({
event: 'read',
data: overlayStream,
}))
}
}

@ -13,7 +13,9 @@ const channels = [
'matches',
'casters',
'hosts',
'stats',
'streams',
'brackets',
]
const channelEvents = [

@ -6,9 +6,10 @@
<p class="nav-item" @click="nav = 'events'" :class="nav === 'events' ? 'active' : ''">Events</p>
<p class="nav-item" @click="nav = 'rosters'" :class="nav === 'rosters' ? 'active' : ''">Rosters</p>
<p class="nav-item" @click="nav = 'matches'" :class="nav === 'matches' ? 'active' : ''">Matches</p>
<p class="nav-item" @click="nav = 'casters'" :class="nav === 'casters' ? 'active' : ''">Casters</p>
<p class="nav-item" @click="nav = 'hosts'" :class="nav === 'hosts' ? 'active' : ''">Hosts</p>
<p class="nav-item" @click="nav = 'casters'" :class="nav === 'casters' ? 'active' : ''">Casters / Hosts</p>
<p class="nav-item" @click="nav = 'stats'" :class="nav === 'stats' ? 'active' : ''">Stats</p>
<p class="nav-item" @click="nav = 'streams'" :class="nav === 'streams' ? 'active' : ''">Streams</p>
<p class="nav-item" @click="nav = 'brackets'" :class="nav === 'brackets' ? 'active' : ''">Brackets</p>
<img class="block w-16 h-16 absolute bottom-10" src="https://cms.aplesports.com/storage/uploads/2020/04/23/5ea178d6570e1APL_New.png" alt="APL Logo">
</div>
<div class="bg-gray-200 w-full px-10 py-4 overflow-y-auto">
@ -23,6 +24,10 @@
<span v-if="!store.state.server">Start</span>
overlay
</button>
<button @click.prevent="relayDo()" class="block mt-4 w-64 h-10 text-white bg-red-700 rounded-lg hover:bg-red-600">
<span v-if="store.state.relay">Stop Relay</span>
<span v-if="!store.state.relay">Start relay</span>
</button>
<!-- <button v-if="store.state.server" :disabled="streamid === ''" @click.prevent="serverDo" class="block mt-4 w-64 h-10 text-white bg-red-700 rounded-lg hover:bg-red-600">
Stop overlay
</button> -->
@ -31,8 +36,9 @@
<Rosters v-if="nav === 'rosters'" />
<Matches v-if="nav === 'matches'" />
<Casters v-if="nav === 'casters'" />
<Hosts v-if="nav === 'hosts'" />
<Stats v-if="nav === 'stats'" />
<Streams v-if="nav === 'streams'" />
<Brackets v-if="nav === 'brackets'" />
</div>
</div>
</template>
@ -45,7 +51,9 @@ import Rosters from '@/components/Rosters.vue'
import Matches from '@/components/Matches.vue'
import Casters from '@/components/Casters.vue'
import Hosts from '@/components/Hosts.vue'
import Stats from '@/components/Stats.vue'
import Streams from '@/components/Streams.vue'
import Brackets from '@/components/Brackets.vue'
export default {
components: {
@ -54,7 +62,9 @@ export default {
Matches,
Casters,
Hosts,
Stats,
Streams,
Brackets,
},
setup() {
const store = inject('store')
@ -63,9 +73,13 @@ export default {
const check = async () => {
const status = await window.ipcRenderer.invoke('status')
const relayStatus = await window.ipcRenderer.invoke('relayStatus')
if (status) {
store.state.server = true
}
if (relayStatus) {
store.state.relay = true
}
}
check()
@ -96,6 +110,7 @@ export default {
started: data.started,
done: data.done,
series: data.series,
resets: data.resets,
})
updateServer()
@ -120,12 +135,23 @@ export default {
}
}
const relayDo = () => {
if (!store.state.relay) {
window.ipcRenderer.invoke('relay', true)
store.state.relay = true
} else {
store.state.relay = false
window.ipcRenderer.invoke('relay', false);
}
}
return {
store,
nav,
streamid,
serverDo,
relayDo,
}
},
}

Loading…
Cancel
Save