Merge branch 'develop/vue/store' into dev

This commit is contained in:
Alexis
2020-12-23 20:46:48 +01:00
17 changed files with 786 additions and 487 deletions

View File

@@ -31,7 +31,7 @@ class UserRepository {
}) })
.catch(() => { .catch(() => {
reject({ reject({
"message": "Database error, couldn't get all users.", "message": "Erreur de base de données, les utilisateurs n'ont pas pu être récupérés.",
"code": 500 "code": 500
}) })
}) })
@@ -48,7 +48,7 @@ class UserRepository {
}) })
.catch(err => { .catch(err => {
reject({ reject({
"message": "User with this UUID was not found.", "message": "L'utilisateur avec cet UUID n'a pas été trouvé.",
"code": 404 "code": 404
}) })
}) })
@@ -65,7 +65,7 @@ class UserRepository {
}) })
.catch(() => { .catch(() => {
reject({ reject({
"message": "User with this email was not found.", "message": "L'utilisateur avec cet email n'a pas été trouvé.",
"code": 404 "code": 404
}) })
}) })
@@ -77,17 +77,17 @@ class UserRepository {
// Checks if body exists and if the model fits, and throws errors if it doesn't // Checks if body exists and if the model fits, and throws errors if it doesn't
if (isEmptyObject(u)) { if (isEmptyObject(u)) {
reject({ reject({
"message": "Request body cannot be empty.", "message": "Le corps de requête ne peut être vide.",
"code": 403 "code": 403
}) })
} else if (!v.validate(u, UserValidation).valid) { } else if (!v.validate(u, UserValidation).valid) {
reject({ reject({
"message": "Schema is not valid - " + v.validate(u, UserValidation).errors, "message": "Structure de la requête invalide - " + v.validate(u, UserValidation).errors,
"code": 403 "code": 403
}) })
} else if (isXSSAttempt(u.name) || isXSSAttempt(u.password) || isXSSAttempt(u.mail)) { } else if (isXSSAttempt(u.name) || isXSSAttempt(u.password) || isXSSAttempt(u.mail)) {
reject({ reject({
"message": "Injection attempt detected, aborting the request.", "message": "Essai d'injection détecté, avortement de la requête.",
"code": 403 "code": 403
}) })
} else { } else {
@@ -115,13 +115,14 @@ class UserRepository {
}) })
.then(newUser => { .then(newUser => {
resolve({ resolve({
"message": "Account successfully created !", "message": `Compte utilisateur #${newUser.id} créé avec succès.`,
"code": 201,
"user": newUser, "user": newUser,
}) })
}) })
.catch(err => { .catch(err => {
resolve({ resolve({
"message": "An error has occured while creating your account.", "message": "Une erreur s'est produite en créant votre compte. Veuillez réessayer ultérieurement ou contactez l'administrateur.",
"code": 500, "code": 500,
}) })
}) })
@@ -145,12 +146,14 @@ class UserRepository {
if (match) { if (match) {
resolve({ resolve({
"message": "User successfully logged in !", "message": `L'utilisateur #${fetchedUser.id} s'est connecté.`,
"code": 200,
"user": fetchedUser, "user": fetchedUser,
}) })
} else { } else {
reject({ reject({
"message": "Les informations de connexion sont erronées.", "message": "Les informations de connexions sont erronées.",
"code": 400,
}) })
} }
}) })
@@ -163,18 +166,34 @@ class UserRepository {
// Check if one user already has that email // Check if one user already has that email
checkIfEmailAvailable(mail) { checkIfEmailAvailable(mail) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!this.validateEmail(mail)) {
reject({
"message": "La requête n'est pas un email valide.",
"code": 400,
})
}
this.getOneByEmail(mail, false) this.getOneByEmail(mail, false)
.then(() => { .then(() => {
reject({ reject({
"message": "L'email est déjà utilisé par un autre utilisateur.", "message": "Cet email est déjà lié à un compte.",
"code": 403 "code": 409
}) })
}) })
.catch(() => { .catch(() => {
resolve(true) resolve({
"message": "Cet email est disponible.",
"code": 200
})
}) })
}) })
} }
validateEmail(email) {
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
}
} }
module.exports = UserRepository module.exports = UserRepository

View File

