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'); 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> </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,13 +2,18 @@
<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-overlay :show="processing" spinner-type="grow" spinner-variant="success" rounded="sm">
<b-card class="mt-5"> <b-card class="mt-5">
<div class="my-4 text-center"> <div class="my-4 text-center">
<b-img src="@/assets/musicbrainz.svg" id="provider-banner"/> <b-img src="@/assets/musicbrainz.svg" id="provider-banner"/>
</div> </div>
<div class="my-2 text-center text-danger" v-if="authFailed">
Username or password invalid!
</div>
<div class="mt-4"> <div class="mt-4">
<b-form @submit.prevent="performLogin" v-if="true"> <b-form @submit.prevent="performLogin" @input="formChanged" v-if="true">
<b-form-group <b-form-group
id="input-group-1" id="input-group-1"
label="Username:" label="Username:"
@ -20,6 +25,7 @@
required required
placeholder="" placeholder=""
autocomplete="off" autocomplete="off"
:state="formState"
></b-form-input> ></b-form-input>
</b-form-group> </b-form-group>
@ -33,6 +39,7 @@
placeholder="" placeholder=""
autocomplete="off" autocomplete="off"
type="password" type="password"
:state="formState"
></b-form-input> ></b-form-input>
</b-form-group> </b-form-group>
@ -41,9 +48,9 @@
</div> </div>
</b-form> </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;
} }
} }
} }