From 4fade93b431eb16957c445b47bcbddbb68fb791a Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sun, 19 Mar 2023 15:51:04 +0530 Subject: [PATCH 01/77] Fix compilation --- src/utils/strings/frenchConstants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/strings/frenchConstants.tsx b/src/utils/strings/frenchConstants.tsx index e1f236cc7e..a073dd56f1 100644 --- a/src/utils/strings/frenchConstants.tsx +++ b/src/utils/strings/frenchConstants.tsx @@ -553,7 +553,7 @@ const frenchConstants = { LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE: 'Votre navigateur ou un complément bloque ente qui ne peut sauvegarder les données sur votre stockage local. Veuillez relancer cette page après avoir changé de mode de navigation.', RETRY: 'Réessayer', - SEND_OTT: 'Envoyer l'OTP', + SEND_OTT: "Envoyer l'OTP", EMAIl_ALREADY_OWNED: 'Cet e-mail est déjà pris', EMAIL_UDPATE_SUCCESSFUL: 'Votre e-mail a été mis à jour', UPLOAD_FAILED: 'Échec du chargement', From 6da5198c749f9d1679be6eda0589cd3187c090eb Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sun, 19 Mar 2023 15:54:23 +0530 Subject: [PATCH 02/77] Add TimerProgress componenet --- src/components/Authenicator/TimerProgress.tsx | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/components/Authenicator/TimerProgress.tsx diff --git a/src/components/Authenicator/TimerProgress.tsx b/src/components/Authenicator/TimerProgress.tsx new file mode 100644 index 0000000000..c9234f4acf --- /dev/null +++ b/src/components/Authenicator/TimerProgress.tsx @@ -0,0 +1,46 @@ +import React, { useState, useEffect } from 'react'; + +const TimerProgress = ({ period }) => { + const [progress, setProgress] = useState(0); + const [ticker, setTicker] = useState(null); + const microSecondsInPeriod = period * 1000000; + + const startTicker = () => { + const ticker = setInterval(() => { + updateTimeRemaining(); + }, 10); + setTicker(ticker); + }; + + const updateTimeRemaining = () => { + const timeRemaining = + microSecondsInPeriod - + ((new Date().getTime() * 1000) % microSecondsInPeriod); + setProgress(timeRemaining / microSecondsInPeriod); + }; + + useEffect(() => { + startTicker(); + return () => clearInterval(ticker); + }, []); + + const color = progress > 0.4 ? 'green' : 'orange'; + + return ( +
+ {issuer} +
++ {account} +
++ {code} +
++ next +
++ {nextCode} +
+No results found.
+ {/*Add a new secret to get started.
+Download ente auth mobile app to manage your secrets
*/} + => {
+ const masterKey = await getActualKey();
+ try {
+ const authKeyData = await getAuthKey();
+ const cryptoWorker = await ComlinkCryptoWorker.getInstance();
+ const authentitorKey = await cryptoWorker.decryptB64(
+ authKeyData.encryptedKey,
+ authKeyData.header,
+ masterKey
+ );
+ // always fetch all data from server for now
+ const authEntity: AuthEntity[] = await getDiff(0);
+ const authCodes = await Promise.all(
+ authEntity
+ .filter((f) => !f.isDeleted)
+ .map(async (entity) => {
+ const decryptedCode = await cryptoWorker.decryptMetadata(
+ entity.encryptedData,
+ entity.header,
+ authentitorKey
+ );
+ return Code.fromRawData(entity.id, decryptedCode);
+ })
+ );
+ // sort by issuer name which can be undefined also
+ authCodes.sort((a, b) => {
+ if (a.issuer && b.issuer) {
+ return a.issuer.localeCompare(b.issuer);
+ }
+ if (a.issuer) {
+ return -1;
+ }
+ if (b.issuer) {
+ return 1;
+ }
+ return 0;
+ });
+ return authCodes;
+ } catch (e) {
+ logError(e, 'get authenticator entities failed');
+ throw e;
+ }
+};
+
+export const getAuthKey = async () => {
+ try {
+ const resp = await HTTPService.get(
+ `${ENDPOINT}/authenticator/key`,
+ {},
+ {
+ 'X-Auth-Token': getToken(),
+ }
+ );
+ return resp.data;
+ } catch (e) {
+ logError(e, 'Get key failed');
+ throw e;
+ }
+};
+
+// return a promise which resolves to list of AuthEnitity
+export const getDiff = async (
+ sinceTime: number,
+ limit = 2500
+): Promise => {
+ try {
+ const resp = await HTTPService.get(
+ `${ENDPOINT}/authenticator/entity/diff`,
+ {
+ sinceTime,
+ limit,
+ },
+ {
+ 'X-Auth-Token': getToken(),
+ }
+ );
+ return resp.data.diff;
+ } catch (e) {
+ logError(e, 'Get diff failed');
+ throw e;
+ }
+};
From 208e6c07fe80a54b61917b7ae4a609b49e2762fc Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Mon, 20 Mar 2023 09:26:29 +0530
Subject: [PATCH 08/77] Refactor
---
src/components/Authenicator/OTPDisplay.tsx | 50 ++++++++++++----------
src/components/Sidebar/UtilitySection.tsx | 2 +-
2 files changed, 29 insertions(+), 23 deletions(-)
diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx
index b0c1faec38..9be78988c2 100644
--- a/src/components/Authenicator/OTPDisplay.tsx
+++ b/src/components/Authenicator/OTPDisplay.tsx
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { TOTP, HOTP } from 'otpauth';
+import { Code } from 'types/authenticator/code';
import TimerProgress from './TimerProgress';
const TOTPDisplay = ({ issuer, account, code, nextCode }) => {
@@ -82,59 +83,64 @@ const TOTPDisplay = ({ issuer, account, code, nextCode }) => {
);
};
-const OTPDisplay = ({
- secret,
- type,
- algorithm,
- timePeriod,
- issuer,
- account,
-}) => {
+interface OTPDisplayProps {
+ codeInfo: Code;
+}
+
+const OTPDisplay = (props: OTPDisplayProps) => {
+ const { codeInfo } = props;
const [code, setCode] = useState('');
const [nextcode, setNextCode] = useState('');
const generateCodes = () => {
const currentTime = new Date().getTime();
- if (type.toLowerCase() === 'totp') {
+ if (codeInfo.type.toLowerCase() === 'totp') {
const totp = new TOTP({
- secret,
- algorithm,
- period: timePeriod ?? 30,
+ secret: codeInfo.secret,
+ algorithm: codeInfo.algorithm,
+ period: codeInfo.period ?? Code.defaultPeriod,
+ digits: codeInfo.digits,
});
setCode(totp.generate());
setNextCode(
- totp.generate({ timestamp: currentTime + timePeriod * 1000 })
+ totp.generate({
+ timestamp: currentTime + codeInfo.period * 1000,
+ })
);
- } else if (type.toLowerCase() === 'hotp') {
- const hotp = new HOTP({ secret, counter: 0, algorithm });
+ } else if (codeInfo.type.toLowerCase() === 'hotp') {
+ const hotp = new HOTP({
+ secret: codeInfo.secret,
+ counter: 0,
+ algorithm: codeInfo.algorithm,
+ });
setCode(hotp.generate());
setNextCode(hotp.generate({ counter: 1 }));
}
};
useEffect(() => {
+ generateCodes();
let intervalId;
- // compare case insensitive type
- if (type.toLowerCase() === 'totp') {
+ if (codeInfo.type.toLowerCase() === 'totp') {
intervalId = setInterval(() => {
generateCodes();
}, 1000);
- } else if (type.toLowerCase() === 'hotp') {
+ } else if (codeInfo.type.toLowerCase() === 'hotp') {
intervalId = setInterval(() => {
generateCodes();
}, 1000);
}
return () => clearInterval(intervalId);
- }, [secret, type, algorithm, timePeriod]);
+ }, [codeInfo]);
return (
-
+
diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx
index f797b92cb1..5d1ff91420 100644
--- a/src/components/Sidebar/UtilitySection.tsx
+++ b/src/components/Sidebar/UtilitySection.tsx
@@ -99,7 +99,7 @@ export default function UtilitySection({ closeSidebar }) {
{constants.DEDUPLICATE_FILES}
- {isInternalUser() && (
+ {!isInternalUser() && (
{constants.AUTHENTICATOR_SECTION}
From 5618e65fe10e9057eb7cf5d257dcb78be2d503db Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Mon, 20 Mar 2023 09:31:12 +0530
Subject: [PATCH 09/77] [ente Authenticator] Remove hardcoded codes
---
src/pages/authenticator/index.tsx | 93 ++++++-------------------------
1 file changed, 17 insertions(+), 76 deletions(-)
diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx
index 40421483a1..05022424ab 100644
--- a/src/pages/authenticator/index.tsx
+++ b/src/pages/authenticator/index.tsx
@@ -1,83 +1,32 @@
import React, { useEffect, useState } from 'react';
import OTPDisplay from 'components/Authenicator/OTPDisplay';
-const random = [
- {
- issuer: 'Google',
- account: 'example@gmail.com',
- secret: '6GJ2E2RQKJ36BY6A',
- type: 'TOTP',
- algorithm: 'SHA1',
- period: 30,
- },
- {
- issuer: 'Facebook',
- account: 'example@gmail.com',
- secret: 'RVZJ7N6KJKJGQ2VX',
- type: 'TOTP',
- algorithm: 'SHA256',
- period: 60,
- },
- {
- issuer: 'Twitter',
- account: 'example@gmail.com',
- secret: 'ZPUE6KJ3WGZ3HPKJ',
- type: 'TOTP',
- algorithm: 'SHA256',
- period: 60,
- },
- {
- issuer: 'GitHub',
- account: 'example@gmail.com',
- secret: 'AG6U5KJYHPRRNRZI',
- type: 'TOTP',
- algorithm: 'SHA1',
- period: 30,
- },
- {
- issuer: 'Amazon',
- account: 'example@gmail.com',
- secret: 'Q2FR2KJVKJFFKMWZ',
- type: 'TOTP',
- algorithm: 'SHA256',
- period: 60,
- },
- {
- issuer: 'LinkedIn',
- account: 'example@gmail.com',
- secret: 'SWRG4KJ4J3LNDW2Z',
- type: 'TOTP',
- algorithm: 'SHA256',
- period: 60,
- },
- {
- issuer: 'Dropbox',
- account: 'example@gmail.com',
- secret: 'G5U6OKJU3JRM72ZK',
- type: 'TOTP',
- algorithm: 'SHA1',
- period: 30,
- },
-];
+import { getAuthCodes } from 'services/authenticator/authenticatorService';
const OTPPage = () => {
- const [secrets, setSecrets] = useState([]);
+ const [codes, setCodes] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
- const fetchSecrets = async () => {
+ const fetchCodes = async () => {
try {
- setSecrets(random);
+ getAuthCodes().then((res) => {
+ setCodes(res);
+ });
} catch (error) {
console.error(error);
}
};
- fetchSecrets();
+ fetchCodes();
}, []);
- const filteredSecrets = secrets.filter(
+ const filteredCodes = codes.filter(
(secret) =>
- secret.issuer.toLowerCase().includes(searchTerm.toLowerCase()) ||
- secret.account.toLowerCase().includes(searchTerm.toLowerCase())
+ (secret.issuer ?? '')
+ .toLowerCase()
+ .includes(searchTerm.toLowerCase()) ||
+ (secret.account ?? '')
+ .toLowerCase()
+ .includes(searchTerm.toLowerCase())
);
return (
@@ -98,7 +47,7 @@ const OTPPage = () => {
/>
- {filteredSecrets.length === 0 ? (
+ {filteredCodes.length === 0 ? (
{
Download ente auth mobile app to manage your secrets
*/}
) : (
- filteredSecrets.map((secret) => (
-
+ filteredCodes.map((code) => (
+
))
)}
From 937d114f7c220b8a75ad3f4adc71bf3f79952b2d Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Mon, 20 Mar 2023 10:23:14 +0530
Subject: [PATCH 10/77] Add hook to download ente auth app
---
src/pages/authenticator/index.tsx | 37 +++++++++++++++++++++++++++----
1 file changed, 33 insertions(+), 4 deletions(-)
diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx
index 05022424ab..dbb6cee99f 100644
--- a/src/pages/authenticator/index.tsx
+++ b/src/pages/authenticator/index.tsx
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
import OTPDisplay from 'components/Authenicator/OTPDisplay';
import { getAuthCodes } from 'services/authenticator/authenticatorService';
+import { Button } from '@mui/material';
const OTPPage = () => {
const [codes, setCodes] = useState([]);
@@ -29,8 +30,31 @@ const OTPPage = () => {
.includes(searchTerm.toLowerCase())
);
+ const DownloadApp = () => {
+ return (
+
+ Download our mobile app to add & manage your secrets.
+
+
+
+
+ );
+ };
+
return (
- // center the page
{
textAlign: 'center',
marginTop: '32px',
}}>
- No results found.
- {/* Add a new secret to get started.
- Download ente auth mobile app to manage your secrets
*/}
+ {searchTerm.length !== 0 ? (
+ No results found.
+ ) : (
+
+ )}
) : (
filteredCodes.map((code) => (
))
)}
+
+
+
);
};
From aaa3a273c0c5d7c1494101a886799d33e0fb5f50 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Mon, 20 Mar 2023 10:26:40 +0530
Subject: [PATCH 11/77] Show authenticator section to internal users only
---
src/components/Sidebar/UtilitySection.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx
index 5d1ff91420..f797b92cb1 100644
--- a/src/components/Sidebar/UtilitySection.tsx
+++ b/src/components/Sidebar/UtilitySection.tsx
@@ -99,7 +99,7 @@ export default function UtilitySection({ closeSidebar }) {
{constants.DEDUPLICATE_FILES}
- {!isInternalUser() && (
+ {isInternalUser() && (
{constants.AUTHENTICATOR_SECTION}
From 7c8739a57b351ef12ee660768b19a5123d469bcf Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Mon, 20 Mar 2023 10:43:17 +0530
Subject: [PATCH 12/77] [ente auth] redirect to root if key is missing
---
src/pages/authenticator/index.tsx | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx
index dbb6cee99f..c5f5fab0b5 100644
--- a/src/pages/authenticator/index.tsx
+++ b/src/pages/authenticator/index.tsx
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
import OTPDisplay from 'components/Authenicator/OTPDisplay';
import { getAuthCodes } from 'services/authenticator/authenticatorService';
import { Button } from '@mui/material';
+import { CustomError } from 'utils/error';
const OTPPage = () => {
const [codes, setCodes] = useState([]);
@@ -10,10 +11,20 @@ const OTPPage = () => {
useEffect(() => {
const fetchCodes = async () => {
try {
- getAuthCodes().then((res) => {
- setCodes(res);
- });
+ getAuthCodes()
+ .then((res) => {
+ setCodes(res);
+ })
+ .catch((err) => {
+ if (err.message === CustomError.KEY_MISSING) {
+ window.location.href = '/';
+ return;
+ }
+
+ console.error('something wrong here', err);
+ });
} catch (error) {
+ console.error('something wrong where asdas');
console.error(error);
}
};
From e8d8ffacc995287c6348773e831d406c8abbc4b7 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Mon, 20 Mar 2023 11:00:33 +0530
Subject: [PATCH 13/77] Fix routing on redirect to creds page
---
src/pages/authenticator/index.tsx | 9 +++++++--
src/pages/credentials/index.tsx | 4 +++-
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx
index c5f5fab0b5..967be144b8 100644
--- a/src/pages/authenticator/index.tsx
+++ b/src/pages/authenticator/index.tsx
@@ -3,8 +3,11 @@ import OTPDisplay from 'components/Authenicator/OTPDisplay';
import { getAuthCodes } from 'services/authenticator/authenticatorService';
import { Button } from '@mui/material';
import { CustomError } from 'utils/error';
+import { PAGES } from 'constants/pages';
+import { useRouter } from 'next/router';
const OTPPage = () => {
+ const router = useRouter();
const [codes, setCodes] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
@@ -17,10 +20,12 @@ const OTPPage = () => {
})
.catch((err) => {
if (err.message === CustomError.KEY_MISSING) {
- window.location.href = '/';
+ router.push({
+ pathname: PAGES.CREDENTIALS,
+ query: { redirectPage: PAGES.AUTHENICATOR },
+ });
return;
}
-
console.error('something wrong here', err);
});
} catch (error) {
diff --git a/src/pages/credentials/index.tsx b/src/pages/credentials/index.tsx
index 02c54f3894..2e3359a839 100644
--- a/src/pages/credentials/index.tsx
+++ b/src/pages/credentials/index.tsx
@@ -30,6 +30,8 @@ import VerifyMasterPasswordForm, {
export default function Credentials() {
const router = useRouter();
+ const routeReidrectPage =
+ router.query.redirectPage?.toString() ?? PAGES.GALLERY;
const [keyAttributes, setKeyAttributes] = useState();
const appContext = useContext(AppContext);
const [user, setUser] = useState();
@@ -85,7 +87,7 @@ export default function Credentials() {
await decryptAndStoreToken(key);
const redirectURL = appContext.redirectURL;
appContext.setRedirectURL(null);
- router.push(redirectURL ?? PAGES.GALLERY);
+ router.push(redirectURL ?? routeReidrectPage ?? PAGES.GALLERY);
} catch (e) {
logError(e, 'useMasterPassword failed');
}
From 02e2de1ef588733eb2c743339113f9418aa3b43a Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Mon, 20 Mar 2023 11:04:50 +0530
Subject: [PATCH 14/77] Hide search box when there's no entry
---
src/pages/authenticator/index.tsx | 17 +++++++++++------
.../authenticator/authenticatorService.ts | 3 +++
2 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx
index 967be144b8..f40cf5ef15 100644
--- a/src/pages/authenticator/index.tsx
+++ b/src/pages/authenticator/index.tsx
@@ -78,13 +78,18 @@ const OTPPage = () => {
alignItems: 'center',
justifyContent: 'flex-start',
}}>
+
ente Authenticator
- setSearchTerm(e.target.value)}
- />
+ {filteredCodes.length === 0 && searchTerm.length === 0 ? (
+ <>>
+ ) : (
+ setSearchTerm(e.target.value)}
+ />
+ )}
{filteredCodes.length === 0 ? (
diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts
index ebe80149e9..8027661bd2 100644
--- a/src/services/authenticator/authenticatorService.ts
+++ b/src/services/authenticator/authenticatorService.ts
@@ -44,6 +44,9 @@ export const getAuthCodes = async (): Promise => {
}
return 0;
});
+ // remove all entries from authCodes
+ authCodes.splice(0, authCodes.length);
+
return authCodes;
} catch (e) {
logError(e, 'get authenticator entities failed');
From 6d094b3bcad17487a02f03b24e38998fc7001e81 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Mon, 20 Mar 2023 11:05:57 +0530
Subject: [PATCH 15/77] Undo test changes
---
src/services/authenticator/authenticatorService.ts | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts
index 8027661bd2..ebe80149e9 100644
--- a/src/services/authenticator/authenticatorService.ts
+++ b/src/services/authenticator/authenticatorService.ts
@@ -44,9 +44,6 @@ export const getAuthCodes = async (): Promise => {
}
return 0;
});
- // remove all entries from authCodes
- authCodes.splice(0, authCodes.length);
-
return authCodes;
} catch (e) {
logError(e, 'get authenticator entities failed');
From 134cd8343bc83204c53e03e3a64c86b4487e9569 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Mon, 20 Mar 2023 11:58:39 +0530
Subject: [PATCH 16/77] Handle error in generating code
---
src/components/Authenicator/OTPDisplay.tsx | 77 ++++++++++++-------
.../authenticator/authenticatorService.ts | 13 +++-
2 files changed, 61 insertions(+), 29 deletions(-)
diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx
index 9be78988c2..f476dc4ffa 100644
--- a/src/components/Authenicator/OTPDisplay.tsx
+++ b/src/components/Authenicator/OTPDisplay.tsx
@@ -91,30 +91,35 @@ const OTPDisplay = (props: OTPDisplayProps) => {
const { codeInfo } = props;
const [code, setCode] = useState('');
const [nextcode, setNextCode] = useState('');
+ const [codeErr, setCodeErr] = useState('');
const generateCodes = () => {
- const currentTime = new Date().getTime();
- if (codeInfo.type.toLowerCase() === 'totp') {
- const totp = new TOTP({
- secret: codeInfo.secret,
- algorithm: codeInfo.algorithm,
- period: codeInfo.period ?? Code.defaultPeriod,
- digits: codeInfo.digits,
- });
- setCode(totp.generate());
- setNextCode(
- totp.generate({
- timestamp: currentTime + codeInfo.period * 1000,
- })
- );
- } else if (codeInfo.type.toLowerCase() === 'hotp') {
- const hotp = new HOTP({
- secret: codeInfo.secret,
- counter: 0,
- algorithm: codeInfo.algorithm,
- });
- setCode(hotp.generate());
- setNextCode(hotp.generate({ counter: 1 }));
+ try {
+ const currentTime = new Date().getTime();
+ if (codeInfo.type.toLowerCase() === 'totp') {
+ const totp = new TOTP({
+ secret: codeInfo.secret,
+ algorithm: codeInfo.algorithm,
+ period: codeInfo.period ?? Code.defaultPeriod,
+ digits: codeInfo.digits,
+ });
+ setCode(totp.generate());
+ setNextCode(
+ totp.generate({
+ timestamp: currentTime + codeInfo.period * 1000,
+ })
+ );
+ } else if (codeInfo.type.toLowerCase() === 'hotp') {
+ const hotp = new HOTP({
+ secret: codeInfo.secret,
+ counter: 0,
+ algorithm: codeInfo.algorithm,
+ });
+ setCode(hotp.generate());
+ setNextCode(hotp.generate({ counter: 1 }));
+ }
+ } catch (err) {
+ setCodeErr(err.message);
}
};
@@ -138,12 +143,28 @@ const OTPDisplay = (props: OTPDisplayProps) => {
return (
-
+ {codeErr === '' ? (
+
+ ) : (
+
+ {codeErr}
+ {codeInfo.rawData ?? 'no rawdata'}
+
+ )}
);
};
diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts
index ebe80149e9..4f64b08117 100644
--- a/src/services/authenticator/authenticatorService.ts
+++ b/src/services/authenticator/authenticatorService.ts
@@ -28,8 +28,19 @@ export const getAuthCodes = async (): Promise => {
entity.header,
authentitorKey
);
- return Code.fromRawData(entity.id, decryptedCode);
+ try {
+ return Code.fromRawData(entity.id, decryptedCode);
+ } catch (e) {
+ console.log(
+ 'failed to parse code',
+ e,
+ entity.id,
+ decryptedCode
+ );
+ return null;
+ }
})
+ .filter((f) => f !== null || f !== undefined)
);
// sort by issuer name which can be undefined also
authCodes.sort((a, b) => {
From 6cb022dede77c1e000565dc494b4a2c4c6089a9d Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Tue, 21 Mar 2023 14:11:37 +0530
Subject: [PATCH 17/77] Fix code generation
---
src/components/Authenicator/OTPDisplay.tsx | 4 ++--
src/types/authenticator/code.ts | 13 ++++++++++---
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx
index f476dc4ffa..7a851aecac 100644
--- a/src/components/Authenicator/OTPDisplay.tsx
+++ b/src/components/Authenicator/OTPDisplay.tsx
@@ -99,9 +99,9 @@ const OTPDisplay = (props: OTPDisplayProps) => {
if (codeInfo.type.toLowerCase() === 'totp') {
const totp = new TOTP({
secret: codeInfo.secret,
- algorithm: codeInfo.algorithm,
+ algorithm: codeInfo.algorithm ?? Code.defaultAlgo,
period: codeInfo.period ?? Code.defaultPeriod,
- digits: codeInfo.digits,
+ digits: codeInfo.digits ?? Code.defaultDigits,
});
setCode(totp.generate());
setNextCode(
diff --git a/src/types/authenticator/code.ts b/src/types/authenticator/code.ts
index 8e94d7b45d..cc244a4c41 100644
--- a/src/types/authenticator/code.ts
+++ b/src/types/authenticator/code.ts
@@ -12,6 +12,7 @@ type AlgorithmType =
export class Code {
static readonly defaultDigits = 6;
+ static readonly defaultAlgo = 'sha1';
static readonly defaultPeriod = 30;
// id for the corresponding auth entity
@@ -70,12 +71,14 @@ export class Code {
});
const uri = URI.parse(santizedRawData);
- let uriPath = uri.path;
+ let uriPath = decodeURIComponent(uri.path);
if (
uriPath.startsWith('/otpauth://') ||
uriPath.startsWith('otpauth://')
) {
uriPath = uriPath.split('otpauth://')[1];
+ } else if (uriPath.startsWith('otpauth%3A//')) {
+ uriPath = uriPath.split('otpauth%3A//')[1];
}
return new Code(
@@ -94,7 +97,11 @@ export class Code {
private static _getAccount(uriPath: string): string {
try {
const path = decodeURIComponent(uriPath);
- return path.split(':')[1];
+ if (path.includes(':')) {
+ return path.split(':')[1];
+ } else if (path.includes('/')) {
+ return path.split('/')[1];
+ }
} catch (e) {
return '';
}
@@ -122,7 +129,7 @@ export class Code {
private static _getDigits(uriParams): number {
try {
- return parseInt(uriParams['digits'], 10);
+ return parseInt(uriParams['digits'], 10) || Code.defaultDigits;
} catch (e) {
return Code.defaultDigits;
}
From d2ebf79b47d50cbf6575a4b1cbc9316157eda717 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Tue, 21 Mar 2023 14:36:14 +0530
Subject: [PATCH 18/77] Remove logging for rawCode
---
src/services/authenticator/authenticatorService.ts | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts
index 4f64b08117..cdf31f7ab4 100644
--- a/src/services/authenticator/authenticatorService.ts
+++ b/src/services/authenticator/authenticatorService.ts
@@ -31,12 +31,7 @@ export const getAuthCodes = async (): Promise => {
try {
return Code.fromRawData(entity.id, decryptedCode);
} catch (e) {
- console.log(
- 'failed to parse code',
- e,
- entity.id,
- decryptedCode
- );
+ console.log('failed to parse code', e, entity.id);
return null;
}
})
From 0746deff74bdf27a9cb2d98fabe95d306338beb1 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Tue, 21 Mar 2023 16:23:31 +0530
Subject: [PATCH 19/77] Log codeInfo
---
src/components/Authenicator/OTPDisplay.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx
index 7a851aecac..bcaecb760b 100644
--- a/src/components/Authenicator/OTPDisplay.tsx
+++ b/src/components/Authenicator/OTPDisplay.tsx
@@ -119,6 +119,7 @@ const OTPDisplay = (props: OTPDisplayProps) => {
setNextCode(hotp.generate({ counter: 1 }));
}
} catch (err) {
+ console.log('codeInfo', codeInfo);
setCodeErr(err.message);
}
};
From aaa301d09dba71aaf73ae23fcc1c3c4851b15b34 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Wed, 22 Mar 2023 12:44:45 +0530
Subject: [PATCH 20/77] [ente authenticator] Handle \r
---
src/types/authenticator/code.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/types/authenticator/code.ts b/src/types/authenticator/code.ts
index cc244a4c41..de32ad611e 100644
--- a/src/types/authenticator/code.ts
+++ b/src/types/authenticator/code.ts
@@ -51,7 +51,8 @@ export class Code {
static fromRawData(id: string, rawData: string): Code {
let santizedRawData = rawData
.replace(/\+/g, '%2B')
- .replace(/:/g, '%3A');
+ .replace(/:/g, '%3A')
+ .replaceAll('\r', '');
if (santizedRawData.startsWith('"')) {
santizedRawData = santizedRawData.substring(1);
}
From 3a3b1337b328e95c7dc8b1016b15a14beec1b770 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Wed, 22 Mar 2023 13:01:47 +0530
Subject: [PATCH 21/77] [ente auth] improve parsing of issuer from path
---
src/types/authenticator/code.ts | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/types/authenticator/code.ts b/src/types/authenticator/code.ts
index de32ad611e..0e2ff8bba8 100644
--- a/src/types/authenticator/code.ts
+++ b/src/types/authenticator/code.ts
@@ -121,8 +121,16 @@ export class Code {
}
return issuer;
}
- const path = decodeURIComponent(uriPath);
- return path.split(':')[0].substring(1);
+ let path = decodeURIComponent(uriPath);
+ if (path.startsWith('totp/') || path.startsWith('hotp/')) {
+ path = path.substring(5);
+ }
+ if (path.includes(':')) {
+ return path.split(':')[0];
+ } else if (path.includes('-')) {
+ return path.split('-')[0];
+ }
+ return path;
} catch (e) {
return '';
}
From 76710698cdb6c1eb229caed04f49ebd5f18f2baa Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Wed, 22 Mar 2023 13:10:52 +0530
Subject: [PATCH 22/77] [ente auth] Improve bad code display
---
src/components/Authenicator/OTPDisplay.tsx | 35 +++++++++++++---------
1 file changed, 21 insertions(+), 14 deletions(-)
diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx
index bcaecb760b..fd328041b0 100644
--- a/src/components/Authenicator/OTPDisplay.tsx
+++ b/src/components/Authenicator/OTPDisplay.tsx
@@ -83,6 +83,26 @@ const TOTPDisplay = ({ issuer, account, code, nextCode }) => {
);
};
+function BadCodeInfo({ codeInfo, codeErr }) {
+ const [showRawData, setShowRawData] = useState(false);
+
+ return (
+
+ {codeInfo.title}
+ {codeErr}
+
+ {showRawData ? (
+ setShowRawData(false)}>
+ {codeInfo.rawData ?? 'no raw data'}
+
+ ) : (
+ setShowRawData(true)}>Show rawData
+ )}
+
+
+ );
+}
+
interface OTPDisplayProps {
codeInfo: Code;
}
@@ -119,7 +139,6 @@ const OTPDisplay = (props: OTPDisplayProps) => {
setNextCode(hotp.generate({ counter: 1 }));
}
} catch (err) {
- console.log('codeInfo', codeInfo);
setCodeErr(err.message);
}
};
@@ -152,19 +171,7 @@ const OTPDisplay = (props: OTPDisplayProps) => {
nextCode={nextcode}
/>
) : (
-
- {codeErr}
- {codeInfo.rawData ?? 'no rawdata'}
-
+
)}
);
From 6d9767db405d57f3f34871ce41a8a46e2e9aeb4d Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 07:14:08 +0530
Subject: [PATCH 23/77] Remove logging
---
src/pages/authenticator/index.tsx | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx
index f40cf5ef15..6471d2263f 100644
--- a/src/pages/authenticator/index.tsx
+++ b/src/pages/authenticator/index.tsx
@@ -26,11 +26,10 @@ const OTPPage = () => {
});
return;
}
- console.error('something wrong here', err);
+ // do not log errors
});
} catch (error) {
- console.error('something wrong where asdas');
- console.error(error);
+ // do not log errors
}
};
fetchCodes();
From 4c190a319c22bf453f31be621eaa50aec79ccd01 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 07:21:37 +0530
Subject: [PATCH 24/77] Remove exception from log
---
src/services/authenticator/authenticatorService.ts | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts
index cdf31f7ab4..8ef3df69b9 100644
--- a/src/services/authenticator/authenticatorService.ts
+++ b/src/services/authenticator/authenticatorService.ts
@@ -31,7 +31,10 @@ export const getAuthCodes = async (): Promise => {
try {
return Code.fromRawData(entity.id, decryptedCode);
} catch (e) {
- console.log('failed to parse code', e, entity.id);
+ logError(
+ Error('failed to parse code'),
+ 'codeId = ' + entity.id
+ );
return null;
}
})
From c6da52b1f56dd0ecf850e72b83b6325a38d1eadf Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 07:23:52 +0530
Subject: [PATCH 25/77] Switch to /auth instead of /authenticator
---
src/components/Sidebar/UtilitySection.tsx | 2 +-
src/constants/pages/index.ts | 3 ++-
src/pages/authenticator/index.tsx | 2 +-
3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx
index fe1db37023..99c199107f 100644
--- a/src/components/Sidebar/UtilitySection.tsx
+++ b/src/components/Sidebar/UtilitySection.tsx
@@ -63,7 +63,7 @@ export default function UtilitySection({ closeSidebar }) {
const redirectToDeduplicatePage = () => router.push(PAGES.DEDUPLICATE);
- const redirectToAuthenticatorPage = () => router.push(PAGES.AUTHENICATOR);
+ const redirectToAuthenticatorPage = () => router.push(PAGES.AUTH);
const somethingWentWrong = () =>
setDialogMessage({
diff --git a/src/constants/pages/index.ts b/src/constants/pages/index.ts
index 38f2b3a6a5..10594aae62 100644
--- a/src/constants/pages/index.ts
+++ b/src/constants/pages/index.ts
@@ -14,5 +14,6 @@ export enum PAGES {
SHARED_ALBUMS = '/shared-albums',
// ML_DEBUG = '/ml-debug',
DEDUPLICATE = '/deduplicate',
- AUTHENICATOR = '/authenticator',
+ // AUTH page is used to show (auth)enticator codes
+ AUTH = '/auth',
}
diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx
index 6471d2263f..13aed7dc58 100644
--- a/src/pages/authenticator/index.tsx
+++ b/src/pages/authenticator/index.tsx
@@ -22,7 +22,7 @@ const OTPPage = () => {
if (err.message === CustomError.KEY_MISSING) {
router.push({
pathname: PAGES.CREDENTIALS,
- query: { redirectPage: PAGES.AUTHENICATOR },
+ query: { redirectPage: PAGES.AUTH },
});
return;
}
From e63260b5ce4c2388a6e025ecd50779cf4a003c98 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 07:25:07 +0530
Subject: [PATCH 26/77] Fix typo
---
src/pages/credentials/index.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/pages/credentials/index.tsx b/src/pages/credentials/index.tsx
index e9422a8d8f..7c0f2c6516 100644
--- a/src/pages/credentials/index.tsx
+++ b/src/pages/credentials/index.tsx
@@ -31,7 +31,7 @@ import VerifyMasterPasswordForm, {
export default function Credentials() {
const router = useRouter();
- const routeReidrectPage =
+ const routerRedirectPage =
router.query.redirectPage?.toString() ?? PAGES.GALLERY;
const [keyAttributes, setKeyAttributes] = useState();
const appContext = useContext(AppContext);
@@ -88,7 +88,7 @@ export default function Credentials() {
await decryptAndStoreToken(key);
const redirectURL = appContext.redirectURL;
appContext.setRedirectURL(null);
- router.push(redirectURL ?? routeReidrectPage ?? PAGES.GALLERY);
+ router.push(redirectURL ?? routerRedirectPage ?? PAGES.GALLERY);
} catch (e) {
logError(e, 'useMasterPassword failed');
}
From dcaae1f4787fca75026a63689481662f53c25650 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 07:28:38 +0530
Subject: [PATCH 27/77] Refactor
---
src/pages/authenticator/index.tsx | 26 +++++++++++---------------
1 file changed, 11 insertions(+), 15 deletions(-)
diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx
index 13aed7dc58..567a98d6f9 100644
--- a/src/pages/authenticator/index.tsx
+++ b/src/pages/authenticator/index.tsx
@@ -12,24 +12,20 @@ const OTPPage = () => {
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
+ // refactor this code
const fetchCodes = async () => {
try {
- getAuthCodes()
- .then((res) => {
- setCodes(res);
- })
- .catch((err) => {
- if (err.message === CustomError.KEY_MISSING) {
- router.push({
- pathname: PAGES.CREDENTIALS,
- query: { redirectPage: PAGES.AUTH },
- });
- return;
- }
- // do not log errors
+ const res = await getAuthCodes();
+ setCodes(res);
+ } catch (err) {
+ if (err.message === CustomError.KEY_MISSING) {
+ router.push({
+ pathname: PAGES.CREDENTIALS,
+ query: { redirectPage: PAGES.AUTH },
});
- } catch (error) {
- // do not log errors
+ } else {
+ // do not log errors
+ }
}
};
fetchCodes();
From ac713bf9d6f0d6d4826eff7b5e5736c180453ee6 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 07:30:19 +0530
Subject: [PATCH 28/77] Fix typos
---
src/components/Authenicator/OTPDisplay.tsx | 4 ++--
src/services/authenticator/authenticatorService.ts | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx
index fd328041b0..37415e644e 100644
--- a/src/components/Authenicator/OTPDisplay.tsx
+++ b/src/components/Authenicator/OTPDisplay.tsx
@@ -110,7 +110,7 @@ interface OTPDisplayProps {
const OTPDisplay = (props: OTPDisplayProps) => {
const { codeInfo } = props;
const [code, setCode] = useState('');
- const [nextcode, setNextCode] = useState('');
+ const [nextCode, setNextCode] = useState('');
const [codeErr, setCodeErr] = useState('');
const generateCodes = () => {
@@ -168,7 +168,7 @@ const OTPDisplay = (props: OTPDisplayProps) => {
issuer={codeInfo.issuer}
account={codeInfo.account}
code={code}
- nextCode={nextcode}
+ nextCode={nextCode}
/>
) : (
diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts
index 8ef3df69b9..54f0106837 100644
--- a/src/services/authenticator/authenticatorService.ts
+++ b/src/services/authenticator/authenticatorService.ts
@@ -12,7 +12,7 @@ export const getAuthCodes = async (): Promise => {
try {
const authKeyData = await getAuthKey();
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
- const authentitorKey = await cryptoWorker.decryptB64(
+ const authenticatorKey = await cryptoWorker.decryptB64(
authKeyData.encryptedKey,
authKeyData.header,
masterKey
@@ -26,7 +26,7 @@ export const getAuthCodes = async (): Promise => {
const decryptedCode = await cryptoWorker.decryptMetadata(
entity.encryptedData,
entity.header,
- authentitorKey
+ authenticatorKey
);
try {
return Code.fromRawData(entity.id, decryptedCode);
From f8667bf692b08c53b9ce5cb52a3fe9887d257e39 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 07:35:44 +0530
Subject: [PATCH 29/77] Simplify if
---
src/components/Authenicator/OTPDisplay.tsx | 24 +++++++++++-----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx
index 37415e644e..a6cf1e263b 100644
--- a/src/components/Authenicator/OTPDisplay.tsx
+++ b/src/components/Authenicator/OTPDisplay.tsx
@@ -112,6 +112,7 @@ const OTPDisplay = (props: OTPDisplayProps) => {
const [code, setCode] = useState('');
const [nextCode, setNextCode] = useState('');
const [codeErr, setCodeErr] = useState('');
+ const generateCodeInterval = 1000;
const generateCodes = () => {
try {
@@ -145,19 +146,18 @@ const OTPDisplay = (props: OTPDisplayProps) => {
useEffect(() => {
generateCodes();
- let intervalId;
+ const codeType = codeInfo.type;
+ const intervalId =
+ codeType.toLowerCase() === 'totp' ||
+ codeType.toLowerCase() === 'hotp'
+ ? setInterval(() => {
+ generateCodes();
+ }, generateCodeInterval)
+ : null;
- if (codeInfo.type.toLowerCase() === 'totp') {
- intervalId = setInterval(() => {
- generateCodes();
- }, 1000);
- } else if (codeInfo.type.toLowerCase() === 'hotp') {
- intervalId = setInterval(() => {
- generateCodes();
- }, 1000);
- }
-
- return () => clearInterval(intervalId);
+ return () => {
+ if (intervalId) clearInterval(intervalId);
+ };
}, [codeInfo]);
return (
From 2069fff499b2c17672a2d9ebfdc1ff0374195888 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 07:38:54 +0530
Subject: [PATCH 30/77] Convert pure data class to interface
---
src/types/authenticator/auth_entity.ts | 17 +----------------
1 file changed, 1 insertion(+), 16 deletions(-)
diff --git a/src/types/authenticator/auth_entity.ts b/src/types/authenticator/auth_entity.ts
index 083651d310..cd97f52623 100644
--- a/src/types/authenticator/auth_entity.ts
+++ b/src/types/authenticator/auth_entity.ts
@@ -1,23 +1,8 @@
-export class AuthEntity {
+export interface AuthEntity {
id: string;
encryptedData: string | null;
header: string | null;
isDeleted: boolean;
createdAt: number;
updatedAt: number;
- constructor(
- id: string,
- encryptedData: string | null,
- header: string | null,
- isDeleted: boolean,
- createdAt: number,
- updatedAt: number
- ) {
- this.id = id;
- this.encryptedData = encryptedData;
- this.header = header;
- this.isDeleted = isDeleted;
- this.createdAt = createdAt;
- this.updatedAt = updatedAt;
- }
}
From 5688c42d6e4dc9a4fd6ab5684fa296c09ef5a91c Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 07:43:32 +0530
Subject: [PATCH 31/77] Fix string extraction
---
public/locales/en/translation.json | 1 +
src/components/Sidebar/UtilitySection.tsx | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index 906b3c264f..879208e1c9 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -459,6 +459,7 @@
"UPLOAD_GOOGLE_TAKEOUT": "Google takeout",
"CANCEL_UPLOADS": "Cancel uploads",
"DEDUPLICATE_FILES": "Deduplicate files",
+ "AUTHENTICATOR_SECTION": "Authenticator",
"NO_DUPLICATES_FOUND": "You've no duplicate files that can be cleared",
"CLUB_BY_CAPTURE_TIME": "Club by capture time",
"FILES": "Files",
diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx
index 99c199107f..a07c70a118 100644
--- a/src/components/Sidebar/UtilitySection.tsx
+++ b/src/components/Sidebar/UtilitySection.tsx
@@ -102,7 +102,7 @@ export default function UtilitySection({ closeSidebar }) {
{isInternalUser() && (
- {constants.AUTHENTICATOR_SECTION}
+ {t('AUTHENTICATOR_SECTION')}
)}
From c2451105e5bf8c3ca3da1a35f92538de6a908c19 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 08:07:19 +0530
Subject: [PATCH 32/77] Wrap decryption inside tryCatch
---
src/services/authenticator/authenticatorService.ts | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts
index 54f0106837..b0ae58ef6a 100644
--- a/src/services/authenticator/authenticatorService.ts
+++ b/src/services/authenticator/authenticatorService.ts
@@ -23,12 +23,13 @@ export const getAuthCodes = async (): Promise => {
authEntity
.filter((f) => !f.isDeleted)
.map(async (entity) => {
- const decryptedCode = await cryptoWorker.decryptMetadata(
- entity.encryptedData,
- entity.header,
- authenticatorKey
- );
try {
+ const decryptedCode =
+ await cryptoWorker.decryptMetadata(
+ entity.encryptedData,
+ entity.header,
+ authenticatorKey
+ );
return Code.fromRawData(entity.id, decryptedCode);
} catch (e) {
logError(
From 5e7411c405b775d9b60d27d497041a2d7df9c253 Mon Sep 17 00:00:00 2001
From: Neeraj Gupta <254676+ua741@users.noreply.github.com>
Date: Sat, 25 Mar 2023 08:52:01 +0530
Subject: [PATCH 33/77] Extract footer as component
---
src/components/Authenicator/AuthFooder.tsx | 25 +++++++++++++++++++
src/pages/authenticator/index.tsx | 29 ++--------------------
2 files changed, 27 insertions(+), 27 deletions(-)
create mode 100644 src/components/Authenicator/AuthFooder.tsx
diff --git a/src/components/Authenicator/AuthFooder.tsx b/src/components/Authenicator/AuthFooder.tsx
new file mode 100644
index 0000000000..64eeb10295
--- /dev/null
+++ b/src/components/Authenicator/AuthFooder.tsx
@@ -0,0 +1,25 @@
+import { Button } from '@mui/material';
+
+export const AuthFooter = () => {
+ return (
+
+ Download our mobile app to add & manage your secrets.
+
+
+
+
+ );
+};
diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx
index 567a98d6f9..b61adf3332 100644
--- a/src/pages/authenticator/index.tsx
+++ b/src/pages/authenticator/index.tsx
@@ -1,10 +1,10 @@
import React, { useEffect, useState } from 'react';
import OTPDisplay from 'components/Authenicator/OTPDisplay';
import { getAuthCodes } from 'services/authenticator/authenticatorService';
-import { Button } from '@mui/material';
import { CustomError } from 'utils/error';
import { PAGES } from 'constants/pages';
import { useRouter } from 'next/router';
+import { AuthFooter } from 'components/Authenicator/AuthFooder';
const OTPPage = () => {
const router = useRouter();
@@ -12,7 +12,6 @@ const OTPPage = () => {
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
- // refactor this code
const fetchCodes = async () => {
try {
const res = await getAuthCodes();
@@ -41,30 +40,6 @@ const OTPPage = () => {
.includes(searchTerm.toLowerCase())
);
- const DownloadApp = () => {
- return (
-
- Download our mobile app to add & manage your secrets.
-
-
-
-
- );
- };
-
return (
{
))
)}
-
+
);
From a0a15e7e144cd7bc0a674796ba84a75a4a27ad27 Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Thu, 30 Mar 2023 16:08:26 +0530
Subject: [PATCH 34/77] refactor logic to prevent multiple parallel
---
src/components/ExportModal.tsx | 20 ++++------
src/services/exportService.ts | 73 +++++++++++++++++++---------------
2 files changed, 49 insertions(+), 44 deletions(-)
diff --git a/src/components/ExportModal.tsx b/src/components/ExportModal.tsx
index 0de1db7644..7290064dca 100644
--- a/src/components/ExportModal.tsx
+++ b/src/components/ExportModal.tsx
@@ -114,7 +114,7 @@ export default function ExportModal(props: Props) {
setLastExportTime(exportRecord.lastAttemptTimestamp);
await syncFileCounts();
if (exportRecord.stage === ExportStage.INPROGRESS) {
- await startExport();
+ startExport();
}
} catch (e) {
logError(e, 'error handling exportFolder change');
@@ -172,6 +172,7 @@ export default function ExportModal(props: Props) {
throw Error(CustomError.EXPORT_FOLDER_DOES_NOT_EXIST);
}
await updateExportStage(ExportStage.INPROGRESS);
+ setExportProgress({ current: 0, total: 0 });
};
const postExportRun = async () => {
@@ -209,17 +210,12 @@ export default function ExportModal(props: Props) {
}
};
- const startExport = async () => {
- try {
- await preExportRun();
- setExportProgress({ current: 0, total: 0 });
- await exportService.exportFiles(setExportProgress);
- await postExportRun();
- } catch (e) {
- if (e.message !== CustomError.EXPORT_FOLDER_DOES_NOT_EXIST) {
- logError(e, 'startExport failed');
- }
- }
+ const startExport = () => {
+ void exportService.runExport(
+ preExportRun,
+ setExportProgress,
+ postExportRun
+ );
};
const stopExport = async () => {
diff --git a/src/services/exportService.ts b/src/services/exportService.ts
index 36f7a6ea25..5af12e4f79 100644
--- a/src/services/exportService.ts
+++ b/src/services/exportService.ts
@@ -62,7 +62,8 @@ export const ENTE_EXPORT_DIRECTORY = 'ente Photos';
class ExportService {
private electronAPIs: ElectronAPIs;
- private exportInProgress: Promise = null;
+ private exportInProgress: boolean = false;
+ private reRunNeeded = false;
private exportRecordUpdater = new QueueProcessor(1);
private stopExport: boolean = false;
private allElectronAPIsExist: boolean = false;
@@ -96,31 +97,13 @@ class ExportService {
}
}
- enableContinuousExport(startExport: () => Promise) {
+ enableContinuousExport(startExport: () => void) {
try {
if (this.continuousExportEventHandler) {
addLogLine('continuous export already enabled');
return;
}
- const reRunNeeded = { current: false };
- this.continuousExportEventHandler = async () => {
- try {
- addLogLine('continuous export triggered');
- if (this.exportInProgress) {
- addLogLine('export in progress, scheduling re-run');
- reRunNeeded.current = true;
- return;
- }
- await startExport();
- if (reRunNeeded.current) {
- reRunNeeded.current = false;
- addLogLine('re-running export');
- setTimeout(this.continuousExportEventHandler, 0);
- }
- } catch (e) {
- logError(e, 'continuous export failed');
- }
- };
+ this.continuousExportEventHandler = startExport;
this.continuousExportEventHandler();
eventBus.addListener(
Events.LOCAL_FILES_UPDATED,
@@ -171,15 +154,46 @@ class ExportService {
this.stopExport = true;
}
- async exportFiles(updateProgress: (progress: ExportProgress) => void) {
+ runExport = async (
+ preExport: () => Promise,
+ updateProgress: (progress: ExportProgress) => void,
+ postExport: () => Promise
+ ) => {
try {
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
if (this.exportInProgress) {
+ addLogLine('export in progress, scheduling re-run');
this.electronAPIs.sendNotification(
t('EXPORT_NOTIFICATION.IN_PROGRESS')
);
- return await this.exportInProgress;
+ this.reRunNeeded = true;
+ return;
}
+ addLogLine('starting export');
+ this.exportInProgress = true;
+ await preExport();
+ await this.exportFiles(updateProgress);
+ await postExport();
+ addLogLine('export completed');
+ this.exportInProgress = false;
+ if (this.reRunNeeded) {
+ this.reRunNeeded = false;
+ addLogLine('re-running export');
+ setTimeout(
+ () => this.runExport(preExport, updateProgress, postExport),
+ 0
+ );
+ }
+ } catch (e) {
+ if (e.message !== CustomError.EXPORT_FOLDER_DOES_NOT_EXIST) {
+ logError(e, 'runExport failed');
+ }
+ }
+ };
+
+ private async exportFiles(
+ updateProgress: (progress: ExportProgress) => void
+ ) {
+ try {
const exportDir = getData(LS_KEYS.EXPORT)?.folder;
if (!exportDir) {
// no-export folder set
@@ -218,7 +232,7 @@ class ExportService {
);
addLogLine(
- `starting export, filesToExportCount: ${filesToExport?.length}, userPersonalFileCount: ${userPersonalFiles?.length}`
+ `exportFiles: filesToExportCount: ${filesToExport?.length}, userPersonalFileCount: ${userPersonalFiles?.length}`
);
const collectionIDPathMap: CollectionIDPathMap =
@@ -228,7 +242,7 @@ class ExportService {
userCollections,
exportRecord
);
- this.exportInProgress = this.fileExporter(
+ await this.fileExporter(
filesToExport,
collectionIDNameMap,
renamedCollections,
@@ -236,13 +250,8 @@ class ExportService {
updateProgress,
exportDir
);
- const resp = await this.exportInProgress;
- return resp;
} catch (e) {
logError(e, 'exportFiles failed');
- return { paused: false };
- } finally {
- this.exportInProgress = null;
}
}
@@ -556,7 +565,7 @@ class ExportService {
}
isExportInProgress = () => {
- return this.exportInProgress !== null;
+ return this.exportInProgress;
};
exists = (path: string) => {
From 1e7b34763df0ae42ca7aa5ec40d56a0288f7e47f Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Thu, 30 Mar 2023 16:13:45 +0530
Subject: [PATCH 35/77] reset in progress on export fail
---
src/services/exportService.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/services/exportService.ts b/src/services/exportService.ts
index 5af12e4f79..dc6a2125e8 100644
--- a/src/services/exportService.ts
+++ b/src/services/exportService.ts
@@ -184,6 +184,7 @@ class ExportService {
);
}
} catch (e) {
+ this.exportInProgress = false;
if (e.message !== CustomError.EXPORT_FOLDER_DOES_NOT_EXIST) {
logError(e, 'runExport failed');
}
From 7b96c58aa34b8ba26ec8dd74bdc1f380782f6af0 Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Thu, 30 Mar 2023 16:30:05 +0530
Subject: [PATCH 36/77] better handle error and refactor stopExport
---
src/components/ExportModal.tsx | 9 ++-----
src/services/exportService.ts | 46 +++++++++++++++++++++-------------
2 files changed, 31 insertions(+), 24 deletions(-)
diff --git a/src/components/ExportModal.tsx b/src/components/ExportModal.tsx
index 7290064dca..18e6e7d205 100644
--- a/src/components/ExportModal.tsx
+++ b/src/components/ExportModal.tsx
@@ -218,13 +218,8 @@ export default function ExportModal(props: Props) {
);
};
- const stopExport = async () => {
- try {
- exportService.stopRunningExport();
- await postExportRun();
- } catch (e) {
- logError(e, 'stopExport failed');
- }
+ const stopExport = () => {
+ void exportService.stopRunningExport(postExportRun);
};
return (
diff --git a/src/services/exportService.ts b/src/services/exportService.ts
index dc6a2125e8..2c9b677275 100644
--- a/src/services/exportService.ts
+++ b/src/services/exportService.ts
@@ -150,8 +150,13 @@ class ExportService {
}
};
- stopRunningExport() {
- this.stopExport = true;
+ async stopRunningExport(postExport: () => Promise) {
+ try {
+ this.stopExport = true;
+ await postExport();
+ } catch (e) {
+ logError(e, 'stopRunningExport failed');
+ }
}
runExport = async (
@@ -168,23 +173,30 @@ class ExportService {
this.reRunNeeded = true;
return;
}
- addLogLine('starting export');
- this.exportInProgress = true;
- await preExport();
- await this.exportFiles(updateProgress);
- await postExport();
- addLogLine('export completed');
- this.exportInProgress = false;
- if (this.reRunNeeded) {
- this.reRunNeeded = false;
- addLogLine('re-running export');
- setTimeout(
- () => this.runExport(preExport, updateProgress, postExport),
- 0
- );
+ try {
+ addLogLine('starting export');
+ this.exportInProgress = true;
+ await preExport();
+ await this.exportFiles(updateProgress);
+ addLogLine('export completed');
+ } finally {
+ this.exportInProgress = false;
+ if (this.reRunNeeded) {
+ this.reRunNeeded = false;
+ addLogLine('re-running export');
+ setTimeout(
+ () =>
+ this.runExport(
+ preExport,
+ updateProgress,
+ postExport
+ ),
+ 0
+ );
+ }
+ await postExport();
}
} catch (e) {
- this.exportInProgress = false;
if (e.message !== CustomError.EXPORT_FOLDER_DOES_NOT_EXIST) {
logError(e, 'runExport failed');
}
From ccdde7a43c3fa0109e8d652df67a7a0ade48392a Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Fri, 31 Mar 2023 17:23:24 +0530
Subject: [PATCH 37/77] fix page broke export UI update
---
src/components/ExportModal.tsx | 33 ++++++++++----------
src/services/exportService.ts | 57 +++++++++++++++++-----------------
src/types/export/index.ts | 7 +++++
3 files changed, 52 insertions(+), 45 deletions(-)
diff --git a/src/components/ExportModal.tsx b/src/components/ExportModal.tsx
index 18e6e7d205..74365d4332 100644
--- a/src/components/ExportModal.tsx
+++ b/src/components/ExportModal.tsx
@@ -72,6 +72,12 @@ export default function ExportModal(props: Props) {
setExportFolder(exportSettings?.folder);
setContinuousExport(exportSettings?.continuousExport);
syncFileCounts();
+ exportService.setUIUpdaters({
+ updateExportStage: updateExportStage,
+ updateExportProgress: setExportProgress,
+ updateFileExportStats: setFileExportStats,
+ updateLastExportTime: updateExportTime,
+ });
} catch (e) {
logError(e, 'error in exportModal');
}
@@ -90,7 +96,7 @@ export default function ExportModal(props: Props) {
useEffect(() => {
try {
if (continuousExport) {
- exportService.enableContinuousExport(startExport);
+ exportService.enableContinuousExport();
} else {
exportService.disableContinuousExport();
}
@@ -162,7 +168,7 @@ export default function ExportModal(props: Props) {
// HELPER FUNCTIONS
// =======================
- const preExportRun = async () => {
+ const verifyExportFolderExists = () => {
const exportFolder = getData(LS_KEYS.EXPORT)?.folder;
const exportFolderExists = exportService.exists(exportFolder);
if (!exportFolderExists) {
@@ -171,14 +177,6 @@ export default function ExportModal(props: Props) {
);
throw Error(CustomError.EXPORT_FOLDER_DOES_NOT_EXIST);
}
- await updateExportStage(ExportStage.INPROGRESS);
- setExportProgress({ current: 0, total: 0 });
- };
-
- const postExportRun = async () => {
- await updateExportStage(ExportStage.FINISHED);
- await updateExportTime(Date.now());
- await syncFileCounts();
};
const syncFileCounts = async () => {
@@ -211,15 +209,18 @@ export default function ExportModal(props: Props) {
};
const startExport = () => {
- void exportService.runExport(
- preExportRun,
- setExportProgress,
- postExportRun
- );
+ try {
+ verifyExportFolderExists();
+ exportService.runExport();
+ } catch (e) {
+ if (e.message !== CustomError.EXPORT_FOLDER_DOES_NOT_EXIST) {
+ logError(e, 'startExport failed');
+ }
+ }
};
const stopExport = () => {
- void exportService.stopRunningExport(postExportRun);
+ void exportService.stopRunningExport();
};
return (
diff --git a/src/services/exportService.ts b/src/services/exportService.ts
index 2c9b677275..0615812b8a 100644
--- a/src/services/exportService.ts
+++ b/src/services/exportService.ts
@@ -41,14 +41,14 @@ import { Collection } from 'types/collection';
import {
CollectionIDNameMap,
CollectionIDPathMap,
- ExportProgress,
ExportRecord,
ExportRecordV1,
+ ExportUIUpdaters,
FileExportStats,
} from 'types/export';
import { User } from 'types/user';
import { FILE_TYPE, TYPE_JPEG, TYPE_JPG } from 'constants/file';
-import { RecordType } from 'constants/export';
+import { ExportStage, RecordType } from 'constants/export';
import { ElectronAPIs } from 'types/electron';
import { CustomError } from 'utils/error';
import { addLogLine } from 'utils/logging';
@@ -69,12 +69,17 @@ class ExportService {
private allElectronAPIsExist: boolean = false;
private fileReader: FileReader = null;
private continuousExportEventHandler: () => void;
+ private uiUpdater: ExportUIUpdaters;
constructor() {
this.electronAPIs = runningInBrowser() && window['ElectronAPIs'];
this.allElectronAPIsExist = !!this.electronAPIs?.exists;
}
+ async setUIUpdaters(uiUpdater: ExportUIUpdaters) {
+ this.uiUpdater = uiUpdater;
+ }
+
async changeExportDirectory(callback: (newExportDir: string) => void) {
try {
const newRootDir = await this.electronAPIs.selectRootDirectory();
@@ -97,13 +102,13 @@ class ExportService {
}
}
- enableContinuousExport(startExport: () => void) {
+ enableContinuousExport() {
try {
if (this.continuousExportEventHandler) {
addLogLine('continuous export already enabled');
return;
}
- this.continuousExportEventHandler = startExport;
+ this.continuousExportEventHandler = this.runExport;
this.continuousExportEventHandler();
eventBus.addListener(
Events.LOCAL_FILES_UPDATED,
@@ -150,20 +155,16 @@ class ExportService {
}
};
- async stopRunningExport(postExport: () => Promise) {
+ async stopRunningExport() {
try {
this.stopExport = true;
- await postExport();
+ await this.postExport();
} catch (e) {
logError(e, 'stopRunningExport failed');
}
}
- runExport = async (
- preExport: () => Promise,
- updateProgress: (progress: ExportProgress) => void,
- postExport: () => Promise
- ) => {
+ runExport = async () => {
try {
if (this.exportInProgress) {
addLogLine('export in progress, scheduling re-run');
@@ -176,25 +177,18 @@ class ExportService {
try {
addLogLine('starting export');
this.exportInProgress = true;
- await preExport();
- await this.exportFiles(updateProgress);
+ await this.uiUpdater.updateExportStage(ExportStage.INPROGRESS);
+ this.uiUpdater.updateExportProgress({ current: 0, total: 0 });
+ await this.exportFiles();
addLogLine('export completed');
} finally {
this.exportInProgress = false;
if (this.reRunNeeded) {
this.reRunNeeded = false;
addLogLine('re-running export');
- setTimeout(
- () =>
- this.runExport(
- preExport,
- updateProgress,
- postExport
- ),
- 0
- );
+ setTimeout(this.runExport, 0);
}
- await postExport();
+ await this.postExport();
}
} catch (e) {
if (e.message !== CustomError.EXPORT_FOLDER_DOES_NOT_EXIST) {
@@ -203,9 +197,7 @@ class ExportService {
}
};
- private async exportFiles(
- updateProgress: (progress: ExportProgress) => void
- ) {
+ private async exportFiles() {
try {
const exportDir = getData(LS_KEYS.EXPORT)?.folder;
if (!exportDir) {
@@ -260,7 +252,6 @@ class ExportService {
collectionIDNameMap,
renamedCollections,
collectionIDPathMap,
- updateProgress,
exportDir
);
} catch (e) {
@@ -273,7 +264,6 @@ class ExportService {
collectionIDNameMap: CollectionIDNameMap,
renamedCollections: Collection[],
collectionIDPathMap: CollectionIDPathMap,
- updateProgress: (progress: ExportProgress) => void,
exportDir: string
): Promise {
try {
@@ -319,7 +309,10 @@ class ExportService {
RecordType.SUCCESS
);
success++;
- updateProgress({ current: success, total: files.length });
+ this.uiUpdater.updateExportProgress({
+ current: success,
+ total: files.length,
+ });
} catch (e) {
logError(e, 'export failed for a file');
if (
@@ -346,6 +339,12 @@ class ExportService {
}
}
+ async postExport() {
+ await this.uiUpdater.updateExportStage(ExportStage.FINISHED);
+ await this.uiUpdater.updateLastExportTime(Date.now());
+ this.uiUpdater.updateFileExportStats(await this.getFileExportStats());
+ }
+
async addFileExportedRecord(
folder: string,
file: EnteFile,
diff --git a/src/types/export/index.ts b/src/types/export/index.ts
index b607e0c777..c25497d982 100644
--- a/src/types/export/index.ts
+++ b/src/types/export/index.ts
@@ -37,3 +37,10 @@ export interface ExportSettings {
folder: string;
continuousExport: boolean;
}
+
+export interface ExportUIUpdaters {
+ updateExportStage: (stage: ExportStage) => Promise;
+ updateExportProgress: (progress: ExportProgress) => void;
+ updateFileExportStats: (fileExportStats: FileExportStats) => void;
+ updateLastExportTime: (exportTime: number) => Promise;
+}
From b244b60d13bce6e8b5c75be8fa915a450bd15548 Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Fri, 31 Mar 2023 18:17:14 +0530
Subject: [PATCH 38/77] refactor continuousExport and exportFolder logic
---
src/components/ExportModal.tsx | 126 ++++++++++++++++++---------------
src/services/exportService.ts | 1 +
2 files changed, 70 insertions(+), 57 deletions(-)
diff --git a/src/components/ExportModal.tsx b/src/components/ExportModal.tsx
index 74365d4332..b3ae21b6cf 100644
--- a/src/components/ExportModal.tsx
+++ b/src/components/ExportModal.tsx
@@ -1,7 +1,12 @@
import isElectron from 'is-electron';
import React, { useEffect, useState, useContext } from 'react';
import exportService from 'services/exportService';
-import { ExportProgress, ExportSettings, FileExportStats } from 'types/export';
+import {
+ ExportProgress,
+ ExportRecord,
+ ExportSettings,
+ FileExportStats,
+} from 'types/export';
import {
Box,
Button,
@@ -67,67 +72,37 @@ export default function ExportModal(props: Props) {
if (!isElectron()) {
return;
}
- try {
- const exportSettings: ExportSettings = getData(LS_KEYS.EXPORT);
- setExportFolder(exportSettings?.folder);
- setContinuousExport(exportSettings?.continuousExport);
- syncFileCounts();
- exportService.setUIUpdaters({
- updateExportStage: updateExportStage,
- updateExportProgress: setExportProgress,
- updateFileExportStats: setFileExportStats,
- updateLastExportTime: updateExportTime,
- });
- } catch (e) {
- logError(e, 'error in exportModal');
- }
- }, []);
-
- useEffect(() => {
- if (!props.show) {
- return;
- }
- if (exportService.isExportInProgress()) {
- setExportStage(ExportStage.INPROGRESS);
- }
- syncFileCounts();
- }, [props.show]);
-
- useEffect(() => {
- try {
- if (continuousExport) {
- exportService.enableContinuousExport();
- } else {
- exportService.disableContinuousExport();
- }
- } catch (e) {
- logError(e, 'error handling continuousExport change');
- }
- }, [continuousExport]);
-
- useEffect(() => {
- if (!exportFolder) {
- return;
- }
const main = async () => {
try {
- const exportRecord = await exportService.getExportRecord();
- if (!exportRecord) {
- setExportStage(ExportStage.INIT);
- return;
- }
- setExportStage(exportRecord.stage);
- setLastExportTime(exportRecord.lastAttemptTimestamp);
- await syncFileCounts();
- if (exportRecord.stage === ExportStage.INPROGRESS) {
+ exportService.setUIUpdaters({
+ updateExportStage: updateExportStage,
+ updateExportProgress: setExportProgress,
+ updateFileExportStats: setFileExportStats,
+ updateLastExportTime: updateExportTime,
+ });
+ const exportSettings: ExportSettings = getData(LS_KEYS.EXPORT);
+ setExportFolder(exportSettings?.folder);
+ setContinuousExport(exportSettings?.continuousExport);
+ const exportRecord = await syncExportRecord(exportFolder);
+ if (exportRecord?.stage === ExportStage.INPROGRESS) {
startExport();
}
+ if (exportSettings?.continuousExport) {
+ exportService.enableContinuousExport();
+ }
} catch (e) {
- logError(e, 'error handling exportFolder change');
+ logError(e, 'export on mount useEffect failed');
}
};
void main();
- }, [exportFolder]);
+ }, []);
+
+ useEffect(() => {
+ if (!props.show) {
+ return;
+ }
+ void syncFileCounts();
+ }, [props.show]);
// =============
// STATE UPDATERS
@@ -168,6 +143,16 @@ export default function ExportModal(props: Props) {
// HELPER FUNCTIONS
// =======================
+ const onExportFolderChange = async (newFolder: string) => {
+ try {
+ updateExportFolder(newFolder);
+ syncExportRecord(newFolder);
+ } catch (e) {
+ logError(e, 'onExportChange failed');
+ throw e;
+ }
+ };
+
const verifyExportFolderExists = () => {
const exportFolder = getData(LS_KEYS.EXPORT)?.folder;
const exportFolderExists = exportService.exists(exportFolder);
@@ -179,6 +164,27 @@ export default function ExportModal(props: Props) {
}
};
+ const syncExportRecord = async (
+ exportFolder: string
+ ): Promise => {
+ try {
+ const exportRecord = await exportService.getExportRecord(
+ exportFolder
+ );
+ if (!exportRecord) {
+ setExportStage(ExportStage.INIT);
+ return null;
+ }
+ setExportStage(exportRecord.stage);
+ setLastExportTime(exportRecord.lastAttemptTimestamp);
+ void syncFileCounts();
+ return exportRecord;
+ } catch (e) {
+ logError(e, 'syncExportRecord failed');
+ throw e;
+ }
+ };
+
const syncFileCounts = async () => {
try {
const fileExportStats = await exportService.getFileExportStats();
@@ -193,7 +199,7 @@ export default function ExportModal(props: Props) {
// =============
const handleChangeExportDirectoryClick = () => {
- void exportService.changeExportDirectory(updateExportFolder);
+ void exportService.changeExportDirectory(onExportFolderChange);
};
const handleOpenExportDirectoryClick = () => {
@@ -202,9 +208,15 @@ export default function ExportModal(props: Props) {
const toggleContinuousExport = () => {
try {
- updateContinuousExport(!continuousExport);
+ const newContinuousExport = !continuousExport;
+ if (newContinuousExport) {
+ exportService.enableContinuousExport();
+ } else {
+ exportService.disableContinuousExport();
+ }
+ updateContinuousExport(newContinuousExport);
} catch (e) {
- logError(e, 'toggleContinuousExport failed');
+ logError(e, 'onContinuousExportChange failed');
}
};
diff --git a/src/services/exportService.ts b/src/services/exportService.ts
index 0615812b8a..f89082623a 100644
--- a/src/services/exportService.ts
+++ b/src/services/exportService.ts
@@ -158,6 +158,7 @@ class ExportService {
async stopRunningExport() {
try {
this.stopExport = true;
+ this.reRunNeeded = false;
await this.postExport();
} catch (e) {
logError(e, 'stopRunningExport failed');
From 3725f0598e1124fa80aba37c2488d5ef65ab3567 Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Thu, 30 Mar 2023 18:40:42 +0530
Subject: [PATCH 39/77] make label prop optional
---
src/components/DropdownInput.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/DropdownInput.tsx b/src/components/DropdownInput.tsx
index cd53ae1a05..e64a679d10 100644
--- a/src/components/DropdownInput.tsx
+++ b/src/components/DropdownInput.tsx
@@ -16,7 +16,7 @@ export interface DropdownOption {
interface Iprops {
label: string;
- labelProps: TypographyTypeMap['props'];
+ labelProps?: TypographyTypeMap['props'];
options: DropdownOption[];
message?: string;
selectedValue: string;
From 6e0b59d4a5190ced5e666535ff18d16abd862d42 Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Thu, 30 Mar 2023 21:09:40 +0530
Subject: [PATCH 40/77] add messageProps prop dropdownInput
---
src/components/DropdownInput.tsx | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/src/components/DropdownInput.tsx b/src/components/DropdownInput.tsx
index e64a679d10..a0487d4302 100644
--- a/src/components/DropdownInput.tsx
+++ b/src/components/DropdownInput.tsx
@@ -19,8 +19,9 @@ interface Iprops {
labelProps?: TypographyTypeMap['props'];
options: DropdownOption[];
message?: string;
- selectedValue: string;
- setSelectedValue: (selectedValue: T) => void;
+ messageProps?: TypographyTypeMap['props'];
+ selected: string;
+ setSelected: (selectedValue: T) => void;
placeholder?: string;
}
@@ -29,10 +30,12 @@ export default function DropdownInput({
labelProps,
options,
message,
- selectedValue,
+ selected,
placeholder,
- setSelectedValue,
+ setSelected,
+ messageProps,
}: Iprops) {
+ console.log({ ...messageProps });
return (
{label}
@@ -77,9 +80,9 @@ export default function DropdownInput({
options.find((o) => o.value === selected).label
);
}}
- value={selectedValue}
+ value={selected}
onChange={(event: SelectChangeEvent) => {
- setSelectedValue(event.target.value as T);
+ setSelected(event.target.value as T);
}}>
{options.map((option, index) => (
From 14882778c070a56f5cf6b773fad95b1753cf86d5 Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Fri, 31 Mar 2023 12:01:00 +0530
Subject: [PATCH 46/77] changed feedback required copy
---
public/locales/en/translation.json | 3 ++-
src/components/DeleteAccountModal.tsx | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index c9a6a0fc47..26b545f518 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -510,5 +510,6 @@
"DELETE_ACCOUNT_FEEDBACK_LABEL": "We are sorry to see you go. Please explain why you are leaving to help us improve.",
"DELETE_ACCOUNT_FEEDBACK_PLACEHOLDER": "Feedback",
"CONFIRM_DELETE_ACCOUNT_CHECKBOX_LABEL": "Yes, I want to permanently delete this account and all its data",
- "CONFIRM_DELETE_ACCOUNT": "Confirm Account Deletion"
+ "CONFIRM_DELETE_ACCOUNT": "Confirm Account Deletion",
+ "FEEDBACK_REQUIRED": "Kindly help us with this information"
}
diff --git a/src/components/DeleteAccountModal.tsx b/src/components/DeleteAccountModal.tsx
index 64ec6a831c..88bf96f732 100644
--- a/src/components/DeleteAccountModal.tsx
+++ b/src/components/DeleteAccountModal.tsx
@@ -101,7 +101,7 @@ const DeleteAccountModal = ({ open, onClose }: Iprops) => {
REASON_WITH_REQUIRED_FEEDBACK.has(reason as DELETE_REASON) &&
!feedback?.length
) {
- setFieldError('feedback', t('REQUIRED'));
+ setFieldError('feedback', t('FEEDBACK_REQUIRED'));
return;
}
const deleteChallengeResponse = await getAccountDeleteChallenge();
From c27bd720bbd2b6c35d553007795a2ea142e5f3a1 Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Fri, 31 Mar 2023 12:33:14 +0530
Subject: [PATCH 47/77] update authenticate modal to use dialogBoxV2
---
src/components/AuthenticateUserModal.tsx | 29 ++++++++++++------------
1 file changed, 14 insertions(+), 15 deletions(-)
diff --git a/src/components/AuthenticateUserModal.tsx b/src/components/AuthenticateUserModal.tsx
index 4dcc11f040..8ec6f9b837 100644
--- a/src/components/AuthenticateUserModal.tsx
+++ b/src/components/AuthenticateUserModal.tsx
@@ -6,9 +6,9 @@ import { KeyAttributes, User } from 'types/user';
import VerifyMasterPasswordForm, {
VerifyMasterPasswordFormProps,
} from 'components/VerifyMasterPasswordForm';
-import { Dialog, Stack, Typography } from '@mui/material';
import { logError } from 'utils/sentry';
import { t } from 'i18next';
+import DialogBoxV2 from './DialogBoxV2';
interface Iprops {
open: boolean;
onClose: () => void;
@@ -66,22 +66,21 @@ export default function AuthenticateUserModal({
};
return (
-
+ attributes={{
+ title: t('PASSWORD'),
+ }}
+ PaperProps={{ sx: { padding: '8px 12px', maxWidth: '320px' } }}>
+
+
);
}
From 7c12826d9cdbb54e102d1275a26a17ec67a70cc4 Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Fri, 31 Mar 2023 12:33:52 +0530
Subject: [PATCH 48/77] add submitButtonProps prop to VerifyMasterPasswordForm
---
src/components/VerifyMasterPasswordForm.tsx | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/components/VerifyMasterPasswordForm.tsx b/src/components/VerifyMasterPasswordForm.tsx
index c5b80a1229..d69d57f48c 100644
--- a/src/components/VerifyMasterPasswordForm.tsx
+++ b/src/components/VerifyMasterPasswordForm.tsx
@@ -6,7 +6,7 @@ import SingleInputForm, {
import { logError } from 'utils/sentry';
import { CustomError } from 'utils/error';
-import { Input } from '@mui/material';
+import { ButtonProps, Input } from '@mui/material';
import { KeyAttributes, User } from 'types/user';
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
import { t } from 'i18next';
@@ -16,6 +16,7 @@ export interface VerifyMasterPasswordFormProps {
keyAttributes: KeyAttributes;
callback: (key: string, passphrase: string) => void;
buttonText: string;
+ submitButtonProps?: ButtonProps;
}
export default function VerifyMasterPasswordForm({
@@ -23,6 +24,7 @@ export default function VerifyMasterPasswordForm({
keyAttributes,
callback,
buttonText,
+ submitButtonProps,
}: VerifyMasterPasswordFormProps) {
const verifyPassphrase: SingleInputFormProps['callback'] = async (
passphrase,
@@ -72,6 +74,7 @@ export default function VerifyMasterPasswordForm({
callback={verifyPassphrase}
placeholder={t('RETURN_PASSPHRASE_HINT')}
buttonText={buttonText}
+ submitButtonProps={submitButtonProps}
hiddenPreInput={
Date: Fri, 31 Mar 2023 12:47:00 +0530
Subject: [PATCH 49/77] remove button stack when no buttons present
---
src/components/DialogBoxV2/index.tsx | 90 +++++++++++++++-------------
1 file changed, 47 insertions(+), 43 deletions(-)
diff --git a/src/components/DialogBoxV2/index.tsx b/src/components/DialogBoxV2/index.tsx
index be9d76ef98..0c2fa24d13 100644
--- a/src/components/DialogBoxV2/index.tsx
+++ b/src/components/DialogBoxV2/index.tsx
@@ -42,13 +42,13 @@ export default function DialogBoxV2({
);
From 0bfefb8919492bef56e1bb68a64979e42706709a Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Fri, 31 Mar 2023 15:00:11 +0530
Subject: [PATCH 50/77] fix dialogBox close
---
src/pages/_app.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 22cd04ef1d..fa21eccb35 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -306,7 +306,7 @@ export default function App(props) {
useEffect(() => {
setDialogBoxV2View(true);
- }, [dialogBoxV2View]);
+ }, [dialogBoxAttributeV2]);
useEffect(() => {
setNotificationView(true);
From ae03dc22ba36b5f0c5dff61dae231e2997e2642f Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Fri, 31 Mar 2023 15:00:41 +0530
Subject: [PATCH 51/77] update CONFIRM_ACCOUNT_DELETION_MESSAGE formatting
---
public/locales/en/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index 26b545f518..93eacf6733 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -422,7 +422,7 @@
"ASK_FOR_FEEDBACK": "We'll be sorry to see you go. Are you facing some issue?
Please write to us at {{emailID}}, maybe there is a way we can help.
",
"SEND_FEEDBACK": "Yes, send feedback",
"CONFIRM_ACCOUNT_DELETION_TITLE": "Are you sure you want to delete your account?",
- "CONFIRM_ACCOUNT_DELETION_MESSAGE": "Your uploaded data will be scheduled for deletion, and your account will be permanently deleted.
This action is not reversible.
",
+ "CONFIRM_ACCOUNT_DELETION_MESSAGE": "Your uploaded data will be scheduled for deletion, and your account will be permanently deleted.
This action is not reversible.",
"AUTHENTICATE": "Authenticate",
"UPLOADED_TO_SINGLE_COLLECTION": "Uploaded to single collection",
"UPLOADED_TO_SEPARATE_COLLECTIONS": "Uploaded to separate collections",
From 7f6324789bf987591b397b6e6738c98ae8898225 Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Fri, 31 Mar 2023 15:02:51 +0530
Subject: [PATCH 52/77] added EnteButton component
---
src/components/EnteButton.tsx | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 src/components/EnteButton.tsx
diff --git a/src/components/EnteButton.tsx b/src/components/EnteButton.tsx
new file mode 100644
index 0000000000..f7d3246717
--- /dev/null
+++ b/src/components/EnteButton.tsx
@@ -0,0 +1,30 @@
+import Done from '@mui/icons-material/Done';
+import { Button, ButtonProps, CircularProgress } from '@mui/material';
+import { useEffect, useState } from 'react';
+
+interface Iprops extends ButtonProps {
+ loading: boolean;
+}
+
+export default function EnteButton({ children, loading, ...props }: Iprops) {
+ const [success, setSuccess] = useState(false);
+
+ useEffect(() => {
+ if (loading === false) {
+ setSuccess(true);
+ setTimeout(() => setSuccess(false), 2000);
+ }
+ }, [loading]);
+
+ return (
+
+ );
+}
From ea18eef80de38caab9f4861069f40b0c5f996de2 Mon Sep 17 00:00:00 2001
From: Abhinav
Date: Fri, 31 Mar 2023 16:30:17 +0530
Subject: [PATCH 53/77] remove success logic from button and fixed loading and
success button states
---
src/components/EnteButton.tsx | 47 ++++++++++++++++++++++++-----------
1 file changed, 32 insertions(+), 15 deletions(-)
diff --git a/src/components/EnteButton.tsx b/src/components/EnteButton.tsx
index f7d3246717..b684198fc6 100644
--- a/src/components/EnteButton.tsx
+++ b/src/components/EnteButton.tsx
@@ -1,25 +1,42 @@
import Done from '@mui/icons-material/Done';
-import { Button, ButtonProps, CircularProgress } from '@mui/material';
-import { useEffect, useState } from 'react';
+import {
+ Button,
+ ButtonProps,
+ CircularProgress,
+ PaletteColor,
+} from '@mui/material';
interface Iprops extends ButtonProps {
- loading: boolean;
+ loading?: boolean;
+ success?: boolean;
}
-export default function EnteButton({ children, loading, ...props }: Iprops) {
- const [success, setSuccess] = useState(false);
-
- useEffect(() => {
- if (loading === false) {
- setSuccess(true);
- setTimeout(() => setSuccess(false), 2000);
- }
- }, [loading]);
-
+export default function EnteButton({
+ children,
+ loading,
+ success,
+ disabled,
+ sx,
+ ...props
+}: Iprops) {
return (
-