diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..1e75222 --- /dev/null +++ b/.env.development @@ -0,0 +1 @@ +VUE_APP_API_LOCATION=https://videon.k8s.kmlabz.com/api/ \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..1e75222 --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +VUE_APP_API_LOCATION=https://videon.k8s.kmlabz.com/api/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b859245..4f63983 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2490,6 +2490,14 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, + "axios": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", + "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -5475,8 +5483,7 @@ "follow-redirects": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", - "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", - "dev": true + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" }, "for-in": { "version": "1.0.2", diff --git a/package.json b/package.json index 980c5ca..955b53a 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "axios": "^0.21.0", "core-js": "^3.6.5", "lodash": "^4.17.20", "node-sass": "^5.0.0", diff --git a/src/components/Login.vue b/src/components/Login.vue index e60e37e..f1d88ba 100644 --- a/src/components/Login.vue +++ b/src/components/Login.vue @@ -1,44 +1,66 @@ - + + + Wrong username or password Username - + Password - + - - Login + Login \ No newline at end of file diff --git a/src/components/Register.vue b/src/components/Register.vue index ca78e5e..1b847de 100644 --- a/src/components/Register.vue +++ b/src/components/Register.vue @@ -5,41 +5,56 @@ Username - + Password - + Confirm password - + - - Register + Register diff --git a/src/main.js b/src/main.js index ea836a0..6006b60 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,6 @@ import Vue from 'vue' +import axios from 'axios' + import App from './App.vue' import router from './router' import store from './store' @@ -39,9 +41,29 @@ Vue.use(MdRipple); Vue.use(MdDialog); Vue.use(MdSpeedDial); +Vue.prototype.$api = axios.create({ + baseURL: process.env.VUE_APP_API_LOCATION +}); new Vue({ router, store, - render: h => h(App) + render: h => h(App), + created() { + this.$api.interceptors.response.use(undefined, function (err) { + return new Promise(function (resolve, reject) { + if (err.status === 401 && err.config && !err.config.__isRetryRequest) { + this.$store.dispatch("performLogout").then(() => { + this.$router.push({name: 'Welcome'}); + }); + } + reject(err); + }); + }); + this.$api.interceptors.request.use((config) => { + if (this.$store && this.$store.getters.isLoggedIn) { + config.headers["Authorization"] = "Bearer " + this.$store.state.auth.token; + } + }) + } }).$mount('#app') diff --git a/src/router/index.js b/src/router/index.js index f188456..20a927d 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -3,18 +3,30 @@ import VueRouter from 'vue-router' import Dashboard from '@/views/Dashboard' import Welcome from "@/views/Welcome"; -Vue.use(VueRouter) +import store from '@/store' + +Vue.use(VueRouter); const routes = [ { - path: '/', + path: '/welcome', name: 'Welcome', - component: Welcome + component: Welcome, + meta: { + allowVisit(loggedin) { + return !loggedin; + } + } }, { - path: '/dashboard', + path: '/', name: 'Dashboard', - component: Dashboard + component: Dashboard, + meta: { + allowVisit(loggedin) { + return loggedin; + } + } }, { path: '/about', @@ -22,7 +34,12 @@ const routes = [ // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. - component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue') + component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue'), + meta: { + allowVisit() { + return true; + } + } } ] @@ -32,4 +49,25 @@ const router = new VueRouter({ routes }) + +router.beforeEach((to, from, next) => { + + const loggedin = store.getters.isLoggedIn; + + const visitAllowed = to.matched.some(record => record.meta.allowVisit(loggedin)) + + if (visitAllowed) { + next(); + return; + } + + if (loggedin) { + next({name: 'Dashboard'}) + } else { + next({name: 'Welcome'}) + } + +}) + + export default router diff --git a/src/store/auth.js b/src/store/auth.js new file mode 100644 index 0000000..00ccbf8 --- /dev/null +++ b/src/store/auth.js @@ -0,0 +1,99 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import axios from 'axios' + +const JWT_KEY_NAME = "JWT"; +const baseURL = process.env.VUE_APP_API_LOCATION; + +Vue.use(Vuex) + +export default { + state() { + return { + auth: { + processing: false, + token: localStorage.getItem(JWT_KEY_NAME) || '', + name: '' + } + } + }, + mutations: { + auth_started(state) { + state.auth.processing = true; + }, + auth_success(state, token, name) { + state.auth.processing = false; + state.auth.token = token; + state.auth.name = name; + }, + auth_fail(state) { + state.auth.processing = false; + state.auth.token = ''; + state.auth.name = ''; + }, + logout(state) { + state.auth.token = ''; + state.auth.name = ''; + } + }, + actions: { + performLogin({commit}, creds) { + return new Promise((resolve, reject) => { + commit('auth_started') + axios.post("auth/login", creds, {baseURL}).then(resp => { + const token = resp.data.token; + + if (!token) { + return reject(); + } + + const user = creds.name; + localStorage.setItem(JWT_KEY_NAME, token) + commit('auth_success', token, user) + return resolve(resp); + }).catch(err => { + commit('auth_fail') + localStorage.removeItem(JWT_KEY_NAME) + return reject(err); + }) + }) + }, + performRegister({commit}, creds) { + return new Promise((resolve, reject) => { + commit('auth_started') + axios.post("auth/signup", creds, {baseURL}).then(resp => { + const token = resp.data.token; + + if (!token) { + return reject(); + } + + const user = creds.name; + localStorage.setItem(JWT_KEY_NAME, token) + commit('auth_success', token, user) + resolve(resp) + }).catch(err => { + commit('auth_fail') + localStorage.removeItem(JWT_KEY_NAME) + reject(err) + }) + }) + }, + performLogout({commit}) { + return new Promise((resolve) => { + localStorage.removeItem(JWT_KEY_NAME) + commit('logout') + resolve(); + }); + } + }, + modules: {}, + getters: { + isLoggedIn(state) { + return !!state.auth.token; + }, + authInProgress(state) { + return state.auth.processing; + } + } +} diff --git a/src/store/index.js b/src/store/index.js index 332b916..dfe6362 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,15 +1,24 @@ import Vue from 'vue' import Vuex from 'vuex' +import auth from "@/store/auth"; + Vue.use(Vuex) export default new Vuex.Store({ - state: { - }, - mutations: { - }, - actions: { - }, - modules: { - } + state: { + + }, + mutations: { + + }, + actions: { + + }, + modules: { + auth + }, + getters: { + + } })