Implement token autentication, Implement account views

This commit is contained in:
Michael Götz 2018-04-01 17:37:52 +02:00
parent 669ca7c52b
commit 997c2e9b12
11 changed files with 949 additions and 0 deletions

View File

@ -0,0 +1,56 @@
import router from '@/router'
import {API_ROOT} from '@/config.js'
export default {
user: {
authenticated: false
},
// authentication status
authenticated() {
return (localStorage.getItem('user') !== null && JSON.parse(localStorage.getItem('user')).data.token !== '')
},
// Send a request to the login URL and save the returned JWT
login(creds) {
console.log('LOGIN');
window.axios.post(API_ROOT.concat('/token-auth/token/create/'), creds)
.then((response) => {
this.user.authenticated = true;
console.log(response);
localStorage.setItem('user', JSON.stringify(response));
// Redirect to a specified route
window.axios.defaults.headers.common = {
'Authorization': ('Token ' + JSON.parse(localStorage.getItem('user')).data.auth_token)
};
location.reload();
})
.catch(function (error) {
console.log(error);
alert(error.response.data.non_field_errors[0]);
})
},
// To log out
logout: function (redirect) {
window.axios.post(API_ROOT.concat('/token-auth/token/destroy/'),)
.then((response) => {
this.user.authenticated = false;
localStorage.removeItem('user');
console.log(response);
// Redirect to a specified route
window.axios.defaults.headers.common = {
'Authorization': '',
};
location.reload();
if (redirect) {
router.push({name: 'home'});
// router.go(redirect)
}
})
.catch(function (error) {
console.log(error);
alert(error.response.data);
});
}
}

View File

@ -0,0 +1,41 @@
<template>
<div>
<b-tabs>
<b-tab title="Account Details" active>
<account-details :user="user"></account-details>
</b-tab>
<b-tab title="Food App">
<food></food>
</b-tab>
</b-tabs>
</div>
</template>
<script>
import * as CONFIG from '../../config.js';
import AccountDetails from '@/components/account/utils/AccountDetails';
import Food from '@/components/account/utils/Food';
export default {
name: "AccountOverview",
components: {AccountDetails, Food,},
data() {
return {
user: {},
}
},
created() {
window.axios
.get(CONFIG.API_ROOT_ACCOUNT.concat('/'))
.then((response) => {
console.log(response.data);
this.user = response.data[0];
})
.catch(e => console.error(e));
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,51 @@
<template>
<b-row>
<b-col class="justify-content-md-center text-center">
<div v-if="activated">
<p>Dein Account ist nun aktiviert.</p>
<router-link :to="{name: 'home'}">Home</router-link>
</div>
<div v-else>
<p v-if="!error">Account wird aktiviert.</p>
<p v-if="error">Leider konnte der Account nicht aktiviert werden.</p>
<router-link :to="{name: 'home'}">Home</router-link>
</div>
</b-col>
</b-row>
</template>
<script>
import * as CONFIG from '../../config'
export default {
name: "Activation",
data() {
return {
activated: false,
error: false,
}
},
created() {
let url = CONFIG.API_ROOT.concat('/token-auth/users/activate/');
let post_data = {
"uid": this.$route.params.uuid,
"token": this.$route.params.token,
};
window.axios
.post(url, post_data)
.then(response => {
this.activated = true;
}
)
.catch(
e => {
this.error = true;
}
);
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,84 @@
<template>
<div v-if="!isAuthenticated()">
<b-row class="justify-content-center text-center ml-md-auto mt-2">
<b-col cols="11" sm="10" md="7" lg="5" xl="4" class="border pb-2 pt-2">
<h2>Login</h2>
<b-form @submit="onSubmit" v-if="show">
<b-form-group id="username"
label="User Name:"
label-for="username">
<b-form-input id="username"
type="text"
v-model="form.username"
required
placeholder="Enter username">
</b-form-input>
</b-form-group>
<b-form-group id="password"
label="Password:"
label-for="password">
<b-form-input id="password"
type="password"
v-model="form.password"
required
placeholder="Enter password">
</b-form-input>
</b-form-group>
<b-button type="submit" variant="primary">Submit</b-button>
</b-form>
</b-col>
<b-col cols="12" class="">
<br/>
<p>
<router-link :to="{name: 'password-reset-mail'}">Passwort vergessen?</router-link>
</p>
<p>
<router-link :to="{name: 'registration'}">Noch keinen Account?</router-link>
</p>
</b-col>
</b-row>
</div>
<div v-else>
<b-row class="justify-content-center text-center ml-md-auto mt-2">
<b-col cols="11" sm="10" md="7" lg="5" xl="4" class="border pb-2 pt-2">
Du wurdest erfolgreich eingeloggt.
</b-col>
</b-row>
</div>
</template>
<script>
import authentication from "../../authentication";
import router from '../../router'
export default {
name: "Login",
data() {
return {
form: {
username: '',
password: '',
},
show: true
}
},
methods: {
onSubmit(evt) {
evt.preventDefault();
authentication.login({"username": this.form.username, "password": this.form.password}, -1);
},
isAuthenticated: function () {
if (authentication.authenticated()) {
router.go(-1);
return true;
} else {
return false;
}
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,28 @@
<template>
<b-row class="justify-content-center text-center ml-md-auto mt-2">
<b-col cols="11" sm="10" md="7" lg="5" xl="4" class="border pb-2 pt-2">
Du wurdest erfolgreich ausgeloggt.
</b-col>
</b-row>
</template>
<script>
import Auth from '../../authentication'
import router from '../../router'
export default {
name: "Logout",
created() {
if (Auth.authenticated()) {
Auth.logout();
}
setTimeout(function () {
router.go(-1);
}, 3000);
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,101 @@
<template>
<b-row class="justify-content-center text-center ml-md-auto mt-2">
<b-col cols="11" sm="10" md="7" lg="5" xl="4" class="border pb-2 pt-2">
<h2>Passwort Reset</h2>
<p v-if="error">Das Passwort konnte nicht zurück gesetzt werden.</p>
<b-form @submit="onSubmit" v-if="show">
<b-form-group id="password"
label="Password:"
label-for="password">
<b-form-input id="password"
type="password"
v-model="form.password"
required
placeholder="Enter password">
</b-form-input>
</b-form-group>
<b-form-group id="passwordSafety"
label-for="passwordSafety">
<b-form-input id="passwordSafety"
type="password"
v-model="form.passwordSafety"
required
placeholder="Enter password again">
</b-form-input>
</b-form-group>
<div v-if="error" class="error">
<p v-for="error in errorMsgs">{{error}}</p>
</div>
<b-button type="submit" variant="primary">Submit</b-button>
</b-form>
<div v-if="!show">
<p>Passwort wurde erfolgreich zurückgesetzt.</p>
<p>Zurück zum
<router-link :to="{name: 'login'}">Login</router-link>
.
</p>
</div>
</b-col>
</b-row>
</template>
<script>
import * as CONFIG from "../../config";
export default {
name: "PasswordResetMail",
data() {
return {
form: {
password: '',
passwordSafety: ''
},
show: true,
error: false,
errorMsgs: []
}
},
methods: {
onSubmit(evt) {
evt.preventDefault();
this.errorMsgs = [];
let url = CONFIG.API_ROOT.concat('/token-auth/password/reset/confirm/');
let post_data = {
"new_password": this.form.password,
"re_new_password": this.form.passwordSafety,
"uid": this.$route.params.uuid,
"token": this.$route.params.token,
};
window.axios
.post(url, post_data)
.then(response => {
this.show = false;
this.error = false;
}
)
.catch(
e => {
this.error = true;
console.log(e);
if (e.response.data.new_password) {
for (const error of e.response.data.new_password) {
this.errorMsgs.push(error);
}
}
if (e.response.data.non_field_errors) {
this.errorMsgs.push(e.response.data.non_field_errors[0]);
}
console.log(this.errorMsgs)
}
);
},
},
}
</script>
<style scoped>
.error {
font-size: 13px;
color: #ff253a;
}
</style>

View File

@ -0,0 +1,77 @@
<template>
<b-row class="justify-content-center text-center ml-md-auto mt-2">
<b-col cols="11" sm="10" md="7" lg="5" xl="4" class="border pb-2 pt-2">
<h2>Passwort Reset</h2>
<b-form @submit="onSubmit" v-if="show">
<b-form-group id="email"
label="Email-Adresse"
label-for="email"
>
<b-form-input id="email"
type="email"
v-model="form.email"
:state="!form.emailErrorVal"
required
placeholder="Enter email">
</b-form-input>
<b-form-invalid-feedback id="input1LiveFeedback">
<p v-for="error in form.emailError">{{error}}</p>
</b-form-invalid-feedback>
</b-form-group>
<b-button type="submit" variant="primary">Submit</b-button>
</b-form>
<div v-if="!show">
Die E-Mail wurde erfolgreich versandt.
<p>Zurück zu
<router-link :to="{name: 'home'}">Home</router-link>
.
</p>
</div>
</b-col>
</b-row>
</template>
<script>
import * as CONFIG from "../../config";
export default {
name: "PasswordResetMail",
data() {
return {
form: {
email: '',
emailError: '',
emailErrorVal: false,
},
show: true,
error: false,
}
},
methods: {
onSubmit(evt) {
evt.preventDefault();
this.form.emailErrorVal = false;
let url = CONFIG.API_ROOT.concat('/token-auth/password/reset/');
let post_data = {
"email": this.form.email,
};
window.axios
.post(url, post_data)
.then(response => {
this.show = false;
}
)
.catch(
e => {
this.form.emailError = e.response.data.email;
this.form.emailErrorVal = true;
}
);
},
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,155 @@
<template>
<b-row class="justify-content-center text-center ml-md-auto mt-2">
<b-col cols="11" sm="10" md="7" lg="5" xl="4" class="border pb-2 pt-2">
<h2>Registrierung</h2>
<p v-if="error">Der Nutzer konnte nicht registriert werden, bitte versuche es erneut.</p>
<ul>
<li v-for="error in form.passwordError">{{error}}</li>
</ul>
<b-form @submit="onSubmit" v-if="show">
<b-form-group id="username"
label="Nutzername"
label-for="username">
<b-form-input id="username"
type="text"
v-model="form.username"
required
placeholder="Enter username">
</b-form-input>
</b-form-group>
<b-form-group id="email"
label="Email Adresse"
label-for="email"
>
<b-form-input id="email"
type="email"
v-model="form.email"
:state="!$v.form.email.$invalid"
required
placeholder="Enter email">
</b-form-input>
<b-form-invalid-feedback id="input1LiveFeedback">
Bitte gebe eine valide E-Mail an.
</b-form-invalid-feedback>
</b-form-group>
<b-form-group id="password"
label="Passwort"
label-for="password">
<b-form-input id="password"
type="password"
:state="!$v.form.password"
v-model="form.password"
required
placeholder="Enter password">
</b-form-input>
<b-form-invalid-feedback id="input1LiveFeedback">
<ul>
<li v-for="error in form.passwordError.$invalid">{{error}}</li>
</ul>
</b-form-invalid-feedback>
</b-form-group>
<b-form-group id="passwordSafety"
label-for="passwordSafety">
<b-form-input id="passwordSafety"
type="password"
v-model="form.passwordSafety"
:state="!$v.form.passwordSafety.$invalid"
required
placeholder="Enter password again">
</b-form-input>
<b-form-invalid-feedback id="input1LiveFeedback">
Die Passwörter müssen identisch sein
</b-form-invalid-feedback>
</b-form-group>
<b-button type="submit" variant="primary" :disabled="$v.form.$invalid">Submit</b-button>
</b-form>
<div v-if="!show && response">
Dein Account wurde erfolgreich angelegt.
Bitte bestätige deinen Account über die Aktivierungsmail.
</div>
<div v-if="!show && !response">
Die Account daten werden übermittelt.
</div>
</b-col>
</b-row>
</template>
<script>
import * as CONFIG from '../../config'
import {validationMixin} from "vuelidate"
import {required, minLength, sameAs, email} from "vuelidate/lib/validators"
export default {
name: "Registration",
data() {
return {
form: {
username: '',
email: '',
password: '',
passwordError: ['Ein Passwort darf nicht zu ähnlich zum Benutzernamen sein.', ' Ein Passwort muss mindestens 8 Zeichen enthalten.'],
passwordSafety: '',
},
response: false,
show: true,
error: false,
}
},
mixins: [validationMixin],
validations: {
form: {
username: {
required,
},
email: {
required,
email,
},
password: {
required,
minLength: minLength(8),
},
passwordSafety: {
required,
sameAsPassword: sameAs('password'),
},
}
},
methods: {
onSubmit(evt) {
evt.preventDefault();
this.show = false;
this.error = false;
let url = CONFIG.API_ROOT.concat('/token-auth/users/create/');
let post_data = {
"username": this.form.username,
"email": this.form.email,
"password": this.form.password,
// "re_new_password": this.form.passwordSafety,
};
window.axios
.post(url, post_data)
.then(response => {
this.error = false;
this.response = true;
}
)
.catch(
e => {
if (e.response.data.password) {
this.form.passwordError = e.response.data.password;
}
this.error = true;
this.show = true;
}
);
},
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,58 @@
<template>
<div>
<div v-if="!showFormular">
<b-row class="p-1">
<b-col cols="12" sm="12" md="12" lg="8" xl="8" class="p-3 bg-light text-dark">
<h3>{{user.username}}</h3>
<p><strong>Vorname:</strong> {{ user.first_name }}</p>
<p><strong>Nachname:</strong> {{ user.last_name }}</p>
<p><strong>E-Mail:</strong> {{ user.email }}</p>
<!--<a href="{{ url('change-account') }}">Bearbeiten</a>-->
<b-button :pressed.sync="showFormular" variant="primary">{{this.formularButtonLabel}}</b-button>
</b-col>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-4 text-center p-3">
<div class="p-3 bg-light text-dark">
<p>Date joined: {{ user.date_joined | formatDate}}</p>
<p>Last Login: {{ user.last_login | formatDate}}</p>
</div>
</div>
</b-row>
</div>
<div v-if="showFormular">
<account-formular :user="user"></account-formular>
<b-button :pressed.sync="showFormular" variant="primary">{{this.formularButtonLabel}}</b-button>
</div>
</div>
</template>
<script>
import * as CONFIG from '../../../config.js';
import AccountFormular from '@/components/account/utils/AccountFormular';
export default {
name: "AccountDetails",
components: {AccountFormular},
props: ['user'],
data() {
return {
account: {},
showFormular: false,
formularButtonLabel: 'Bearbeiten',
}
},
watch: {
showFormular: function (newShowFormular) {
if (newShowFormular) {
this.formularButtonLabel = 'Fertig'
} else {
this.formularButtonLabel = 'Bearbeiten'
}
},
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,159 @@
<template>
<b-row class="justify-content-center text-center ml-md-auto mt-2">
<b-col cols="11" sm="10" md="7" lg="5" xl="4" class="border pb-2 pt-2 text-left">
<b-form @submit="onSubmit">
<b-form-input id="username"
type="text"
v-model="form.username"
required
placeholder="Nutzername">
</b-form-input>
<b-form-input id="firstName"
type="text"
v-model="form.firstName"
placeholder="Vorname">
</b-form-input>
<b-form-input id="lastName"
type="text"
v-model="form.lastName"
placeholder="Nachname">
</b-form-input>
<b-form-input id="email"
type="email"
v-model="form.email"
:state="!$v.form.email.$invalid"
placeholder="Email Adresse">
</b-form-input>
<multiselect
v-model="form.fakultaet"
:options="fakultaetOptions"
:multiple="false"
:custom-label="optionLabel"
track-by="text"
placeholder="Fakultät"
>
</multiselect>
<b-form-checkbox id="fakultaetsNews"
v-model="form.fakultaetsNews"
value=true
unchecked-value=false>
Fakultätsnachrichten
</b-form-checkbox>
<multiselect
v-model="form.allergens"
:options="allergensOptions"
:multiple="true"
:custom-label="optionLabel"
track-by="text"
placeholder="Allergene"
>
</multiselect>
<b-button type="submit" variant="primary" :disabled="$v.form.$invalid">Submit</b-button>
</b-form>
</b-col>
</b-row>
</template>
<script>
import * as CONFIG from '../../../config';
import {validationMixin} from "vuelidate";
import {required, minLength, sameAs, email} from "vuelidate/lib/validators";
import Multiselect from 'vue-multiselect';
export default {
name: "AccountFormular",
props: ['user'],
components: {Multiselect,},
data() {
return {
form: {
username: '',
firstName: '',
lastName: '',
email: '',
fakultaet: '',
fakultaetsNews: false,
allergens: '',
},
fakultaetOptions: [
{value: 1, text: 'SoWi'},
{value: 2, text: 'GuK'},
{value: 3, text: 'HuWi'},
{value: 4, text: 'WIAI'},
],
allergensOptions: [
{value: 1, text: 'Gluten'},
{value: 2, text: 'Schwein'},
{value: 3, text: 'Rind'},
{value: 4, text: 'KA'},
],
response: false,
show: true,
error: false,
}
},
mixins: [validationMixin],
validations: {
form: {
username: {
required,
},
email: {
required,
email,
},
}
},
created() {
this.updateFormular();
},
watch: {
user: function (newUser) {
this.updateFormular();
}
},
methods: {
updateFormular() {
this.form.username = this.user.username;
this.form.firstName = this.user.firstName;
this.form.lastName = this.user.lastName;
this.form.email = this.user.email;
},
optionLabel(option) {
return option.text;
},
onSubmit(evt) {
this.show = false;
this.error = false;
let url = CONFIG.API_ROOT.concat('/token-auth/users/create/');
let post_data = {
"username": this.form.username,
"email": this.form.email,
"password": this.form.password,
// "re_new_password": this.form.passwordSafety,
};
window.axios
.post(url, post_data)
.then(response => {
this.error = false;
this.response = true;
}
)
.catch(
e => {
if (e.response.data.password) {
this.form.passwordError = e.response.data.password;
}
this.error = true;
this.show = true;
}
);
},
},
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
<style scoped>
</style>

View File

@ -0,0 +1,139 @@
<template>
<div>
<h4>Essen</h4>
<div class="row p-1">
<div v-for="food in foods" class="col col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 p-2">
<b-row class="food border p-2">
<b-col cols="4" class="image">
<a v-if="food.image" :href="food.image.thumb">
<img :src="food.image.thumb" class="media-object" alt="Bild" width="100%">
</a>
<a v-else :href="defaultImageUrl">
<img :src="defaultImageUrl" class="media-object"
alt="Bild" width="100%">
</a>
</b-col>
<b-col cols="4" class="title-rating">
<b-row>
<b-col cols="12">
<h5>{{food.name}}</h5>
</b-col>
</b-row>
<b-row>
<b-col cols="12">
<div v-if="food.rating">
Rating: {{food.rating}}
</div>
</b-col>
</b-row>
</b-col>
<b-col cols="4" class="comment">
<div v-if="food.comment">
<h5>{{food.comment.title}}</h5>
<div class="">
<p>{{food.comment.description}}</p>
</div>
</div>
</b-col>
</b-row>
</div>
</div>
</div>
</template>
<script>
import * as CONFIG from '../../../config.js'
export default {
name: "Food",
data() {
return {
images: [],
ratings: [],
comments: [],
foods: [],
defaultImageUrl: '',
}
},
created() {
window.axios
.get(CONFIG.API_ROOT_ACCOUNT.concat('/food/ratings/'))
.then((response) => {
this.ratings = response.data;
this.mergeArrays();
})
.catch(e => console.error(e));
window.axios
.get(CONFIG.API_ROOT_ACCOUNT.concat('/food/images/'))
.then((response) => {
this.images = response.data;
this.mergeArrays();
})
.catch(e => console.error(e));
window.axios
.get(CONFIG.API_ROOT_ACCOUNT.concat('/food/comments/'))
.then((response) => {
this.comments = response.data;
this.mergeArrays();
})
.catch(e => console.error(e));
axios.get(CONFIG.API_ROOT_FOOD.concat('/meals/images/default'))
.then(response => {
this.defaultImageUrl = response.data.image;
})
.catch(e => {
console.error(e)
});
},
methods: {
mergeArrays: function () {
this.foods = [];
let food_ids = [];
let foods = {};
if (this.images.length > 0) {
for (const image of this.images) {
food_ids.push(image.food.id);
foods[image.food.id] = {};
foods[image.food.id]['name'] = image.food.name;
foods[image.food.id]['image'] = image.image;
}
}
if (this.ratings.length > 0) {
for (const rating of this.ratings) {
food_ids.push(rating.food.id);
if (!foods[rating.food.id]) {
foods[rating.food.id] = {};
}
foods[rating.food.id]['name'] = rating.food.name;
foods[rating.food.id]['rating'] = rating.rating;
}
}
if (this.comments.length > 0) {
for (const comment of this.comments) {
food_ids.push(comment.food.id);
if (!foods[comment.food.id]) {
foods[comment.food.id] = {};
}
foods[comment.food.id]['name'] = comment.food.name;
foods[comment.food.id]['comment'] = {};
foods[comment.food.id]['comment']['title'] = comment.title;
foods[comment.food.id]['comment']['description'] = comment.description;
}
}
for (const key in foods) {
this.foods.push(foods[key]);
}
},
}
}
</script>
<style scoped>
.food {
height: 100%;
width: 100%;
}
</style>