feat: add recaptcha to sign in and forget password
This commit is contained in:
parent
00c807f86d
commit
ddd3d39204
|
|
@ -64,9 +64,10 @@ export const loginHandler = async (
|
|||
args?: {
|
||||
email?: string
|
||||
password?: string
|
||||
headers?: Record<string, string>
|
||||
},
|
||||
) => {
|
||||
const { email, password } = args ?? {}
|
||||
const { email, password, headers } = args ?? {}
|
||||
if (IN_ELECTRON && provider !== "credential") {
|
||||
window.open(`${WEB_URL}/login?provider=${provider}`)
|
||||
} else {
|
||||
|
|
@ -75,7 +76,7 @@ export const loginHandler = async (
|
|||
window.location.href = "/login"
|
||||
return
|
||||
}
|
||||
return signIn.email({ email, password })
|
||||
return signIn.email({ email, password }, { headers })
|
||||
}
|
||||
|
||||
signIn.social({
|
||||
|
|
|
|||
|
|
@ -42,10 +42,16 @@ export function LoginWithPassword({ runtime }: { runtime: LoginRuntime }) {
|
|||
const { present } = useModalStack()
|
||||
const { dismiss } = useCurrentModal()
|
||||
|
||||
const recaptchaRef = useRef<ReCAPTCHA>(null)
|
||||
|
||||
async function onSubmit(values: z.infer<typeof formSchema>) {
|
||||
const token = await recaptchaRef.current?.executeAsync()
|
||||
const res = await loginHandler("credential", runtime, {
|
||||
email: values.email,
|
||||
password: values.password,
|
||||
headers: {
|
||||
"x-token": `r2:${token}`,
|
||||
},
|
||||
})
|
||||
if (res?.error) {
|
||||
toast.error(res.error.message)
|
||||
|
|
@ -117,6 +123,7 @@ export function LoginWithPassword({ runtime }: { runtime: LoginRuntime }) {
|
|||
)}
|
||||
/>
|
||||
<div className="flex flex-col space-y-3">
|
||||
<ReCAPTCHA ref={recaptchaRef} sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY} size="invisible" />
|
||||
<Button
|
||||
type="submit"
|
||||
isLoading={form.formState.isSubmitting}
|
||||
|
|
@ -235,13 +242,7 @@ function RegisterForm() {
|
|||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{env.VITE_RECAPTCHA_V2_SITE_KEY && (
|
||||
<ReCAPTCHA
|
||||
ref={recaptchaRef}
|
||||
sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY}
|
||||
size="invisible"
|
||||
/>
|
||||
)}
|
||||
<ReCAPTCHA ref={recaptchaRef} sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY} size="invisible" />
|
||||
<Button disabled={!isValid} type="submit" className="w-full" size="lg">
|
||||
{t("register.submit")}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -23,13 +23,7 @@ export const ClaimDailyReward = () => {
|
|||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<>
|
||||
{env.VITE_RECAPTCHA_V2_SITE_KEY && (
|
||||
<ReCAPTCHA
|
||||
ref={recaptchaRef}
|
||||
sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY}
|
||||
size="invisible"
|
||||
/>
|
||||
)}
|
||||
<ReCAPTCHA ref={recaptchaRef} sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY} size="invisible" />
|
||||
<Button
|
||||
variant="primary"
|
||||
isLoading={mutation.isPending}
|
||||
|
|
|
|||
|
|
@ -62,16 +62,17 @@ export const loginHandler = async (
|
|||
args?: {
|
||||
email?: string
|
||||
password?: string
|
||||
headers?: Record<string, string>
|
||||
},
|
||||
) => {
|
||||
const { email, password } = args ?? {}
|
||||
const { email, password, headers } = args ?? {}
|
||||
|
||||
if (provider === "credential") {
|
||||
if (!email || !password) {
|
||||
window.location.href = "/login"
|
||||
return
|
||||
}
|
||||
return signIn.email({ email, password })
|
||||
return signIn.email({ email, password }, { headers })
|
||||
}
|
||||
|
||||
signIn.social({
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@ import { Input } from "@follow/components/ui/input/index.js"
|
|||
import { LoadingCircle } from "@follow/components/ui/loading/index.jsx"
|
||||
import { authProvidersConfig } from "@follow/constants"
|
||||
import { DEEPLINK_SCHEME } from "@follow/shared/constants"
|
||||
import { env } from "@follow/shared/env"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import ReCAPTCHA from "react-google-recaptcha"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { Trans, useTranslation } from "react-i18next"
|
||||
import { Link, useLocation, useNavigate } from "react-router"
|
||||
|
|
@ -225,6 +227,8 @@ function LoginWithPassword() {
|
|||
const { isSubmitting } = form.formState
|
||||
const navigate = useNavigate()
|
||||
|
||||
const recaptchaRef = useRef<ReCAPTCHA>(null)
|
||||
|
||||
async function onSubmit(values: z.infer<typeof formSchema>) {
|
||||
if (needTwoFactor && values.code) {
|
||||
const res = await twoFactor.verifyTotp({ code: values.code })
|
||||
|
|
@ -236,7 +240,13 @@ function LoginWithPassword() {
|
|||
return
|
||||
}
|
||||
|
||||
const res = await loginHandler("credential", "app", values)
|
||||
const token = await recaptchaRef.current?.executeAsync()
|
||||
const res = await loginHandler("credential", "app", {
|
||||
...values,
|
||||
headers: {
|
||||
"x-token": `r2:${token}`,
|
||||
},
|
||||
})
|
||||
if (res?.error) {
|
||||
toast.error(res.error.message)
|
||||
return
|
||||
|
|
@ -303,6 +313,7 @@ function LoginWithPassword() {
|
|||
)}
|
||||
/>
|
||||
)}
|
||||
<ReCAPTCHA ref={recaptchaRef} sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY} size="invisible" />
|
||||
<Button type="submit" buttonClassName="!mt-3 w-full" isLoading={isSubmitting} size="lg">
|
||||
{t("login.continueWith", { provider: t("words.email") })}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import { Input } from "@follow/components/ui/input/index.js"
|
|||
import { env } from "@follow/shared/env"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import { useRef } from "react"
|
||||
import ReCAPTCHA from "react-google-recaptcha"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useNavigate } from "react-router"
|
||||
|
|
@ -38,13 +40,23 @@ export function Component() {
|
|||
},
|
||||
})
|
||||
|
||||
const recaptchaRef = useRef<ReCAPTCHA>(null)
|
||||
|
||||
const { isValid } = form.formState
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: async (values: z.infer<typeof forgetPasswordFormSchema>) => {
|
||||
const res = await forgetPassword({
|
||||
email: values.email,
|
||||
redirectTo: `${env.VITE_WEB_URL}/reset-password`,
|
||||
})
|
||||
const token = await recaptchaRef.current?.executeAsync()
|
||||
const res = await forgetPassword(
|
||||
{
|
||||
email: values.email,
|
||||
redirectTo: `${env.VITE_WEB_URL}/reset-password`,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-token": `r2:${token}`,
|
||||
},
|
||||
},
|
||||
)
|
||||
if (res.error) {
|
||||
throw new Error(res.error.message)
|
||||
}
|
||||
|
|
@ -98,6 +110,11 @@ export function Component() {
|
|||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<ReCAPTCHA
|
||||
ref={recaptchaRef}
|
||||
sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY}
|
||||
size="invisible"
|
||||
/>
|
||||
<div className="text-right">
|
||||
<Button disabled={!isValid} type="submit" isLoading={updateMutation.isPending}>
|
||||
{t("login.submit")}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
FormMessage,
|
||||
} from "@follow/components/ui/form/index.jsx"
|
||||
import { Input } from "@follow/components/ui/input/index.js"
|
||||
import { env } from "@follow/shared/env.desktop"
|
||||
import { env } from "@follow/shared/env"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useRef } from "react"
|
||||
import ReCAPTCHA from "react-google-recaptcha"
|
||||
|
|
@ -138,13 +138,7 @@ function RegisterForm() {
|
|||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{env.VITE_RECAPTCHA_V2_SITE_KEY && (
|
||||
<ReCAPTCHA
|
||||
ref={recaptchaRef}
|
||||
sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY}
|
||||
size="invisible"
|
||||
/>
|
||||
)}
|
||||
<ReCAPTCHA ref={recaptchaRef} sitekey={env.VITE_RECAPTCHA_V2_SITE_KEY} size="invisible" />
|
||||
<Button disabled={!isValid} type="submit" className="w-full" size="lg">
|
||||
{t("register.submit")}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ export const envSchema = {
|
|||
VITE_EXTERNAL_API_URL: z.string().optional(),
|
||||
VITE_WEB_PROD_URL: z.string().optional(),
|
||||
VITE_WEB_DEV_URL: z.string().optional(),
|
||||
|
||||
VITE_RECAPTCHA_V2_SITE_KEY: z.string().default("6LdxQ-sqAAAAAN-_za3hUdFkJEO_cu2xHSpLKVan"),
|
||||
VITE_RECAPTCHA_V3_SITE_KEY: z.string().default("6LdG-asqAAAAAEXr96565MKbRvxGEv31XEykRSHV"),
|
||||
}
|
||||
|
||||
export const isDev = false
|
||||
|
|
|
|||
Loading…
Reference in New Issue