Add initial admin page with update feature

This commit is contained in:
Kevin Thomas
2021-08-05 23:39:07 -07:00
parent 790c1fda22
commit d0fa884d83
8 changed files with 197 additions and 6 deletions

View File

@@ -7,7 +7,8 @@ module.exports = function () {
username TEXT UNIQUE,
hashed_password BLOB,
salt BLOB,
name TEXT)`
name TEXT,
is_admin INTEGER)`
)
db.run(`CREATE TABLE IF NOT EXISTS profiles (

View File

@@ -84,6 +84,7 @@ router.get('/profiles/:profileId', function (req, res, next) {
return res.sendStatus(401)
}
// TODO: I'm guessing there's a better way to marshal this data
const profile = {
name: null,
isTimerEnabled: null,

View File

@@ -3,6 +3,39 @@ const crypto = require('crypto')
const db = require('../db')
const router = express.Router()
router.get('/users', function (req, res, next) {
if (!req.user) {
return res.sendStatus(401)
}
// TODO: I'm guessing there's a better way to marshal this data
const users = []
db.all('SELECT username, name, is_admin as isAdmin FROM users', (err, rows) => {
if (err) {
console.log('Error getting profiles')
console.log(err)
return res.sendStatus(500)
}
rows.forEach((row) => {
const user = {
username: null,
name: null,
isAdmin: null
}
user.username = row.username
user.name = row.name
user.isAdmin = row.isAdmin === 1
users.push(user)
})
res.json({ users: users })
})
})
router.post('/users', function (req, res, next) {
const salt = crypto.randomBytes(16)
crypto.pbkdf2(req.body.password, salt, 10000, 32, 'sha256', function (err, hashedPassword) {
@@ -11,11 +44,12 @@ router.post('/users', function (req, res, next) {
res.sendStatus(500)
}
db.run('INSERT INTO users (username, hashed_password, salt, name) VALUES (?, ?, ?, ?)', [
db.run('INSERT INTO users (username, hashed_password, salt, name, is_admin) VALUES (?, ?, ?, ?, ?)', [
req.body.username,
hashedPassword,
salt,
req.body.name
req.body.name,
req.body.isAdmin
], function (err) {
if (err) {
console.log(err)
@@ -38,4 +72,22 @@ router.post('/users', function (req, res, next) {
res.sendStatus(200)
})
router.put('/users', function (req, res, next) {
if (!req.user) {
return res.sendStatus(401)
}
db.run('UPDATE users SET is_admin = ? WHERE username = ?', [req.body.isAdmin, req.body.username], (err) => {
if (err) {
console.log('Error getting profiles')
console.log(err)
return res.sendStatus(500)
}
console.log(`Row(s) updated: ${this.changes}`)
})
res.sendStatus(200)
})
module.exports = router

109
src/components/Admin.vue Normal file
View File

@@ -0,0 +1,109 @@
<template>
<v-container>
<v-col cols="12">
<v-col class="mb-4">
<h1 class="display-2 font-weight-bold mb-3">
Admin Dashboard
</h1>
</v-col>
<v-simple-table>
<thead>
<tr>
<th class="text-left">
Username
</th>
<th class="text-left">
Is Admin
</th>
<th class="text-left">
Delete User
</th>
</tr>
</thead>
<tbody>
<tr
v-for="user in users"
:key="user.username"
>
<td>{{ user.username }}</td>
<td>
<v-switch
v-model="user.isAdmin"
:label="`${user.isAdmin ? 'True' : 'False'}`"
@change="updateUser(user.username, user.isAdmin); snackbar = true"
/>
</td>
<td>
<v-btn
@click="updateUser(user.username)"
>
Delete
</v-btn>
</td>
</tr>
</tbody>
</v-simple-table>
<v-snackbar
v-model="snackbar"
timeout="3000"
>
{{ updateText }}
<template v-slot:action="{ attrs }">
<v-btn
text
v-bind="attrs"
@click="snackbar = false"
>
Close
</v-btn>
</template>
</v-snackbar>
</v-col>
</v-container>
</template>
<script>
export default {
name: 'Admin',
data: () => ({
users: [],
snackbar: false,
updateText: ''
}),
created () {
this.getUsers()
},
methods: {
getUsers () {
this.$http.get('https://localhost:3000/users')
.then(response => {
if (response.status === 200) {
this.users = response.data.users
}
})
.catch(function (error) {
console.error(error.response)
})
},
updateUser (username, isAdmin) {
this.$http.put('https://localhost:3000/users', {
username: username,
isAdmin: isAdmin ? 1 : 0
})
.then(response => {
if (response.status === 200) {
console.log('User updated')
this.updateText = 'User updated'
}
})
.catch(function (error) {
console.error(error.response)
this.updateText = 'Error updating user'
})
}
}
}
</script>

View File

@@ -21,7 +21,9 @@
<v-list-item-group
v-model="group"
>
<v-list-item>
<v-list-item
@click="home"
>
<v-list-item-icon>
<v-icon>mdi-home</v-icon>
</v-list-item-icon>
@@ -42,7 +44,6 @@
<v-list-item>
<v-switch
v-model="$vuetify.theme.dark"
inset
label="Dark Mode"
/>
</v-list-item>
@@ -61,6 +62,12 @@ export default {
group: null
}),
methods: {
home () {
this.$router.push('/')
},
admin () {
this.$router.push('/admin')
},
logout () {
this.$http.get('https://localhost:3000/logout')
.then(response => {

View File

@@ -73,7 +73,8 @@ export default {
this.$http.post('https://localhost:3000/users', {
name: this.name,
username: this.username,
password: this.password
password: this.password,
isAdmin: 1
})
.then(response => {
if (response.status === 200) {

View File

@@ -31,6 +31,11 @@ const routes = [
// 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/Signup.vue')
},
{
path: '/admin',
name: 'Admin',
component: () => import(/* webpackChunkName: "about" */ '../views/AdminPage.vue')
}
]

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

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