add login and token refresh

This commit is contained in:
MarcZierle 2022-10-19 13:57:42 +02:00
parent f5148dd017
commit dc0e5451ce
9 changed files with 182 additions and 7 deletions

15
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": {
"autoprefixer": "^9.8.8",
"axios": "^0.24.0",
"axios-auth-refresh": "^3.3.4",
"lorem-ipsum": "^2.0.8",
"postcss": "^7.0.39",
"tailwind-children": "^0.5.0",
@ -859,6 +860,14 @@
"follow-redirects": "^1.14.4"
}
},
"node_modules/axios-auth-refresh": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/axios-auth-refresh/-/axios-auth-refresh-3.3.4.tgz",
"integrity": "sha512-cGq3bZu+lip5j+byaQRZaZ3wpCUxs93jGV0614VYP5k2H1vbdoaw6HGazaUJxcRsFMctR3DItCAx1Dn7KerlcA==",
"peerDependencies": {
"axios": ">= 0.18 < 0.19.0 || >= 0.19.1"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -4758,6 +4767,12 @@
"follow-redirects": "^1.14.4"
}
},
"axios-auth-refresh": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/axios-auth-refresh/-/axios-auth-refresh-3.3.4.tgz",
"integrity": "sha512-cGq3bZu+lip5j+byaQRZaZ3wpCUxs93jGV0614VYP5k2H1vbdoaw6HGazaUJxcRsFMctR3DItCAx1Dn7KerlcA==",
"requires": {}
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",

View File

