mirror of
https://github.com/MarcZierle/photo-log-frontend.git
synced 2025-04-07 13:04:37 +00:00
add login and token refresh
This commit is contained in:
parent
f5148dd017
commit
dc0e5451ce
15
package-lock.json
generated
15
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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/')
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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')
|
||||
|
@ -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',
|
||||
|
@ -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
38
src/views/LoginView.vue
Normal 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>
|
Loading…
Reference in New Issue
Block a user