This commit is contained in:
parent
0a09a1b0b7
commit
633f2af5eb
1
.env.development
Normal file
1
.env.development
Normal file
@ -0,0 +1 @@
|
|||||||
|
VUE_APP_API_LOCATION=https://videon.k8s.kmlabz.com/api/
|
1
.env.production
Normal file
1
.env.production
Normal file
@ -0,0 +1 @@
|
|||||||
|
VUE_APP_API_LOCATION=https://videon.k8s.kmlabz.com/api/
|
11
package-lock.json
generated
11
package-lock.json
generated
@ -2490,6 +2490,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
|
||||||
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
|
"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": {
|
"babel-eslint": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
|
||||||
@ -5475,8 +5483,7 @@
|
|||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.13.0",
|
"version": "1.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
|
||||||
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==",
|
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"for-in": {
|
"for-in": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^0.21.0",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"node-sass": "^5.0.0",
|
"node-sass": "^5.0.0",
|
||||||
|
@ -1,44 +1,66 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<form novalidate @submit.prevent="performLogin">
|
|
||||||
|
<form novalidate @submit.prevent="performLogin" @input="loginCredsChanged">
|
||||||
<md-card-content>
|
<md-card-content>
|
||||||
|
<md-content class="text-alert" v-if="invalidLogin">Wrong username or password</md-content>
|
||||||
<md-field>
|
<md-field>
|
||||||
<label>Username</label>
|
<label>Username</label>
|
||||||
<md-input :disabled="processing"></md-input>
|
<md-input :disabled="authInProgress" v-model="creds.name"></md-input>
|
||||||
</md-field>
|
</md-field>
|
||||||
|
|
||||||
<md-field>
|
<md-field>
|
||||||
<label>Password</label>
|
<label>Password</label>
|
||||||
<md-input :disabled="processing" type="password"></md-input>
|
<md-input :disabled="authInProgress" v-model="creds.password" type="password"></md-input>
|
||||||
</md-field>
|
</md-field>
|
||||||
|
|
||||||
</md-card-content>
|
</md-card-content>
|
||||||
|
|
||||||
<md-card-actions>
|
<md-card-actions>
|
||||||
<md-progress-spinner :md-diameter="30" :md-stroke="3" md-mode="indeterminate" v-if="processing"
|
<md-progress-spinner :md-diameter="30" :md-stroke="3" md-mode="indeterminate" v-if="authInProgress"
|
||||||
class="md-accent"></md-progress-spinner>
|
class="md-accent"></md-progress-spinner>
|
||||||
<md-button type="submit" class="md-primary" :disabled="processing">Login</md-button>
|
<md-button type="submit" class="md-primary" :disabled="authInProgress">Login</md-button>
|
||||||
</md-card-actions>
|
</md-card-actions>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Login",
|
name: "Login",
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
processing: false
|
creds: {
|
||||||
|
name: "",
|
||||||
|
password: ""
|
||||||
|
},
|
||||||
|
invalidLogin: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
performLogin() {
|
performLogin() {
|
||||||
this.processing = true;
|
this.$store.dispatch("performLogin", this.creds).then(() => {
|
||||||
|
this.$router.push({name: 'Dashboard'});
|
||||||
|
}).catch(() => {
|
||||||
|
this.invalidLogin = true;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loginCredsChanged() {
|
||||||
|
this.invalidLogin = false;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['authInProgress'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.text-alert {
|
||||||
|
color: red;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
@ -5,41 +5,56 @@
|
|||||||
|
|
||||||
<md-field>
|
<md-field>
|
||||||
<label>Username</label>
|
<label>Username</label>
|
||||||
<md-input :disabled="processing"></md-input>
|
<md-input v-model="form.name" :disabled="authInProgress"></md-input>
|
||||||
</md-field>
|
</md-field>
|
||||||
|
|
||||||
<md-field :md-toggle-password="false">
|
<md-field :md-toggle-password="false">
|
||||||
<label>Password</label>
|
<label>Password</label>
|
||||||
<md-input type="password" :disabled="processing"></md-input>
|
<md-input v-model="form.password" type="password" :disabled="authInProgress"></md-input>
|
||||||
</md-field>
|
</md-field>
|
||||||
|
|
||||||
<md-field :md-toggle-password="false">
|
<md-field :md-toggle-password="false">
|
||||||
<label>Confirm password</label>
|
<label>Confirm password</label>
|
||||||
<md-input :disabled="processing" type="password"></md-input>
|
<md-input v-model="form.passwordConfirm" :disabled="authInProgress" type="password"></md-input>
|
||||||
</md-field>
|
</md-field>
|
||||||
|
|
||||||
</md-card-content>
|
</md-card-content>
|
||||||
|
|
||||||
<md-card-actions>
|
<md-card-actions>
|
||||||
<md-progress-spinner :md-diameter="30" :md-stroke="3" md-mode="indeterminate" v-if="processing"
|
<md-progress-spinner :md-diameter="30" :md-stroke="3" md-mode="indeterminate" v-if="authInProgress"
|
||||||
class="md-accent"></md-progress-spinner>
|
class="md-accent"></md-progress-spinner>
|
||||||
<md-button type="submit" class="md-primary" :disabled="processing">Register</md-button>
|
<md-button type="submit" class="md-primary" :disabled="authInProgress && passwordGood">Register</md-button>
|
||||||
</md-card-actions>
|
</md-card-actions>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import {mapGetters} from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Register",
|
name: "Register",
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
processing: false
|
form: {
|
||||||
|
name: "",
|
||||||
|
password: "",
|
||||||
|
passwordConfirm: ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
performRegister() {
|
performRegister() {
|
||||||
this.processing = true;
|
const creds = {name: this.form.name, password: this.form.password};
|
||||||
|
this.$store.dispatch("performRegister", creds).then(() => {
|
||||||
|
this.$router.push({name: 'Dashboard'});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['authInProgress']),
|
||||||
|
passwordGood() {
|
||||||
|
return this.form.password !== "" && this.form.password === this.form.passwordConfirm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
</md-list-item>
|
</md-list-item>
|
||||||
|
|
||||||
<md-list-item>
|
<md-list-item>
|
||||||
<md-button class="md-raised md-accent">Logout</md-button>
|
<md-button class="md-raised md-accent" @click="performLogout">Logout</md-button>
|
||||||
</md-list-item>
|
</md-list-item>
|
||||||
|
|
||||||
</md-list>
|
</md-list>
|
||||||
@ -23,7 +23,14 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "WorkspaceDrawerContent"
|
name: "WorkspaceDrawerContent",
|
||||||
|
methods: {
|
||||||
|
performLogout() {
|
||||||
|
this.$store.dispatch("performLogout").then(() => {
|
||||||
|
this.$router.push({name: "Welcome"})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
24
src/main.js
24
src/main.js
@ -1,4 +1,6 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
@ -39,9 +41,29 @@ Vue.use(MdRipple);
|
|||||||
Vue.use(MdDialog);
|
Vue.use(MdDialog);
|
||||||
Vue.use(MdSpeedDial);
|
Vue.use(MdSpeedDial);
|
||||||
|
|
||||||
|
Vue.prototype.$api = axios.create({
|
||||||
|
baseURL: process.env.VUE_APP_API_LOCATION
|
||||||
|
});
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
router,
|
router,
|
||||||
store,
|
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')
|
}).$mount('#app')
|
||||||
|
@ -3,18 +3,30 @@ import VueRouter from 'vue-router'
|
|||||||
import Dashboard from '@/views/Dashboard'
|
import Dashboard from '@/views/Dashboard'
|
||||||
import Welcome from "@/views/Welcome";
|
import Welcome from "@/views/Welcome";
|
||||||
|
|
||||||
Vue.use(VueRouter)
|
import store from '@/store'
|
||||||
|
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/welcome',
|
||||||
name: 'Welcome',
|
name: 'Welcome',
|
||||||
component: Welcome
|
component: Welcome,
|
||||||
|
meta: {
|
||||||
|
allowVisit(loggedin) {
|
||||||
|
return !loggedin;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/dashboard',
|
path: '/',
|
||||||
name: 'Dashboard',
|
name: 'Dashboard',
|
||||||
component: Dashboard
|
component: Dashboard,
|
||||||
|
meta: {
|
||||||
|
allowVisit(loggedin) {
|
||||||
|
return loggedin;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/about',
|
path: '/about',
|
||||||
@ -22,7 +34,12 @@ const routes = [
|
|||||||
// route level code-splitting
|
// route level code-splitting
|
||||||
// this generates a separate chunk (about.[hash].js) for this route
|
// this generates a separate chunk (about.[hash].js) for this route
|
||||||
// which is lazy-loaded when the route is visited.
|
// 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
|
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
|
export default router
|
||||||
|
99
src/store/auth.js
Normal file
99
src/store/auth.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,24 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
|
|
||||||
|
import auth from "@/store/auth";
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
state: {
|
state: {
|
||||||
|
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|
||||||
},
|
},
|
||||||
modules: {
|
modules: {
|
||||||
|
auth
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user