mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-15 05:41:34 +00:00
we still want those tokens, we will use them in the mobile app. but we don't want them client side, otherwise proxy users remain logged in. we centralize the logic around storing the token, and turn `setUser` into an action vs a mutation
227 lines
7.8 KiB
Vue
227 lines
7.8 KiB
Vue
<template>
|
|
<div class="w-full h-screen bg-bg">
|
|
<div class="w-full flex h-full items-center justify-center">
|
|
<div v-if="criticalError" class="w-full max-w-md rounded border border-error border-opacity-25 bg-error bg-opacity-10 p-4">
|
|
<p class="text-center text-lg font-semibold">{{ $strings.MessageServerCouldNotBeReached }}</p>
|
|
</div>
|
|
<div v-else-if="showInitScreen" class="w-full max-w-lg px-4 md:px-8 pb-8 pt-4">
|
|
<p class="text-3xl text-white text-center mb-4">Initial Server Setup</p>
|
|
<div class="w-full h-px bg-white bg-opacity-10 my-4" />
|
|
|
|
<form @submit.prevent="submitServerSetup">
|
|
<p class="text-lg font-semibold mb-2 pl-1 text-center">Create Root User</p>
|
|
<ui-text-input-with-label v-model="newRoot.username" label="Username" :disabled="processing" class="w-full mb-3 text-sm" />
|
|
<ui-text-input-with-label v-model="newRoot.password" label="Password" type="password" :disabled="processing" class="w-full mb-3 text-sm" />
|
|
<ui-text-input-with-label v-model="confirmPassword" label="Confirm Password" type="password" :disabled="processing" class="w-full mb-3 text-sm" />
|
|
|
|
<p class="text-lg font-semibold mt-6 mb-2 pl-1 text-center">Directory Paths</p>
|
|
<ui-text-input-with-label v-model="ConfigPath" label="Config Path" disabled class="w-full mb-3 text-sm" />
|
|
<ui-text-input-with-label v-model="MetadataPath" label="Metadata Path" disabled class="w-full mb-3 text-sm" />
|
|
|
|
<div class="w-full flex justify-end py-3">
|
|
<ui-btn type="submit" :disabled="processing" color="primary" class="leading-none">{{ processing ? 'Initializing...' : $strings.ButtonSubmit }}</ui-btn>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div v-else-if="isInit" class="w-full max-w-md px-8 pb-8 pt-4 -mt-40">
|
|
<p class="text-3xl text-white text-center mb-4">{{ $strings.HeaderLogin }}</p>
|
|
<div class="w-full h-px bg-white bg-opacity-10 my-4" />
|
|
<p v-if="error" class="text-error text-center py-2">{{ error }}</p>
|
|
<form @submit.prevent="submitForm">
|
|
<label class="text-xs text-gray-300 uppercase">{{ $strings.LabelUsername }}</label>
|
|
<ui-text-input v-model="username" :disabled="processing" class="mb-3 w-full" />
|
|
|
|
<label class="text-xs text-gray-300 uppercase">{{ $strings.LabelPassword }}</label>
|
|
<ui-text-input v-model="password" type="password" :disabled="processing" class="w-full mb-3" />
|
|
<div class="w-full flex justify-end py-3">
|
|
<ui-btn type="submit" :disabled="processing" color="primary" class="leading-none">{{ processing ? 'Checking...' : $strings.ButtonSubmit }}</ui-btn>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
layout: 'blank',
|
|
data() {
|
|
return {
|
|
error: null,
|
|
criticalError: null,
|
|
processing: false,
|
|
username: '',
|
|
password: null,
|
|
showInitScreen: false,
|
|
isInit: false,
|
|
newRoot: {
|
|
username: 'root',
|
|
password: ''
|
|
},
|
|
confirmPassword: '',
|
|
ConfigPath: '',
|
|
MetadataPath: ''
|
|
}
|
|
},
|
|
watch: {
|
|
user(newVal) {
|
|
if (newVal) {
|
|
if (!this.$store.state.libraries.currentLibraryId) {
|
|
// No libraries available to this user
|
|
if (this.$store.getters['user/getIsRoot']) {
|
|
// If root user go to config/libraries
|
|
this.$router.replace('/config/libraries')
|
|
} else {
|
|
this.$router.replace('/oops?message=No libraries available')
|
|
}
|
|
} else {
|
|
if (this.$route.query.redirect) {
|
|
const isAdminUser = this.$store.getters['user/getIsAdminOrUp']
|
|
const redirect = this.$route.query.redirect
|
|
// If not admin user then do not redirect to config pages other than your stats
|
|
if (isAdminUser || !redirect.startsWith('/config/') || redirect === '/config/stats') {
|
|
this.$router.replace(redirect)
|
|
return
|
|
}
|
|
}
|
|
|
|
this.$router.replace(`/library/${this.$store.state.libraries.currentLibraryId}`)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
user() {
|
|
return this.$store.state.user.user
|
|
}
|
|
},
|
|
methods: {
|
|
async submitServerSetup() {
|
|
if (!this.newRoot.username || !this.newRoot.username.trim()) {
|
|
this.$toast.error('Must enter a root username')
|
|
return
|
|
}
|
|
if (this.newRoot.password !== this.confirmPassword) {
|
|
this.$toast.error('Password mismatch')
|
|
return
|
|
}
|
|
if (!this.newRoot.password) {
|
|
if (!confirm('Are you sure you want to create the root user with no password?')) {
|
|
return
|
|
}
|
|
}
|
|
this.processing = true
|
|
|
|
const payload = {
|
|
newRoot: { ...this.newRoot }
|
|
}
|
|
const success = await this.$axios
|
|
.$post('/init', payload)
|
|
.then(() => true)
|
|
.catch((error) => {
|
|
console.error('Failed', error.response)
|
|
const errorMsg = error.response ? error.response.data || 'Unknown Error' : 'Unknown Error'
|
|
this.$toast.error(errorMsg)
|
|
return false
|
|
})
|
|
|
|
if (!success) {
|
|
this.processing = false
|
|
return
|
|
}
|
|
|
|
location.reload()
|
|
},
|
|
setUser({ user, userDefaultLibraryId, serverSettings, Source, ereaderDevices }) {
|
|
this.$store.commit('setServerSettings', serverSettings)
|
|
this.$store.commit('setSource', Source)
|
|
this.$store.commit('libraries/setEReaderDevices', ereaderDevices)
|
|
this.$setServerLanguageCode(serverSettings.language)
|
|
|
|
if (serverSettings.chromecastEnabled) {
|
|
console.log('Chromecast enabled import script')
|
|
require('@/plugins/chromecast.js').default(this)
|
|
}
|
|
|
|
this.$store.commit('libraries/setCurrentLibrary', userDefaultLibraryId)
|
|
|
|
this.$store.dispatch('user/setUser', user)
|
|
this.$store.dispatch('user/loadUserSettings')
|
|
},
|
|
async submitForm() {
|
|
this.error = null
|
|
this.processing = true
|
|
|
|
const payload = {
|
|
username: this.username,
|
|
password: this.password || ''
|
|
}
|
|
const authRes = await this.$axios.$post('/login', payload).catch((error) => {
|
|
console.error('Failed', error.response)
|
|
if (error.response) this.error = error.response.data
|
|
else this.error = 'Unknown Error'
|
|
return false
|
|
})
|
|
if (authRes?.error) {
|
|
this.error = authRes.error
|
|
} else if (authRes) {
|
|
this.setUser(authRes)
|
|
}
|
|
this.processing = false
|
|
},
|
|
checkAuth() {
|
|
const headers = {}
|
|
|
|
const token = localStorage.getItem('token')
|
|
if (token) {
|
|
headers.Authorization = `Bearer ${token}`
|
|
}
|
|
|
|
this.processing = true
|
|
|
|
return this.$axios
|
|
.$post('/api/authorize', null, { headers })
|
|
.then((res) => {
|
|
this.setUser(res)
|
|
this.processing = false
|
|
return true
|
|
})
|
|
.catch((error) => {
|
|
console.error('Authorize error', error)
|
|
this.processing = false
|
|
return false
|
|
})
|
|
},
|
|
checkStatus() {
|
|
this.processing = true
|
|
this.$axios
|
|
.$get('/status')
|
|
.then((res) => {
|
|
this.processing = false
|
|
this.isInit = res.isInit
|
|
this.showInitScreen = !res.isInit
|
|
this.$setServerLanguageCode(res.language)
|
|
if (this.showInitScreen) {
|
|
this.ConfigPath = res.ConfigPath || ''
|
|
this.MetadataPath = res.MetadataPath || ''
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.error('Status check failed', error)
|
|
this.processing = false
|
|
this.criticalError = 'Status check failed'
|
|
})
|
|
}
|
|
},
|
|
async mounted() {
|
|
var userfound = await this.checkAuth()
|
|
|
|
// if valid user no need to check status
|
|
if (userfound) {
|
|
return
|
|
} else {
|
|
this.checkStatus()
|
|
}
|
|
}
|
|
}
|
|
</script>
|