@@ -22,7 +22,33 @@ interface RecaptchaOptions {
2222}
2323
2424// reCAPTCHA site key - in production, this should come from environment variables
25- const RECAPTCHA_SITE_KEY = import . meta. env . VITE_RECAPTCHA_SITEKEY
25+ const RECAPTCHA_SITE_KEY =
26+ import . meta. env . VITE_RECAPTCHA_SITEKEY || '6LecbjksAAAAAJCp4NMbtNEHucXwiyhMjMWpXwhZ'
27+
28+ /**
29+ * Load reCAPTCHA script dynamically to avoid CORB issues
30+ * @param siteKey - reCAPTCHA site key
31+ * @returns Promise<void>
32+ */
33+ function loadRecaptchaScript ( siteKey : string ) : Promise < void > {
34+ return new Promise ( ( resolve , reject ) => {
35+ // Check if script is already loaded
36+ if ( document . querySelector ( 'script[src*="recaptcha/enterprise.js"]' ) ) {
37+ resolve ( )
38+ return
39+ }
40+
41+ const script = document . createElement ( 'script' )
42+ script . src = `https://www.google.com/recaptcha/enterprise.js?render=${ siteKey } `
43+ script . async = true
44+ script . defer = true
45+
46+ script . onload = ( ) => resolve ( )
47+ script . onerror = ( ) => reject ( new Error ( 'Failed to load reCAPTCHA script' ) )
48+
49+ document . head . appendChild ( script )
50+ } )
51+ }
2652
2753/**
2854 * Initialize reCAPTCHA and get a token
@@ -31,36 +57,47 @@ const RECAPTCHA_SITE_KEY = import.meta.env.VITE_RECAPTCHA_SITEKEY
3157 */
3258export async function getRecaptchaToken ( action : string = 'contact_form' ) : Promise < string > {
3359 return new Promise ( ( resolve , reject ) => {
34- // Wait for grecaptcha to be available
35- const checkGrecaptcha = ( ) => {
36- if ( window . grecaptcha && window . grecaptcha . enterprise ) {
37- window . grecaptcha . enterprise . ready ( async ( ) => {
38- try {
39- const token = await window . grecaptcha . enterprise . execute ( RECAPTCHA_SITE_KEY , {
40- action,
41- } )
42- resolve ( token )
43- } catch ( error ) {
44- console . error ( 'reCAPTCHA execution failed:' , error )
45- reject (
46- new Error (
47- `reCAPTCHA execution failed: ${ error instanceof Error ? error . message : 'Unknown error' } ` ,
48- ) ,
49- )
50- }
51- } )
52- } else {
53- // Retry after a short delay
54- setTimeout ( checkGrecaptcha , 100 )
60+ try {
61+ // Load reCAPTCHA script if not already loaded
62+ loadRecaptchaScript ( RECAPTCHA_SITE_KEY )
63+
64+ // Wait for grecaptcha to be available
65+ const checkGrecaptcha = ( ) => {
66+ if ( window . grecaptcha && window . grecaptcha . enterprise ) {
67+ window . grecaptcha . enterprise . ready ( async ( ) => {
68+ try {
69+ const token = await window . grecaptcha . enterprise . execute ( RECAPTCHA_SITE_KEY , {
70+ action,
71+ } )
72+ resolve ( token )
73+ } catch ( error ) {
74+ console . error ( 'reCAPTCHA execution failed:' , error )
75+ reject (
76+ new Error (
77+ `reCAPTCHA execution failed: ${ error instanceof Error ? error . message : 'Unknown error' } ` ,
78+ ) ,
79+ )
80+ }
81+ } )
82+ } else {
83+ // Retry after a short delay
84+ setTimeout ( checkGrecaptcha , 100 )
85+ }
5586 }
56- }
5787
58- checkGrecaptcha ( )
88+ checkGrecaptcha ( )
5989
60- // Timeout after 10 seconds
61- setTimeout ( ( ) => {
62- reject ( new Error ( 'reCAPTCHA timeout - service not available' ) )
63- } , 10000 )
90+ // Timeout after 10 seconds
91+ setTimeout ( ( ) => {
92+ reject ( new Error ( 'reCAPTCHA timeout - service not available' ) )
93+ } , 10000 )
94+ } catch ( error ) {
95+ reject (
96+ new Error (
97+ `reCAPTCHA initialization failed: ${ error instanceof Error ? error . message : 'Unknown error' } ` ,
98+ ) ,
99+ )
100+ }
64101 } )
65102}
66103
0 commit comments