@@ -17,7 +17,6 @@ const Users = new UserRepository();
const getUsers = () => { const getUsers = () => {
return Users.getAll() return Users.getAll()
.catch(err => { .catch(err => {
console.log(err)
throw err throw err
}) })
} }
@@ -38,11 +37,10 @@ router.get('/', async (req, res) => {
}) })
// GET ONE FORM UUID ------------------ // GET ONE FROM UUID ------------------
const getUserByUUID = (uuid) => { const getUserByUUID = (uuid) => {
return Users.getOneByUUID(uuid) return Users.getOneByUUID(uuid)
.catch(err => { .catch(err => {
console.log(err)
throw err throw err
}) })
} }
@@ -63,11 +61,34 @@ router.get('/:uuid/', async (req, res) => {
}) })
// CHECK IF MAIL IS AVAILABLE ------------------
const checkIfEmailAvailable = (mail) => {
return Users.checkIfEmailAvailable(mail)
.catch(err => {
throw err
})
}
router.get('/available/:mail/', async (req, res) => {
checkIfEmailAvailable(req.params.mail)
.then(v => {
res.setHeader('Content-Type', 'application/json;charset=utf-8')
res.end(JSON.stringify(v))
})
.catch(err => {
res.status(err.code).send(JSON.stringify(
{
"error": err.message,
"code": err.code
})
)
})
})
// LOG A USER ------------------ // LOG A USER ------------------
const logUser = (mail, password) => { const logUser = (mail, password) => {
return Users.logUser(mail, password) return Users.logUser(mail, password)
.catch(err => { .catch(err => {
console.log(err)
throw err throw err
}) })
} }

View File

@@ -3,10 +3,16 @@ import api from './api'
const resource = "/users" const resource = "/users"
export default { export default {
login(data) { getOneFromUUID(uuid) {
return api.post(`${resource}/login`, data) return api.get(`${resource}/${uuid}`,)
}, },
register(data) { checkEmailAvailable(mail) {
return api.post(`${resource}`, data) return api.get(`${resource}/available/${mail}`)
}, },
login(data) {
return api.post(`${resource}/login`, data)
},
register(data) {
return api.post(`${resource}`, data)
},
} }

View File

@@ -1,60 +1,61 @@
<template> <template>
<nav class="navbar navbar-expand-sm fixed-top navbar-dark bg-dark"> <nav class="navbar navbar-expand-sm fixed-top navbar-dark bg-dark">
<router-link :to="'/'" class="navbar-brand font-display font-weight-700">Auracle</router-link> <router-link :to="'/'" class="navbar-brand font-display font-weight-700">Auracle</router-link>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbar"> <div class="collapse navbar-collapse" id="navbar">
<ul class="navbar-nav mr-auto" v-if="links.length != 0"> <ul class="navbar-nav mr-auto" v-if="links.length != 0">
<li class="nav-item" v-for="(link, index) in links" :key="index"> <li class="nav-item" v-for="(link, index) in links" :key="index">
<router-link :to="link.url" class="nav-link">{{ link.text }}</router-link> <router-link :to="link.url" class="nav-link">{{ link.text }}</router-link>
</li> </li>
</ul> </ul>
<div v-if="user" class="navbar-nav"> <div v-if="user" class="navbar-nav">
<router-link :to="'/profil'" class="nav-link">{{ user.name }}</router-link> <router-link :to="'/profil'" class="nav-link">{{ user.name }}</router-link>
<div @click="logoutUser()" class="nav-link">Deconnexion</div> <div @click="logoutUser()" class="nav-link">Deconnexion</div>
</div> </div>
<div v-else class="navbar-nav"> <div v-else class="navbar-nav">
<router-link :to="'/connexion'" class="nav-link">Connexion</router-link> <router-link :to="'/connexion'" class="nav-link">Connexion</router-link>
</div> </div>
</div> </div>
</nav> </nav>
</template> </template>
<script> <script>
export default {
name: 'navbar', export default {
data() { name: 'navbar',
return { data() {
links: [ return {
{ links: [
text: 'Sortilèges', {
url: '/sorts', text: 'Sortilèges',
}, url: '/sorts',
{
text: 'Écoles',
url: '/ecoles',
},
{
text: 'Chronologie',
url: '/ages',
}
]
}
}, },
computed: { {
user() { text: 'Écoles',
return this.$store.state.user url: '/ecoles',
}
}, },
methods: { {
logoutUser() { text: 'Chronologie',
this.$cookies.remove('U_') url: '/ages',
this.$store.commit('logout')
this.$router.push('/')
}
} }
]
} }
},
computed: {
user() {
return this.$store.getters.getUserProfile
}
},
methods: {
logoutUser() {
this.$store.dispatch('user_logout');
this.$router.push('/');
}
}
}
</script> </script>
<style lang="scss"></style> <style lang="scss"></style>

