aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/routes/auth/apiKey.js23
-rw-r--r--src/api/routes/user/apiKey.js27
-rw-r--r--src/api/routes/user/changePasswordPOST.js40
-rw-r--r--src/api/routes/user/userGET.js21
-rw-r--r--src/site/pages/dashboard/account.vue153
5 files changed, 241 insertions, 23 deletions
diff --git a/src/api/routes/auth/apiKey.js b/src/api/routes/auth/apiKey.js
deleted file mode 100644
index 84df2e3..0000000
--- a/src/api/routes/auth/apiKey.js
+++ /dev/null
@@ -1,23 +0,0 @@
-const Route = require('../../structures/Route');
-
-class apiKeyGET extends Route {
- constructor() {
- super('/auth/apiKey', 'get');
- }
-
- run(req, res, user) {
- return res.json({ message: 'Hai hai api works.' });
- }
-}
-
-class apiKeyPOST extends Route {
- constructor() {
- super('/auth/apiKey', 'post');
- }
-
- run(req, res, user) {
- return res.json({ message: 'Hai hai api works.' });
- }
-}
-
-module.exports = [apiKeyGET, apiKeyPOST];
diff --git a/src/api/routes/user/apiKey.js b/src/api/routes/user/apiKey.js
new file mode 100644
index 0000000..820e28c
--- /dev/null
+++ b/src/api/routes/user/apiKey.js
@@ -0,0 +1,27 @@
+const Route = require('../../structures/Route');
+const randomstring = require('randomstring');
+const moment = require('moment');
+
+class apiKeyPOST extends Route {
+ constructor() {
+ super('/user/apikey/change', 'post');
+ }
+
+ async run(req, res, db, user) {
+ const now = moment.utc().toDate();
+ const apiKey = randomstring.generate(64);
+ await db.table('users')
+ .where({ id: user.id })
+ .update({
+ apiKey,
+ apiKeyEditedAt: now
+ });
+
+ return res.json({
+ message: 'Successfully created new api key',
+ apiKey
+ });
+ }
+}
+
+module.exports = apiKeyPOST;
diff --git a/src/api/routes/user/changePasswordPOST.js b/src/api/routes/user/changePasswordPOST.js
new file mode 100644
index 0000000..d73cff3
--- /dev/null
+++ b/src/api/routes/user/changePasswordPOST.js
@@ -0,0 +1,40 @@
+const Route = require('../../structures/Route');
+const log = require('../../utils/Log');
+const bcrypt = require('bcrypt');
+const moment = require('moment');
+
+class changePasswordPOST extends Route {
+ constructor() {
+ super('/user/password/change', 'post');
+ }
+
+ async run(req, res, db, user) {
+ if (!req.body) return res.status(400).json({ message: 'No body provided' });
+ const { password, newPassword } = req.body;
+ if (!password || !newPassword) return res.status(401).json({ message: 'Invalid body provided' });
+ if (password === newPassword) return res.status(400).json({ message: 'Passwords have to be different' });
+
+ if (newPassword.length < 6 || newPassword.length > 64) {
+ return res.status(400).json({ message: 'Password must have 6-64 characters' });
+ }
+
+ let hash;
+ try {
+ hash = await bcrypt.hash(newPassword, 10);
+ } catch (error) {
+ log.error('Error generating password hash');
+ log.error(error);
+ return res.status(401).json({ message: 'There was a problem processing your account' });
+ }
+
+ const now = moment.utc().toDate();
+ await db.table('users').where('id', user.id).update({
+ password: hash,
+ passwordEditedAt: now
+ });
+
+ return res.json({ message: 'The password was changed successfully' });
+ }
+}
+
+module.exports = changePasswordPOST;
diff --git a/src/api/routes/user/userGET.js b/src/api/routes/user/userGET.js
new file mode 100644
index 0000000..7929aac
--- /dev/null
+++ b/src/api/routes/user/userGET.js
@@ -0,0 +1,21 @@
+const Route = require('../../structures/Route');
+
+class usersGET extends Route {
+ constructor() {
+ super('/users/me', 'get');
+ }
+
+ run(req, res, db, user) {
+ return res.json({
+ message: 'Successfully retrieved user',
+ user: {
+ id: user.id,
+ username: user.username,
+ isAdmin: user.isAdmin,
+ apiKey: user.apiKey
+ }
+ });
+ }
+}
+
+module.exports = usersGET;
diff --git a/src/site/pages/dashboard/account.vue b/src/site/pages/dashboard/account.vue
new file mode 100644
index 0000000..e3570c7
--- /dev/null
+++ b/src/site/pages/dashboard/account.vue
@@ -0,0 +1,153 @@
+<style lang="scss" scoped>
+ @import '~/assets/styles/_colors.scss';
+ section { background-color: $backgroundLight1 !important; }
+ section.hero div.hero-body {
+ align-items: baseline;
+ }
+ div.search-container {
+ display: flex;
+ justify-content: center;
+ }
+</style>
+<style lang="scss">
+ @import '~/assets/styles/_colors.scss';
+</style>
+
+
+<template>
+ <section class="hero is-fullheight">
+ <div class="hero-body">
+ <div class="container">
+ <div class="columns">
+ <div class="column is-narrow">
+ <Sidebar />
+ </div>
+ <div class="column">
+ <h2 class="subtitle">Account settings</h2>
+ <hr>
+
+ <b-field label="Username"
+ message="Nothing to do here"
+ horizontal>
+ <b-input v-model="user.username"
+ expanded
+ disabled />
+ </b-field>
+
+ <b-field label="Current password"
+ message="If you want to change your password input the current one here"
+ horizontal>
+ <b-input v-model="user.password"
+ type="password"
+ expanded />
+ </b-field>
+
+ <b-field label="New password"
+ message="Your new password"
+ horizontal>
+ <b-input v-model="user.newPassword"
+ type="password"
+ expanded />
+ </b-field>
+
+ <b-field label="New password again"
+ message="Your new password once again"
+ horizontal>
+ <b-input v-model="user.reNewPassword"
+ type="password"
+ expanded />
+ </b-field>
+
+ <div class="mb2 mt2 text-center">
+ <button class="button is-primary"
+ @click="changePassword">Change password</button>
+ </div>
+
+ <b-field label="Api key"
+ message="This API key lets you use the service from other apps?"
+ horizontal>
+ <b-input v-model="user.apiKey"
+ expanded />
+ </b-field>
+
+ <div class="mb2 mt2 text-center">
+ <button class="button is-primary"
+ @click="promptNewAPIKey">Request new API key</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+</template>
+
+<script>
+import Sidebar from '~/components/sidebar/Sidebar.vue';
+
+export default {
+ components: {
+ Sidebar
+ },
+ data() {
+ return {
+ user: {}
+ };
+ },
+ computed: {
+ config() {
+ return this.$store.state.config;
+ }
+ },
+ metaInfo() {
+ return { title: 'Account' };
+ },
+ mounted() {
+ this.$ga.page({
+ page: '/dashboard/account',
+ title: 'Settings',
+ location: window.location.href
+ });
+ this.getUserSetttings();
+ },
+ methods: {
+ async getUserSetttings() {
+ try {
+ const response = await this.axios.get(`${this.config.baseURL}/users/me`);
+ this.user = response.data.user;
+ } catch (error) {
+ this.$onPromiseError(error);
+ }
+ },
+ async changePassword() {
+ if (!this.user.password || !this.user.newPassword || !this.user.reNewPassword) return;
+ if (this.user.newPassword !== this.user.reNewPassword) return;
+
+ try {
+ const response = await this.axios.post(`${this.config.baseURL}/user/password/change`,
+ {
+ password: this.user.password,
+ newPassword: this.user.newPassword
+ });
+ this.$toast.open(response.data.message);
+ } catch (error) {
+ this.$onPromiseError(error);
+ }
+ },
+ promptNewAPIKey() {
+ this.$dialog.confirm({
+ message: 'Are you sure you want to regenerate your API key?',
+ onConfirm: () => this.requestNewAPIKey()
+ });
+ },
+ async requestNewAPIKey() {
+ try {
+ const response = await this.axios.post(`${this.config.baseURL}/user/apikey/change`);
+ this.user.apiKey = response.data.apiKey;
+ this.$toast.open(response.data.message);
+ } catch (error) {
+ this.$onPromiseError(error);
+ }
+ }
+ }
+};
+</script>