diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json index 049b48111..5182ae6b8 100644 --- a/apps/web/package-lock.json +++ b/apps/web/package-lock.json @@ -82,6 +82,7 @@ "react-virtuoso": "^4.12.3", "snarkdown": "^2.0.0", "timeago.js": "4.0.2", + "validator": "^13.15.35", "w3c-keyname": "^2.2.6", "wouter": "2.12.1", "zustand": "4.5.5", @@ -104,6 +105,7 @@ "@types/react-dom": "18.3.0", "@types/react-modal": "3.16.3", "@types/react-scroll-sync": "^0.9.0", + "@types/validator": "^13.15.10", "@types/wicg-file-system-access": "^2023.10.5", "@vitejs/plugin-react-swc": "^3.7.2", "autoprefixer": "^10.4.19", @@ -4942,6 +4944,13 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/wicg-file-system-access": { "version": "2023.10.6", "resolved": "https://registry.npmjs.org/@types/wicg-file-system-access/-/wicg-file-system-access-2023.10.6.tgz", @@ -12841,6 +12850,15 @@ "node": ">=8" } }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/varint": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", diff --git a/apps/web/package.json b/apps/web/package.json index aa53ba071..7e57426b3 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -24,6 +24,7 @@ "@notesnook/crypto": "file:../../packages/crypto", "@notesnook/desktop": "file:../desktop", "@notesnook/editor": "file:../../packages/editor", + "@notesnook/icons": "file:../../packages/icons", "@notesnook/intl": "file:../../packages/intl", "@notesnook/logger": "file:../../packages/logger", "@notesnook/streamable-fs": "file:../../packages/streamable-fs", @@ -31,7 +32,6 @@ "@notesnook/themes-server": "file:../../servers/themes", "@notesnook/ui": "file:../../packages/ui", "@notesnook/web-clipper": "file:../../extensions/web-clipper", - "@notesnook/icons": "file:../../packages/icons", "@paddle/paddle-js": "^1.4.2", "@react-pdf-viewer/core": "^3.12.0", "@react-pdf-viewer/toolbar": "^3.12.0", @@ -80,6 +80,7 @@ "react-virtuoso": "^4.12.3", "snarkdown": "^2.0.0", "timeago.js": "4.0.2", + "validator": "^13.15.35", "w3c-keyname": "^2.2.6", "wouter": "2.12.1", "zustand": "4.5.5", @@ -102,6 +103,7 @@ "@types/react-dom": "18.3.0", "@types/react-modal": "3.16.3", "@types/react-scroll-sync": "^0.9.0", + "@types/validator": "^13.15.10", "@types/wicg-file-system-access": "^2023.10.5", "@vitejs/plugin-react-swc": "^3.7.2", "autoprefixer": "^10.4.19", diff --git a/apps/web/src/views/auth.tsx b/apps/web/src/views/auth.tsx index fe40ba569..f80fd8c97 100644 --- a/apps/web/src/views/auth.tsx +++ b/apps/web/src/views/auth.tsx @@ -54,6 +54,7 @@ import { showToast } from "../utils/toast"; import AuthContainer from "../components/auth-container"; import { useTimer } from "../hooks/use-timer"; import { AuthErrorText } from "../components/error-text"; +import isEmail from "validator/lib/isEmail"; import { AuthenticatorType, User } from "@notesnook/core"; import { showLogoutConfirmation } from "../dialogs/confirm"; import { TaskManager } from "../common/task-manager"; @@ -246,6 +247,8 @@ function Login(props: BaseAuthComponentProps<"login">) { subtitle: strings.authWait() }} onSubmit={async (form) => { + if (!isEmail(form.email)) throw new Error(strings.emailInvalid()); + const result = (await userstore.login(form)) as | MFAErrorData | undefined; @@ -281,7 +284,7 @@ function Login(props: BaseAuthComponentProps<"login">) { ) : null} ) { }} openURL={openURL} onSubmit={async (form) => { + if (!isEmail(form.email)) throw new Error(strings.emailInvalid()); + if (form.password !== form["confirm-password"]) { throw new Error(strings.passwordNotMatched()); } @@ -352,7 +357,7 @@ function Signup(props: BaseAuthComponentProps<"signup">) { <> ) { return; } + if (!isEmail(form.email)) throw new Error(strings.emailInvalid()); + const url = await db.user.recoverAccount(form.email.toLowerCase()); console.log(url); if (IS_TESTING) { @@ -621,7 +628,7 @@ function AccountRecovery(props: BaseAuthComponentProps<"recover">) { <> - +