user account page done (- styling)

This commit is contained in:
2023-09-07 08:40:21 +02:00
parent 10d0703d78
commit 733cbf2dbb
4 changed files with 238 additions and 85 deletions

View File

@@ -150,7 +150,7 @@ class SQLDB {
command = 'UPDATE ' + table + ' SET ';
let updatedValues = '';
for ( let value in operation.newValues ) {
updatedValues += value + ' = ' + this.sqlConnection.escape( operation.newValues[ value ] ) + ', ';
updatedValues += value + ' = ' + this.sqlConnection.escape( String( operation.newValues[ value ] ) ) + ', ';
}
command += updatedValues.slice( 0, updatedValues.length - 2 );
command += ' WHERE ' + operation.property + ' = ' + this.sqlConnection.escape( operation.searchQuery );

View File

@@ -41,7 +41,6 @@ module.exports = ( app, settings ) => {
response.status( 500 ).send( { 'data': 'There was an error reading data from the database. If this error persists, please contact the administrators', 'status': false } );
} );
} else {
console.log( 'unauthorised' );
response.status( 403 ).sendFile( path.join( __dirname + '/../ui/en/errors/403.html' ) );
}
} );
@@ -84,7 +83,7 @@ module.exports = ( app, settings ) => {
}
} );
} else {
response.send( 'missingCredentials' );
response.status( 400 ).send( 'missingCredentials' );
}
} );
@@ -101,7 +100,7 @@ module.exports = ( app, settings ) => {
} else if ( tokType === 'enhanced' ) {
response.sendFile( path.join( __dirname + '/../ui/en/2fa/2faEnhanced.html' ) );
} else {
response.sendFile( path.join( __dirname + '/../ui/en/2fa/2faInvalid.html' ) );
response.status( 403 ).sendFile( path.join( __dirname + '/../ui/en/2fa/2faInvalid.html' ) );
}
} );
@@ -117,7 +116,7 @@ module.exports = ( app, settings ) => {
authOk[ request.body.token ] = 'ok';
}
response.send( 'ok' );
} else response.send( 'wrong' );
} else response.status( 403 ).send( 'wrong' );
} );
app.get( '/user/2fa/check', ( request, response ) => {
@@ -148,12 +147,16 @@ module.exports = ( app, settings ) => {
} );
app.get( '/user/resendEmail', ( req, res ) => {
( async () => {
let tok = generator.generateToken( 60 );
mailTokens[ tok ] = req.session.username;
mailManager.sendMail( req.session.username, await twoFA.generateSignupEmail( tok, settings.yourDomain, settings.name ), 'Confirm your email', settings.mailSender );
} )();
res.send( 'sent' );
if ( req.session.username ) {
( async () => {
let tok = generator.generateToken( 60 );
mailTokens[ tok ] = req.session.username;
mailManager.sendMail( req.session.username, await twoFA.generateSignupEmail( tok, settings.yourDomain, settings.name ), 'Confirm your email', settings.mailSender );
} )();
res.send( 'sent' );
} else {
res.status( 403 ).send( 'unauthorised' );
}
} );
app.post( '/user/signup', bodyParser.json(), ( request, response ) => {
@@ -202,7 +205,7 @@ module.exports = ( app, settings ) => {
response.sendFile( path.join( __dirname + '/../ui/en/signup/disallowTwoFA.html' ) );
}
} else {
response.sendFile( path.join( __dirname + '/../ui/en/signup/invalid.html' ) );
response.status( 400 ).sendFile( path.join( __dirname + '/../ui/en/signup/invalid.html' ) );
}
} );
@@ -214,7 +217,16 @@ module.exports = ( app, settings ) => {
response.send( 'ok' );
}
} else {
response.send( 'unauthorised' );
response.status( 403 ).send( 'unauthorised' );
}
} );
app.post( '/user/settings', bodyParser.json(), ( req, res ) => {
if ( req.session.username ) {
db.writeDataSimple( 'users', 'email', req.session.username, req.body );
res.send( 'ok' );
} else {
res.status( 403 ).send( 'unauthorised' );
}
} );

View File

