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) => { router.delete('/users/:userId', (req, res) => {
if (!req.user) { if (!req.user) {
return res.sendStatus(401) 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-btn
v-bind="attrs" v-bind="attrs"
v-on="on" v-on="on"
@click="$refs.registerUserForm.reset()" @click="resetRegisterUserForm"
> >
Register User Register User
</v-btn> </v-btn>
@@ -137,7 +137,7 @@
v-model="snackbar" v-model="snackbar"
timeout="3000" timeout="3000"
> >
{{ updateText }} {{ snackbarText }}
<template v-slot:action="{ attrs }"> <template v-slot:action="{ attrs }">
<v-btn <v-btn

View File

@@ -29,6 +29,16 @@
Home Home
</v-list-item-title> </v-list-item-title>
</v-list-item> </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-list-item
v-if="isAdmin" v-if="isAdmin"
@click="admin" @click="admin"

View File

@@ -78,7 +78,7 @@
class="mx-3 my-3" class="mx-3 my-3"
:disabled="playDisabled" :disabled="playDisabled"
v-on="on" v-on="on"
@click="$refs.profileForm.reset()" @click="resetProfileForm"
> >
Save Profile As... Save Profile As...
</v-btn> </v-btn>
@@ -481,7 +481,7 @@
v-bind="attrs" v-bind="attrs"
class="mx-3 my-3 mb-5" class="mx-3 my-3 mb-5"
v-on="on" v-on="on"
@click="$refs.uploadSampleForm.reset()" @click="resetUploadSampleForm"
> >
Upload Sample Upload Sample
</v-btn> </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: {}, currentUser: {},
users: [], users: [],
snackbar: false, snackbar: false,
updateText: '', snackbarText: '',
registerUserDialog: false, registerUserDialog: false,
isUserValid: false, isUserValid: false,
name: '', name: '',
@@ -44,11 +44,11 @@ export default {
}) })
.then(response => { .then(response => {
if (response.status === 200) { if (response.status === 200) {
this.updateText = 'User updated' this.snackbarText = 'User updated'
} }
}) })
.catch(() => { .catch(() => {
this.updateText = 'Error updating user' this.snackbarText = 'Error updating user'
}) })
}, },
updateUserUpload (id, canUpload) { updateUserUpload (id, canUpload) {
@@ -57,11 +57,11 @@ export default {
}) })
.then(response => { .then(response => {
if (response.status === 200) { if (response.status === 200) {
this.updateText = 'User updated' this.snackbarText = 'User updated'
} }
}) })
.catch(() => { .catch(() => {
this.updateText = 'Error updating user' this.snackbarText = 'Error updating user'
}) })
}, },
deleteUser (id) { deleteUser (id) {
@@ -84,11 +84,16 @@ export default {
.then(response => { .then(response => {
if (response.status === 200) { if (response.status === 200) {
this.registerUserDialog = false this.registerUserDialog = false
this.updateText = 'User Registered' this.snackbarText = 'User Registered'
this.snackbar = true this.snackbar = true
this.getUsers() this.getUsers()
} }
}) })
},
resetRegisterUserForm () {
if (this.$refs.registerUserForm) {
this.$refs.registerUserForm.reset()
}
} }
} }
} }

View File

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

View File

@@ -393,6 +393,16 @@ export default {
this.$vuetify.theme.dark = response.data.user.darkMode 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', path: '/login',
name: 'Login', name: 'Login',
// route level code-splitting component: () => import('../views/LoginView.vue')
// 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')
}, },
{ {
path: '/register', path: '/register',
name: 'Register', name: 'Register',
// route level code-splitting component: () => import('../views/RegisterView.vue')
// 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')
}, },
{ {
path: '/admin', path: '/admin',
name: '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') next('/register')
} }
}) })
.catch((error) => { .catch(() => {
console.error(error.response)
next('/register') next('/register')
}) })
} else if (to.name === 'Admin') { } else if (to.name === 'Admin') {
@@ -63,8 +61,7 @@ router.beforeEach((to, from, next) => {
next('/') next('/')
} }
}) })
.catch((error) => { .catch(() => {
console.error(error.response)
next('/') next('/')
}) })
} else if (to.name === 'Register') { } else if (to.name === 'Register') {
@@ -76,10 +73,21 @@ router.beforeEach((to, from, next) => {
next() next()
} }
}) })
.catch((error) => { .catch(() => {
console.error(error.response)
next('/login') next('/login')
}) })
} else if (to.name === 'Account') {
instance.get('/auth')
.then(response => {
if (response.status === 200) {
next()
} else {
next('/register')
}
})
.catch(() => {
next('/register')
})
} else { } else {
next() 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>