Add comment spam classification and captcha checks.
Score artwork comments with local signatures plus Together AI, and optionally require Turnstile before posting.
This commit is contained in:
@@ -7,6 +7,8 @@ const providerAdapters = {
|
||||
return api.render(container, {
|
||||
sitekey: siteKey,
|
||||
theme,
|
||||
appearance: 'always',
|
||||
size: 'normal',
|
||||
callback: (token) => onToken?.(token || ''),
|
||||
'expired-callback': () => onToken?.(''),
|
||||
'error-callback': () => onToken?.(''),
|
||||
@@ -80,6 +82,15 @@ function loadCaptchaScript(src) {
|
||||
const existing = document.querySelector(`script[src="${src}"]`)
|
||||
|
||||
if (existing) {
|
||||
// The registration page may have loaded the same Turnstile script
|
||||
// already. In that case its load event has fired before this promise
|
||||
// was created, so waiting only for another load event would never
|
||||
// resolve and the widget would remain an empty container.
|
||||
if (window.turnstile || window.grecaptcha || window.hcaptcha) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
if (existing.dataset.loaded === 'true') {
|
||||
resolve()
|
||||
return
|
||||
@@ -109,6 +120,11 @@ function loadCaptchaScript(src) {
|
||||
export default function TurnstileField({ provider = 'turnstile', siteKey, scriptUrl = '', onToken, theme = 'dark', className = '' }) {
|
||||
const containerRef = useRef(null)
|
||||
const widgetIdRef = useRef(null)
|
||||
const onTokenRef = useRef(onToken)
|
||||
|
||||
useEffect(() => {
|
||||
onTokenRef.current = onToken
|
||||
}, [onToken])
|
||||
|
||||
useEffect(() => {
|
||||
const adapter = providerAdapters[provider] || providerAdapters.turnstile
|
||||
@@ -130,17 +146,17 @@ export default function TurnstileField({ provider = 'turnstile', siteKey, script
|
||||
widgetIdRef.current = adapter.render(api, containerRef.current, {
|
||||
siteKey,
|
||||
theme,
|
||||
onToken,
|
||||
onToken: (token) => onTokenRef.current?.(token),
|
||||
})
|
||||
}
|
||||
|
||||
loadCaptchaScript(scriptUrl).catch(() => onToken?.('')).finally(() => {
|
||||
loadCaptchaScript(scriptUrl).catch(() => onTokenRef.current?.('')).finally(() => {
|
||||
const api = window[adapter.globalName]
|
||||
if (typeof api?.ready === 'function') {
|
||||
api.ready(mountWidget)
|
||||
} else {
|
||||
mountWidget()
|
||||
}
|
||||
// Do not call turnstile.ready() here. Cloudflare rejects ready() when
|
||||
// api.js was loaded with async/defer (as it is on the registration
|
||||
// page). Polling until render() exists works for both an already-loaded
|
||||
// shared script and a script that is still loading.
|
||||
mountWidget()
|
||||
|
||||
if (widgetIdRef.current === null) {
|
||||
intervalId = window.setInterval(mountWidget, 250)
|
||||
@@ -152,10 +168,10 @@ export default function TurnstileField({ provider = 'turnstile', siteKey, script
|
||||
if (intervalId) {
|
||||
window.clearInterval(intervalId)
|
||||
}
|
||||
adapter.cleanup(window[adapter.globalName], widgetIdRef.current, containerRef.current, onToken)
|
||||
adapter.cleanup(window[adapter.globalName], widgetIdRef.current, containerRef.current, (token) => onTokenRef.current?.(token))
|
||||
widgetIdRef.current = null
|
||||
}
|
||||
}, [className, onToken, provider, scriptUrl, siteKey, theme])
|
||||
}, [provider, scriptUrl, siteKey, theme])
|
||||
|
||||
if (!siteKey) {
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user