From 0c2b2b434110ab500007d331d2933c87af60a896 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sun, 30 Dec 2018 15:07:12 -0800 Subject: [PATCH] Get account pages working --- .../scripts/components/dashboard/Account.ts | 58 ++++++ .../scripts/components/dashboard/Account.vue | 51 ----- .../dashboard/account/ChangePassword.ts | 91 +++++++++ .../dashboard/account/ChangePassword.vue | 91 --------- .../account/TwoFactorAuthentication.ts | 188 +++++++++++++++++ .../account/TwoFactorAuthentication.vue | 191 ------------------ .../dashboard/account/UpdateEmail.ts | 77 +++++++ .../dashboard/account/UpdateEmail.vue | 81 -------- .../assets/scripts/pterodactyl-shims.d.ts | 3 +- resources/assets/scripts/router.ts | 2 +- 10 files changed, 417 insertions(+), 416 deletions(-) create mode 100644 resources/assets/scripts/components/dashboard/Account.ts delete mode 100644 resources/assets/scripts/components/dashboard/Account.vue create mode 100644 resources/assets/scripts/components/dashboard/account/ChangePassword.ts delete mode 100644 resources/assets/scripts/components/dashboard/account/ChangePassword.vue create mode 100644 resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.ts delete mode 100644 resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.vue create mode 100644 resources/assets/scripts/components/dashboard/account/UpdateEmail.ts delete mode 100644 resources/assets/scripts/components/dashboard/account/UpdateEmail.vue diff --git a/resources/assets/scripts/components/dashboard/Account.ts b/resources/assets/scripts/components/dashboard/Account.ts new file mode 100644 index 000000000..4ced6f87f --- /dev/null +++ b/resources/assets/scripts/components/dashboard/Account.ts @@ -0,0 +1,58 @@ +import Vue from 'vue'; +import Navigation from "../core/Navigation"; +import Flash from "../Flash"; +import UpdateEmail from "./account/UpdateEmail"; +import ChangePassword from "./account/ChangePassword"; +import TwoFactorAuthentication from "./account/TwoFactorAuthentication"; +import Modal from "../core/Modal"; + +export default Vue.component('account', { + components: { + TwoFactorAuthentication, + Modal, + ChangePassword, + UpdateEmail, + Flash, + Navigation + }, + + data: function () { + return { + modalVisible: false, + }; + }, + + methods: { + openModal: function () { + this.modalVisible = true; + window.events.$emit('two_factor:open'); + }, + }, + + template: ` +
+ +
+ + + + +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ `, +}) diff --git a/resources/assets/scripts/components/dashboard/Account.vue b/resources/assets/scripts/components/dashboard/Account.vue deleted file mode 100644 index f23f5dc8c..000000000 --- a/resources/assets/scripts/components/dashboard/Account.vue +++ /dev/null @@ -1,51 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/dashboard/account/ChangePassword.ts b/resources/assets/scripts/components/dashboard/account/ChangePassword.ts new file mode 100644 index 000000000..86fa093f5 --- /dev/null +++ b/resources/assets/scripts/components/dashboard/account/ChangePassword.ts @@ -0,0 +1,91 @@ +import Vue from 'vue'; +import { isObject } from 'lodash'; +import {AxiosError} from "axios"; + +export default Vue.component('change-password', { + data: function () { + return { + current: '', + newPassword: '', + confirmNew: '', + }; + }, + + methods: { + submitForm: function () { + this.$flash.clear(); + this.$validator.pause(); + + window.axios.put(this.route('api.client.account.update-password'), { + current_password: this.current, + password: this.newPassword, + password_confirmation: this.confirmNew, + }) + .then(() => this.current = '') + .then(() => { + this.newPassword = ''; + this.confirmNew = ''; + + this.$flash.success(this.$t('dashboard.account.password.updated')); + }) + .catch((err: AxiosError) => { + if (!err.response) { + this.$flash.error('There was an error with the network request. Please try again.'); + console.error(err); + return; + } + + const response = err.response; + if (response.data && isObject(response.data.errors)) { + response.data.errors.forEach((error: any) => { + this.$flash.error(error.detail); + }); + } + }) + .then(() => { + this.$validator.resume(); + (this.$refs.current as HTMLElement).focus(); + }) + } + }, + + template: ` +
+
+
+

{{ $t('dashboard.account.password.title') }}

+
+ + +
+
+ + +

{{ errors.first('password') }}

+

{{ $t('dashboard.account.password.requirements') }}

+
+
+ + +

{{ errors.first('password_confirmation') }}

+
+
+ +
+
+
+
+ `, +}); diff --git a/resources/assets/scripts/components/dashboard/account/ChangePassword.vue b/resources/assets/scripts/components/dashboard/account/ChangePassword.vue deleted file mode 100644 index 494c76be2..000000000 --- a/resources/assets/scripts/components/dashboard/account/ChangePassword.vue +++ /dev/null @@ -1,91 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.ts b/resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.ts new file mode 100644 index 000000000..22b62fb6c --- /dev/null +++ b/resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.ts @@ -0,0 +1,188 @@ +import Vue from 'vue'; +import {isObject} from 'lodash'; +import {AxiosError, AxiosResponse} from "axios"; + +export default Vue.component('two-factor-authentication', { + data: function () { + return { + spinner: true, + token: '', + submitDisabled: true, + response: { + enabled: false, + qr_image: '', + secret: '', + }, + }; + }, + + /** + * Before the component is mounted setup the event listener. This event is fired when a user + * presses the 'Configure 2-Factor' button on their account page. Once this happens we fire off + * a HTTP request to get their information. + */ + mounted: function () { + window.events.$on('two_factor:open', () => { + this.prepareModalContent(); + }); + }, + + watch: { + token: function (value) { + this.submitDisabled = value.length !== 6; + }, + }, + + methods: { + /** + * Determine the correct content to show in the modal. + */ + prepareModalContent: function () { + // Reset the data object when the modal is opened again. + // @ts-ignore + Object.assign(this.$data, this.$options.data()); + + this.$flash.clear(); + window.axios.get(this.route('account.two_factor')) + .then((response: AxiosResponse) => { + this.response = response.data; + this.spinner = false; + Vue.nextTick().then(() => { + (this.$refs.token as HTMLElement).focus(); + }) + }) + .catch((err: AxiosError) => { + if (!err.response) { + this.$flash.error(err.message); + console.error(err); + return; + } + + const response = err.response; + if (response.data && isObject(response.data.errors)) { + response.data.errors.forEach((error: any) => { + this.$flash.error(error.detail); + }); + } + + this.$emit('close'); + }); + }, + + /** + * Enable two-factor authentication on the account by validating the token provided by the user. + * Close the modal once the request completes so that the success or error message can be shown + * to the user. + */ + enableTwoFactor: function () { + return this._callInternalApi('account.two_factor.enable', 'enabled'); + }, + + /** + * Disables two-factor authentication for the client account and closes the modal. + */ + disableTwoFactor: function () { + return this._callInternalApi('account.two_factor.disable', 'disabled'); + }, + + /** + * Call the Panel API endpoint and handle errors. + * + * @private + */ + _callInternalApi: function (route: string, langKey: string) { + this.$flash.clear(); + this.spinner = true; + + window.axios.post(this.route(route), {token: this.token}) + .then((response: AxiosResponse) => { + if (response.data.success) { + this.$flash.success(this.$t(`dashboard.account.two_factor.${langKey}`)); + } else { + this.$flash.error(this.$t('dashboard.account.two_factor.invalid')); + } + }) + .catch((error: AxiosError) => { + if (!error.response) { + this.$flash.error(error.message); + return; + } + + const response = error.response; + if (response.data && isObject(response.data.errors)) { + response.data.errors.forEach((e: any) => { + this.$flash.error(e.detail); + }); + } + }) + .then(() => { + this.spinner = false; + this.$emit('close'); + }); + } + }, + + template: ` +
+
+ +
+
+

{{ $t('dashboard.account.two_factor.disable.title') }}

+
+ + +

{{ errors.first('token') }}

+
+
+ + +
+
+
+

{{ $t('dashboard.account.two_factor.setup.title') }}

+
+
+
+ Two-factor qr image +
+
+

{{ $t('dashboard.account.two_factor.setup.help') }}

+

{{response.secret}}

+
+
+
+
+ + +

{{ errors.first('token') }}

+
+
+ +
+
+
+
+
+ ` +}) diff --git a/resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.vue b/resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.vue deleted file mode 100644 index 387f275e8..000000000 --- a/resources/assets/scripts/components/dashboard/account/TwoFactorAuthentication.vue +++ /dev/null @@ -1,191 +0,0 @@ - - - diff --git a/resources/assets/scripts/components/dashboard/account/UpdateEmail.ts b/resources/assets/scripts/components/dashboard/account/UpdateEmail.ts new file mode 100644 index 000000000..1e18cd158 --- /dev/null +++ b/resources/assets/scripts/components/dashboard/account/UpdateEmail.ts @@ -0,0 +1,77 @@ +import Vue from 'vue'; +import { get, isObject } from 'lodash'; +import { mapState } from 'vuex'; +import {ApplicationState} from "../../../store/types"; +import {AxiosError} from "axios"; + +export default Vue.component('update-email', { + data: function () { + return { + email: get(this.$store.state, 'auth.user.email', ''), + password: '', + }; + }, + + computed: { + ...mapState({ + user: (state: ApplicationState) => state.auth.user, + }) + }, + + methods: { + /** + * Update a user's email address on the Panel. + */ + submitForm: function () { + this.$flash.clear(); + this.$store.dispatch('auth/updateEmail', { email: this.email, password: this.password }) + .then(() => { + this.$flash.success(this.$t('dashboard.account.email.updated')); + }) + .catch((error: AxiosError) => { + if (!error.response) { + this.$flash.error(error.message); + return; + } + + const response = error.response; + if (response.data && isObject(response.data.errors)) { + response.data.errors.forEach((e: any) => { + this.$flash.error(e.detail); + }); + } + }) + .then(() => { + this.$data.password = ''; + }); + }, + }, + + template: ` +
+
+
+

{{ $t('dashboard.account.email.title') }}

+
+ + +

{{ errors.first('email') }}

+
+
+ + +
+
+ +
+
+
+
+ `, +}); diff --git a/resources/assets/scripts/components/dashboard/account/UpdateEmail.vue b/resources/assets/scripts/components/dashboard/account/UpdateEmail.vue deleted file mode 100644 index a228480a1..000000000 --- a/resources/assets/scripts/components/dashboard/account/UpdateEmail.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - diff --git a/resources/assets/scripts/pterodactyl-shims.d.ts b/resources/assets/scripts/pterodactyl-shims.d.ts index 5b5793838..d4b002bc5 100644 --- a/resources/assets/scripts/pterodactyl-shims.d.ts +++ b/resources/assets/scripts/pterodactyl-shims.d.ts @@ -3,6 +3,7 @@ import {Store} from "vuex"; import {FlashInterface} from "./mixins/flash"; import {AxiosInstance} from "axios"; import {Vue as VueType} from "vue/types/vue"; +import {ApplicationState} from "./store/types"; declare global { interface Window { @@ -17,7 +18,7 @@ declare global { declare module 'vue/types/options' { interface ComponentOptions { - $store?: Store, + $store?: Store, $options?: { sockets?: { [s: string]: (data: any) => void, diff --git a/resources/assets/scripts/router.ts b/resources/assets/scripts/router.ts index 7e76c60c4..86947d30b 100644 --- a/resources/assets/scripts/router.ts +++ b/resources/assets/scripts/router.ts @@ -6,7 +6,7 @@ const route = require('./../../../vendor/tightenco/ziggy/src/js/route').default; // Base Vuejs Templates import Login from './components/auth/Login'; import Dashboard from './components/dashboard/Dashboard'; -import Account from './components/dashboard/Account.vue'; +import Account from './components/dashboard/Account'; import ResetPassword from './components/auth/ResetPassword'; import User from './models/user'; import {