remove browser authentication popup

add startup and login views
moved data loading from components to Startup.vue
add logout feature
change library name loading to lazy loading
This commit is contained in:
Gauthier Roebroeck 2019-12-10 16:45:09 +08:00
parent ca3ef08939
commit c6ac232fdf
9 changed files with 196 additions and 59 deletions

View file

@ -112,7 +112,7 @@ export default Vue.extend({
}
},
async created () {
this.libraryName = await this.getLibraryName()
this.libraryName = await this.getLibraryNameLazy(this.libraryId)
},
mounted () {
// fill series skeletons if an index is provided, so scroll position can be restored
@ -226,13 +226,6 @@ export default Vue.extend({
this.series.splice(page.number * page.size, page.size, ...page.content)
this.pagesState[page.number] = LoadState.Loaded
},
async getLibraryName (): Promise<string> {
if (this.libraryId !== 0) {
return (await this.$komgaLibraries.getLibrary(this.libraryId)).name
} else {
return 'All libraries'
}
},
getLibraryNameLazy (libraryId: any): string {
if (libraryId !== 0) {
return (this.$store.getters.getLibraryById(libraryId)).name

View file

@ -98,13 +98,6 @@ export default Vue.extend({
this.dialogReset(val)
}
},
async mounted () {
try {
await this.$store.dispatch('getLibraries')
} catch (e) {
this.showSnack(e.message)
}
},
computed: {
libraries (): LibraryDto[] {
return this.$store.state.komgaLibraries.libraries

View file

@ -5,7 +5,8 @@ export default {
install (Vue: typeof _Vue) {
Vue.prototype.$http = axios.create({
baseURL: process.env.VUE_APP_KOMGA_API_URL ? process.env.VUE_APP_KOMGA_API_URL : window.location.origin,
withCredentials: true
withCredentials: true,
headers: { 'X-Requested-With': 'XMLHttpRequest' }
} as AxiosRequestConfig)
}
}

View file

@ -11,7 +11,8 @@ const vuexModule: Module<any, any> = {
users: [] as UserWithSharedLibrariesDto[]
},
getters: {
meAdmin: state => state.me.hasOwnProperty('roles') && state.me.roles.includes('ADMIN')
meAdmin: state => state.me.hasOwnProperty('roles') && state.me.roles.includes('ADMIN'),
authenticated: state => state.me.hasOwnProperty('id')
},
mutations: {
setMe (state, user: UserDto) {
@ -22,6 +23,16 @@ const vuexModule: Module<any, any> = {
}
},
actions: {
async getMeWithAuth ({ commit }, { login, password }: { login: string, password: string }) {
commit('setMe', await service.getMeWithAuth(login, password))
},
async logout ({ commit }) {
try {
await service.getMeWithAuth('', '')
} catch (e) {
}
commit('setMe', {})
},
async getMe ({ commit }) {
commit('setMe', await service.getMe())
},

View file

@ -11,7 +11,7 @@ const adminGuard = (to: any, from: any, next: any) => {
else next()
}
export default new Router({
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
@ -75,12 +75,20 @@ export default new Router({
}
]
},
{
path: '/startup',
name: 'startup',
component: () => import(/* webpackChunkName: "startup" */ './views/Startup.vue')
},
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: "login" */ './views/Login.vue')
},
{
path: '*',
name:
'notfound',
component:
() => import(/* webpackChunkName: "notfound" */ './views/PageNotFound.vue')
name: 'notfound',
component: () => import(/* webpackChunkName: "notfound" */ './views/PageNotFound.vue')
}
],
scrollBehavior (to, from, savedPosition) {
@ -93,3 +101,10 @@ export default new Router({
}
}
})
router.beforeEach((to, from, next) => {
if (to.name !== 'startup' && to.name !== 'login' && !lStore.getters.authenticated) next({ name: 'startup' })
else next()
})
export default router

View file

@ -9,6 +9,31 @@ export default class KomgaUsersService {
this.http = http
}
async getMeWithAuth (login: string, password: string): Promise<UserDto> {
try {
return (await this.http.get(
`${API_USERS}/me`,
{
auth: {
username: login,
password: password
}
}
)).data
} catch (e) {
let msg = 'An error occurred while trying to login'
if (e.response) {
if (e.response.status === 401) {
msg = 'Invalid authentication'
}
}
if (e.response.data.message) {
msg += `: ${e.response.data.message}`
}
throw new Error(msg)
}
}
async getMe (): Promise<UserDto> {
try {
return (await this.http.get(`${API_USERS}/me`)).data

View file

@ -96,14 +96,14 @@
</v-list-item-content>
</v-list-item>
<!-- <v-list-item @click="logout">-->
<!-- <v-list-item-icon>-->
<!-- <v-icon>mdi-power</v-icon>-->
<!-- </v-list-item-icon>-->
<!-- <v-list-item-content>-->
<!-- <v-list-item-title>Log out</v-list-item-title>-->
<!-- </v-list-item-content>-->
<!-- </v-list-item>-->
<v-list-item @click="logout">
<v-list-item-icon>
<v-icon>mdi-power</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>Log out</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
@ -114,20 +114,6 @@
<library-delete-dialog v-model="modalDeleteLibrary"
:library="libraryToDelete">
</library-delete-dialog>
<v-snackbar
v-model="snackbar"
bottom
color="error"
>
{{ snackText }}
<v-btn
text
@click="snackbar = false"
>
Close
</v-btn>
</v-snackbar>
</div>
</template>
@ -142,9 +128,7 @@ export default Vue.extend({
drawerVisible: true,
modalAddLibrary: false,
modalDeleteLibrary: false,
libraryToDelete: {} as LibraryDto,
snackbar: false,
snackText: ''
libraryToDelete: {} as LibraryDto
}),
computed: {
libraries (): LibraryDto[] {
@ -165,27 +149,17 @@ export default Vue.extend({
return []
}
},
async mounted () {
try {
await this.$store.dispatch('getMe')
await this.$store.dispatch('getLibraries')
} catch (e) {
this.showSnack(e.message)
}
},
methods: {
toggleDrawer () {
this.drawerVisible = !this.drawerVisible
},
showSnack (message: string) {
this.snackText = message
this.snackbar = true
},
promptDeleteLibrary (library: LibraryDto) {
this.libraryToDelete = library
this.modalDeleteLibrary = true
},
logout () {
this.$store.dispatch('logout')
this.$router.push({ name: 'login' })
}
}
})

View file

@ -0,0 +1,95 @@
<template>
<div class="ma-3">
<v-row align="center" justify="center">
<v-img src="../assets/logo.svg"
max-width="400"
/>
</v-row>
<form novalidate @submit.prevent="performLogin">
<v-row justify="center">
<v-col cols="3" xs="12">
<v-text-field v-model="form.login"
label="Login"
autocomplete="username"
/>
</v-col>
</v-row>
<v-row justify="center">
<v-col cols="3" xs="12">
<v-text-field v-model="form.password"
label="Password"
type="password"
autocomplete="current-password"
/>
</v-col>
</v-row>
<v-row justify="center">
<v-col cols="3" xs="12">
<v-btn color="primary"
type="submit"
>Login
</v-btn>
</v-col>
</v-row>
</form>
<v-snackbar
v-model="snackbar"
bottom
color="error"
>
{{ snackText }}
<v-btn
text
@click="snackbar = false"
>
Close
</v-btn>
</v-snackbar>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
name: 'Login',
data: () => ({
form: {
login: '',
password: ''
},
snackbar: false,
snackText: ''
}),
methods: {
async performLogin () {
try {
await this.$store.dispatch(
'getMeWithAuth',
{
login: this.form.login,
password: this.form.password
})
await this.$store.dispatch('getLibraries')
this.$router.push({ name: 'home' })
} catch (e) {
this.showSnack(e.message)
}
},
showSnack (message: string) {
this.snackText = message
this.snackbar = true
}
}
})
</script>
<style scoped>
</style>

View file

@ -0,0 +1,30 @@
<template>
<div class="ma-3">
<v-row align="center" justify="center">
<v-img src="../assets/logo.svg"
max-width="400"
/>
</v-row>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
name: 'Startup',
async mounted () {
try {
await this.$store.dispatch('getMe')
await this.$store.dispatch('getLibraries')
this.$router.push({ name: 'home' })
} catch (e) {
this.$router.push({ name: 'login' })
}
}
})
</script>
<style scoped>
</style>