mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-10 04:22:27 +10:00
Merge pull request #485 from gianantoniopini/develop
Delete Account: fix for Firebase cloud function and error handling improvements
This commit is contained in:
@ -10,41 +10,53 @@ function timeout(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function asyncForEach(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await callback(array[index], index, array);
|
||||
const deleteUserFunctionHandler = async (_, { auth }) => {
|
||||
if (!auth) {
|
||||
throw new functions.https.HttpsError(
|
||||
'failed-precondition',
|
||||
'The function must be called while authenticated.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const userId = auth.uid;
|
||||
const updates = {};
|
||||
|
||||
const userResumesDataSnapshot = await admin
|
||||
.database()
|
||||
.ref('resumes')
|
||||
.orderByChild('user')
|
||||
.equalTo(userId)
|
||||
.once('value');
|
||||
const userResumes = userResumesDataSnapshot.val();
|
||||
if (userResumes) {
|
||||
Object.keys(userResumes).forEach(async (resumeId) => {
|
||||
updates[`resumes/${resumeId}`] = null;
|
||||
});
|
||||
}
|
||||
|
||||
const userDataSnapshot = await admin
|
||||
.database()
|
||||
.ref(`users/${userId}`)
|
||||
.once('value');
|
||||
const user = userDataSnapshot.val();
|
||||
if (user) {
|
||||
updates[`users/${userId}`] = null;
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await admin.database().ref().update(updates);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
throw new functions.https.HttpsError('internal', error.message);
|
||||
}
|
||||
};
|
||||
|
||||
exports.deleteUser = functions
|
||||
.runWith({ memory: '256MB' })
|
||||
.https.onCall((_, { auth }) => {
|
||||
if (!auth) {
|
||||
throw new functions.https.HttpsError(
|
||||
'failed-precondition',
|
||||
'The function must be called while authenticated.',
|
||||
);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const resumesRef = admin.database().ref('resumes');
|
||||
|
||||
resumesRef.once('value', async (snapshot) => {
|
||||
const data = snapshot.val();
|
||||
|
||||
const resumes = Object.keys(data).filter(
|
||||
(x) => data[x].user === auth.uid,
|
||||
);
|
||||
|
||||
await asyncForEach(resumes, async (id) => {
|
||||
await admin.database().ref(`resumes/${id}`).remove();
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
.https.onCall(deleteUserFunctionHandler);
|
||||
|
||||
exports.printResume = functions
|
||||
.runWith({ memory: '1GB' })
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { memo, useContext, useState } from 'react';
|
||||
import { FaAngleDown } from 'react-icons/fa';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
import { toast } from 'react-toastify';
|
||||
import UserContext from '../../../../contexts/UserContext';
|
||||
import Button from '../../../shared/Button';
|
||||
import Heading from '../../../shared/Heading';
|
||||
@ -17,6 +18,9 @@ const Settings = ({ id }) => {
|
||||
const [deleteText, setDeleteText] = useState(
|
||||
t('builder.settings.dangerZone.button'),
|
||||
);
|
||||
const [isDeleteAccountInProgress, setDeleteAccountInProgress] = useState(
|
||||
false,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { deleteAccount } = useContext(UserContext);
|
||||
@ -34,16 +38,21 @@ const Settings = ({ id }) => {
|
||||
dispatch({ type: 'change_language', payload: lang });
|
||||
};
|
||||
|
||||
const handleDeleteAccount = () => {
|
||||
const handleDeleteAccount = async () => {
|
||||
if (deleteText === t('builder.settings.dangerZone.button')) {
|
||||
setDeleteText(t('shared.buttons.confirmation'));
|
||||
return;
|
||||
}
|
||||
|
||||
setDeleteText('Buh bye! :(');
|
||||
setTimeout(() => {
|
||||
deleteAccount();
|
||||
}, 500);
|
||||
setDeleteAccountInProgress(true);
|
||||
|
||||
try {
|
||||
await deleteAccount();
|
||||
} catch (error) {
|
||||
toast.error('An error occurred deleting your account.');
|
||||
setDeleteAccountInProgress(false);
|
||||
setDeleteText(t('builder.settings.dangerZone.button'));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -96,7 +105,11 @@ const Settings = ({ id }) => {
|
||||
<p className="leading-loose">{t('builder.settings.dangerZone.text')}</p>
|
||||
|
||||
<div className="mt-4 flex">
|
||||
<Button isDelete onClick={handleDeleteAccount}>
|
||||
<Button
|
||||
isDelete
|
||||
onClick={handleDeleteAccount}
|
||||
isLoading={isDeleteAccountInProgress}
|
||||
>
|
||||
{deleteText}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -73,19 +73,55 @@ const UserProvider = ({ children }) => {
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
const reauthenticateWithGoogle = async () => {
|
||||
const { currentUser } = firebase.auth();
|
||||
const provider = new firebase.auth.GoogleAuthProvider();
|
||||
|
||||
try {
|
||||
const userCredential = await currentUser.reauthenticateWithPopup(
|
||||
provider,
|
||||
);
|
||||
return userCredential;
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const reauthenticate = async () => {
|
||||
const { currentUser } = firebase.auth();
|
||||
|
||||
if (currentUser.isAnonymous) {
|
||||
return;
|
||||
}
|
||||
|
||||
const googleAuthProvider = new firebase.auth.GoogleAuthProvider();
|
||||
const authProviderIsGoogle =
|
||||
currentUser.providerData &&
|
||||
currentUser.providerData.length > 0 &&
|
||||
currentUser.providerData[0].providerId === googleAuthProvider.providerId;
|
||||
|
||||
if (authProviderIsGoogle) {
|
||||
await reauthenticateWithGoogle();
|
||||
} else {
|
||||
const errorMessage = 'Unable to determine reauthentication method.';
|
||||
toast.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteAccount = async () => {
|
||||
const { currentUser } = firebase.auth();
|
||||
const deleteUser = firebase.functions().httpsCallable('deleteUser');
|
||||
deleteUser();
|
||||
|
||||
await reauthenticate();
|
||||
|
||||
await deleteUser();
|
||||
|
||||
try {
|
||||
deleteUser();
|
||||
await currentUser.delete();
|
||||
} catch (e) {
|
||||
if (e.code === 'auth/requires-recent-login') {
|
||||
await loginWithGoogle();
|
||||
await currentUser.delete();
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
} finally {
|
||||
logout();
|
||||
toast(
|
||||
|
||||
Reference in New Issue
Block a user