View File

@@ -61,6 +61,7 @@
<template v-slot:modal-footer="{ close }"> <template v-slot:modal-footer="{ close }">
<button type="button" class="btn btn-danger" data-dismiss="modal" @click="close()">Fermer</button> <button type="button" class="btn btn-danger" data-dismiss="modal" @click="close()">Fermer</button>
<!-- <input type="button" class="btn btn-success" value="Enregistrer comme nouveau" @click="cloneSpell()"> -->
<input type="submit" class="btn btn-primary" value="Enregistrer" form="update-spell"> <input type="submit" class="btn btn-primary" value="Enregistrer" form="update-spell">
</template> </template>
</b-modal> </b-modal>

View File

@@ -76,75 +76,77 @@ import { RepositoryFactory } from "~/api/repositories"
const Spells = RepositoryFactory.get('spells') const Spells = RepositoryFactory.get('spells')
export default { export default {
name: 'spell-card', name: 'spell-card',
props: { props: {
spell: Object, spell: Object,
}, },
data() { data() {
return { return {
main_school: this.spell.schools[0].name, main_school: this.spell.schools[0].name,
} }
}, },
created() { created() {
this.main_school = this.main_school.toLowerCase() this.main_school = this.main_school.toLowerCase()
}, },
computed: { computed: {
user() { user() {
return this.$store.state.user return this.$store.getters.getUserProfile
} }
}, },
methods: { methods: {
editSpell(spell) { editSpell(spell) {
this.$emit('editSpell', spell) this.$emit('editSpell', spell)
},
deleteSpell(spell) {
Spells.deleteOne(this.spell.id)
.then(() => {
this.$emit('deleteSpell', spell)
})
}
}, },
deleteSpell(spell) {
Spells.deleteOne(this.spell.id)
.then(() => {
this.$emit('deleteSpell', spell)
})
}
},
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.spell-card {
footer { .spell-card {
a { footer {
cursor: pointer; a {
} cursor: pointer;
} }
@mixin colorschool($sname,$scolor) {
&.#{$sname} {
.spellcard { border-left-color: $scolor; }
.h3>a { color: $scolor; }
}
}
@include colorschool(lumomancie,#babaa4);
@include colorschool(vitamancie,#57ab6e);
@include colorschool(obstrumancie,#bd4a66);
@include colorschool(tenebromancie,#404842);
@include colorschool(necromancie,#5d4777);
@include colorschool(morbomancie,#d8733d);
@include colorschool(pyromancie,#b6362a);
@include colorschool(hydromancie,#3f68c7);
@include colorschool(electromancie,#cd9731);
@include colorschool(terramancie,#7e5540);
@include colorschool(sidéromancie,#58697a);
@include colorschool(caelomancie,#a8a8a8);
@include colorschool(légimancie,#5dbabd);
@include colorschool(illusiomancie,#9f63a1);
@include colorschool(cruciomancie,#252451);
@include colorschool(chronomancie,#79896a);
@include colorschool(spatiomancie,#2d4776);
@include colorschool(kénomancie,#101010);
@include colorschool(lutomancie,#4e2827);
@include colorschool(échomancie,#6d9fd1);
@include colorschool(protomancie,#4f5751);
@include colorschool(rebumancie,#8e7245);
@include colorschool(vocamancie,#247864);
@include colorschool(somamancie,#976c67);
@include colorschool(antimancie,#ad95c1);
} }
@mixin colorschool($sname,$scolor) {
&.#{$sname} {
.spellcard { border-left-color: $scolor; }
.h3>a { color: $scolor; }
}
}
@include colorschool(lumomancie,#babaa4);
@include colorschool(vitamancie,#57ab6e);
@include colorschool(obstrumancie,#bd4a66);
@include colorschool(tenebromancie,#404842);
@include colorschool(necromancie,#5d4777);
@include colorschool(morbomancie,#d8733d);
@include colorschool(pyromancie,#b6362a);
@include colorschool(hydromancie,#3f68c7);
@include colorschool(electromancie,#cd9731);
@include colorschool(terramancie,#7e5540);
@include colorschool(sidéromancie,#58697a);
@include colorschool(caelomancie,#a8a8a8);
@include colorschool(légimancie,#5dbabd);
@include colorschool(illusiomancie,#9f63a1);
@include colorschool(cruciomancie,#252451);
@include colorschool(chronomancie,#79896a);
@include colorschool(spatiomancie,#2d4776);
@include colorschool(kénomancie,#101010);
@include colorschool(lutomancie,#4e2827);
@include colorschool(échomancie,#6d9fd1);
@include colorschool(protomancie,#4f5751);
@include colorschool(rebumancie,#8e7245);
@include colorschool(vocamancie,#247864);
@include colorschool(somamancie,#976c67);
@include colorschool(antimancie,#ad95c1);
}
</style> </style>

View File

@@ -1,17 +1,18 @@
// Core // Core
import Vue from 'vue' import Vue from 'vue'
import Vuex from 'vuex'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import App from './app.vue' import App from './app.vue'
// Environment
require('dotenv').config()
import store from './store'
import Globals from './global-components.js' import Globals from './global-components.js'
Globals.forEach(component => { Globals.forEach(component => {
Vue.component(component.name, component) Vue.component(component.name, component)
}); });
// Environment
require('dotenv').config()
// Cookies // Cookies
import VueCookies from 'vue-cookies' import VueCookies from 'vue-cookies'
Vue.use(VueCookies) Vue.use(VueCookies)
@@ -55,38 +56,6 @@ Vue.filter('truncate', filter)
import router from './routes' import router from './routes'
Vue.use(VueRouter) Vue.use(VueRouter)
// Store
Vue.use(Vuex)
let user = Vue.$cookies.get('U_')
const store = new Vuex.Store({
state: user ?
{ status: { loggedIn: true }, user }
: { status: { loggedIn: false }, user: null },
mutations: {
loginSucceed (state, user) {
state.status.loggedIn = true
state.user = user
},
loginFail (state) {
state.status.loggedIn = false
state.user = null
},
logout(state) {
state.status.loggedIn = false;
state.user = null;
},
registerSucceed (state, user) {
state.status.loggedIn = true
state.user = user
},
registerFail (state) {
state.status.loggedIn = false
state.user = null
},
}
})
// Mount Vue // Mount Vue
const app = new Vue({ const app = new Vue({
render: h => h(App), render: h => h(App),

View File

@@ -1,13 +1,13 @@
<template> <template>
<div class="container-fluid p-4" id="spell-container"> <div class="container-fluid p-4" id="spell-container">
<h1 class="display-3 font-display mb-3">Sortilège</h1> <h1 class="display-3 font-display mb-3">Sortilège</h1>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
} }
</script> </script>
<style> <style>

View File

@@ -1,18 +1,12 @@
<template> <template>
<div class="container-fluid p-4" id="spell-container"> <div class="container-fluid p-4" id="spell-container">
<h1 class="display-3 font-display mb-3">Chronologie</h1> <h1 class="display-3 font-display mb-3">Chronologie</h1>
<timeline/> </div>
</div>
</template> </template>
<script> <script>
import Timeline from '~/components/timeline/timeline'
export default { export default {
name: 'timeline-page', name: 'timeline-page',
components: {
'timeline': Timeline,
}
} }
</script> </script>

View File

@@ -1,92 +1,176 @@
<template> <template>
<div class="container-fluid"> <div class="container-fluid">
<section class="d-flex justify-content-center align-items-center"> <section class="d-flex justify-content-center align-items-center">
<main> <main>
<div class="title font-display mb-3">Connexion</div> <div class="title font-display mb-3">Connexion</div>
<form @submit="logUser">
<div class="form-group"> <form @submit="logUser">
<label for="email">Addresse mail</label>
<input <div class="form-group">
v-model="email" <label for="email">Addresse mail</label>
type="email" <input
id="email" v-model="email"
class="form-control" type="email"
placeholder="john.doe@gmail.com" id="email"
autocomplete="email"> class="form-control"
<small class="form-text text-muted">Votre addresse mail nous servira principalement à vous identifier et à retrouver votre compte si vous oubliez vos informations de connexion.</small> :class="{
</div> 'is-invalid': errors.email || errors.login,
<div class="form-group"> 'is-valid': submitted && !errors.email }"
<label for="password">Mot de passe</label> placeholder="john.doe@gmail.com"
<input autocomplete="email">
v-model="password"
type="password" <small
id="password" v-if="!errors.email"
class="form-control" class="form-text text-muted"
autocomplete="current-password"> >
</div> Votre addresse mail nous servira principalement à vous identifier et à retrouver votre compte si vous oubliez vos informations de connexion.
<button type="submit" class="btn btn-primary">Connexion</button> </small>
<router-link :to="'/inscription'" class="btn btn-dark">Inscription</router-link> <small
<small class="form-text text-muted">Vous avez oublié votre mot de passe ? Cliquez-ici pour le changer !</small> v-if="errors.email"
</form> class="form-text text-danger"
</main> >
</section> {{ errors.email }}
</div> </small>
</div>
<div class="form-group">
<label for="password">Mot de passe</label>
<input
v-model="password"
type="password"
id="password"
class="form-control"
:class="{ 'is-invalid': errors.password || errors.login }"
autocomplete="current-password">
<small
v-if="!errors.password"
class="form-text text-muted"
>
Nous vous conseillons d'utiliser un mot de passe unique pour cette application seulement.
</small>
<small
v-if="errors.password"
class="form-text text-danger"
>
{{ errors.password }}
</small>
</div>
<div class="form-check">
<input
type="checkbox"
v-model="remember_me"
id="remember-me"
class="form-check-input">
<label
class="form-check-label"
for="remember-me"
>
Rester connecté
</label>
</div>
<div class="form-group">
<small v-if="errors.login" class="text-danger">{{ errors.login }}</small>
</div>
<button type="submit" class="btn btn-primary">Connexion</button>
<router-link :to="'/inscription'" class="btn btn-dark">Inscription</router-link>
<small class="form-text text-muted">Mot de passe oublié ? <a href="#">Vous pouvez le changer ici !</a></small>
</form>
</main>
</section>
</div>
</template> </template>
<script> <script>
// API
import { RepositoryFactory } from "~/api/repositories"
const Users = RepositoryFactory.get('users')
export default { export default {
name: 'login-page', name: 'login-page',
data() { data() {
return { return {
email: "", email: "",
password: "", password: "",
submitted: false, remember_me: false,
errors: { submitted: false,
login: ""
} errors: {
email: "",
password: "",
login: "",
} }
}
}, },
methods: { methods: {
async logUser(e) { async logUser(e) {
e.preventDefault() e.preventDefault();
Users.login({ // Resets old errors if any
"mail": this.email, for (const o in this.errors) {
"password": this.password this.errors[o] = ""
})
.then(v => {
let user = v.data.user
this.$cookies.set("U_", user, 60 * 60 * 24 * 30)
this.$store.commit('loginSucceed', user)
this.$router.push('/profil')
})
.catch(() => {
this.$store.commit('loginFailed')
})
} }
this.submitted = true;
let userInput = {
"mail": this.email,
"password": this.password
};
if (!userInput.mail) {
this.errors.email = "Vous devez renseigner une addresse mail."
}
if (!userInput.password) {
this.errors.password = "Vous devez renseigner un mot de passe."
}
// Stops the method if any errors exist
for (const o in this.errors) {
if (this.errors[o] != "") {
this.submitted = false;
}
}
if (this.submitted) {
this.$store.dispatch('user_login', userInput)
.then(() => {
this.$router.push('/profil')
})
.catch(err => {
if (err.status === 404) {
this.errors.email = err.data.error;
} else {
this.errors.login = "Une erreur inconnue est survenue, rééssayez plus tard.";
}
});
}
},
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
section { section {
min-height: calc(100vh - 56px); min-height: calc(100vh - 56px);
main { main {
width: 600px; width: 600px;
.title {
font-size: 5rem;
font-weight: 700;
text-align: center;
}
}
}
@media only screen and (max-width: 600px) {
.title { .title {
font-size: 3.5rem; font-size: 5rem;
font-weight: 700;
text-align: center;
} }
}
} }
@media only screen and (max-width: 600px) {
section {
main {
.title {
font-size: 3.5rem;
}
}
}
}
</style> </style>

View File

@@ -1,24 +1,40 @@
<template> <template>
<div class="container-fluid p-4" id="spell-container"> <div
<div class="display-3 font-display mb-3"> v-if="user"
<span class="username">{{ user.name }}</span> class="container-fluid p-4"
<span v-if="user.verified" class="verified"><i class="mad">check_circle</i></span> id="spell-container"
</div> >
</div> <h2 class="display-3 font-display mb-3">
<span class="username">{{ user.name }}</span>
<span
v-if="user.verified"
class="verified"
>
<i class="mad">check_circle</i>
</span>
</h2>
</div>
</template> </template>
<script> <script>
export default { export default {
name: 'profile-page', name: 'profile-page',
computed: { data() {
user() { return {
return this.$store.state.user }
} },
}, computed: {
user() {
return this.$store.getters.getUserProfile
}
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.title { .title {
font-size: 5rem; font-size: 5rem;
font-weight: 700; font-weight: 700;
@@ -28,4 +44,5 @@ export default {
font-size: 3.5rem; font-size: 3.5rem;
} }
} }
</style> </style>

View File

@@ -1,143 +1,193 @@
<template> <template>
<div class="container-fluid"> <div class="container-fluid">
<section class="d-flex justify-content-center align-items-center"> <section class="d-flex justify-content-center align-items-center">
<main> <main>
<div class="title font-display mb-3">Inscription</div> <div class="title font-display mb-3">Inscription</div>
<form @submit="registerUser"> <form @submit="registerUser">
<div class="form-group">
<label for="email">Pseudo</label> <div class="form-group">
<input <label for="email">Nom d'utilisateur</label>
v-model="name" <input
:class="{ v-model="name"
'is-invalid': errors.name, :class="{
'is-valid': submitted && !errors.name }" 'is-invalid': errors.name,
type="text" 'is-valid': submitted && !errors.name }"
id="username" type="text"
class="form-control" id="username"
placeholder="John Doe" class="form-control"
autocomplete="username"> placeholder="John Doe"
<small v-if="!errors.name" class="form-text text-muted">Vous pouvez changer votre pseudo à tout moment.</small> autocomplete="username">
<small v-if="errors.name" class="text-danger">{{ errors.name }}</small> <small
</div> v-if="!errors.name"
<div class="form-group"> class="form-text text-muted"
<label for="email">Addresse mail</label> >
<input Vous pouvez changer votre pseudo à tout moment.
v-model="email" </small>
:class="{
'is-invalid': errors.email, <small
'is-valid': submitted && !errors.email }" v-if="errors.name"
type="email" class="form-text text-danger"
id="email" >
class="form-control" {{ errors.name }}
placeholder="john.doe@gmail.com" </small>
autocomplete="email"> </div>
<small v-if="!errors.email" class="form-text text-muted">Votre addresse mail nous servira principalement à vous identifier et à retrouver votre compte si vous oubliez vos informations de connexion.</small>
<small v-if="errors.email" class="text-danger">{{ errors.email }}</small> <div class="form-group">
</div> <label for="email">Adresse mail</label>
<div class="form-group"> <input
<label for="password">Mot de passe</label> v-model="email"
<input :class="{
:class="{ 'is-invalid': errors.email,
'is-invalid': errors.password, 'is-valid': submitted && !errors.email }"
'is-valid': submitted && !errors.password }" type="email"
v-model="password" id="email"
type="password" class="form-control"
id="password" placeholder="john.doe@gmail.com"
class="form-control" autocomplete="email">
autocomplete="password"> <small
</div> v-if="!errors.email"
<div class="form-group"> class="form-text text-muted"
<label for="password_check">Confirmer le mot de passe</label> >
<input Votre addresse mail nous servira principalement à vous identifier et à retrouver votre compte si vous oubliez vos informations de connexion.
v-model="password_check" </small>
:class="{
'is-invalid': errors.password_check || errors.password, <small
'is-valid': submitted && !errors.password_check}" v-if="errors.email"
type="password" class="form-text text-danger"
id="password_check" >
class="form-control" {{ errors.email }}
autocomplete="password-verification"> </small>
<small v-if="!errors.password && !errors.password_check" class="form-text text-muted">Votre mot de passe doit idéalement contenir des symboles ainsi que des lettres et des chiffres.</small> </div>
<small v-if="errors.password" class="text-danger">{{ errors.password }}</small>
<small v-if="errors.password_check" class="text-danger">{{ errors.password_check }}</small> <div class="form-group">
</div> <label for="password">Mot de passe</label>
<button type="submit" class="btn btn-dark">Inscription</button> <input
</form> :class="{
</main> 'is-invalid': errors.password,
</section> 'is-valid': submitted && !errors.password }"
</div> v-model="password"
type="password"
id="password"
class="form-control"
autocomplete="password">
</div>
<div class="form-group">
<label for="password_check">Confirmer le mot de passe</label>
<input
v-model="password_check"
:class="{
'is-invalid': errors.password_check || errors.password,
'is-valid': submitted && !errors.password_check}"
type="password"
id="password_check"
class="form-control"
autocomplete="password-verification">
<small
v-if="!errors.password && !errors.password_check"
class="form-text text-muted"
>
Votre mot de passe doit idéalement contenir des symboles ainsi que des lettres et des chiffres.
</small>
<small
v-if="errors.password"
class="form-text text-danger"
>
{{ errors.password }}
</small>
<small
v-if="errors.password_check"
class="form-text text-danger"
>
{{ errors.password_check }}
</small>
</div>
<div class="form-group">
<small v-if="errors.register" class="text-danger">{{ errors.register }}</small>
</div>
<button type="submit" class="btn btn-dark">Inscription</button>
</form>
</main>
</section>
</div>
</template> </template>
<script> <script>
// API
import { RepositoryFactory } from "~/api/repositories"
const Users = RepositoryFactory.get('users')
export default { export default {
name: 'register-page', name: 'register-page',
data() { data() {
return { return {
name: "", name: "",
email: "", email: "",
password: "", password: "",
password_check: "", password_check: "",
submitted: false, submitted: false,
errors: { errors: {
name: "", name: "",
email: "", email: "",
password: "", password: "",
password_check: "", password_check: "",
}, register: "",
} },
},
methods: {
async registerUser(e) {
e.preventDefault()
// Resets old errors if any
for (const o in this.errors) {
this.errors[o] = ""
}
this.submitted = true
if (!this.name) {
this.errors.name = "Vous devez renseigner un pseudonyme."
}
if (!this.email) {
this.errors.email = "Vous devez renseigner une addresse mail."
}
if (!this.password) {
this.errors.password = "Vous devez renseigner un mot de passe."
}
if ((this.password.localeCompare(this.password_check)) != 0) {
this.errors.password_check = "Les mots de passe ne correspondent pas."
}
// Stops the method if any errors exist
for (const o in this.errors) {
if (this.errors[o]) {
return true
}
}
Users.register({
"name": this.name,
"mail": this.email,
"password": this.password
})
.then(v => {
let user = v.data.user
this.$cookies.set("U_", user, 60 * 60 * 24 * 30)
this.$store.commit('registerSucceed', user)
this.$router.push('/profil')
})
.catch(err => {
let error = err.response.data.error
this.errors.email = error
this.$store.commit('registerFail')
})
}
} }
},
methods: {
async registerUser(e) {
e.preventDefault();
// Resets old errors if any
for (const o in this.errors) {
this.errors[o] = ""
}
this.submitted = true;
let userInput = {
"name": this.name,
"mail": this.email,
"password": this.password
}
if (!userInput.name) {
this.errors.name = "Vous devez renseigner un pseudonyme."
}
if (!userInput.mail) {
this.errors.email = "Vous devez renseigner une addresse mail."
}
if (!userInput.password) {
this.errors.password = "Vous devez renseigner un mot de passe."
}
if ((userInput.password.localeCompare(this.password_check)) != 0) {
this.errors.password_check = "Les mots de passe ne correspondent pas."
}
// Stops the method if any errors exist
for (const o in this.errors) {
if (this.errors[o] != "") {
this.submitted = false;
}
}
if (this.submitted) {
this.$store.dispatch('user_register', userInput)
.then(() => {
this.$router.push('/');
})
.catch(err => {
if (err.status === 409) {
this.errors.email = err.data.error;
} else {
this.errors.register = "Une erreur inconnue est survenue, rééssayez plus tard.";
}
});
}
}
}
} }
</script> </script>

View File

@@ -1,6 +1,7 @@
import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
// import store from './store'
// Pages // Pages
import Index from "~/pages/index-page" import Index from "~/pages/index-page"
@@ -18,80 +19,84 @@ import Profile from "~/pages/user/profile-page"
// Routes // Routes
const routes = [ const routes = [
{ {
path: "*", path: "*",
redirect: "/", redirect: "/",
}, },
{ {
path: '/', path: '/',
component: Index, component: Index,
}, },
{ {
path: '/connexion', path: '/connexion',
component: Login, component: Login,
meta: { meta: {
antiAuthGuard: true loginFilter: true
} }
}, },
{ {
path: '/inscription', path: '/inscription',
component: Register, component: Register,
meta: { meta: {
antiAuthGuard: true loginFilter: true
} }
}, },
{ {
path: '/sorts', path: '/sorts',
component: Spells, component: Spells,
}, },
{ {
path: '/sorts/:id', path: '/sorts/:id',
component: SpellSingle, component: SpellSingle,
props: true, props: true,
}, },
{ {
path: '/ecoles', path: '/ecoles',
component: Schools, component: Schools,
}, },
{ {
path: '/ecoles/:id', path: '/ecoles/:id',
component: SchoolSingle, component: SchoolSingle,
props: true, props: true,
}, },
{ {
path: '/ages', path: '/ages',
component: Timeline, component: Timeline,
}, },
{ {
path: '/profil', path: '/profil',
component: Profile, component: Profile,
props: true, props: true,
meta: { meta: {
authGuard: true, logoutFilter: true,
} }
}, },
] ]
const router = new VueRouter({ const router = new VueRouter({
mode: 'history', mode: 'history',
routes, routes,
linkActiveClass: "", linkActiveClass: "",
linkExactActiveClass: "active", linkExactActiveClass: "active",
}) })
router.beforeEach((to, from, next) => { // router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.authGuard)) { // if (to.matched.some(record => record.meta.loginFilter)) {
if (Vue.$cookies.get('U_') == null) { // if (store.getters.getUserProfile) {
next({ // next({
path: '/connexion', // path: '/',
params: { nextUrl: to.fullPath }, // });
}) // }
} else { // next();
next() // } else if (to.matched.some(record => record.meta.logoutFilter)) {
} // if (!store.getters.getUserProfile) {
} else { // next({
next() // path: '/connexion',
} // });
}) // }
// next();
// }
// next();
// })
export default router; export default router;

19
client/src/store/index.js Normal file
View File

@@ -0,0 +1,19 @@
import Vue from 'vue';
import Vuex from 'vuex';
import VueCookies from 'vue-cookies';
Vue.use(VueCookies);
Vue.$cookies.config('30d','','');
Vue.use(Vuex);
import user from './modules/user.store';
const debug = process.env.NODE_ENV !== 'production';
export default new Vuex.Store({
modules: {
user
},
strict: debug
});

View File

@@ -0,0 +1,23 @@
const state = {
}
const getters = {
}
const actions = {
}
const mutations = {
}
export default {
namespaced: true,
state,
getters,
actions,
mutations,
}

View File

@@ -0,0 +1,88 @@
// import cookie from 'vue-cookies';
// API
import { RepositoryFactory } from "~/api/repositories";
const Users = RepositoryFactory.get('users');
export const namespaced = true;
const state = {
status: {
logged: false,
profile: null,
}
};
const getters = {
getUserProfile: state => {
if (state.status.logged) {
return state.status.profile
} else {
return false
}
}
};
const mutations = {
loginUser(state, user) {
state.status.profile = user;
state.status.logged = true;
},
logoutUser(state) {
state.status.profile = null;
state.status.logged = false;
},
registerUser() {
}
};
const actions = {
user_login ({ commit }, data) {
return new Promise((resolve, reject) => {
Users.login(data)
.then(v => {
let user = v.data.user;
// cookie.set('user_token', user.uuid);
commit('loginUser', user);
resolve(user);
})
.catch(err => {
reject(err.response);
});
})
},
user_logout ({ commit }) {
// cookie.remove('user_token');
commit('logoutUser');
},
user_register ({ commit }, data) {
return new Promise((resolve, reject) => {
Users.register(data)
.then(() => {
commit('registerUser');
resolve();
})
.catch(err => {
reject(err.response);
});
})
}
};
// Check if a cookie with user token exists
// if (cookie.get('user_token')) {
// mutations.loginUser(state, cookie.get('user_token'));
// }
export default {
state,
getters,
actions,
mutations,
};

View File

@@ -1,20 +1,20 @@
const path = require('path') const path = require('path')
module.exports = { module.exports = {
configureWebpack: { configureWebpack: {
resolve: { resolve: {
alias: { alias: {
"~": path.resolve(__dirname, 'src/') "~": path.resolve(__dirname, 'src/')
} }
}
},
css: {
loaderOptions: {
scss: {
prependData: `
@import "@/assets/scss/_variables.scss";
`
}
}
} }
},
css: {
loaderOptions: {
scss: {
prependData: `
@import "@/assets/scss/_variables.scss";
`
}
}
}
}; };