mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Editing accounts, change root account username, removed token expiration
This commit is contained in:
		
							parent
							
								
									e534d015be
								
							
						
					
					
						commit
						1f2afe4d92
					
				| @ -1,5 +1,5 @@ | |||||||
| <template> | <template> | ||||||
|   <modals-modal v-model="show" :width="800" :height="500" :processing="processing"> |   <modals-modal v-model="show" :width="800" :height="'unset'" :processing="processing"> | ||||||
|     <template #outer> |     <template #outer> | ||||||
|       <div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden"> |       <div class="absolute top-0 left-0 p-5 w-2/3 overflow-hidden"> | ||||||
|         <p class="font-book text-3xl text-white truncate">{{ title }}</p> |         <p class="font-book text-3xl text-white truncate">{{ title }}</p> | ||||||
| @ -8,18 +8,22 @@ | |||||||
|     <form @submit.prevent="submitForm"> |     <form @submit.prevent="submitForm"> | ||||||
|       <div class="px-4 w-full text-sm py-6 rounded-lg bg-bg shadow-lg border border-black-300"> |       <div class="px-4 w-full text-sm py-6 rounded-lg bg-bg shadow-lg border border-black-300"> | ||||||
|         <div class="w-full p-8"> |         <div class="w-full p-8"> | ||||||
|           <div class="flex py-2"> |           <div class="flex py-2 -mx-2"> | ||||||
|             <ui-text-input-with-label v-model="newUser.username" label="Username" class="mx-2" /> |             <div class="w-1/2 px-2"> | ||||||
|             <ui-text-input-with-label v-model="newUser.password" label="Password" type="password" class="mx-2" /> |               <ui-text-input-with-label v-model="newUser.username" label="Username" class="mx-2" /> | ||||||
|  |             </div> | ||||||
|  |             <div class="w-1/2 px-2"> | ||||||
|  |               <ui-text-input-with-label v-if="!isEditingRoot" v-model="newUser.password" :label="isNew ? 'Password' : 'Change Password'" type="password" class="mx-2" /> | ||||||
|  |             </div> | ||||||
|           </div> |           </div> | ||||||
|           <div class="flex py-2"> |           <div class="flex py-2"> | ||||||
|             <div class="px-2"> |             <div class="px-2"> | ||||||
|               <ui-input-dropdown v-model="newUser.type" label="Account Type" :editable="false" :items="accountTypes" /> |               <ui-input-dropdown v-model="newUser.type" label="Account Type" :disabled="isEditingRoot" :editable="false" :items="accountTypes" /> | ||||||
|             </div> |             </div> | ||||||
|             <div class="flex-grow" /> |             <div class="flex-grow" /> | ||||||
|             <div class="flex items-center pt-4 px-2"> |             <div v-show="!isEditingRoot" class="flex items-center pt-4 px-2"> | ||||||
|               <p class="px-3 font-semibold">Is Active</p> |               <p class="px-3 font-semibold" :class="isEditingRoot ? 'text-gray-300' : ''">Is Active</p> | ||||||
|               <ui-toggle-switch v-model="newUser.isActive" /> |               <ui-toggle-switch v-model="newUser.isActive" :disabled="isEditingRoot" /> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|           <div class="flex pt-4"> |           <div class="flex pt-4"> | ||||||
| @ -68,7 +72,10 @@ export default { | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     title() { |     title() { | ||||||
|       return this.isNew ? 'Add New Account' : `Update "${(this.account || {}).username}" Account` |       return this.isNew ? 'Add New Account' : `Update Account: ${(this.account || {}).username}` | ||||||
|  |     }, | ||||||
|  |     isEditingRoot() { | ||||||
|  |       return this.account && this.account.type === 'root' | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
| @ -77,6 +84,39 @@ export default { | |||||||
|         this.$toast.error('Enter a username') |         this.$toast.error('Enter a username') | ||||||
|         return |         return | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       if (this.isNew) { | ||||||
|  |         this.submitCreateAccount() | ||||||
|  |       } else { | ||||||
|  |         this.submitUpdateAccount() | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     submitUpdateAccount() { | ||||||
|  |       var account = { ...this.newUser } | ||||||
|  |       if (!account.password || account.type === 'root') { | ||||||
|  |         delete account.password | ||||||
|  |       } | ||||||
|  |       if (account.type === 'root' && !account.isActive) return | ||||||
|  | 
 | ||||||
|  |       this.processing = true | ||||||
|  |       this.$axios | ||||||
|  |         .$patch(`/api/user/${this.account.id}`, account) | ||||||
|  |         .then((data) => { | ||||||
|  |           this.processing = false | ||||||
|  |           if (data.error) { | ||||||
|  |             this.$toast.error(`Failed to update account: ${data.error}`) | ||||||
|  |           } else { | ||||||
|  |             this.$toast.success('Account updated') | ||||||
|  |             this.show = false | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |         .catch((error) => { | ||||||
|  |           console.error('Failed to update account', error) | ||||||
|  |           this.processing = false | ||||||
|  |           this.$toast.error('Failed to update account') | ||||||
|  |         }) | ||||||
|  |     }, | ||||||
|  |     submitCreateAccount() { | ||||||
|       if (!this.newUser.password) { |       if (!this.newUser.password) { | ||||||
|         this.$toast.error('Must have a password, only root user can have an empty password') |         this.$toast.error('Must have a password, only root user can have an empty password') | ||||||
|         return |         return | ||||||
| @ -84,25 +124,22 @@ export default { | |||||||
| 
 | 
 | ||||||
|       var account = { ...this.newUser } |       var account = { ...this.newUser } | ||||||
|       this.processing = true |       this.processing = true | ||||||
|       if (this.isNew) { |       this.$axios | ||||||
|         this.$axios |         .$post('/api/user', account) | ||||||
|           .$post('/api/user', account) |         .then((data) => { | ||||||
|           .then((data) => { |           this.processing = false | ||||||
|             this.processing = false |           if (data.error) { | ||||||
|             if (data.error) { |             this.$toast.error(`Failed to create account: ${data.error}`) | ||||||
|               this.$toast.error(`Failed to create account: ${data.error}`) |           } else { | ||||||
|             } else { |  | ||||||
|               console.log('New Account:', data.user) |  | ||||||
|               this.$toast.success('New account created') |  | ||||||
|               this.show = false |  | ||||||
|             } |  | ||||||
|           }) |  | ||||||
|           .catch((error) => { |  | ||||||
|             console.error('Failed to create account', error) |  | ||||||
|             this.processing = false |  | ||||||
|             this.$toast.success('New account created') |             this.$toast.success('New account created') | ||||||
|           }) |             this.show = false | ||||||
|       } |           } | ||||||
|  |         }) | ||||||
|  |         .catch((error) => { | ||||||
|  |           console.error('Failed to create account', error) | ||||||
|  |           this.processing = false | ||||||
|  |           this.$toast.error('Failed to create account') | ||||||
|  |         }) | ||||||
|     }, |     }, | ||||||
|     toggleActive() { |     toggleActive() { | ||||||
|       this.newUser.isActive = !this.newUser.isActive |       this.newUser.isActive = !this.newUser.isActive | ||||||
|  | |||||||
| @ -1,10 +1,10 @@ | |||||||
| <template> | <template> | ||||||
|   <div class="w-full"> |   <div class="w-full" :class="disabled ? 'cursor-not-allowed' : ''"> | ||||||
|     <p class="px-1 text-sm font-semibold">{{ label }}</p> |     <p class="px-1 text-sm font-semibold" :class="disabled ? 'text-gray-300' : ''">{{ label }}</p> | ||||||
|     <div ref="wrapper" class="relative"> |     <div ref="wrapper" class="relative"> | ||||||
|       <form @submit.prevent="submitForm"> |       <form @submit.prevent="submitForm"> | ||||||
|         <div ref="inputWrapper" class="flex-wrap relative w-full shadow-sm flex items-center bg-primary border border-gray-600 rounded px-2 py-2"> |         <div ref="inputWrapper" class="flex-wrap relative w-full shadow-sm flex items-center border border-gray-600 rounded px-2 py-2" :class="disabled ? 'bg-bg pointer-events-none text-gray-400' : 'bg-primary'"> | ||||||
|           <input ref="input" v-model="textInput" :readonly="!editable" class="h-full w-full bg-primary focus:outline-none px-1" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" /> |           <input ref="input" v-model="textInput" :disabled="disabled" :readonly="!editable" class="h-full w-full bg-transparent focus:outline-none px-1" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" /> | ||||||
|         </div> |         </div> | ||||||
|       </form> |       </form> | ||||||
| 
 | 
 | ||||||
| @ -33,6 +33,7 @@ | |||||||
| export default { | export default { | ||||||
|   props: { |   props: { | ||||||
|     value: [String, Number], |     value: [String, Number], | ||||||
|  |     disabled: Boolean, | ||||||
|     label: String, |     label: String, | ||||||
|     items: { |     items: { | ||||||
|       type: Array, |       type: Array, | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| <template> | <template> | ||||||
|   <div> |   <div> | ||||||
|     <div class="border rounded-full border-black-100 flex items-center cursor-pointer w-12 justify-end" :class="toggleColor" @click="clickToggle"> |     <div class="border rounded-full border-black-100 flex items-center cursor-pointer w-12 justify-start" :class="className" @click="clickToggle"> | ||||||
|       <span class="rounded-full border w-6 h-6 border-black-50 bg-white shadow transform transition-transform duration-100" :class="!toggleValue ? '-translate-x-6' : ''"> </span> |       <span class="rounded-full border w-6 h-6 border-black-50 shadow transform transition-transform duration-100" :class="switchClassName"></span> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| @ -17,7 +17,8 @@ export default { | |||||||
|     offColor: { |     offColor: { | ||||||
|       type: String, |       type: String, | ||||||
|       default: 'primary' |       default: 'primary' | ||||||
|     } |     }, | ||||||
|  |     disabled: Boolean | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     toggleValue: { |     toggleValue: { | ||||||
| @ -28,12 +29,18 @@ export default { | |||||||
|         this.$emit('input', val) |         this.$emit('input', val) | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     toggleColor() { |     className() { | ||||||
|  |       if (this.disabled) return 'bg-bg cursor-not-allowed' | ||||||
|       return this.toggleValue ? `bg-${this.onColor}` : `bg-${this.offColor}` |       return this.toggleValue ? `bg-${this.onColor}` : `bg-${this.offColor}` | ||||||
|  |     }, | ||||||
|  |     switchClassName() { | ||||||
|  |       var bgColor = this.disabled ? 'bg-gray-300' : 'bg-white' | ||||||
|  |       return this.toggleValue ? 'translate-x-6 ' + bgColor : bgColor | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     clickToggle() { |     clickToggle() { | ||||||
|  |       if (this.disabled) return | ||||||
|       this.toggleValue = !this.toggleValue |       this.toggleValue = !this.toggleValue | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "audiobookshelf-client", |   "name": "audiobookshelf-client", | ||||||
|   "version": "1.0.4", |   "version": "1.0.5", | ||||||
|   "description": "Audiobook manager and player", |   "description": "Audiobook manager and player", | ||||||
|   "main": "index.js", |   "main": "index.js", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|  | |||||||
| @ -27,6 +27,7 @@ | |||||||
|             </td> |             </td> | ||||||
|             <td> |             <td> | ||||||
|               <div class="w-full flex justify-center"> |               <div class="w-full flex justify-center"> | ||||||
|  |                 <span class="material-icons hover:text-gray-400 cursor-pointer text-base pr-2" @click="editUser(user)">edit</span> | ||||||
|                 <span v-show="user.type !== 'root'" class="material-icons text-base hover:text-error cursor-pointer" @click="deleteUserClick(user)">delete</span> |                 <span v-show="user.type !== 'root'" class="material-icons text-base hover:text-error cursor-pointer" @click="deleteUserClick(user)">delete</span> | ||||||
|               </div> |               </div> | ||||||
|             </td> |             </td> | ||||||
| @ -76,7 +77,7 @@ | |||||||
|     </div> |     </div> | ||||||
|     <div class="fixed bottom-0 left-0 w-10 h-10" @dblclick="setDeveloperMode"></div> |     <div class="fixed bottom-0 left-0 w-10 h-10" @dblclick="setDeveloperMode"></div> | ||||||
| 
 | 
 | ||||||
|     <modals-account-modal v-model="showAccountModal" /> |     <modals-account-modal v-model="showAccountModal" :account="selectedAccount" /> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| @ -91,6 +92,7 @@ export default { | |||||||
|     return { |     return { | ||||||
|       isResettingAudiobooks: false, |       isResettingAudiobooks: false, | ||||||
|       users: [], |       users: [], | ||||||
|  |       selectedAccount: null, | ||||||
|       showAccountModal: false, |       showAccountModal: false, | ||||||
|       isDeletingUser: false, |       isDeletingUser: false, | ||||||
|       newServerSettings: {} |       newServerSettings: {} | ||||||
| @ -145,10 +147,6 @@ export default { | |||||||
|     scanCovers() { |     scanCovers() { | ||||||
|       this.$root.socket.emit('scan_covers') |       this.$root.socket.emit('scan_covers') | ||||||
|     }, |     }, | ||||||
|     clickAddUser() { |  | ||||||
|       this.showAccountModal = true |  | ||||||
|       // this.$toast.info('Under Construction: User management coming soon.') |  | ||||||
|     }, |  | ||||||
|     loadUsers() { |     loadUsers() { | ||||||
|       this.$axios |       this.$axios | ||||||
|         .$get('/api/users') |         .$get('/api/users') | ||||||
| @ -175,6 +173,14 @@ export default { | |||||||
|           }) |           }) | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     clickAddUser() { | ||||||
|  |       this.selectedAccount = null | ||||||
|  |       this.showAccountModal = true | ||||||
|  |     }, | ||||||
|  |     editUser(user) { | ||||||
|  |       this.selectedAccount = user | ||||||
|  |       this.showAccountModal = true | ||||||
|  |     }, | ||||||
|     deleteUserClick(user) { |     deleteUserClick(user) { | ||||||
|       if (this.isDeletingUser) return |       if (this.isDeletingUser) return | ||||||
|       if (confirm(`Are you sure you want to permanently delete user "${user.username}"?`)) { |       if (confirm(`Are you sure you want to permanently delete user "${user.username}"?`)) { | ||||||
| @ -198,7 +204,7 @@ export default { | |||||||
|     }, |     }, | ||||||
|     addUpdateUser(user) { |     addUpdateUser(user) { | ||||||
|       if (!this.users) return |       if (!this.users) return | ||||||
|       var index = this.users.find((u) => u.id === user.id) |       var index = this.users.findIndex((u) => u.id === user.id) | ||||||
|       if (index >= 0) { |       if (index >= 0) { | ||||||
|         this.users.splice(index, 1, user) |         this.users.splice(index, 1, user) | ||||||
|       } else { |       } else { | ||||||
|  | |||||||
| @ -27,7 +27,7 @@ export default { | |||||||
|     return { |     return { | ||||||
|       error: null, |       error: null, | ||||||
|       processing: false, |       processing: false, | ||||||
|       username: 'root', |       username: '', | ||||||
|       password: null |       password: null | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| export default function ({ $axios, store }) { | export default function ({ $axios, store }) { | ||||||
|   $axios.onRequest(config => { |   $axios.onRequest(config => { | ||||||
|     // console.log('Making request to ' + config.url)
 |  | ||||||
|     if (config.url.startsWith('http:') || config.url.startsWith('https:')) { |     if (config.url.startsWith('http:') || config.url.startsWith('https:')) { | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
| @ -11,6 +10,7 @@ export default function ({ $axios, store }) { | |||||||
| 
 | 
 | ||||||
|     if (process.env.NODE_ENV === 'development') { |     if (process.env.NODE_ENV === 'development') { | ||||||
|       config.url = `/dev${config.url}` |       config.url = `/dev${config.url}` | ||||||
|  |       console.log('Making request to ' + config.url) | ||||||
|     } |     } | ||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "audiobookshelf", |   "name": "audiobookshelf", | ||||||
|   "version": "1.0.4", |   "version": "1.0.5", | ||||||
|   "description": "Self-hosted audiobook server for managing and playing audiobooks.", |   "description": "Self-hosted audiobook server for managing and playing audiobooks.", | ||||||
|   "main": "index.js", |   "main": "index.js", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ const User = require('./objects/User') | |||||||
| const { isObject } = require('./utils/index') | const { isObject } = require('./utils/index') | ||||||
| 
 | 
 | ||||||
| class ApiController { | class ApiController { | ||||||
|   constructor(db, scanner, auth, streamManager, rssFeeds, downloadManager, emitter) { |   constructor(db, scanner, auth, streamManager, rssFeeds, downloadManager, emitter, clientEmitter) { | ||||||
|     this.db = db |     this.db = db | ||||||
|     this.scanner = scanner |     this.scanner = scanner | ||||||
|     this.auth = auth |     this.auth = auth | ||||||
| @ -12,6 +12,7 @@ class ApiController { | |||||||
|     this.rssFeeds = rssFeeds |     this.rssFeeds = rssFeeds | ||||||
|     this.downloadManager = downloadManager |     this.downloadManager = downloadManager | ||||||
|     this.emitter = emitter |     this.emitter = emitter | ||||||
|  |     this.clientEmitter = clientEmitter | ||||||
| 
 | 
 | ||||||
|     this.router = express() |     this.router = express() | ||||||
|     this.init() |     this.init() | ||||||
| @ -34,12 +35,13 @@ class ApiController { | |||||||
|     this.router.get('/metadata/:id/:trackIndex', this.getMetadata.bind(this)) |     this.router.get('/metadata/:id/:trackIndex', this.getMetadata.bind(this)) | ||||||
|     this.router.patch('/match/:id', this.match.bind(this)) |     this.router.patch('/match/:id', this.match.bind(this)) | ||||||
| 
 | 
 | ||||||
|     this.router.get('/users', this.getUsers.bind(this)) |  | ||||||
|     this.router.post('/user', this.createUser.bind(this)) |  | ||||||
|     this.router.delete('/user/:id', this.deleteUser.bind(this)) |  | ||||||
|     this.router.delete('/user/audiobook/:id', this.resetUserAudiobookProgress.bind(this)) |     this.router.delete('/user/audiobook/:id', this.resetUserAudiobookProgress.bind(this)) | ||||||
|     this.router.patch('/user/password', this.userChangePassword.bind(this)) |     this.router.patch('/user/password', this.userChangePassword.bind(this)) | ||||||
|     this.router.patch('/user/settings', this.userUpdateSettings.bind(this)) |     this.router.patch('/user/settings', this.userUpdateSettings.bind(this)) | ||||||
|  |     this.router.get('/users', this.getUsers.bind(this)) | ||||||
|  |     this.router.post('/user', this.createUser.bind(this)) | ||||||
|  |     this.router.patch('/user/:id', this.updateUser.bind(this)) | ||||||
|  |     this.router.delete('/user/:id', this.deleteUser.bind(this)) | ||||||
| 
 | 
 | ||||||
|     this.router.patch('/serverSettings', this.updateServerSettings.bind(this)) |     this.router.patch('/serverSettings', this.updateServerSettings.bind(this)) | ||||||
| 
 | 
 | ||||||
| @ -273,7 +275,7 @@ class ApiController { | |||||||
|     var newUser = new User(account) |     var newUser = new User(account) | ||||||
|     var success = await this.db.insertUser(newUser) |     var success = await this.db.insertUser(newUser) | ||||||
|     if (success) { |     if (success) { | ||||||
|       this.emitter('user_added', newUser) |       this.clientEmitter(req.user.id, 'user_added', newUser) | ||||||
|       res.json({ |       res.json({ | ||||||
|         user: newUser.toJSONForBrowser() |         user: newUser.toJSONForBrowser() | ||||||
|       }) |       }) | ||||||
| @ -284,6 +286,36 @@ class ApiController { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   async updateUser(req, res) { | ||||||
|  |     if (req.user.type !== 'root') { | ||||||
|  |       Logger.error('User other than root attempting to update user', req.user) | ||||||
|  |       return res.sendStatus(403) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var user = this.db.users.find(u => u.id === req.params.id) | ||||||
|  |     if (!user) { | ||||||
|  |       return res.sendStatus(404) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var account = req.body | ||||||
|  |     // Updating password
 | ||||||
|  |     if (account.password) { | ||||||
|  |       account.pash = await this.auth.hashPass(account.password) | ||||||
|  |       delete account.password | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var hasUpdated = user.update(account) | ||||||
|  |     if (hasUpdated) { | ||||||
|  |       await this.db.updateEntity('user', user) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     this.clientEmitter(req.user.id, 'user_updated', user.toJSONForBrowser()) | ||||||
|  |     res.json({ | ||||||
|  |       success: true, | ||||||
|  |       user: user.toJSONForBrowser() | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   async deleteUser(req, res) { |   async deleteUser(req, res) { | ||||||
|     if (req.params.id === 'root') { |     if (req.params.id === 'root') { | ||||||
|       return res.sendStatus(500) |       return res.sendStatus(500) | ||||||
| @ -304,7 +336,7 @@ class ApiController { | |||||||
| 
 | 
 | ||||||
|     var userJson = user.toJSONForBrowser() |     var userJson = user.toJSONForBrowser() | ||||||
|     await this.db.removeEntity('user', user.id) |     await this.db.removeEntity('user', user.id) | ||||||
|     this.emitter('user_removed', userJson) |     this.clientEmitter(req.user.id, 'user_removed', userJson) | ||||||
|     res.json({ |     res.json({ | ||||||
|       success: true |       success: true | ||||||
|     }) |     }) | ||||||
|  | |||||||
| @ -68,7 +68,7 @@ class Auth { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   generateAccessToken(payload) { |   generateAccessToken(payload) { | ||||||
|     return jwt.sign(payload, process.env.TOKEN_SECRET, { expiresIn: '1800s' }); |     return jwt.sign(payload, process.env.TOKEN_SECRET); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   verifyToken(token) { |   verifyToken(token) { | ||||||
|  | |||||||
| @ -94,7 +94,7 @@ class Db { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   insertSettings(settings) { |   insertSettings(settings) { | ||||||
|     return this.settingsDb.insert(settings).then((results) => { |     return this.settingsDb.insert([settings]).then((results) => { | ||||||
|       Logger.debug(`[DB] Inserted ${results.inserted} settings`) |       Logger.debug(`[DB] Inserted ${results.inserted} settings`) | ||||||
|       this.settings = this.settings.concat(settings) |       this.settings = this.settings.concat(settings) | ||||||
|     }).catch((error) => { |     }).catch((error) => { | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ class Server { | |||||||
|     this.streamManager = new StreamManager(this.db, this.MetadataPath) |     this.streamManager = new StreamManager(this.db, this.MetadataPath) | ||||||
|     this.rssFeeds = new RssFeeds(this.Port, this.db) |     this.rssFeeds = new RssFeeds(this.Port, this.db) | ||||||
|     this.downloadManager = new DownloadManager(this.db, this.MetadataPath, this.emitter.bind(this)) |     this.downloadManager = new DownloadManager(this.db, this.MetadataPath, this.emitter.bind(this)) | ||||||
|     this.apiController = new ApiController(this.db, this.scanner, this.auth, this.streamManager, this.rssFeeds, this.downloadManager, this.emitter.bind(this)) |     this.apiController = new ApiController(this.db, this.scanner, this.auth, this.streamManager, this.rssFeeds, this.downloadManager, this.emitter.bind(this), this.clientEmitter.bind(this)) | ||||||
|     this.hlsController = new HlsController(this.db, this.scanner, this.auth, this.streamManager, this.emitter.bind(this), this.MetadataPath) |     this.hlsController = new HlsController(this.db, this.scanner, this.auth, this.streamManager, this.emitter.bind(this), this.MetadataPath) | ||||||
| 
 | 
 | ||||||
|     this.server = null |     this.server = null | ||||||
| @ -54,11 +54,27 @@ class Server { | |||||||
|     return this.db.serverSettings |     return this.db.serverSettings | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   getClientsForUser(userId) { | ||||||
|  |     return Object.values(this.clients).filter(c => c.user && c.user.id === userId) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   emitter(ev, data) { |   emitter(ev, data) { | ||||||
|     // Logger.debug('EMITTER', ev)
 |     // Logger.debug('EMITTER', ev)
 | ||||||
|     this.io.emit(ev, data) |     this.io.emit(ev, data) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   clientEmitter(userId, ev, data) { | ||||||
|  |     var clients = this.getClientsForUser(userId) | ||||||
|  |     if (!clients.length) { | ||||||
|  |       return Logger.error(`[Server] clientEmitter - no clients found for user ${userId}`) | ||||||
|  |     } | ||||||
|  |     clients.forEach((client) => { | ||||||
|  |       if (client.socket) { | ||||||
|  |         client.socket.emit(ev, data) | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   async fileAddedUpdated({ path, fullPath }) { } |   async fileAddedUpdated({ path, fullPath }) { } | ||||||
|   async fileRemoved({ path, fullPath }) { } |   async fileRemoved({ path, fullPath }) { } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -68,6 +68,22 @@ class User { | |||||||
|     this.settings = user.settings || this.getDefaultUserSettings() |     this.settings = user.settings || this.getDefaultUserSettings() | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   update(payload) { | ||||||
|  |     var hasUpdates = false | ||||||
|  |     const keysToCheck = ['pash', 'type', 'username', 'isActive'] | ||||||
|  |     keysToCheck.forEach((key) => { | ||||||
|  |       if (payload[key] !== undefined) { | ||||||
|  |         if (key === 'isActive' || payload[key]) { // pash, type, username must evaluate to true (cannot be null or empty)
 | ||||||
|  |           if (payload[key] !== this[key]) { | ||||||
|  |             hasUpdates = true | ||||||
|  |             this[key] = payload[key] | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |     return hasUpdates | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   updateAudiobookProgress(stream) { |   updateAudiobookProgress(stream) { | ||||||
|     if (!this.audiobooks) this.audiobooks = {} |     if (!this.audiobooks) this.audiobooks = {} | ||||||
|     if (!this.audiobooks[stream.audiobookId]) { |     if (!this.audiobooks[stream.audiobookId]) { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user