fix: rewrite ALTCHA component for reliable event binding
- Use refs for callbacks to avoid stale closures - Create widget via innerHTML and bind events immediately - Remove dev bypass - use real ALTCHA token - Add console logging for debugging
This commit is contained in:
parent
9f1dd857c4
commit
913fbdb8f7
|
|
@ -28,24 +28,21 @@ export default function LoginPage() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const performLogin = useCallback(async () => {
|
const performLogin = useCallback(async () => {
|
||||||
// Use development bypass for now
|
if (!altchaToken) {
|
||||||
const token = process.env.NODE_ENV === 'development' ? 'BYPASS_DEV_MODE' : altchaToken;
|
|
||||||
|
|
||||||
if (!token && !altchaVerified) {
|
|
||||||
setError('Please complete the security verification');
|
setError('Please complete the security verification');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await login(emailRef.current, passwordRef.current, token);
|
await login(emailRef.current, passwordRef.current, altchaToken);
|
||||||
router.push('/');
|
router.push('/');
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err.message || 'Login failed. Check your credentials.');
|
setError(err.message || 'Login failed. Check your credentials.');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [login, router, altchaToken, altchaVerified]);
|
}, [login, router, altchaToken]);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -114,7 +111,7 @@ export default function LoginPage() {
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn-primary w-full"
|
className="btn-primary w-full"
|
||||||
disabled={loading || (process.env.NODE_ENV !== 'development' && !altchaVerified)}
|
disabled={loading || !altchaVerified}
|
||||||
>
|
>
|
||||||
{loading ? 'Signing in...' : 'Sign In'}
|
{loading ? 'Signing in...' : 'Sign In'}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useRef, useCallback } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
interface AltchaProps {
|
interface AltchaProps {
|
||||||
challengeurl: string;
|
challengeurl: string;
|
||||||
|
|
@ -9,63 +9,46 @@ interface AltchaProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Altcha({ challengeurl, onVerified, onError }: AltchaProps) {
|
export default function Altcha({ challengeurl, onVerified, onError }: AltchaProps) {
|
||||||
const widgetRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const scriptLoaded = useRef(false);
|
const callbacksRef = useRef({ onVerified, onError });
|
||||||
|
callbacksRef.current = { onVerified, onError };
|
||||||
|
|
||||||
const handleStateChange = useCallback((e: Event) => {
|
useEffect(() => {
|
||||||
const detail = (e as CustomEvent).detail;
|
// Load script if not already loaded
|
||||||
if (detail?.state === 'verified' && detail?.payload) {
|
if (!document.querySelector('script[data-altcha]')) {
|
||||||
onVerified?.(detail.payload);
|
const script = document.createElement('script');
|
||||||
} else if (detail?.state === 'error') {
|
script.src = 'https://cdn.jsdelivr.net/npm/altcha@2.3.0/dist/altcha.min.js';
|
||||||
onError?.();
|
script.type = 'module';
|
||||||
|
script.async = true;
|
||||||
|
script.setAttribute('data-altcha', 'true');
|
||||||
|
document.head.appendChild(script);
|
||||||
}
|
}
|
||||||
}, [onVerified, onError]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const container = containerRef.current;
|
||||||
if (scriptLoaded.current) return;
|
|
||||||
scriptLoaded.current = true;
|
|
||||||
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = 'https://cdn.jsdelivr.net/npm/altcha@2.3.0/dist/altcha.min.js';
|
|
||||||
script.type = 'module';
|
|
||||||
script.async = true;
|
|
||||||
document.head.appendChild(script);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const container = widgetRef.current;
|
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const observer = new MutationObserver(() => {
|
// Create the widget element
|
||||||
const widget = container.querySelector('altcha-widget');
|
container.innerHTML = `<altcha-widget challengeurl="${challengeurl}"></altcha-widget>`;
|
||||||
if (widget) {
|
|
||||||
widget.addEventListener('statechange', handleStateChange);
|
|
||||||
observer.disconnect();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(container, { childList: true, subtree: true });
|
|
||||||
|
|
||||||
// Also try immediately in case widget already exists
|
|
||||||
const widget = container.querySelector('altcha-widget');
|
const widget = container.querySelector('altcha-widget');
|
||||||
if (widget) {
|
if (!widget) return;
|
||||||
widget.addEventListener('statechange', handleStateChange);
|
|
||||||
observer.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
const handler = (e: Event) => {
|
||||||
observer.disconnect();
|
const detail = (e as CustomEvent).detail;
|
||||||
const w = container.querySelector('altcha-widget');
|
console.log('[ALTCHA] statechange:', detail);
|
||||||
if (w) {
|
if (detail?.state === 'verified' && detail?.payload) {
|
||||||
w.removeEventListener('statechange', handleStateChange);
|
callbacksRef.current.onVerified?.(detail.payload);
|
||||||
|
} else if (detail?.state === 'error') {
|
||||||
|
callbacksRef.current.onError?.();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [handleStateChange]);
|
|
||||||
|
|
||||||
return (
|
widget.addEventListener('statechange', handler);
|
||||||
<div ref={widgetRef} dangerouslySetInnerHTML={{
|
|
||||||
__html: `<altcha-widget challengeurl="${challengeurl}" debug></altcha-widget>`
|
return () => {
|
||||||
}} />
|
widget.removeEventListener('statechange', handler);
|
||||||
);
|
};
|
||||||
|
}, [challengeurl]);
|
||||||
|
|
||||||
|
return <div ref={containerRef} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue