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() { created() {
// The basic app is created... Currently showing a loading screen (as soon as mounted) // 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'); this.$store.dispatch('setAppReady');
});
}
} }
} }
</script> </script>

View File

@ -35,6 +35,10 @@ export default new class {
return !!localStorage.getItem(LOCAL_STORAGE_KEY) return !!localStorage.getItem(LOCAL_STORAGE_KEY)
} }
clearTokenFromLocalStorage() {
localStorage.removeItem(LOCAL_STORAGE_KEY);
}
_performApiCall(method, url, data, precheckToken, expectedStatus, errorTexts = COMMON_ERROR_CODES) { _performApiCall(method, url, data, precheckToken, expectedStatus, errorTexts = COMMON_ERROR_CODES) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -86,7 +90,7 @@ export default new class {
return new Promise((resolve, reject) => { 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", 401: "Invalid credentials",
...COMMON_ERROR_CODES ...COMMON_ERROR_CODES
}).then((data) => { }).then((data) => {

View File

@ -16,20 +16,55 @@
<b-nav-item to="/about">About</b-nav-item> <b-nav-item to="/about">About</b-nav-item>
</b-navbar-nav> </b-navbar-nav>
<b-navbar-nav class="ml-auto" v-if="$store.getters.isLoggedIn"> <b-navbar-nav class="ml-auto" v-if="$store.getters.isLoggedIn">
<b-nav-item-dropdown :text="$store.state.userdata.name" right> <b-nav-item-dropdown right no-caret>
<b-dropdown-text>Lorem Ipsum dolor sit amaet</b-dropdown-text> <template #button-content>
<b-dropdown-item href="#">Logout</b-dropdown-item> <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-nav-item-dropdown>
</b-navbar-nav> </b-navbar-nav>
</b-collapse> </b-collapse>
</b-navbar> </b-navbar>
</template> </template>
<script> <script>
export default { 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> </script>

View File

@ -15,6 +15,41 @@ Vue.prototype.$api = api
Vue.use(BootstrapVue) Vue.use(BootstrapVue)
Vue.use(IconsPlugin) 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({ new Vue({
router, router,
store, store,

View File

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