mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-07-13 10:55:05 +02:00
Update ereaders to handle refreshing, epubjs to use custom request method, separate accessToken in store
This commit is contained in:
parent
25fe4dee3a
commit
d3402e30c2
9 changed files with 67 additions and 46 deletions
|
@ -311,7 +311,7 @@ export default {
|
||||||
|
|
||||||
if (data.user.id === this.user.id && data.user.accessToken !== this.user.accessToken) {
|
if (data.user.id === this.user.id && data.user.accessToken !== this.user.accessToken) {
|
||||||
console.log('Current user access token was updated')
|
console.log('Current user access token was updated')
|
||||||
this.$store.commit('user/setUserToken', data.user.accessToken)
|
this.$store.commit('user/setAccessToken', data.user.accessToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$toast.success(this.$strings.ToastAccountUpdateSuccess)
|
this.$toast.success(this.$strings.ToastAccountUpdateSuccess)
|
||||||
|
|
|
@ -104,9 +104,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
userToken() {
|
|
||||||
return this.$store.getters['user/getToken']
|
|
||||||
},
|
|
||||||
libraryItemId() {
|
libraryItemId() {
|
||||||
return this.libraryItem?.id
|
return this.libraryItem?.id
|
||||||
},
|
},
|
||||||
|
@ -234,10 +231,7 @@ export default {
|
||||||
async extract() {
|
async extract() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
var buff = await this.$axios.$get(this.ebookUrl, {
|
var buff = await this.$axios.$get(this.ebookUrl, {
|
||||||
responseType: 'blob',
|
responseType: 'blob'
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${this.userToken}`
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
const archive = await Archive.open(buff)
|
const archive = await Archive.open(buff)
|
||||||
const originalFilesObject = await archive.getFilesObject()
|
const originalFilesObject = await archive.getFilesObject()
|
||||||
|
|
|
@ -57,9 +57,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
userToken() {
|
|
||||||
return this.$store.getters['user/getToken']
|
|
||||||
},
|
|
||||||
/** @returns {string} */
|
/** @returns {string} */
|
||||||
libraryItemId() {
|
libraryItemId() {
|
||||||
return this.libraryItem?.id
|
return this.libraryItem?.id
|
||||||
|
|
|
@ -26,9 +26,6 @@ export default {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
userToken() {
|
|
||||||
return this.$store.getters['user/getToken']
|
|
||||||
},
|
|
||||||
libraryItemId() {
|
libraryItemId() {
|
||||||
return this.libraryItem?.id
|
return this.libraryItem?.id
|
||||||
},
|
},
|
||||||
|
@ -96,11 +93,8 @@ export default {
|
||||||
},
|
},
|
||||||
async initMobi() {
|
async initMobi() {
|
||||||
// Fetch mobi file as blob
|
// Fetch mobi file as blob
|
||||||
var buff = await this.$axios.$get(this.ebookUrl, {
|
const buff = await this.$axios.$get(this.ebookUrl, {
|
||||||
responseType: 'blob',
|
responseType: 'blob'
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${this.userToken}`
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
var reader = new FileReader()
|
var reader = new FileReader()
|
||||||
reader.onload = async (event) => {
|
reader.onload = async (event) => {
|
||||||
|
|
|
@ -55,7 +55,8 @@ export default {
|
||||||
loadedRatio: 0,
|
loadedRatio: 0,
|
||||||
page: 1,
|
page: 1,
|
||||||
numPages: 0,
|
numPages: 0,
|
||||||
pdfDocInitParams: null
|
pdfDocInitParams: null,
|
||||||
|
isRefreshing: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -152,7 +153,34 @@ export default {
|
||||||
this.page++
|
this.page++
|
||||||
this.updateProgress()
|
this.updateProgress()
|
||||||
},
|
},
|
||||||
error(err) {
|
async refreshToken() {
|
||||||
|
if (this.isRefreshing) return
|
||||||
|
this.isRefreshing = true
|
||||||
|
const newAccessToken = await this.$store.dispatch('user/refreshToken').catch((error) => {
|
||||||
|
console.error('Failed to refresh token', error)
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
if (!newAccessToken) {
|
||||||
|
// Redirect to login on failed refresh
|
||||||
|
this.$router.push('/login')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force Vue to re-render the PDF component by creating a new object
|
||||||
|
this.pdfDocInitParams = {
|
||||||
|
url: this.ebookUrl,
|
||||||
|
httpHeaders: {
|
||||||
|
Authorization: `Bearer ${newAccessToken}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isRefreshing = false
|
||||||
|
},
|
||||||
|
async error(err) {
|
||||||
|
if (err && err.status === 401) {
|
||||||
|
console.log('Received 401 error, refreshing token')
|
||||||
|
await this.refreshToken()
|
||||||
|
return
|
||||||
|
}
|
||||||
console.error(err)
|
console.error(err)
|
||||||
},
|
},
|
||||||
resize() {
|
resize() {
|
||||||
|
|
|
@ -189,7 +189,7 @@ export default {
|
||||||
|
|
||||||
this.$store.commit('libraries/setCurrentLibrary', userDefaultLibraryId)
|
this.$store.commit('libraries/setCurrentLibrary', userDefaultLibraryId)
|
||||||
this.$store.commit('user/setUser', user)
|
this.$store.commit('user/setUser', user)
|
||||||
this.$store.commit('user/setUserToken', user.accessToken)
|
this.$store.commit('user/setAccessToken', user.accessToken)
|
||||||
|
|
||||||
this.$store.dispatch('user/loadUserSettings')
|
this.$store.dispatch('user/loadUserSettings')
|
||||||
},
|
},
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default function ({ $axios, store, $root, app }) {
|
||||||
if (originalRequest.url === '/auth/refresh' || originalRequest.url === '/login') {
|
if (originalRequest.url === '/auth/refresh' || originalRequest.url === '/login') {
|
||||||
// Refresh failed or login failed, redirect to login
|
// Refresh failed or login failed, redirect to login
|
||||||
store.commit('user/setUser', null)
|
store.commit('user/setUser', null)
|
||||||
store.commit('user/setUserToken', null)
|
store.commit('user/setAccessToken', null)
|
||||||
app.router.push('/login')
|
app.router.push('/login')
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
|
@ -72,23 +72,13 @@ export default function ({ $axios, store, $root, app }) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Attempt to refresh the token
|
// Attempt to refresh the token
|
||||||
const response = await $axios.$post('/auth/refresh')
|
// Updates store if successful, otherwise clears store and throw error
|
||||||
const newAccessToken = response.user.accessToken
|
const newAccessToken = await store.dispatch('user/refreshToken')
|
||||||
|
|
||||||
if (!newAccessToken) {
|
if (!newAccessToken) {
|
||||||
console.error('No new access token received')
|
console.error('No new access token received')
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the token in store and localStorage
|
|
||||||
store.commit('user/setUser', response.user)
|
|
||||||
store.commit('user/setUserToken', newAccessToken)
|
|
||||||
|
|
||||||
// Emit event used to re-authenticate socket in default.vue since $root is not available here
|
|
||||||
if (app.$eventBus) {
|
|
||||||
app.$eventBus.$emit('token_refreshed', newAccessToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the original request with new token
|
// Update the original request with new token
|
||||||
if (!originalRequest.headers) {
|
if (!originalRequest.headers) {
|
||||||
originalRequest.headers = {}
|
originalRequest.headers = {}
|
||||||
|
@ -106,9 +96,7 @@ export default function ({ $axios, store, $root, app }) {
|
||||||
// Process queued requests with error
|
// Process queued requests with error
|
||||||
processQueue(refreshError, null)
|
processQueue(refreshError, null)
|
||||||
|
|
||||||
// Clear user data and redirect to login
|
// Redirect to login
|
||||||
store.commit('user/setUser', null)
|
|
||||||
store.commit('user/setUserToken', null)
|
|
||||||
app.router.push('/login')
|
app.router.push('/login')
|
||||||
|
|
||||||
return Promise.reject(refreshError)
|
return Promise.reject(refreshError)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export const state = () => ({
|
export const state = () => ({
|
||||||
user: null,
|
user: null,
|
||||||
|
accessToken: null,
|
||||||
settings: {
|
settings: {
|
||||||
orderBy: 'media.metadata.title',
|
orderBy: 'media.metadata.title',
|
||||||
orderDesc: false,
|
orderDesc: false,
|
||||||
|
@ -25,7 +26,7 @@ export const getters = {
|
||||||
getIsRoot: (state) => state.user && state.user.type === 'root',
|
getIsRoot: (state) => state.user && state.user.type === 'root',
|
||||||
getIsAdminOrUp: (state) => state.user && (state.user.type === 'admin' || state.user.type === 'root'),
|
getIsAdminOrUp: (state) => state.user && (state.user.type === 'admin' || state.user.type === 'root'),
|
||||||
getToken: (state) => {
|
getToken: (state) => {
|
||||||
return state.user?.accessToken || null
|
return state.accessToken || null
|
||||||
},
|
},
|
||||||
getUserMediaProgress:
|
getUserMediaProgress:
|
||||||
(state) =>
|
(state) =>
|
||||||
|
@ -145,6 +146,27 @@ export const actions = {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load userSettings from local storage', error)
|
console.error('Failed to load userSettings from local storage', error)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
refreshToken({ state, commit }) {
|
||||||
|
return this.$axios
|
||||||
|
.$post('/auth/refresh')
|
||||||
|
.then(async (response) => {
|
||||||
|
const newAccessToken = response.user.accessToken
|
||||||
|
commit('setUser', response.user)
|
||||||
|
commit('setAccessToken', newAccessToken)
|
||||||
|
// Emit event used to re-authenticate socket in default.vue since $root is not available here
|
||||||
|
if (this.$eventBus) {
|
||||||
|
this.$eventBus.$emit('token_refreshed', newAccessToken)
|
||||||
|
}
|
||||||
|
return newAccessToken
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to refresh token', error)
|
||||||
|
commit('setUser', null)
|
||||||
|
commit('setAccessToken', null)
|
||||||
|
// Calling function handles redirect to login
|
||||||
|
throw error
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,14 +174,12 @@ export const mutations = {
|
||||||
setUser(state, user) {
|
setUser(state, user) {
|
||||||
state.user = user
|
state.user = user
|
||||||
},
|
},
|
||||||
setUserToken(state, token) {
|
setAccessToken(state, token) {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
localStorage.removeItem('token')
|
localStorage.removeItem('token')
|
||||||
if (state.user) {
|
state.accessToken = null
|
||||||
state.user.accessToken = null
|
} else {
|
||||||
}
|
state.accessToken = token
|
||||||
} else if (state.user) {
|
|
||||||
state.user.accessToken = token
|
|
||||||
localStorage.setItem('token', token)
|
localStorage.setItem('token', token)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,7 +23,7 @@ class RateLimiterFactory {
|
||||||
windowMs = parseInt(process.env.RATE_LIMIT_AUTH_WINDOW)
|
windowMs = parseInt(process.env.RATE_LIMIT_AUTH_WINDOW)
|
||||||
}
|
}
|
||||||
|
|
||||||
let max = 20 // 20 attempts default
|
let max = 40 // 40 attempts default
|
||||||
if (parseInt(process.env.RATE_LIMIT_AUTH_MAX) > 0) {
|
if (parseInt(process.env.RATE_LIMIT_AUTH_MAX) > 0) {
|
||||||
max = parseInt(process.env.RATE_LIMIT_AUTH_MAX)
|
max = parseInt(process.env.RATE_LIMIT_AUTH_MAX)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue