-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add reCAPTCHA #93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
6c4ae3c
785ee80
751d1a6
137635a
424b71d
68087b6
4110192
426b7ff
b218741
d6d0062
76e0de0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,8 +1,19 @@ | ||||||||||||||||||||||||||||||||||||
| import { getConfig } from '@edx/frontend-platform/config'; | ||||||||||||||||||||||||||||||||||||
| import { ReactElement } from 'react'; | ||||||||||||||||||||||||||||||||||||
| import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| import PlanDetailsPage from '@/components/plan-details-pages/PlanDetailsPage'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // TODO: unnecessary layer of abstraction, just move component logic into this file. | ||||||||||||||||||||||||||||||||||||
| const PlanDetails: React.FC = () => ( | ||||||||||||||||||||||||||||||||||||
| <PlanDetailsPage /> | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| const PlanDetails = (): ReactElement => { | ||||||||||||||||||||||||||||||||||||
| const { RECAPTCHA_SITE_KEY_WEB } = getConfig(); | ||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||
| <GoogleReCaptchaProvider | ||||||||||||||||||||||||||||||||||||
| reCaptchaKey={RECAPTCHA_SITE_KEY_WEB} | ||||||||||||||||||||||||||||||||||||
| useEnterprise | ||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||
| <PlanDetailsPage /> | ||||||||||||||||||||||||||||||||||||
| </GoogleReCaptchaProvider> | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+9
to
+15
|
||||||||||||||||||||||||||||||||||||
| return ( | |
| <GoogleReCaptchaProvider | |
| reCaptchaKey={RECAPTCHA_SITE_KEY_WEB} | |
| useEnterprise | |
| > | |
| <PlanDetailsPage /> | |
| </GoogleReCaptchaProvider> | |
| const hasValidRecaptchaKey = typeof RECAPTCHA_SITE_KEY_WEB === 'string' && RECAPTCHA_SITE_KEY_WEB.trim() !== ''; | |
| return hasValidRecaptchaKey ? ( | |
| <GoogleReCaptchaProvider | |
| reCaptchaKey={RECAPTCHA_SITE_KEY_WEB} | |
| useEnterprise | |
| > | |
| <PlanDetailsPage /> | |
| </GoogleReCaptchaProvider> | |
| ) : ( | |
| <PlanDetailsPage /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import { getConfig } from '@edx/frontend-platform'; | ||
| import { logError, logInfo } from '@edx/frontend-platform/logging'; | ||
| import { useCallback } from 'react'; | ||
| import { useGoogleReCaptcha } from 'react-google-recaptcha-v3'; | ||
|
|
||
| const useRecaptchaToken = (actionName = 'submit') => { | ||
| const { executeRecaptcha } = useGoogleReCaptcha(); | ||
| const { RECAPTCHA_SITE_KEY_WEB } = getConfig(); | ||
|
|
||
| const isLoading = !(executeRecaptcha || RECAPTCHA_SITE_KEY_WEB); | ||
brobro10000 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const isReady = !!executeRecaptcha && RECAPTCHA_SITE_KEY_WEB; | ||
brobro10000 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| const executeWithFallback = useCallback(async () => { | ||
| if (isReady) { | ||
| const token = await executeRecaptcha(actionName); | ||
| if (!token) { | ||
| throw new Error(`Failed to obtain reCAPTCHA verification token for action: ${actionName}. Please try again or contact support if the issue persists.`); | ||
| } | ||
| return token; | ||
| } | ||
|
|
||
| // Fallback: no reCAPTCHA or not ready | ||
| if (RECAPTCHA_SITE_KEY_WEB) { | ||
| logInfo(`reCAPTCHA not ready for action: ${actionName}. Proceeding without token.`); | ||
| } | ||
| return null; | ||
| }, [isReady, RECAPTCHA_SITE_KEY_WEB, executeRecaptcha, actionName]); | ||
|
|
||
| const getToken = useCallback(async (): Promise<string | null> => { | ||
| try { | ||
| return await executeWithFallback(); | ||
| } catch (err: any) { | ||
| logError(err?.message || 'Failed to execute reCAPTCHA'); | ||
| return null; | ||
| } | ||
| }, [executeWithFallback]); | ||
|
|
||
| return { | ||
| getToken, | ||
| isReady, | ||
| isLoading, | ||
| }; | ||
| }; | ||
|
|
||
| export default useRecaptchaToken; | ||
Uh oh!
There was an error while loading. Please reload this page.