Implemented login/logout

This commit is contained in:
Pünkösd Marcell 2020-11-26 03:18:28 +01:00
parent 53e8aa9a9c
commit f85f16e2a5
6 changed files with 183 additions and 46 deletions

View File

@ -1 +1 @@
VUE_APP_API_LOCATION=http://localhost:5000/
VUE_APP_API_LOCATION=http://localhost:5000/api/

View File

@ -31,9 +31,34 @@ export default {
},
created() {
// The basic app is created... Currently showing a loading screen (as soon as mounted)
this.$store.dispatch('storeUserData','testuser').then(() => {
if (this.$api.haveToken) {
this.$api.getMyInfo().then(({name}) => {
this.$store.dispatch('storeUserData', name).then(() => {
this.$router.push('/').catch(() => {});
this.$store.dispatch('setAppReady');
});
}).catch(({status, text}) => {
if (status === 401) {
this.$api.clearTokenFromLocalStorage();
this.$router.push('/login').catch(() => {});
this.$store.dispatch('setAppReady');
} else {
this.$showToast(text);
}
});
} else {
this.$router.push('/login').catch(() => {});
this.$store.dispatch('setAppReady');
});
}
}
}
</script>

View File

@ -35,6 +35,10 @@ export default new class {
return !!localStorage.getItem(LOCAL_STORAGE_KEY)
}
clearTokenFromLocalStorage() {
localStorage.removeItem(LOCAL_STORAGE_KEY);
}
_performApiCall(method, url, data, precheckToken, expectedStatus, errorTexts = COMMON_ERROR_CODES) {
return new Promise((resolve, reject) => {
@ -86,7 +90,7 @@ export default new class {
return new Promise((resolve, reject) => {
this._performApiCall('post', '/auth/login', {name, password}, false, 201, {
this._performApiCall('post', '/auth/login', {name, password}, false, 200, {
401: "Invalid credentials",
...COMMON_ERROR_CODES
}).then((data) => {

View File

@ -16,20 +16,55 @@
<b-nav-item to="/about">About</b-nav-item>
</b-navbar-nav>
<b-navbar-nav class="ml-auto" v-if="$store.getters.isLoggedIn">
<b-nav-item-dropdown :text="$store.state.userdata.name" right>
<b-dropdown-text>Lorem Ipsum dolor sit amaet</b-dropdown-text>
<b-dropdown-item href="#">Logout</b-dropdown-item>
<b-nav-item-dropdown right no-caret>
<template #button-content>
<b-icon icon="person-fill"/>
{{ $store.state.userdata.name }}
</template>
<b-overlay :show="logoutProcessing">
<b-dropdown-text>Logged in to...</b-dropdown-text>
<b-dropdown-text>
<b-img src="@/assets/musicbrainz.svg" width="200px"/>
</b-dropdown-text>
<b-dropdown-divider></b-dropdown-divider>
<b-dropdown-item @click="performLogout">Logout</b-dropdown-item>
</b-overlay>
</b-nav-item-dropdown>
</b-navbar-nav>
</b-collapse>
</b-navbar>
</template>
<script>
export default {
name: "Navbar"
name: "Navbar",
data() {
return {
logoutProcessing: false
}
},
methods: {
performLogout() {
this.logoutProcessing = true;
this.$api.performLogout().then(() => {
this.$store.dispatch('storeUserData', null).then(() => {
this.$router.push('/login').catch(() => {
});
this.logoutProcessing = false;
})
}).catch(({text}) => {
this.$showToast(text);
this.logoutProcessing = false;
});
}
}
}
</script>

View File

@ -15,6 +15,41 @@ Vue.prototype.$api = api
Vue.use(BootstrapVue)
Vue.use(IconsPlugin)
Vue.prototype.$showToast = function (text, type = 'error', local=true) {
let options = {}
switch (type) {
case "error":
options = {
title: "Error!",
variant: "danger"
}
break;
case "user_error":
options = {
title: "Warning!",
variant: "warning"
}
break;
case "success":
options = {
title: "Success!",
variant: "success"
}
break;
}
const bvToast = local ? this.$bvToast : this.$root.$bvToast
bvToast.toast(text, {
...options,
toaster: 'b-toaster-top-center',
solid: true,
appendToast: false
})
}
new Vue({
router,
store,

View File

@ -2,48 +2,55 @@
<b-container>
<b-row>
<b-col class="mx-auto" cols="12" lg="4" sm="8">
<b-card class="mt-5">
<div class="my-4 text-center">
<b-img src="@/assets/musicbrainz.svg" id="provider-banner"/>
</div>
<div class="mt-4">
<b-overlay :show="processing" spinner-type="grow" spinner-variant="success" rounded="sm">
<b-card class="mt-5">
<div class="my-4 text-center">
<b-img src="@/assets/musicbrainz.svg" id="provider-banner"/>
</div>
<b-form @submit.prevent="performLogin" v-if="true">
<b-form-group
id="input-group-1"
label="Username:"
label-for="input-1"
>
<b-form-input
id="input-1"
v-model="form.username"
required
placeholder=""
autocomplete="off"
></b-form-input>
</b-form-group>
<div class="my-2 text-center text-danger" v-if="authFailed">
Username or password invalid!
</div>
<div class="mt-4">
<b-form-group id="input-group-2" label="Password:" label-for="input-2"
description="Yes, the same one you use on MusicBrainz"
>
<b-form-input
id="input-2"
v-model="form.password"
required
placeholder=""
autocomplete="off"
type="password"
></b-form-input>
</b-form-group>
<b-form @submit.prevent="performLogin" @input="formChanged" v-if="true">
<b-form-group
id="input-group-1"
label="Username:"
label-for="input-1"
>
<b-form-input
id="input-1"
v-model="form.username"
required
placeholder=""
autocomplete="off"
:state="formState"
></b-form-input>
</b-form-group>
<div class="text-right">
<b-button type="submit" variant="primary">Login</b-button>
</div>
</b-form>
<b-form-group id="input-group-2" label="Password:" label-for="input-2"
description="Yes, the same one you use on MusicBrainz"
>
<b-form-input
id="input-2"
v-model="form.password"
required
placeholder=""
autocomplete="off"
type="password"
:state="formState"
></b-form-input>
</b-form-group>
<div class="text-right">
<b-button type="submit" variant="primary">Login</b-button>
</div>
</b-form>
</div>
</b-card>
</div>
</b-card>
</b-overlay>
</b-col>
</b-row>
</b-container>
@ -57,12 +64,43 @@ export default {
form: {
username: "",
password: ""
}
},
processing: false,
authFailed: false
}
},
methods: {
performLogin() {
this.processing = true;
this.$api.performLogin(this.form.username, this.form.password).then(({name}) => {
this.$store.dispatch('storeUserData', name).then(() => {
this.$router.push('/').catch(() => {
});
this.processing = false;
});
}).catch(({status, text}) => {
if (status === 401) {
this.authFailed = true;
} else {
this.$showToast(text);
}
this.processing = false;
});
},
formChanged() {
this.authFailed = false;
}
},
computed: {
formState() {
return this.authFailed ? false : null;
}
}
}