Account deletion working

This commit is contained in:
2021-01-30 02:34:40 +11:00
parent 6468b02647
commit 9b71688903
12 changed files with 178 additions and 5 deletions
+1
View File
@@ -10,3 +10,4 @@ DB_HOSTNAME=127.0.0.1
DB_DATABASE=template
DB_USERNAME=template
DB_PASSWORD=pikachu
DB_TIMEZONE=Australia/Sydney
+2 -1
View File
@@ -23,8 +23,9 @@ This should get the template working in development mode.
- ~~validate email~~
- ~~login (with cookies)~~
- ~~logout (with cookies)~~
- account deletion and management
- ~~account deletion~~
- annoying "This site uses cookies" message
- CSS template?
- Administration Panel
- Exclusive to admin accounts
- ban/unban accounts
+14 -2
View File
@@ -1,9 +1,21 @@
import React from 'react';
import React, { useState } from 'react';
import { Redirect } from 'react-router-dom';
import { useCookies } from 'react-cookie';
import DeleteAccount from '../panels/delete-account';
const Account = props => {
const [cookies, setCookie] = useCookies(['loggedin']);
//check for logged in redirect
if (!cookies['loggedin']) {
return <Redirect to='/' />;
}
return (
<div className='page'>
<h1 className='middle centered'>Account</h1>
<h1 className='centered'>Account</h1>
<DeleteAccount />
</div>
);
};
+1 -1
View File
@@ -18,7 +18,7 @@ const LogIn = props => {
return (
<div className='page'>
<h1 className='middle centered'>Login</h1>
<h1 className='centered'>Login</h1>
<form className='constricted' onSubmit={
evt => {
evt.preventDefault();
+1 -1
View File
@@ -19,7 +19,7 @@ const SignUp = props => {
return (
<div className='page'>
<h1 className='middle centered'>Signup</h1>
<h1 className='centered'>Signup</h1>
<form className='constricted' onSubmit={
evt => {
evt.preventDefault();
@@ -0,0 +1,51 @@
import React, { useState } from 'react';
//DOCS: isolated the delete account button into it's own panel, so it can be easily moved as needed
const DeleteAccount = props => {
const [open, setOpen] = useState(false);
if (!open) {
return <button onClick={() => setOpen(true)}>Delete Account</button>
}
let passwordElement;
return (
<form className='constricted' onSubmit={async evt => {
evt.preventDefault();
const password = passwordElement.value;
passwordElement.value = '';
await handleSubmit(password);
}}>
<div>
<label htmlFor="password">Password:</label>
<input type="password" name="password" ref={e => passwordElement = e} />
</div>
<button type='submit'>Delete Account</button>
<button type='cancel' onClick={() => { passwordElement.value = ''; setOpen(false); }}>Cancel</button>
</form>
);
};
const handleSubmit = async (password) => {
//generate a new formdata payload
let formData = new FormData();
formData.append('password', password);
const result = await fetch('/api/accounts/deletion', { method: 'POST', body: formData });
if (!result.ok) {
alert(await result.text());
} else {
//force logout
fetch('/api/accounts/logout', { method: 'POST' })
.then(alert(await result.text()))
.then(() => window.location.reload(true)) //BUFGIX: force reload of the header element
.catch(e => console.error(e))
;
}
};
export default DeleteAccount;
+46
View File
@@ -17,6 +17,7 @@
"express-formidable": "^1.2.0",
"express-session": "^1.17.1",
"mariadb": "^2.5.2",
"node-cron": "^2.0.3",
"nodemailer": "^6.4.17",
"react-cookie": "^4.0.3",
"regenerator-runtime": "^0.13.7",
@@ -5722,6 +5723,19 @@
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==",
"dev": true
},
"node_modules/node-cron": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-2.0.3.tgz",
"integrity": "sha512-eJI+QitXlwcgiZwNNSRbqsjeZMp5shyajMR81RZCqeW0ZDEj4zU9tpd4nTh/1JsBiKbF8d08FCewiipDmVIYjg==",
"hasInstallScript": true,
"dependencies": {
"opencollective-postinstall": "^2.0.0",
"tz-offset": "0.0.1"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/node-forge": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
@@ -5986,6 +6000,14 @@
"node": ">=6"
}
},
"node_modules/opencollective-postinstall": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
"integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
"bin": {
"opencollective-postinstall": "index.js"
}
},
"node_modules/opener": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
@@ -8061,6 +8083,11 @@
"is-typedarray": "^1.0.0"
}
},
"node_modules/tz-offset": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/tz-offset/-/tz-offset-0.0.1.tgz",
"integrity": "sha512-kMBmblijHJXyOpKzgDhKx9INYU4u4E1RPMB0HqmKSgWG8vEcf3exEfLh4FFfzd3xdQOw9EuIy/cP0akY6rHopQ=="
},
"node_modules/uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
@@ -14298,6 +14325,15 @@
}
}
},
"node-cron": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-2.0.3.tgz",
"integrity": "sha512-eJI+QitXlwcgiZwNNSRbqsjeZMp5shyajMR81RZCqeW0ZDEj4zU9tpd4nTh/1JsBiKbF8d08FCewiipDmVIYjg==",
"requires": {
"opencollective-postinstall": "^2.0.0",
"tz-offset": "0.0.1"
}
},
"node-forge": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
@@ -14503,6 +14539,11 @@
"mimic-fn": "^2.1.0"
}
},
"opencollective-postinstall": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
"integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q=="
},
"opener": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
@@ -16219,6 +16260,11 @@
"is-typedarray": "^1.0.0"
}
},
"tz-offset": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/tz-offset/-/tz-offset-0.0.1.tgz",
"integrity": "sha512-kMBmblijHJXyOpKzgDhKx9INYU4u4E1RPMB0HqmKSgWG8vEcf3exEfLh4FFfzd3xdQOw9EuIy/cP0akY6rHopQ=="
},
"uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+1
View File
@@ -32,6 +32,7 @@
"express-formidable": "^1.2.0",
"express-session": "^1.17.1",
"mariadb": "^2.5.2",
"node-cron": "^2.0.3",
"nodemailer": "^6.4.17",
"react-cookie": "^4.0.3",
"regenerator-runtime": "^0.13.7",
+52
View File
@@ -0,0 +1,52 @@
//libraries
const utils = require('util');
const bcrypt = require('bcryptjs');
var cron = require('node-cron');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const { accounts } = require('../database/models');
//api/accounts/deletion
const route = async (req, res) => {
//make sure the account is logged in
if (req.cookies['loggedin'] !== process.env.WEB_ADDRESS) {
return res.status(401).send('invalid session status');
}
//compare the user's password
const compare = utils.promisify(bcrypt.compare);
const match = await compare(req.fields.password, req.session.account.hash);
if (!match) {
return res.status(401).send('incorrect password');
}
//set the deletion time (2 days from now)
const interval = new Date(new Date().setDate(new Date().getDate() + 2)); //wow
await accounts.update({
deletion: interval
},
{
where: {
id: req.session.account.id
}
});
//finally
return res.status(200).send('account will be deleted in two days - log in to cancel');
};
//actually delete the accounts
cron.schedule('0 * * * *', () => {
console.log('wiping accounts');
accounts.destroy({
where: {
deletion: {
[Op.lt]: Sequelize.fn('NOW')
}
}
});
});
module.exports = route;
+1
View File
@@ -6,5 +6,6 @@ router.post('/signup', require('./signup'));
router.get('/validation', require('./validation'));
router.post('/login', require('./login'));
router.post('/logout', require('./logout'));
router.post('/deletion', require('./deletion'));
module.exports = router;
+7
View File
@@ -40,6 +40,13 @@ const route = async (req, res) => {
req.session.account = account;
res.cookie('loggedin', process.env.WEB_ADDRESS);
//cancel deletion if any
await accounts.update({ deletion: null }, {
where: {
id: account.id
}
});
//finally
res.status(200).send('login succeeded');
};
+1
View File
@@ -3,6 +3,7 @@ const Sequelize = require('sequelize');
const sequelize = new Sequelize(process.env.DB_DATABASE, process.env.DB_USERNAME, process.env.DB_PASSWORD, {
host: process.env.DB_HOSTADDR,
dialect: 'mariadb',
timezone: process.env.DB_TIMEZONE,
logging: false
});