Account deletion working
This commit is contained in:
@@ -10,3 +10,4 @@ DB_HOSTNAME=127.0.0.1
|
||||
DB_DATABASE=template
|
||||
DB_USERNAME=template
|
||||
DB_PASSWORD=pikachu
|
||||
DB_TIMEZONE=Australia/Sydney
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
Generated
+46
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
};
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user