mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 20:01:17 +00:00
Add login and auth handling
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
bb46ba5d0e
commit
0075da1eba
8 changed files with 429 additions and 55 deletions
7
package-lock.json
generated
7
package-lock.json
generated
|
@ -8,6 +8,7 @@
|
||||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"codejar": "^4.2.0",
|
"codejar": "^4.2.0",
|
||||||
|
"mitt": "^3.0.1",
|
||||||
"prismjs": "^1.29.0",
|
"prismjs": "^1.29.0",
|
||||||
"vue": "^3.4.27",
|
"vue": "^3.4.27",
|
||||||
"vue-router": "^4.3.3"
|
"vue-router": "^4.3.3"
|
||||||
|
@ -3373,6 +3374,12 @@
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mitt": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"codejar": "^4.2.0",
|
"codejar": "^4.2.0",
|
||||||
|
"mitt": "^3.0.1",
|
||||||
"prismjs": "^1.29.0",
|
"prismjs": "^1.29.0",
|
||||||
"vue": "^3.4.27",
|
"vue": "^3.4.27",
|
||||||
"vue-router": "^4.3.3"
|
"vue-router": "^4.3.3"
|
||||||
|
|
98
src/components/_headNav.vue
Normal file
98
src/components/_headNav.vue
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
<template>
|
||||||
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a
|
||||||
|
class="navbar-brand"
|
||||||
|
href="#"
|
||||||
|
@click.prevent
|
||||||
|
>
|
||||||
|
<i class="fas fa-robot fa-fw me-1 text-info" />
|
||||||
|
Twitch-Bot
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="navbar-toggler"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#navbarSupportedContent"
|
||||||
|
aria-controls="navbarSupportedContent"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
|
<span class="navbar-toggler-icon" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="navbarSupportedContent"
|
||||||
|
class="collapse navbar-collapse"
|
||||||
|
>
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0" />
|
||||||
|
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
||||||
|
<li
|
||||||
|
v-if="isLoggedIn"
|
||||||
|
class="nav-item dropdown"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="nav-link d-flex align-items-center"
|
||||||
|
href="#"
|
||||||
|
role="button"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="rounded-circle nav-profile-image"
|
||||||
|
:src="profileImage"
|
||||||
|
>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-end">
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
class="dropdown-item"
|
||||||
|
href="#"
|
||||||
|
@click.prevent="logout"
|
||||||
|
>Sign-out</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
computed: {
|
||||||
|
profileImage(): string {
|
||||||
|
return this.$root.userInfo?.profile_image_url || ''
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
logout() {
|
||||||
|
this.bus.emit('logout')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
name: 'TwitchBotEditorHeadNav',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
isLoggedIn: {
|
||||||
|
required: true,
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.nav-profile-image {
|
||||||
|
max-width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,60 +1,49 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="h-100">
|
<div class="h-100">
|
||||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
<head-nav :is-logged-in="true" />
|
||||||
<div class="container-fluid">
|
|
||||||
<a class="navbar-brand" href="#" @click.prevent>
|
|
||||||
<i class="fas fa-robot fa-fw mr-1"></i>
|
|
||||||
Twitch-Bot
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
|
||||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0"></ul>
|
|
||||||
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
|
||||||
<li class="nav-item dropdown">
|
|
||||||
<a class="nav-link d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false">
|
|
||||||
<img class="rounded-circle nav-profile-image" src="https://placehold.co/400">
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-end">
|
|
||||||
<li><a class="dropdown-item" href="#">Sign-out</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<div class="layoutNav bg-body-tertiary d-flex">
|
<div class="layoutNav bg-body-tertiary d-flex">
|
||||||
<div class="nav flex-grow-1">
|
<div class="nav flex-grow-1">
|
||||||
|
<div class="layoutNavHeading">
|
||||||
<div class="layoutNavHeading">Core</div>
|
Core
|
||||||
<a href="#" class="nav-link">
|
</div>
|
||||||
<i class="fas fa-cog fa-fw me-1"></i>
|
<a
|
||||||
|
href="#"
|
||||||
|
class="nav-link"
|
||||||
|
>
|
||||||
|
<i class="fas fa-cog fa-fw me-1" />
|
||||||
General Settings
|
General Settings
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="layoutNavHeading">Chat Interaction</div>
|
<div class="layoutNavHeading">
|
||||||
<a href="#" class="nav-link">
|
Chat Interaction
|
||||||
<i class="fas fa-envelope-open-text fa-fw me-1"></i>
|
</div>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="nav-link"
|
||||||
|
>
|
||||||
|
<i class="fas fa-envelope-open-text fa-fw me-1" />
|
||||||
Auto-Messages
|
Auto-Messages
|
||||||
</a>
|
</a>
|
||||||
<a href="#" class="nav-link">
|
<a
|
||||||
<i class="fas fa-inbox fa-fw me-1"></i>
|
href="#"
|
||||||
|
class="nav-link"
|
||||||
|
>
|
||||||
|
<i class="fas fa-inbox fa-fw me-1" />
|
||||||
Rules
|
Rules
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="layoutNavHeading">Modules</div>
|
<div class="layoutNavHeading">
|
||||||
<a href="#" class="nav-link">
|
Modules
|
||||||
<i class="fas fa-dice fa-fw me-1"></i>
|
</div>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="nav-link"
|
||||||
|
>
|
||||||
|
<i class="fas fa-dice fa-fw me-1" />
|
||||||
Raffle
|
Raffle
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layoutContent">
|
<div class="layoutContent">
|
||||||
|
@ -67,7 +56,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
import HeadNav from './_headNav.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: { HeadNav },
|
||||||
|
|
||||||
name: 'TwitchBotEditorApp',
|
name: 'TwitchBotEditorApp',
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -114,6 +107,10 @@ export default defineComponent({
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layoutNav>.nav>.nav-link.disabled {
|
||||||
|
color: var(--bs-nav-link-disabled-color);
|
||||||
|
}
|
||||||
|
|
||||||
.layoutNavHeading {
|
.layoutNavHeading {
|
||||||
color: color-mix(in srgb, var(--bs-body-color) 50%, transparent);
|
color: color-mix(in srgb, var(--bs-body-color) 50%, transparent);
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
@ -121,12 +118,4 @@ export default defineComponent({
|
||||||
padding: 1.75rem 1rem 0.75rem;
|
padding: 1.75rem 1rem 0.75rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-profile-image {
|
|
||||||
max-width: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
69
src/components/login.vue
Normal file
69
src/components/login.vue
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<template>
|
||||||
|
<div class="h-100">
|
||||||
|
<head-nav :is-logged-in="false" />
|
||||||
|
<div class="content d-flex align-items-center justify-content-center">
|
||||||
|
<button
|
||||||
|
class="btn btn-twitch"
|
||||||
|
:disabled="loading"
|
||||||
|
@click="openAuthURL"
|
||||||
|
>
|
||||||
|
<i class="fab fa-twitch fa-fw me-1" />
|
||||||
|
Login with Twitch
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
import HeadNav from './_headNav.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { HeadNav },
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
authURL() {
|
||||||
|
const scopes = []
|
||||||
|
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
params.set('client_id', this.$root.vars.TwitchClientID)
|
||||||
|
params.set('redirect_uri', window.location.href.split('#')[0].split('?')[0])
|
||||||
|
params.set('response_type', 'token')
|
||||||
|
params.set('scope', scopes.join(' '))
|
||||||
|
|
||||||
|
return `https://id.twitch.tv/oauth2/authorize?${params.toString()}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
openAuthURL(): void {
|
||||||
|
window.location.href = this.authURL
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$root.bus.on('login-processing', (loading: boolean) => {
|
||||||
|
this.loading = loading
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
name: 'TwitchBotEditorLogin',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.btn-twitch {
|
||||||
|
background-color: #6441a5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
height: calc(100vh - 56px);
|
||||||
|
}
|
||||||
|
</style>
|
49
src/helpers/configNotify.ts
Normal file
49
src/helpers/configNotify.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
class ConfigNotifyListener {
|
||||||
|
private backoff: number = 100
|
||||||
|
|
||||||
|
private listener: Function
|
||||||
|
|
||||||
|
private socket: WebSocket | null = null
|
||||||
|
|
||||||
|
constructor(listener: Function) {
|
||||||
|
this.listener = listener
|
||||||
|
this.connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
private connect(): void {
|
||||||
|
if (this.socket) {
|
||||||
|
this.socket.close()
|
||||||
|
this.socket = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseURL = window.location.href.split('#')[0].replace(/^http/, 'ws')
|
||||||
|
this.socket = new WebSocket(`${baseURL}config-editor/notify-config`)
|
||||||
|
|
||||||
|
this.socket.onopen = () => {
|
||||||
|
console.debug('[notify] Socket connected')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.onmessage = evt => {
|
||||||
|
const msg = JSON.parse(evt.data)
|
||||||
|
|
||||||
|
console.debug(`[notify] Socket message received type=${msg.msg_type}`)
|
||||||
|
this.backoff = 100 // We've received a message, reset backoff
|
||||||
|
|
||||||
|
if (msg.msg_type !== 'ping') {
|
||||||
|
this.listener(msg.msg_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.onclose = evt => {
|
||||||
|
console.debug(`[notify] Socket was closed wasClean=${evt.wasClean}`)
|
||||||
|
this.updateBackoffAndReconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateBackoffAndReconnect(): void {
|
||||||
|
this.backoff = Math.min(this.backoff * 1.5, 10000)
|
||||||
|
window.setTimeout(() => this.connect(), this.backoff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConfigNotifyListener
|
162
src/main.ts
162
src/main.ts
|
@ -1,24 +1,178 @@
|
||||||
/* eslint-disable sort-imports */
|
/* eslint-disable sort-imports */
|
||||||
|
|
||||||
import './style.scss'
|
import './style.scss' // Internal global styles
|
||||||
import 'bootstrap/dist/css/bootstrap.css'
|
import 'bootstrap/dist/css/bootstrap.css' // Bootstrap 5 Styles
|
||||||
import '@fortawesome/fontawesome-free/css/all.css'
|
import '@fortawesome/fontawesome-free/css/all.css' // All FA free icons
|
||||||
|
|
||||||
import 'bootstrap/dist/js/bootstrap.bundle'
|
import 'bootstrap/dist/js/bootstrap.bundle' // Popper & Bootstrap globally available
|
||||||
|
|
||||||
import { createApp, h } from 'vue'
|
import { createApp, h } from 'vue'
|
||||||
|
import mitt from 'mitt'
|
||||||
|
|
||||||
|
import ConfigNotifyListener from './helpers/configNotify'
|
||||||
|
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import App from './components/app.vue'
|
import App from './components/app.vue'
|
||||||
|
import Login from './components/login.vue'
|
||||||
|
|
||||||
const app = createApp({
|
const app = createApp({
|
||||||
|
computed: {
|
||||||
|
fetchOpts(): RequestInit {
|
||||||
|
return {
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': `Bearer ${this.token}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
tokenRenewAt(): Date | null {
|
||||||
|
if (this.tokenExpiresAt === null || this.tokenExpiresAt.getTime() < this.now.getTime()) {
|
||||||
|
// We don't know when it expires or it's expired, we can't renew
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// We renew 720sec before expiration (0.8 * 1h)
|
||||||
|
return new Date(this.tokenExpiresAt.getTime() - 720000)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data(): Object {
|
||||||
|
return {
|
||||||
|
now: new Date(),
|
||||||
|
token: '',
|
||||||
|
tokenExpiresAt: null as Date | null,
|
||||||
|
tokenUser: '',
|
||||||
|
|
||||||
|
userInfo: null as null | {},
|
||||||
|
|
||||||
|
tickers: {},
|
||||||
|
|
||||||
|
vars: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Checks whether the API returned an 403 and in case it did triggers
|
||||||
|
* a logout and throws the user back into the login screen
|
||||||
|
*
|
||||||
|
* @param resp The response to the fetch request
|
||||||
|
* @returns The Response object from the resp parameter
|
||||||
|
*/
|
||||||
|
check403(resp: Response): Response {
|
||||||
|
if (resp.status === 403) {
|
||||||
|
// User token is not valid and therefore should be removed
|
||||||
|
// which essentially triggers a logout
|
||||||
|
this.logout()
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp
|
||||||
|
},
|
||||||
|
|
||||||
|
loadVars(): Promise<void | Response> {
|
||||||
|
return fetch('editor/vars.json')
|
||||||
|
.then((resp: Response) => resp.json())
|
||||||
|
.then((data: any) => { this.vars = data })
|
||||||
|
},
|
||||||
|
|
||||||
|
login(token: string, expiresAt: Date, username: string): void {
|
||||||
|
this.token = token
|
||||||
|
this.tokenExpiresAt = expiresAt
|
||||||
|
this.tokenUser = username
|
||||||
|
window.localStorage.setItem('twitch-bot-token', JSON.stringify({ expiresAt, token, username }))
|
||||||
|
// Nuke the Twitch auth-response from the browser history
|
||||||
|
window.history.replaceState(null, '', window.location.href.split('#')[0])
|
||||||
|
|
||||||
|
fetch(`config-editor/user?user=${this.tokenUser}`, this.$root.fetchOpts)
|
||||||
|
.then((resp: Response) => this.$root.check403(resp))
|
||||||
|
.then((resp: Response) => resp.json())
|
||||||
|
.then((data: any) => {
|
||||||
|
this.userInfo = data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
logout(): void {
|
||||||
|
window.localStorage.removeItem('twitch-bot-token')
|
||||||
|
this.token = ''
|
||||||
|
this.tokenExpiresAt = null
|
||||||
|
this.tokenUser = ''
|
||||||
|
},
|
||||||
|
|
||||||
|
registerTicker(id: string, func: TimerHandler, intervalMs: number): void {
|
||||||
|
this.unregisterTicker(id)
|
||||||
|
this.tickers[id] = window.setInterval(func, intervalMs)
|
||||||
|
},
|
||||||
|
|
||||||
|
renewToken(): void {
|
||||||
|
if (!this.tokenRenewAt || this.tokenRenewAt.getTime() > this.now.getTime()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('config-editor/refreshToken', this.$root.fetchOpts)
|
||||||
|
.then((resp: Response) => this.$root.check403(resp))
|
||||||
|
.then((resp: Response) => resp.json())
|
||||||
|
.then((data: any) => this.login(data.token, new Date(data.expiresAt), data.user))
|
||||||
|
},
|
||||||
|
|
||||||
|
unregisterTicker(id: string): void {
|
||||||
|
if (this.tickers[id]) {
|
||||||
|
window.clearInterval(this.tickers[id])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted(): void {
|
||||||
|
this.bus.on('logout', this.logout)
|
||||||
|
|
||||||
|
this.$root.registerTicker('updateRootNow', () => { this.now = new Date() }, 30000)
|
||||||
|
this.$root.registerTicker('renewToken', () => this.renewToken(), 60000)
|
||||||
|
|
||||||
|
// Start background-listen for config updates
|
||||||
|
new ConfigNotifyListener((msgType: string) => { this.$root.bus.emit(msgType) })
|
||||||
|
|
||||||
|
this.loadVars()
|
||||||
|
|
||||||
|
const params = new URLSearchParams(window.location.hash.replace(/^[#/]+/, ''))
|
||||||
|
const authToken = params.get('access_token')
|
||||||
|
if (authToken) {
|
||||||
|
this.$root.bus.emit('login-processing', true)
|
||||||
|
fetch('config-editor/login', {
|
||||||
|
body: JSON.stringify({ token: authToken }),
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
.then((resp: Response): any => {
|
||||||
|
if (resp.status !== 200) {
|
||||||
|
throw new Error(`login failed, status=${resp.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.json()
|
||||||
|
})
|
||||||
|
.then((data: any) => this.login(data.token, new Date(data.expiresAt), data.user))
|
||||||
|
} else {
|
||||||
|
const tokenData = window.localStorage.getItem('twitch-bot-token')
|
||||||
|
if (tokenData !== null) {
|
||||||
|
const data = JSON.parse(tokenData)
|
||||||
|
this.login(data.token, new Date(data.expiresAt), data.username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
name: 'TwitchBotEditor',
|
name: 'TwitchBotEditor',
|
||||||
render() {
|
render() {
|
||||||
|
if (this.token) {
|
||||||
return h(App)
|
return h(App)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h(Login)
|
||||||
},
|
},
|
||||||
|
|
||||||
router,
|
router,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.config.globalProperties.bus = mitt()
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { createRouter, createMemoryHistory } from 'vue-router'
|
||||||
//import AuthView from './components/auth.vue'
|
//import AuthView from './components/auth.vue'
|
||||||
//import ChatView from './components/chatview.vue'
|
//import ChatView from './components/chatview.vue'
|
||||||
|
|
||||||
|
const Root = {}
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
// {
|
// {
|
||||||
// component: AuthView,
|
// component: AuthView,
|
||||||
|
@ -12,6 +14,11 @@ const routes = [
|
||||||
// component: ChatView,
|
// component: ChatView,
|
||||||
// path: '/chat',
|
// path: '/chat',
|
||||||
// },
|
// },
|
||||||
|
{
|
||||||
|
component: Root,
|
||||||
|
name: 'root',
|
||||||
|
path: '/',
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
|
Loading…
Reference in a new issue