@ -11,6 +11,7 @@
"dependencies": {
"autoprefixer": "^9.8.8",
"axios": "^0.24.0",
"axios-auth-refresh": "^3.3.4",
"lorem-ipsum": "^2.0.8",
"postcss": "^7.0.39",
"tailwind-children": "^0.5.0",

View File

@ -45,8 +45,10 @@ export default defineComponent({
}
},
created() {
store.dispatch('loadAuth')
console.log("Starting connection to WebSocket Server")
this.connection = new WebSocket("wss://zierle-training-staging.riezel.com/ws/notifications/")
this.connection = new WebSocket("wss://zierle-training.riezel.com/ws/notifications/")
this.connection.onmessage = function(event) {
console.log(event);

View File

@ -1,4 +1,7 @@
import axios from 'axios'
import createAuthRefreshInterceptor from 'axios-auth-refresh';
import store from '../store'
const apiClient = axios.create({
baseURL: 'https://zierle-training.riezel.com/api/v1',
@ -10,6 +13,35 @@ const apiClient = axios.create({
}
})
const refreshAuthLLogic = (failedRequest) => {
apiClient.post('/token/refresh/', {'refresh': store.getters.auth.tokens.refresh}).then(response => {
store.dispatch('setTokens', response.data)
failedRequest.response.config.headers['Authorization'] = 'Bearer ' + store.getters.auth.tokens.access
return Promise.resolve()
}).catch(error => {
store.dispatch('logout')
return Promise.reject()
});
}
export function setupApiClient() {
createAuthRefreshInterceptor(apiClient, refreshAuthLLogic, {
statusCodes: [401, 403]
});
}
apiClient.interceptors.request.use(function(config) {
let auth = store.getters.auth
if (auth.authenticated) {
config.headers['Authorization'] = 'Bearer ' + auth.tokens.access
}
return config
})
export function login(username, password) {
return apiClient.post('/token/', {email: username, password})
}
export function getPhotoLogList() {
return apiClient.get('/photologs/')
}

View File

@ -12,10 +12,14 @@
<router-link :to="{name: 'CreateLog'}">+ New Log</router-link>
<router-link :to="{name: 'LogTemplatesList'}">All Templates</router-link>
<router-link :to="{name: 'CreateLogTemplate'}">+ New Template</router-link>
<router-link :to="{name: 'Login'}" v-if="!loggedIn">Login</router-link>
<div v-else @click="logout">Logged in as {{authUser}}</div>
</nav>
</template>
<script>
import store from '@/store/index.js'
export default {
name: 'NavBar',
data() {
@ -28,6 +32,14 @@ export default {
mounted() {
window.addEventListener('scroll', this.updateScrollPosition)
},
computed: {
loggedIn() {
return store.getters.auth.authenticated
},
authUser() {
return store.getters.auth.user
}
},
methods: {
updateScrollPosition() {
this.scrollPosition = window.scrollY
@ -37,6 +49,10 @@ export default {
} else if (this.showNavBg && this.scrollPosition <= 10) {
this.showNavBg = false
}
},
logout() {
store.dispatch('logout')
this.$router.push({name: 'Login'})
}
},
}
@ -61,4 +77,4 @@ a:hover {
.router-link-exact-active {
font-weight: bold;
}
</style>
</style>

View File

@ -2,6 +2,7 @@ import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import { setupApiClient } from './api'
import { createMetaManager, plugin as metaPlugin } from 'vue-meta'
@ -22,5 +23,7 @@ app.use(naive)
loadAndSetLocale(i18n, 'en')
setupApiClient()
//await router.isReady()
app.mount('#app')

View File

@ -13,6 +13,7 @@ const CreateLogTemplate = () => import('../views/CreateLogTemplate.vue')
const CameraCapture = () => import('../views/CameraCapture.vue')
const ManagePhotos = () => import('../views/ManagePhotos.vue')
const DevView = () => import('../views/DevView.vue')
const LoginView = () => import('../views/LoginView.vue')
const routes = [
{
@ -20,6 +21,11 @@ const routes = [
name: 'Home',
component: HomeView,
},
{
path: '/login',
name: 'Login',
component: LoginView,
},
{
path: '/logs',
name: 'LogsList',

View File

@ -16,6 +16,7 @@ import {
updatePhoto,
deletePhoto,
cropPhoto,
login as apiLogin,
} from '@/api'
export default createStore({
@ -27,8 +28,23 @@ export default createStore({
photoTags: null,
photos: [],
photosStatus: {},
auth: {
authenticated: false,
user: null,
tokens: {
access: null,
refresh: null,
}
},
},
mutations: {
SET_AUTH(state, {authenticated, tokens, user}) {
state.auth.authenticated = authenticated
state.auth.user = user
state.auth.tokens = tokens
localStorage.setItem('auth', JSON.stringify(state.auth))
},
SET_PHOTO_STATUS(state, {id, status}) {
state.photosStatus[id] = status
},
@ -113,13 +129,56 @@ export default createStore({
}
},
actions: {
setTokens({commit, state}, {access}) {
var refresh = state.auth.tokens.refresh
commit('SET_AUTH', {
authenticated: true,
tokens: {
access,
refresh,
},
user: state.auth.user,
})
},
login({commit}, {username, password}) {
return new Promise((resolve, reject) => {
apiLogin(username, password).then(response => {
commit('SET_AUTH', {
authenticated: true,
tokens: response.data,
user: username,
})
resolve(response)
}).catch(error => {
console.log(error)
reject(error)
})
resolve()
})
},
logout({commit}) {
commit('SET_AUTH', {
authenticated: false,
tokens: {
access: null,
refresh: null,
},
user: '',
})
},
loadAuth({commit}) {
let auth = JSON.parse(localStorage.getItem('auth'))
if (auth) {
commit('SET_AUTH', auth)
}
},
setPhotoStatus({commit}, {id, status}) {
commit('SET_PHOTO_STATUS', {id, status})
},
loadPhotoLogList({commit}) {
return new Promise((resolve, reject) => {
getPhotoLogList().then((response) => {
commit('SET_PHOTO_LOG_LIST', response.data)
commit('SET_PHOTO_LOG_LIST', response.data['results'])
resolve()
}).catch((error) => {
console.log(error)
@ -130,7 +189,7 @@ export default createStore({
loadPhotoLogTemplateList({commit}) {
return new Promise((resolve, reject) => {
getPhotoLogTemplateList().then((response) => {
commit('SET_PHOTO_LOG_TEMPLATE_LIST', response.data)
commit('SET_PHOTO_LOG_TEMPLATE_LIST', response.data['results'])
resolve()
}).catch((error) => {
console.log(error)
@ -203,7 +262,7 @@ export default createStore({
loadPhotoGroups({commit}) {
return new Promise((resolve, reject) => {
getPhotoGroups().then((response) => {
commit('SET_PHOTO_GROUPS', response.data)
commit('SET_PHOTO_GROUPS', response.data['results'])
resolve(response.data)
}).catch((error) => {
reject(error)
@ -213,7 +272,7 @@ export default createStore({
loadPhotoTags({commit}) {
return new Promise((resolve, reject) => {
getPhotoTags().then((response) => {
commit('SET_PHOTO_TAGS', response.data)
commit('SET_PHOTO_TAGS', response.data['results'])
resolve(response.data)
}).catch((error) => {
reject(error)
@ -223,7 +282,7 @@ export default createStore({
loadPhotosInGroup({commit}, group_id) {
return new Promise((resolve, reject) => {
getPhotosByGroup(group_id).then((response) => {
commit('SET_PHOTOS_IN_GROUP', {group_id, photos: response.data})
commit('SET_PHOTOS_IN_GROUP', {group_id, photos: response.data['results']})
resolve(response.data)
}).catch((error) => {
reject(error)
@ -278,6 +337,9 @@ export default createStore({
},
},
getters: {
auth(state) {
return state.auth
},
photoLogById: (state) => (id) => {
let log = state.photoLogList.filter(log => log.id == id)
if (log.length > 0) {

38
src/views/LoginView.vue Normal file
View File

@ -0,0 +1,38 @@
<template>
<div class="pt-10">
<h1 class="font-bold text-lg">Login</h1>
<n-input placeholder="E-Mail" v-model:value="email" />
<n-input type="password" v-model:value="pin" placeholder="PIN" />
<n-button @click="login">Login</n-button>
</div>
</template>
<script>
import store from '@/store/index.js'
export default {
setup () {
return {}
},
data() {return {
email: '',
pin: ''
}},
methods: {
login() {
store.dispatch('login', {
username: this.email,
password: this.pin,
}).then(() => {
this.$router.push({name: 'Home'})
}).catch(() =>{
alert("Login failed")
})
},
},
}
</script>
<style lang="scss" scoped>
</style>