Add account page

This commit is contained in:
Kevin Thomas
2021-11-27 23:42:00 -08:00
parent fc29667c96
commit fc79362510
11 changed files with 247 additions and 25 deletions

View File

@@ -232,6 +232,33 @@ router.patch('/users/dark-mode', (req, res) => {
})
})
router.patch('/users/password', (req, res) => {
if (!req.user) {
return res.sendStatus(401)
}
const salt = crypto.randomBytes(16)
crypto.pbkdf2(req.body.password, salt, 10000, 32, 'sha256', (err, hashedPassword) => {
if (err) {
logger.error(err)
return res.sendStatus(500)
}
db.run('UPDATE users SET hashed_password = ?, salt = ? WHERE id = ?', [
hashedPassword,
salt,
req.user.id
], (err) => {
if (err) {
logger.error(err)
return res.sendStatus(500)
}
return res.sendStatus(200)
})
})
})
router.delete('/users/:userId', (req, res) => {
if (!req.user) {
return res.sendStatus(401)

View File

@@ -0,0 +1,99 @@
<template>
<v-container>
<v-row>
<v-col class="mb-5">
<h1 class="display-2 font-weight-bold mb-3">
Account
</h1>
</v-col>
</v-row>
<v-col cols="12">
<v-row>
ID: {{ currentUser.id }}
</v-row>
<v-row>
Username: {{ currentUser.username }}
</v-row>
<v-row>
Name: {{ currentUser.name }}
</v-row>
</v-col>
<v-dialog
v-model="changePasswordDialog"
max-width="600px"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
class="my-3"
v-bind="attrs"
v-on="on"
@click="resetChangePasswordForm"
>
Change Password
</v-btn>
</template>
<v-form
ref="changePasswordForm"
v-model="isPasswordValid"
>
<v-card>
<v-card-title>
<span class="text-h5">Change Password</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12">
<v-text-field
v-model="password"
type="password"
:rules="[rules.required]"
label="Password"
required
/>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
text
@click="changePasswordDialog = false"
>
Close
</v-btn>
<v-btn
text
:disabled="!isPasswordValid"
@click="updatePassword"
>
Change Password
</v-btn>
</v-card-actions>
</v-card>
</v-form>
</v-dialog>
<v-snackbar
v-model="snackbar"
timeout="3000"
>
{{ snackbarText }}
<template v-slot:action="{ attrs }">
<v-btn
text
v-bind="attrs"
@click="snackbar = false"
>
Close
</v-btn>
</template>
</v-snackbar>
</v-container>
</template>
<script src="./account.js"></script>

View File

@@ -16,7 +16,7 @@
<v-btn
v-bind="attrs"
v-on="on"
@click="$refs.registerUserForm.reset()"
@click="resetRegisterUserForm"
>
Register User
</v-btn>
@@ -137,7 +137,7 @@
v-model="snackbar"
timeout="3000"
>
{{ updateText }}
{{ snackbarText }}
<template v-slot:action="{ attrs }">
<v-btn

View File

@@ -29,6 +29,16 @@
Home
</v-list-item-title>
</v-list-item>
<v-list-item
@click="account"
>
<v-list-item-icon>
<v-icon>mdi-account</v-icon>
</v-list-item-icon>
<v-list-item-title>
Account
</v-list-item-title>
</v-list-item>
<v-list-item
v-if="isAdmin"
@click="admin"

View File

@@ -78,7 +78,7 @@
class="mx-3 my-3"
:disabled="playDisabled"
v-on="on"
@click="$refs.profileForm.reset()"
@click="resetProfileForm"
>
Save Profile As...
</v-btn>
@@ -481,7 +481,7 @@
v-bind="attrs"
class="mx-3 my-3 mb-5"
v-on="on"
@click="$refs.uploadSampleForm.reset()"
@click="resetUploadSampleForm"
>
Upload Sample
</v-btn>

45
src/components/account.js Normal file
View File

@@ -0,0 +1,45 @@
export default {
name: 'Admin',
data: () => ({
currentUser: {},
changePasswordDialog: false,
isPasswordValid: false,
password: '',
snackbar: false,
snackbarText: '',
rules: {
required: v => !!v || 'Required'
}
}),
created () {
this.getCurrentUser()
},
methods: {
getCurrentUser () {
this.$http.get('/users/current')
.then(response => {
if (response.status === 200) {
this.currentUser = response.data.user
}
})
},
updatePassword () {
this.$http.patch('/users/password', {
password: this.password
})
.then(response => {
if (response.status === 200) {
this.changePasswordDialog = false
this.snackbarText = 'Password Changed'
this.snackbar = true
}
})
},
resetChangePasswordForm () {
if (this.$refs.changePasswordForm) {
this.$refs.changePasswordForm.reset()
}
}
}
}

View File

@@ -5,7 +5,7 @@ export default {
currentUser: {},
users: [],
snackbar: false,
updateText: '',
snackbarText: '',
registerUserDialog: false,
isUserValid: false,
name: '',
@@ -44,11 +44,11 @@ export default {
})
.then(response => {
if (response.status === 200) {
this.updateText = 'User updated'
this.snackbarText = 'User updated'
}
})
.catch(() => {
this.updateText = 'Error updating user'
this.snackbarText = 'Error updating user'
})
},
updateUserUpload (id, canUpload) {
@@ -57,11 +57,11 @@ export default {
})
.then(response => {
if (response.status === 200) {
this.updateText = 'User updated'
this.snackbarText = 'User updated'
}
})
.catch(() => {
this.updateText = 'Error updating user'
this.snackbarText = 'Error updating user'
})
},
deleteUser (id) {
@@ -84,11 +84,16 @@ export default {
.then(response => {
if (response.status === 200) {
this.registerUserDialog = false
this.updateText = 'User Registered'
this.snackbarText = 'User Registered'
this.snackbar = true
this.getUsers()
}
})
},
resetRegisterUserForm () {
if (this.$refs.registerUserForm) {
this.$refs.registerUserForm.reset()
}
}
}
}

