From 1c3c30a86c65d8ab89fa92ec32891dd2afdf5e93 Mon Sep 17 00:00:00 2001 From: Hom Yan Date: Tue, 28 Sep 2021 16:56:58 +0800 Subject: [PATCH] feat: reupload cloud backup after email & phone change success --- .../components/dialogs/SettingEmailDialog.tsx | 95 +++++++++++++++--- .../dialogs/SettingPhoneNumberDialog.tsx | 99 +++++++++++++++---- 2 files changed, 161 insertions(+), 33 deletions(-) diff --git a/packages/dashboard/src/pages/Settings/components/dialogs/SettingEmailDialog.tsx b/packages/dashboard/src/pages/Settings/components/dialogs/SettingEmailDialog.tsx index 56d38b6ab48e..7966262e34c6 100644 --- a/packages/dashboard/src/pages/Settings/components/dialogs/SettingEmailDialog.tsx +++ b/packages/dashboard/src/pages/Settings/components/dialogs/SettingEmailDialog.tsx @@ -4,10 +4,21 @@ import { Box, Typography } from '@material-ui/core' import { makeStyles } from '@masknet/theme' import { UserContext } from '../../hooks/UserContext' import { useDashboardI18N } from '../../../../locales' -import { sendCode, useLanguage, verifyCode } from '../../api' +import { + fetchBackupValue, + fetchDownloadLink, + fetchUploadLink, + sendCode, + uploadBackupValue, + useLanguage, + verifyCode, + VerifyCodeRequest, +} from '../../api' import { emailRegexp } from '../../regexp' import { CountdownButton, MaskTextField, useSnackbar } from '@masknet/theme' import { Locale, Scenario, AccountType } from '../../type' +import { decryptBackup, encryptBackup } from '@masknet/backup-format' +import { encode } from '@msgpack/msgpack' const useStyles = makeStyles()({ container: { @@ -34,6 +45,12 @@ export default function SettingEmailDialog({ open, onClose }: SettingEmailDialog const [invalidEmail, setInvalidEmail] = useState(false) const [invalidCode, setInvalidCode] = useState(false) + // cloud backup + const [backupInfo, setBackupInfo] = useState({ + url: '', + abstract: '', + }) + const sendButton = useRef(null) const handleClose = () => { @@ -47,11 +64,19 @@ export default function SettingEmailDialog({ open, onClose }: SettingEmailDialog validCheck() } } else { - const result = await verifyCode({ + const params = { account: email, type: AccountType.email, code, - }).catch((err) => { + } + + if (step === 0) { + return checkCloudBackup(params) + } else if (backupInfo.abstract) { + return uploadCloudBackup(params) + } + + const result = await verifyCode(params).catch((err) => { if (err.status === 400) { // incorrect code setInvalidCode(true) @@ -59,22 +84,62 @@ export default function SettingEmailDialog({ open, onClose }: SettingEmailDialog }) if (result) { - if (step === 0) { - // original email verified - setEmail('') - setCode('') - setStep(1) - } else { - const msg = user.email ? t.settings_alert_email_updated() : t.settings_alert_email_set() - snackbar.enqueueSnackbar(msg, { variant: 'success' }) - - updateUser({ email }) - onClose() - } + onSuccess() } } } + const originalEmailVerified = () => { + setEmail('') + setCode('') + setStep(1) + } + + const onSuccess = () => { + const msg = user.email ? t.settings_alert_email_updated() : t.settings_alert_email_set() + snackbar.enqueueSnackbar(msg, { variant: 'success' }) + + updateUser({ email }) + onClose() + } + + const checkCloudBackup = async (params: VerifyCodeRequest) => { + const res = await fetchDownloadLink(params).catch((error) => { + if (error.status === 400) { + setInvalidCode(true) + } else if (error.status === 404) { + // no cloud backup file + originalEmailVerified() + } + }) + + if (res) { + originalEmailVerified() + setBackupInfo({ + url: res.downloadURL, + abstract: res.abstract, + }) + } + } + + const uploadCloudBackup = async (params: VerifyCodeRequest) => { + if (!user.email || !user.backupPassword) return + + const encrypted = await fetchBackupValue(backupInfo.url) + const decrypted = await decryptBackup(encode(user.email + user.backupPassword), encrypted) + const data = await encryptBackup(encode(email + user.backupPassword), decrypted) + const uploadUrl = await fetchUploadLink({ ...params, abstract: backupInfo.abstract }).catch((error) => { + if (error.status === 400) { + setInvalidCode(true) + } + }) + + if (uploadUrl) { + await uploadBackupValue(uploadUrl, data) + onSuccess() + } + } + const validCheck = () => { if (!email) return setInvalidEmail(true) diff --git a/packages/dashboard/src/pages/Settings/components/dialogs/SettingPhoneNumberDialog.tsx b/packages/dashboard/src/pages/Settings/components/dialogs/SettingPhoneNumberDialog.tsx index bbab26ee901f..205229e50363 100644 --- a/packages/dashboard/src/pages/Settings/components/dialogs/SettingPhoneNumberDialog.tsx +++ b/packages/dashboard/src/pages/Settings/components/dialogs/SettingPhoneNumberDialog.tsx @@ -4,10 +4,21 @@ import { Box, Typography } from '@material-ui/core' import { makeStyles } from '@masknet/theme' import { UserContext } from '../../hooks/UserContext' import { useDashboardI18N } from '../../../../locales' -import { sendCode, useLanguage, verifyCode } from '../../api' import { phoneRegexp } from '../../regexp' import { CountdownButton, MaskTextField, useSnackbar } from '@masknet/theme' import { Scenario, Locale, AccountType } from '../../type' +import { + fetchBackupValue, + fetchDownloadLink, + fetchUploadLink, + sendCode, + uploadBackupValue, + useLanguage, + verifyCode, + VerifyCodeRequest, +} from '../../api' +import { decryptBackup, encryptBackup } from '@masknet/backup-format' +import { encode } from '@msgpack/msgpack' const useStyles = makeStyles()({ container: { @@ -35,6 +46,12 @@ export default function SettingPhoneNumberDialog({ open, onClose }: SettingPhone const [invalidPhone, setInvalidPhone] = useState(false) const [invalidCode, setInvalidCode] = useState(false) + // cloud backup + const [backupInfo, setBackupInfo] = useState({ + url: '', + abstract: '', + }) + const sendButton = useRef(null) const handleCountryCodeChange = (event: React.ChangeEvent) => { @@ -54,11 +71,19 @@ export default function SettingPhoneNumberDialog({ open, onClose }: SettingPhone validCheck() } } else { - const result = await verifyCode({ + const params = { account: countryCode + phone, type: AccountType.phone, code, - }).catch((err) => { + } + + if (step === 0) { + return checkCloudBackup(params) + } else if (backupInfo.abstract) { + return uploadCloudBackup(params) + } + + const result = await verifyCode(params).catch((err) => { if (err.status === 400) { // incorrect code setInvalidCode(true) @@ -66,22 +91,60 @@ export default function SettingPhoneNumberDialog({ open, onClose }: SettingPhone }) if (result) { - if (step === 0) { - // original email verified - setCountryCode('+1') - setPhone('') - setCode('') - setStep(1) - } else { - const msg = user.phone - ? t.settings_alert_phone_number_updated() - : t.settings_alert_phone_number_set() - snackbar.enqueueSnackbar(msg, { variant: 'success' }) - - updateUser({ phone: `${countryCode} ${phone}` }) - onClose() - } + onSuccess() + } + } + } + + const originalPhoneVerified = () => { + setCountryCode('+1') + setPhone('') + setCode('') + setStep(1) + } + + const onSuccess = () => { + const msg = user.phone ? t.settings_alert_phone_number_updated() : t.settings_alert_phone_number_set() + snackbar.enqueueSnackbar(msg, { variant: 'success' }) + + updateUser({ phone: `${countryCode} ${phone}` }) + onClose() + } + + const checkCloudBackup = async (params: VerifyCodeRequest) => { + const res = await fetchDownloadLink(params).catch((error) => { + if (error.status === 400) { + setInvalidCode(true) + } else if (error.status === 404) { + // no cloud backup file + originalPhoneVerified() } + }) + + if (res) { + originalPhoneVerified() + setBackupInfo({ + url: res.downloadURL, + abstract: res.abstract, + }) + } + } + + const uploadCloudBackup = async (params: VerifyCodeRequest) => { + if (!user.phone || !user.backupPassword) return + + const encrypted = await fetchBackupValue(backupInfo.url) + const decrypted = await decryptBackup(encode(user.phone.replace(' ', '') + user.backupPassword), encrypted) + const data = await encryptBackup(encode(countryCode + phone + user.backupPassword), decrypted) + const uploadUrl = await fetchUploadLink({ ...params, abstract: backupInfo.abstract }).catch((error) => { + if (error.status === 400) { + setInvalidCode(true) + } + }) + + if (uploadUrl) { + await uploadBackupValue(uploadUrl, data) + onSuccess() } }