add login and token !

This commit is contained in:
YuukanOO 2021-01-21 16:46:15 +01:00
parent b28c897f5d
commit 0cf8ca8597
10 changed files with 160 additions and 21 deletions

View File

@ -9500,6 +9500,11 @@
"integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
"dev": true "dev": true
}, },
"shvl": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/shvl/-/shvl-2.0.2.tgz",
"integrity": "sha512-G3KkIXPza3dgkt6Bo8zIl5K/KvAAhbG6o9KfAjhPvrIIzzAhnfc2ztv1i+iPTbNNM43MaBUqIaZwqVjkSgY/rw=="
},
"signal-exit": { "signal-exit": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
@ -11031,6 +11036,22 @@
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.0.tgz", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.0.tgz",
"integrity": "sha512-W74OO2vCJPs9/YjNjW8lLbj+jzT24waTo2KShI8jLvJW8OaIkgb3wuAMA7D+ZiUxDOx3ubwSZTaJBip9G8a3aQ==" "integrity": "sha512-W74OO2vCJPs9/YjNjW8lLbj+jzT24waTo2KShI8jLvJW8OaIkgb3wuAMA7D+ZiUxDOx3ubwSZTaJBip9G8a3aQ=="
}, },
"vuex-persistedstate": {
"version": "4.0.0-beta.3",
"resolved": "https://registry.npmjs.org/vuex-persistedstate/-/vuex-persistedstate-4.0.0-beta.3.tgz",
"integrity": "sha512-T4IRD27qoUWh+8qr6T6zVp15xO7x/nPgnU13OD0C2uUwA7U9PhGozrj6lvVmMYDyRgc36J0msMXn3GvwHjkIhA==",
"requires": {
"deepmerge": "^4.2.2",
"shvl": "^2.0.2"
},
"dependencies": {
"deepmerge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
}
}
},
"watchpack": { "watchpack": {
"version": "1.7.5", "version": "1.7.5",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",

View File

@ -12,7 +12,8 @@
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-router": "^3.4.9", "vue-router": "^3.4.9",
"vuelidate": "^0.7.6", "vuelidate": "^0.7.6",
"vuex": "^3.6.0" "vuex": "^3.6.0",
"vuex-persistedstate": "^4.0.0-beta.3"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0", "@vue/cli-plugin-babel": "~4.5.0",

View File

@ -4,6 +4,8 @@
<router-link :to="{ name: 'links' }">Derniers liens publiés</router-link> <router-link :to="{ name: 'links' }">Derniers liens publiés</router-link>
/ /
<router-link :to="{ name: 'register' }">Créer un compte</router-link> <router-link :to="{ name: 'register' }">Créer un compte</router-link>
/
<router-link :to="{ name: 'login' }">Se connecter</router-link>
</nav> </nav>
<router-view></router-view> <router-view></router-view>
<notifications-panel /> <notifications-panel />

View File

@ -1,6 +1,11 @@
class Api { class Api {
constructor(baseUrl) { constructor(baseUrl) {
this.baseUrl = baseUrl; this._baseUrl = baseUrl;
this._getToken = null;
}
useToken(cb) {
this._getToken = cb;
} }
getLinks() { getLinks() {
@ -19,10 +24,29 @@ class Api {
return this._post("/api/accounts", data); return this._post("/api/accounts", data);
} }
login(data) {
return this._post("/api/accounts/token", data);
}
_authorizationHeader() {
if (!this._getToken) {
return {};
}
const token = this._getToken();
if (!token) {
return {};
}
return { Authorization: `Bearer ${token}` };
}
_get(path) { _get(path) {
return fetch(this.baseUrl + path, { return fetch(this._baseUrl + path, {
headers: { headers: {
Accept: "application/json", Accept: "application/json",
...this._authorizationHeader(),
}, },
}).then((r) => { }).then((r) => {
if (!r.ok) { if (!r.ok) {
@ -34,11 +58,12 @@ class Api {
} }
_post(path, data) { _post(path, data) {
return fetch(this.baseUrl + path, { return fetch(this._baseUrl + path, {
method: "POST", method: "POST",
headers: { headers: {
Accept: "application/json", Accept: "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",
...this._authorizationHeader(),
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
}).then((r) => { }).then((r) => {
@ -50,6 +75,8 @@ class Api {
return r; return r;
} }
// if (!r.headers['Content-Length'] === '0') {
return r.json(); return r.json();
}); });
} }

View File

@ -13,7 +13,7 @@ import { mapState } from "vuex";
export default { export default {
computed: { computed: {
...mapState(["messages"]), ...mapState("notification", ["messages"]),
}, },
}; };
</script> </script>

View File

@ -4,6 +4,7 @@ import Layout from "./Layout.vue";
import createRouter from "./router"; import createRouter from "./router";
import createStore from "./store"; import createStore from "./store";
import Notifications from "./plugins/Notifications"; import Notifications from "./plugins/Notifications";
import api from "./api";
Vue.config.productionTip = false; Vue.config.productionTip = false;
Vue.use(Vuelidate); Vue.use(Vuelidate);
@ -12,6 +13,8 @@ Vue.use(Notifications);
const router = createRouter(); const router = createRouter();
const store = createStore(); const store = createStore();
api.useToken(() => store.state.auth.token);
new Vue({ new Vue({
router, router,
store, store,

View File

@ -0,0 +1,52 @@
<template>
<div>
<h1>Connexion</h1>
<form @submit.prevent="login">
<error v-if="err" :error="err" />
<textfield label="Nom d'utilisteur" :field="$v.username" />
<textfield label="Mot de passe" type="password" :field="$v.password" />
<button type="submit" :disabled="$v.$invalid">Se connecter</button>
</form>
</div>
</template>
<script>
import Textfield from "../components/Textfield";
import Error from "../components/Error";
import { required } from "vuelidate/lib/validators";
export default {
components: { Textfield, Error },
data() {
return {
err: null,
username: "",
password: "",
};
},
methods: {
async login() {
this.err = null;
try {
await this.$store.dispatch("auth/login", {
username: this.username,
password: this.password,
});
this.$redirectWithMessage({ name: "links" }, "Vous êtes authentifié !");
} catch (e) {
this.err = e;
}
},
},
validations: {
username: {
required,
},
password: {
required,
},
},
};
</script>

View File

@ -1,7 +1,7 @@
export default { export default {
install(Vue) { install(Vue) {
Vue.prototype.$message = function(message) { Vue.prototype.$message = function(message) {
this.$store.dispatch("addMessage", message); this.$store.dispatch("notification/addMessage", message);
}; };
Vue.prototype.$redirectWithMessage = function(to, message) { Vue.prototype.$redirectWithMessage = function(to, message) {

View File

@ -2,6 +2,7 @@ import Vue from "vue";
import VueRouter from "vue-router"; import VueRouter from "vue-router";
import Links from "./pages/Links.vue"; import Links from "./pages/Links.vue";
import Register from "./pages/Register.vue"; import Register from "./pages/Register.vue";
import Login from "./pages/Login.vue";
import LinkDetail from "./pages/LinkDetail.vue"; import LinkDetail from "./pages/LinkDetail.vue";
Vue.use(VueRouter); Vue.use(VueRouter);
@ -17,6 +18,7 @@ export default function createRouter() {
props: true, props: true,
}, },
{ path: "/register", name: "register", component: Register }, { path: "/register", name: "register", component: Register },
{ path: "/login", name: "login", component: Login },
{ path: "*", redirect: "/" }, { path: "*", redirect: "/" },
], ],
}); });

View File

@ -1,10 +1,15 @@
import Vue from "vue"; import Vue from "vue";
import Vuex from "vuex"; import Vuex from "vuex";
import createPersistedState from "vuex-persistedstate";
import api from "./api";
Vue.use(Vuex); Vue.use(Vuex);
export default function createStore() { export default function createStore() {
return new Vuex.Store({ return new Vuex.Store({
modules: {
notification: {
namespaced: true,
state: { state: {
messages: [], messages: [],
}, },
@ -23,5 +28,31 @@ export default function createStore() {
setTimeout(() => commit("removeMessage", message), 3000); setTimeout(() => commit("removeMessage", message), 3000);
}, },
}, },
},
auth: {
namespaced: true,
state: {
token: null,
username: null,
},
mutations: {
setUser(state, { token, username }) {
state.token = token;
state.username = username;
},
},
actions: {
async login({ commit }, data) {
const result = await api.login(data);
commit("setUser", result);
},
},
},
},
plugins: [
createPersistedState({
paths: ["auth"],
}),
],
}); });
} }