diff --git a/package-lock.json b/package-lock.json index 4a1fcce0..43a886e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1843,6 +1843,16 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, + "axios": { + "version": "0.18.0", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", + "dev": true, + "requires": { + "follow-redirects": "^1.3.0", + "is-buffer": "^1.1.5" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -2418,6 +2428,11 @@ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, + "bulma": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.1.tgz", + "integrity": "sha512-wRSO2LXB+qI9Pyz2id+uZr4quz5aftSN7Ay1ysr1+krzVp3utD+Ci4CeKuZdrYGc800t65b7heXBL6qw2Wo/lQ==" + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -11830,6 +11845,12 @@ "vue-style-loader": "^4.1.0" } }, + "vue-router": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.1.tgz", + "integrity": "sha512-vLLoY452L+JBpALMP5UHum9+7nzR9PeIBCghU9ZtJ1eWm6ieUI8Zb/DI3MYxH32bxkjzYV1LRjNv4qr8d+uX/w==", + "dev": true + }, "vue-style-loader": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz", diff --git a/package.json b/package.json index f79a3547..be98bd4b 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,15 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "bulma": "^0.7.1", "vue": "^2.5.17" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.0.1", "@vue/cli-plugin-eslint": "^3.0.1", "@vue/cli-service": "^3.0.1", + "axios": "^0.18.0", + "vue-router": "^3.0.1", "vue-template-compiler": "^2.5.17" }, "eslintConfig": { diff --git a/siteconfig.json b/siteconfig.json new file mode 100644 index 00000000..57d746ed --- /dev/null +++ b/siteconfig.json @@ -0,0 +1,3 @@ +{ + "API_URL": "http://localhost:8080/api/v1/" +} diff --git a/src/App.vue b/src/App.vue index fcc56627..32c1b5b3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,28 +1,23 @@ diff --git a/src/auth/index.js b/src/auth/index.js new file mode 100644 index 00000000..7ecf2315 --- /dev/null +++ b/src/auth/index.js @@ -0,0 +1,91 @@ +import {HTTP} from '../http-common' +import router from '../router' +// const API_URL = 'http://localhost:8082/api/v1/' +// const LOGIN_URL = 'http://localhost:8082/login' + +export default { + + user: { + authenticated: false, + infos: {} + }, + + login (context, creds, redirect) { + HTTP.post('login', { + username: creds.username, + password: creds.password + }) + .then(response => { + // Save the token to local storage for later use + localStorage.removeItem('token') // Delete an eventually preexisting old token + localStorage.setItem('token', response.data.token) + + // Tell others the user is autheticated + this.user.authenticated = true + this.getUserInfos() + + // Hide the loader + context.loading = false + + // Redirect if nessecary + if (redirect) { + router.push({ name: redirect }) + } + }) + .catch(e => { + // Hide the loader + context.loading = false + if (e.response) { + context.error = e.response.data.message + if (e.response.status === 401) { + context.error = context.translate('login').wrong + } + } + }) + }, + + logout () { + localStorage.removeItem('token') + router.push({ name: 'login' }) + this.user.authenticated = false + }, + + checkAuth () { + let jwt = localStorage.getItem('token') + this.getUserInfos() + this.user.authenticated = false + if (jwt) { + let infos = this.user.infos + let ts = Math.round((new Date()).getTime() / 1000) + if (infos.exp >= ts) { + this.user.authenticated = true + } + } + }, + + getUserInfos () { + let jwt = localStorage.getItem('token') + if (jwt) { + this.user.infos = this.parseJwt(localStorage.getItem('token')) + return this.parseJwt(localStorage.getItem('token')) + } else { + return {} + } + }, + + parseJwt (token) { + let base64Url = token.split('.')[1] + let base64 = base64Url.replace('-', '+').replace('_', '/') + return JSON.parse(window.atob(base64)) + }, + + getAuthHeader () { + return { + 'Authorization': 'Bearer ' + localStorage.getItem('token') + } + }, + + getToken () { + return localStorage.getItem('token') + } +} diff --git a/src/http-common/index.js b/src/http-common/index.js new file mode 100644 index 00000000..297ee29a --- /dev/null +++ b/src/http-common/index.js @@ -0,0 +1,6 @@ +import axios from 'axios' +let config = require('../../siteconfig.json') + +export const HTTP = axios.create({ + baseURL: config.API_URL +}) diff --git a/src/main.js b/src/main.js index fca74cfc..0eb05997 100644 --- a/src/main.js +++ b/src/main.js @@ -1,8 +1,14 @@ import Vue from 'vue' import App from './App.vue' +import router from './router' +import auth from './auth' Vue.config.productionTip = false +// Check the user's auth status when the app starts +auth.checkAuth() + new Vue({ + router, render: h => h(App) }).$mount('#app') diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 00000000..662836bf --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,22 @@ +import Vue from 'vue' +import Router from 'vue-router' + +import HomeComponent from '@/components/Home' +import LoginComponent from '@/components/Login' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'home', + component: HomeComponent + }, + { + path: '/login', + name: 'login', + component: LoginComponent + } + ] +}) \ No newline at end of file