2021-08-18 00:01:11 +02:00
< template >
2021-08-26 16:47:51 +02:00
< div id = "page-wrapper" class = "page p-6" : class = "streamAudiobook ? 'streaming' : ''" >
2021-08-18 00:01:11 +02:00
< div class = "w-full max-w-4xl mx-auto" >
2021-08-22 17:46:04 +02:00
< div class = "flex items-center mb-2" >
< h1 class = "text-2xl" > Users < / h1 >
< div class = "mx-2 w-7 h-7 flex items-center justify-center rounded-full cursor-pointer hover:bg-white hover:bg-opacity-10 text-center" @click ="clickAddUser" >
< span class = "material-icons" style = "font-size: 1.4rem" > add < / span >
< / div >
<!-- < ui -btn small :padding-x ="4" class = "h-8" > Create User < / u i - b t n > - - >
< / div >
2021-08-18 00:01:11 +02:00
< div class = "h-0.5 bg-primary bg-opacity-50 w-full" / >
2021-08-22 17:46:04 +02:00
< div class = "p-4 text-center" >
< table id = "accounts" class = "mb-8" >
< tr >
< th > Username < / th >
< th > Account Type < / th >
< th style = "width: 200px" > Created At < / th >
2021-08-27 14:01:47 +02:00
< th style = "width: 100px" > < / th >
2021-08-22 17:46:04 +02:00
< / tr >
< tr v-for ="user in users" :key="user.id" >
2021-08-27 14:01:47 +02:00
< td >
{ { user . username } } < span class = "text-xs text-gray-400 italic pl-4" > ( { { user . id } } ) < / span >
< / td >
2021-08-22 17:46:04 +02:00
< td > { { user . type } } < / td >
< td class = "text-sm font-mono" >
{ { new Date ( user . createdAt ) . toISOString ( ) } }
< / td >
2021-08-27 14:01:47 +02:00
< td >
< div class = "w-full flex justify-center" >
< span v-show ="user.type !== 'root'" class="material-icons text-base hover:text-error cursor-pointer" @click="deleteUserClick(user)" > delete < / span >
< / div >
< / td >
2021-08-22 17:46:04 +02:00
< / tr >
< / table >
2021-08-18 00:01:11 +02:00
< / div >
< div class = "h-0.5 bg-primary bg-opacity-50 w-full" / >
2021-08-25 03:24:40 +02:00
< div class = "py-4 mb-8" >
2021-09-05 02:58:39 +02:00
< p class = "text-2xl" > Scanner < / p >
2021-08-25 03:24:40 +02:00
< div class = "flex items-start py-2" >
2021-09-05 02:58:39 +02:00
< div class = "py-2" >
< div class = "flex items-center" >
< ui -toggle -switch v -model = " newServerSettings.scannerParseSubtitle " @input ="updateScannerParseSubtitle" / >
< ui -tooltip :text ="parseSubtitleTooltip" >
< p class = "pl-4 text-lg" > Parse Subtitles < span class = "material-icons icon-text" > info _outlined < / span > < / p >
< / u i - t o o l t i p >
< / div >
< / div >
2021-08-25 03:24:40 +02:00
< div class = "flex-grow" / >
< div class = "w-40 flex flex-col" >
< ui -btn color = "success" class = "mb-4" :loading ="isScanning" :disabled ="isScanningCovers" @click ="scan" > Scan < / ui -btn >
< ui -btn color = "primary" small :padding-x ="2" :loading ="isScanningCovers" :disabled ="isScanning" @click ="scanCovers" > Scan for Covers < / ui -btn >
< / div >
< / div >
2021-08-18 00:01:11 +02:00
< / div >
2021-08-21 23:23:35 +02:00
< div class = "h-0.5 bg-primary bg-opacity-50 w-full" / >
< div class = "flex items-center py-4" >
2021-08-22 17:46:04 +02:00
< ui -btn color = "bg" small :padding-x ="4" :loading ="isResettingAudiobooks" @click ="resetAudiobooks" > Reset All Audiobooks < / ui -btn >
2021-08-21 23:23:35 +02:00
< / div >
2021-08-18 00:51:07 +02:00
< div class = "h-0.5 bg-primary bg-opacity-50 w-full" / >
2021-08-21 23:23:35 +02:00
2021-08-18 13:50:24 +02:00
< div class = "flex items-center py-4" >
2021-08-21 20:02:24 +02:00
< p class = "font-mono" > v { { $config . version } } < / p >
2021-08-21 01:29:10 +02:00
< div class = "flex-grow" / >
< p class = "pr-2 text-sm font-book text-yellow-400" > Report bugs , request features , provide feedback , and contribute on < a class = "underline" href = "https://github.com/advplyr/audiobookshelf" target = "_blank" > github < / a > . < / p >
< a href = "https://github.com/advplyr/audiobookshelf" target = "_blank" class = "text-white hover:text-gray-200 hover:scale-150 hover:rotate-6 transform duration-500" >
< svg xmlns = "http://www.w3.org/2000/svg" fill = "currentColor" width = "24" height = "24" viewBox = "0 0 24 24" >
< path
d = "M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
/ >
< / svg >
< / a >
2021-08-18 13:50:24 +02:00
< / div >
2021-08-18 00:01:11 +02:00
< / div >
2021-08-23 21:08:54 +02:00
< div class = "fixed bottom-0 left-0 w-10 h-10" @dblclick ="setDeveloperMode" > < / div >
2021-08-27 14:01:47 +02:00
< modals -account -modal v -model = " showAccountModal " / >
2021-08-18 00:01:11 +02:00
< / div >
< / template >
< script >
export default {
2021-08-27 14:01:47 +02:00
asyncData ( { store , redirect } ) {
if ( ! store . getters [ 'user/getIsRoot' ] ) {
redirect ( '/?error=unauthorized' )
}
} ,
2021-08-18 00:01:11 +02:00
data ( ) {
2021-08-21 23:23:35 +02:00
return {
2021-08-22 17:46:04 +02:00
isResettingAudiobooks : false ,
2021-08-27 14:01:47 +02:00
users : [ ] ,
showAccountModal : false ,
2021-09-05 02:58:39 +02:00
isDeletingUser : false ,
newServerSettings : { }
}
} ,
watch : {
serverSettings ( newVal , oldVal ) {
if ( newVal && ! oldVal ) {
this . newServerSettings = { ... this . serverSettings }
}
2021-08-21 23:23:35 +02:00
}
2021-08-18 00:01:11 +02:00
} ,
2021-08-18 13:50:24 +02:00
computed : {
2021-09-05 02:58:39 +02:00
parseSubtitleTooltip ( ) {
return 'Extract subtitles from audiobook directory names.<br>Subtitle must be seperated by " - "<br>i.e. "Book Title - A Subtitle Here" has the subtitle "A Subtitle Here"'
} ,
serverSettings ( ) {
return this . $store . state . serverSettings
} ,
2021-08-18 13:50:24 +02:00
streamAudiobook ( ) {
return this . $store . state . streamAudiobook
2021-08-25 03:24:40 +02:00
} ,
isScanning ( ) {
return this . $store . state . isScanning
} ,
isScanningCovers ( ) {
return this . $store . state . isScanningCovers
2021-08-18 13:50:24 +02:00
}
} ,
2021-08-18 00:01:11 +02:00
methods : {
2021-09-05 02:58:39 +02:00
updateScannerParseSubtitle ( val ) {
var payload = {
scannerParseSubtitle : val
}
this . $store
. dispatch ( 'updateServerSettings' , payload )
. then ( ( success ) => {
console . log ( 'Updated Server Settings' , success )
} )
. catch ( ( error ) => {
console . error ( 'Failed to update server settings' , error )
} )
} ,
2021-08-23 21:08:54 +02:00
setDeveloperMode ( ) {
var value = ! this . $store . state . developerMode
this . $store . commit ( 'setDeveloperMode' , value )
this . $toast . info ( ` Developer Mode ${ value ? 'Enabled' : 'Disabled' } ` )
} ,
2021-08-18 00:01:11 +02:00
scan ( ) {
this . $root . socket . emit ( 'scan' )
2021-08-21 23:23:35 +02:00
} ,
2021-08-25 03:24:40 +02:00
scanCovers ( ) {
this . $root . socket . emit ( 'scan_covers' )
} ,
2021-08-22 17:46:04 +02:00
clickAddUser ( ) {
2021-08-27 14:01:47 +02:00
this . showAccountModal = true
// this.$toast.info('Under Construction: User management coming soon.')
2021-08-22 17:46:04 +02:00
} ,
loadUsers ( ) {
this . $axios
. $get ( '/api/users' )
. then ( ( users ) => {
this . users = users
} )
. catch ( ( error ) => {
console . error ( 'Failed' , error )
} )
} ,
2021-08-21 23:23:35 +02:00
resetAudiobooks ( ) {
if ( confirm ( 'WARNING! This action will remove all audiobooks from the database including any updates or matches you have made. This does not do anything to your actual files. Shall we continue?' ) ) {
this . isResettingAudiobooks = true
this . $axios
. $delete ( '/api/audiobooks' )
. then ( ( ) => {
this . isResettingAudiobooks = false
this . $toast . success ( 'Successfully reset audiobooks' )
} )
. catch ( ( error ) => {
console . error ( 'failed to reset audiobooks' , error )
this . isResettingAudiobooks = false
this . $toast . error ( 'Failed to reset audiobooks - stop docker and manually remove appdata' )
} )
}
2021-08-27 14:01:47 +02:00
} ,
deleteUserClick ( user ) {
if ( this . isDeletingUser ) return
if ( confirm ( ` Are you sure you want to permanently delete user " ${ user . username } "? ` ) ) {
this . isDeletingUser = true
this . $axios
. $delete ( ` /api/user/ ${ user . id } ` )
. then ( ( data ) => {
this . isDeletingUser = false
if ( data . error ) {
this . $toast . error ( data . error )
} else {
this . $toast . success ( 'User deleted' )
}
} )
. catch ( ( error ) => {
console . error ( 'Failed to delete user' , error )
this . $toast . error ( 'Failed to delete user' )
this . isDeletingUser = false
} )
}
} ,
addUpdateUser ( user ) {
if ( ! this . users ) return
var index = this . users . find ( ( u ) => u . id === user . id )
if ( index >= 0 ) {
this . users . splice ( index , 1 , user )
} else {
this . users . push ( user )
}
} ,
userRemoved ( user ) {
this . users = this . users . filter ( ( u ) => u . id !== user . id )
} ,
init ( attempts = 0 ) {
if ( ! this . $root . socket ) {
if ( attempts > 10 ) {
return console . error ( 'Failed to setup socket listeners' )
}
setTimeout ( ( ) => {
this . init ( ++ attempts )
} , 250 )
return
}
this . $root . socket . on ( 'user_added' , this . addUpdateUser )
this . $root . socket . on ( 'user_updated' , this . addUpdateUser )
this . $root . socket . on ( 'user_removed' , this . userRemoved )
2021-09-05 02:58:39 +02:00
this . newServerSettings = this . serverSettings ? { ... this . serverSettings } : { }
2021-08-18 00:01:11 +02:00
}
} ,
2021-08-22 17:46:04 +02:00
mounted ( ) {
this . loadUsers ( )
2021-08-27 14:01:47 +02:00
this . init ( )
} ,
beforeDestroy ( ) {
if ( this . $root . socket ) {
this . $root . socket . off ( 'user_added' , this . newUserAdded )
this . $root . socket . off ( 'user_updated' , this . userUpdated )
}
2021-08-22 17:46:04 +02:00
}
}
< / script >
< style >
# accounts {
table - layout : fixed ;
border - collapse : collapse ;
width : 100 % ;
}
# accounts td ,
# accounts th {
border : 1 px solid # 2 e2e2e ;
padding : 8 px 8 px ;
text - align : left ;
}
# accounts tr : nth - child ( even ) {
background - color : # 3 a3a3a ;
}
# accounts tr : hover {
background - color : # 444 ;
}
# accounts th {
font - size : 0.8 rem ;
font - weight : 600 ;
padding - top : 5 px ;
padding - bottom : 5 px ;
background - color : # 333 ;
2021-08-18 00:01:11 +02:00
}
2021-08-22 17:46:04 +02:00
< / style >