From 35808abdf63f2db6498ed59234e639e2b273d6de Mon Sep 17 00:00:00 2001 From: advplyr Date: Fri, 27 Aug 2021 07:01:47 -0500 Subject: [PATCH] Adding and deleting users --- client/components/app/Appbar.vue | 5 +- client/components/modals/AccountModal.vue | 131 ++++++++++++++++++++++ client/components/modals/Modal.vue | 2 +- client/components/ui/InputDropdown.vue | 6 +- client/components/ui/ToggleSwitch.vue | 34 ++++++ client/layouts/default.vue | 5 + client/package.json | 2 +- client/pages/account.vue | 3 + client/pages/config/index.vue | 78 ++++++++++++- client/store/user.js | 1 + package.json | 2 +- server/ApiController.js | 52 +++++++++ server/Auth.js | 3 +- server/Db.js | 3 + server/User.js | 8 +- 15 files changed, 323 insertions(+), 12 deletions(-) create mode 100644 client/components/modals/AccountModal.vue create mode 100644 client/components/ui/ToggleSwitch.vue diff --git a/client/components/app/Appbar.vue b/client/components/app/Appbar.vue index 1e06bc26..f54fbd59 100644 --- a/client/components/app/Appbar.vue +++ b/client/components/app/Appbar.vue @@ -11,7 +11,7 @@
- + settings @@ -56,6 +56,9 @@ export default { user() { return this.$store.state.user.user }, + isRootUser() { + return this.$store.getters['user/getIsRoot'] + }, username() { return this.user ? this.user.username : 'err' }, diff --git a/client/components/modals/AccountModal.vue b/client/components/modals/AccountModal.vue new file mode 100644 index 00000000..d6c1f3c6 --- /dev/null +++ b/client/components/modals/AccountModal.vue @@ -0,0 +1,131 @@ + + + diff --git a/client/components/modals/Modal.vue b/client/components/modals/Modal.vue index 7f3be701..411eb625 100644 --- a/client/components/modals/Modal.vue +++ b/client/components/modals/Modal.vue @@ -1,5 +1,5 @@ diff --git a/client/store/user.js b/client/store/user.js index 18d42a72..b155b9e0 100644 --- a/client/store/user.js +++ b/client/store/user.js @@ -12,6 +12,7 @@ export const state = () => ({ }) export const getters = { + getIsRoot: (state) => state.user && state.user.type === 'root', getToken: (state) => { return state.user ? state.user.token : null }, diff --git a/package.json b/package.json index ca4012a6..42684467 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf", - "version": "0.9.84-beta", + "version": "0.9.85-beta", "description": "Self-hosted audiobook server for managing and playing audiobooks.", "main": "index.js", "scripts": { diff --git a/server/ApiController.js b/server/ApiController.js index 943485c8..5aec56b9 100644 --- a/server/ApiController.js +++ b/server/ApiController.js @@ -1,5 +1,6 @@ const express = require('express') const Logger = require('./Logger') +const User = require('./User') const { isObject } = require('./utils/index') class ApiController { @@ -33,10 +34,14 @@ class ApiController { 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.patch('/user/password', this.userChangePassword.bind(this)) this.router.patch('/user/settings', this.userUpdateSettings.bind(this)) + + this.router.post('/authorize', this.authorize.bind(this)) this.router.get('/genres', this.getGenres.bind(this)) @@ -255,6 +260,53 @@ class ApiController { }) } + async createUser(req, res) { + var account = req.body + account.id = (Math.trunc(Math.random() * 1000) + Date.now()).toString(36) + account.pash = await this.auth.hashPass(account.password) + delete account.password + account.token = await this.auth.generateAccessToken({ userId: account.id }) + account.createdAt = Date.now() + var newUser = new User(account) + var success = await this.db.insertUser(newUser) + if (success) { + this.emitter('user_added', newUser) + res.json({ + user: newUser.toJSONForBrowser() + }) + } else { + res.json({ + error: 'Failed to save new user' + }) + } + } + + async deleteUser(req, res) { + if (req.params.id === 'root') { + return res.sendStatus(500) + } + if (req.user.id === req.params.id) { + Logger.error('Attempting to delete themselves...') + return res.sendStatus(500) + } + var user = this.db.users.find(u => u.id === req.params.id) + if (!user) { + Logger.error('User not found') + return res.json({ + error: 'User not found' + }) + } + + // Todo: check if user is logged in and cancel streams + + var userJson = user.toJSONForBrowser() + await this.db.removeEntity('user', user.id) + this.emitter('user_removed', userJson) + res.json({ + success: true + }) + } + getGenres(req, res) { res.json({ genres: this.db.getGenres() diff --git a/server/Auth.js b/server/Auth.js index d1fcb8d4..67130190 100644 --- a/server/Auth.js +++ b/server/Auth.js @@ -2,7 +2,6 @@ const bcrypt = require('bcryptjs') const jwt = require('jsonwebtoken') const Logger = require('./Logger') - class Auth { constructor(db) { this.db = db @@ -90,7 +89,7 @@ class Auth { var password = req.body.password || '' Logger.debug('Check Auth', username, !!password) - var user = this.users.find(u => u.id === username) + var user = this.users.find(u => u.username === username) if (!user) { return res.json({ error: 'User not found' }) diff --git a/server/Db.js b/server/Db.js index 6b02716c..f6d157ba 100644 --- a/server/Db.js +++ b/server/Db.js @@ -58,6 +58,7 @@ class Db { pash: '', stream: null, token, + isActive: true, createdAt: Date.now() }) } @@ -115,8 +116,10 @@ class Db { return this.usersDb.insert([user]).then((results) => { Logger.debug(`[DB] Inserted user ${results.inserted}`) this.users.push(user) + return true }).catch((error) => { Logger.error(`[DB] Insert user Failed ${error}`) + return false }) } diff --git a/server/User.js b/server/User.js index e300b61a..69423571 100644 --- a/server/User.js +++ b/server/User.js @@ -6,6 +6,7 @@ class User { this.type = null this.stream = null this.token = null + this.isActive = true this.createdAt = null this.audiobooks = null this.settings = {} @@ -34,6 +35,7 @@ class User { stream: this.stream, token: this.token, audiobooks: this.audiobooks, + isActive: this.isActive, createdAt: this.createdAt, settings: this.settings } @@ -47,6 +49,7 @@ class User { stream: this.stream, token: this.token, audiobooks: this.audiobooks, + isActive: this.isActive, createdAt: this.createdAt, settings: this.settings } @@ -57,10 +60,11 @@ class User { this.username = user.username this.pash = user.pash this.type = user.type - this.stream = user.stream + this.stream = user.stream || null this.token = user.token this.audiobooks = user.audiobooks || null - this.createdAt = user.createdAt + this.isActive = (user.isActive === undefined || user.id === 'root') ? true : !!user.isActive + this.createdAt = user.createdAt || Date.now() this.settings = user.settings || this.getDefaultUserSettings() }