import React, { useEffect, useRef, useState } from 'react'; import 'altcha'; // ALTCHA خودمیزبان: widget با گرفتن challenge از بک‌اند، proof-of-work را در // پس‌زمینه‌ی مرورگر حل می‌کند و مقدار base64 حل‌شده را در event `verified` می‌دهد. // این مقدار باید در بدنه‌ی درخواستِ endpoint عمومی با کلید `altcha` ارسال شود. interface AltchaProps { onVerified: (payload: string) => void; challengeUrl?: string; } // برچسب‌های فارسی widget (ترجمه‌ی رسمی locale fa). const FA_STRINGS = JSON.stringify({ label: 'من ربات نیستم', verifying: 'در حال بررسی...', verified: 'تأیید شد', waitAlert: 'در حال بررسی... لطفاً منتظر بمانید.', error: 'احراز هویت ناموفق بود. کمی بعد دوباره تلاش کنید.', expired: 'احراز هویت منقضی شد. دوباره تلاش کنید.', }); // altcha-widget یک custom element است؛ به JSX معرفی می‌شود (React 19: namespace زیر React.JSX). declare module 'react' { namespace JSX { interface IntrinsicElements { 'altcha-widget': React.DetailedHTMLProps, HTMLElement> & { challengeurl?: string; strings?: string; }; } } } export default function Altcha({ onVerified, challengeUrl = '/api/v1/altcha/challenge' }: AltchaProps) { const ref = useRef(null); // null=در حال بررسی وضعیت، true/false=فعال/غیرفعال بودن کپچا در سرور const [enabled, setEnabled] = useState(null); useEffect(() => { let alive = true; fetch('/api/v1/altcha/config') .then((r) => r.json()) .then((c) => { if (!alive) return; setEnabled(!!c.enabled); if (!c.enabled) onVerified(''); // کپچا خاموش → فرم بدون مانع ارسال شود }) .catch(() => { if (alive) setEnabled(false); }); return () => { alive = false; }; }, [onVerified]); useEffect(() => { const el = ref.current; if (!el) return; const onStateChange = (e: Event) => { const detail = (e as CustomEvent).detail as { state?: string; payload?: string }; if (detail?.state === 'verified' && detail.payload) { onVerified(detail.payload); } }; el.addEventListener('statechange', onStateChange); return () => el.removeEventListener('statechange', onStateChange); }, [onVerified, enabled]); if (enabled !== true) return null; return } challengeurl={challengeUrl} strings={FA_STRINGS} />; }