View File

@@ -10,6 +10,9 @@ export default {
home () {
this.$router.push('/')
},
account () {
this.$router.push('/account')
},
admin () {
this.$router.push('/admin')
},

View File

@@ -393,6 +393,16 @@ export default {
this.$vuetify.theme.dark = response.data.user.darkMode
}
})
},
resetProfileForm () {
if (this.$refs.profileForm) {
this.$refs.profileForm.reset()
}
},
resetUploadSampleForm () {
if (this.$refs.uploadSampleForm) {
this.$refs.uploadSampleForm.reset()
}
}
}
}

View File

@@ -14,23 +14,22 @@ const routes = [
{
path: '/login',
name: 'Login',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/LoginView.vue')
component: () => import('../views/LoginView.vue')
},
{
path: '/register',
name: 'Register',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/RegisterView.vue')
component: () => import('../views/RegisterView.vue')
},
{
path: '/admin',
name: 'Admin',
component: () => import(/* webpackChunkName: "about" */ '../views/AdminView.vue')
component: () => import('../views/AdminView.vue')
},
{
path: '/account',
name: 'Account',
component: () => import('../views/AccountView.vue')
}
]
@@ -50,8 +49,7 @@ router.beforeEach((to, from, next) => {
next('/register')
}
})
.catch((error) => {
console.error(error.response)
.catch(() => {
next('/register')
})
} else if (to.name === 'Admin') {
@@ -63,8 +61,7 @@ router.beforeEach((to, from, next) => {
next('/')
}
})
.catch((error) => {
console.error(error.response)
.catch(() => {
next('/')
})
} else if (to.name === 'Register') {
@@ -76,10 +73,21 @@ router.beforeEach((to, from, next) => {
next()
}
})
.catch((error) => {
console.error(error.response)
.catch(() => {
next('/login')
})
} else if (to.name === 'Account') {
instance.get('/auth')
.then(response => {
if (response.status === 200) {
next()
} else {
next('/register')
}
})
.catch(() => {
next('/register')
})
} else {
next()
}

15
src/views/AccountView.vue Normal file
View File

@@ -0,0 +1,15 @@
<template>
<Account />
</template>
<script>
import Account from '../components/Account'
export default {
name: 'AccountView',
components: {
Account
}
}
</script>