@@ -12,7 +12,6 @@ import { defineStore } from "pinia";
export const useBackendStore = defineStore ( 'backend', {
state: () => ( { 'guestPurchase': false, 'guestPurchaseAllowed': false } ),
getters: {
getVisitedSetupPages: ( state ) => state.visitedSetupPages,
getIsGuestPurchase: ( state ) => state.guestPurchase,
getIsGuestPurchaseAllowed: ( state ) => state.guestPurchaseAllowed,
},

View File

@@ -1,45 +1,79 @@
<template>
<div>
<h1>Account</h1>
<p>Welcome, {{ accountData.first_name }} {{ accountData.name }}!</p>
<p>Welcome, {{ accountData.first_name }} {{ accountData.name }}! <button @click="logout()">Log out</button></p>
<button @click="resendMailConfirmation()" v-if="!accountData.mail_confirmed">Resend confirmation email</button>
<table class="userData">
<tr>
<td>
Email
</td>
<td>
{{ accountData.email }}
</td>
</tr>
<tr>
<td>
Name
</td>
<td>
{{ accountData.first_name }} {{ accountData.name }}
</td>
</tr>
<tr>
<td>
Email notifications
</td>
<td>
<div v-if="accountData.marketing">Enabled</div>
<div v-else>Disabled</div>
</td>
</tr>
<tr>
<td>
Two-Factor Authentication
</td>
<td>
<div v-if="accountData.two_fa == 'enhanced'">Enhanced</div>
<div v-else-if="accountData.two_fa == 'standard'">Standard</div>
<div v-else>Disabled</div>
</td>
</tr>
</table>
<div class="userData-wrapper">
<table class="userData">
<tr>
<td>
Email
</td>
<td>
<div v-if="!isEditingAccount">
{{ accountData.email }}
</div>
<div v-else>
<input type="email" name="email" id="email" v-model="accountData.email" @keyup="emailLiveChecker()"><br><br>
<p v-if="emailStatus" class="email-status">{{ emailStatus }}</p>
</div>
</td>
</tr>
<tr>
<td>
Name
</td>
<td>
<div v-if="!isEditingAccount">
{{ accountData.first_name }} {{ accountData.name }}
</div>
<div v-else>
<input type="text" name="first_name" id="first_name" v-model="accountData.first_name">
<input type="text" name="name" id="name" v-model="accountData.name">
</div>
</td>
</tr>
<tr>
<td>
Email notifications
</td>
<td>
<div v-if="!isEditingAccount">
<div v-if="accountData.marketing == 'true'">Enabled</div>
<div v-else>Disabled</div>
</div>
<select name="emailNotification" id="emailNotification" v-model="accountData.marketing" v-else>
<option value="true">Enabled</option>
<option value="false">Disabled</option>
</select>
</td>
</tr>
<tr v-if="accountData.mail_confirmed && twoFASetting !== 'disable'">
<td>
Two-Factor Authentication
</td>
<td>
<div v-if="!isEditingAccount">
<div v-if="accountData.two_fa == 'enhanced'">Enhanced</div>
<div v-else-if="accountData.two_fa == 'standard'">Standard</div>
<div v-else>Disabled</div>
</div>
<select name="two_fa" id="two_fa" v-model="accountData.two_fa" v-else>
<option value="enhanced">Enhanced</option>
<option value="standard">Standard</option>
<option value="disabled" v-if="twoFASetting === 'allow'">Disabled</option>
</select>
</td>
</tr>
</table>
<div>
<button @click="toggleEditing">
<div v-if="!isEditingAccount">Edit</div>
<div v-else>Cancel</div>
</button>
<button @click="save()" v-if="isEditingAccount">Save</button>
</div>
</div>
<notifications ref="notification" location="topright" size="bigger"></notifications>
<popups ref="popups" size="big" @data="data => { savePwd( data ) }"></popups>
</div>
@@ -53,9 +87,23 @@
</style>
<style scoped>
.userData-wrapper {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.userData {
width: 50%;
}
.email-status {
margin-top: -10px;
color: red;
font-style: italic;
margin-bottom: 20px;
}
</style>
<script>
@@ -68,6 +116,9 @@
data () {
return {
accountData: {},
isEditingAccount: false,
emailStatus: '',
twoFASetting: 'allow',
}
},
components: {
@@ -78,6 +129,38 @@
...mapStores( useUserStore )
},
methods: {
logout() {
fetch( '/user/logout' ).then( res => {
this.$router.push( '/login' );
this.userStore.setUserAuth( false );
} );
},
toggleEditing () {
if ( this.isEditingAccount ) {
this.loadData();
} else {
this.emailLiveChecker();
}
this.isEditingAccount = !this.isEditingAccount;
},
save() {
let fetchOptions = {
method: 'post',
body: JSON.stringify( this.accountData ),
headers: {
'Content-Type': 'application/json',
'charset': 'utf-8'
}
};
fetch( localStorage.getItem( 'url' ) + '/user/settings', fetchOptions ).then( res => {
if ( res.status === 200 ) {
this.$refs.notification.createNotification( 'Settings updated!', 5, 'ok', 'normal' );
this.isEditingAccount = false;
} else {
this.$refs.notification.createNotification( 'An error occurred whilst updating the settings. Please retry', 20, 'error', 'normal' );
}
} );
},
resendMailConfirmation() {
fetch( localStorage.getItem( 'url' ) + '/user/resendEmail' ).then( res => {
if ( res.status === 200 ) {
@@ -86,42 +169,101 @@
this.$refs.notification.createNotification( 'An error occurred whilst sending the confirmation mail. Please retry', 20, 'error', 'normal' );
}
} );
},
emailLiveChecker () {
setTimeout( () => {
if ( this.checkEmail() ) {
this.emailStatus = '';
} else {
this.emailStatus = 'Invalid email address';
}
}, 100 );
},
checkEmail () {
const mail = this.accountData.email ?? '';
let stat = { 'atPos': 0, 'topLevelPos': 0 };
for ( let l in mail ) {
if ( stat[ 'atPos' ] > 0 ) {
if ( mail[ l ] === '@' ) {
return false;
} else if ( mail[ l ] === '.' ) {
if ( stat[ 'topLevelPos' ] > 0 ) {
if ( l > stat[ 'topLevelPos' ] + 2 ) {
stat[ 'topLevelPos' ] = parseInt( l );
} else {
return false;
}
} else {
if ( l > stat[ 'atPos' ] + 2 ) {
stat[ 'topLevelPos' ] = parseInt( l );
} else {
return false;
}
}
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) ) ) {
return false
}
} else {
if ( mail[ l ] === '@' ) {
if ( l > 2 ) {
stat[ 'atPos' ] = parseInt( l );
} else {
return false;
}
} else if ( !( /[a-z]/.test( mail[ l ] ) || /[A-Z]/.test( mail[ l ] ) || /[1-9]/.test( mail[ l ] ) || mail[ l ] === '.' ) ) {
return false;
}
}
}
if ( mail.length > stat[ 'topLevelPos' ] + 2 && stat[ 'topLevelPos' ] > 0 && stat[ 'atPos' ] > 0 ) {
return true;
} else {
return false;
}
},
loadData () {
// TODO: FUTURE Also get all orders of user (using join functions)
fetch( localStorage.getItem( 'url' ) + '/settings/2fa' ).then( res => {
if ( res.status === 200 ) {
res.text().then( text => {
this.twoFASetting = text;
} );
}
} );
fetch( localStorage.getItem( 'url' ) + '/user/details' ).then( res => {
if ( res.status === 200 ) {
res.json().then( data => {
if ( data.status ) {
this.accountData = data.data;
if ( !this.accountData.two_fa ) {
this.accountData.two_fa = 'disabled';
}
if ( !data.data.mail_confirmed ) {
setTimeout( () => {
this.$refs.notification.createNotification( 'Your account is unverified. Please confirm your email using the link we have sent to your email!', 20, 'info', 'normal' );
}, 1000 );
}
} else {
this.userStore.setUserAuth( false );
this.userStore.setUser2fa( false );
this.$router.push( '/login' );
}
} );
} else if ( res.status === 403 || res.status === 404 || res.status === 500 ) {
this.userStore.setUserAuth( false );
this.userStore.setUser2fa( false );
this.$router.push( '/login' );
}
} ).catch( err => {
console.warn( '[ AccountView ] Loading failed with the following message: ' + err );
} );
if ( this.userStore.getUserTwoFACompliant ) {
this.userStore.setUser2fa( false );
}
}
},
created () {
// TODO: FUTURE Also get all orders of user (using join functions)
fetch( localStorage.getItem( 'url' ) + '/user/details' ).then( res => {
if ( res.status === 200 ) {
res.json().then( data => {
if ( data.status ) {
this.accountData = data.data;
if ( this.accountData.marketing ) {
this.accountData.marketing = true;
} else {
this.accountData.marketing = false;
}
if ( !data.data.mail_confirmed ) {
setTimeout( () => {
this.$refs.notification.createNotification( 'Your account is unverified. Please confirm your email using the link we have sent to your email!', 20, 'info', 'normal' );
}, 1000 );
}
} else {
this.userStore.setUserAuth( false );
this.userStore.setUser2fa( false );
this.$router.push( '/login' );
}
} );
} else if ( res.status === 403 || res.status === 404 || res.status === 500 ) {
this.userStore.setUserAuth( false );
this.userStore.setUser2fa( false );
this.$router.push( '/login' );
}
} ).catch( err => {
console.warn( '[ AccountView ] Loading failed with the following message: ' + err );
} );
if ( this.userStore.getUserTwoFACompliant ) {
this.userStore.setUser2fa( false );
}
this.loadData();
}
}
</script>