<!DOCTYPE html>
<html lang="en">
<head>
<title>CrowdSec Captcha</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- If you still want Tailwind or any custom styling, keep your <style> or <link> references -->
<style>/*! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.fixed{position:fixed}.right-0{right:0}.top-0{top:0}.flex{display:flex}.h-24{height:6rem}.h-3\/5{height:60%}.h-6{height:1.5rem}.h-full{height:100%}.h-screen{height:100vh}.w-24{width:6rem}.w-6{width:1.5rem}.w-full{width:100%}.w-screen{width:100vw}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.rounded-xl{border-radius:.75rem}.border-2{border-width:2px}.border-black{--tw-border-opacity:1;border-color:rgb(0 0 0/var(--tw-border-opacity))}.p-4{padding:1rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.dark .dark\:border-white{--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity))}.dark .dark\:bg-slate-900{--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity))}.dark .dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}@media (min-width:640px){.sm\:w-2\/3{width:66.666667%}}@media (min-width:768px){.md\:h-2\/5{height:40%}.md\:flex-row{flex-direction:row}.md\:text-2xl{font-size:1.5rem;line-height:2rem}.md\:text-lg{font-size:1.125rem;line-height:1.75rem}}@media (min-width:1024px){.lg\:w-1\/2{width:50%}.lg\:text-3xl{font-size:1.875rem;line-height:2.25rem}.lg\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:1280px){.xl\:text-4xl{font-size:2.25rem;line-height:2.5rem}}</style>
</head>
<body class="h-screen w-screen dark:bg-slate-900 dark:text-white">
<div class="h-full w-full flex flex-col justify-center items-center">
<div class="flex flex-col justify-between items-center border-2 border-black dark:border-white rounded-xl p-4 h-3/5 md:h-2/5 w-full sm:w-2/3 lg:w-1/2">
<div class="fixed p-4 top-0 right-0 dark-mode-toggle"></div>
<div class="flex flex-col items-center space-y-4">
<svg fill="black" class="h-24 w-24" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="exclamation-triangle" role="img" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 576 512">
<path d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"></path>
</svg>
<h1 class="text-2xl lg:text-3xl xl:text-4xl" id="verify-heading">Verify you're human</h1>
<!-- Form that CrowdSec bouncer expects to post to. -->
<form action="" method="POST" class="flex flex-col space-y-1" id="captcha-form">
<!-- We'll dynamically insert a hidden input here with the Turnstile token once user passes. -->
</form>
<!-- IFRAME to your Next.js Turnstile Page -->
<iframe
id="turnstile-iframe"
src="https://security.faaaster.io/securitycheck?ip=155.133.132.66"
style="border:none; width:100%; height:100px;"
></iframe>
<!-- Hidden field to store the validation token -->
<input type="hidden" id="validation-token" value="383e06573f26db2acd3932f219971a7529c3accce19775b61fb9b41dde680552" />
</div>
<div class="flex-col md:flex-row text-sm flex items-center md:space-x-2 mt-4">
<p id="powered-by-text">This security check is powered by </p><span> </span>
<a href="https://www.faaaster.io/" target="_blank" rel="noopener" class="flex flex-col items-center space-x-1">
<span>Faaaster</span>
</a>
</div>
</div>
</div>
<script>
// Language detection and translation
const translations = {
'en': {
verify: "Verify you're human",
poweredBy: "This security check is powered by "
},
'fr': {
verify: "Vérifiez que vous êtes humain",
poweredBy: "Cette vérification de sécurité est propulsée par"
}
};
function setPageLanguage() {
const userLang = navigator.language || navigator.userLanguage;
const langCode = userLang.split('-')[0].toLowerCase();
const langTexts = translations[langCode] || translations['en']; // Default to English if language not supported
// Update heading
const heading = document.getElementById('verify-heading');
heading.textContent = langTexts.verify;
// Update powered by text
const poweredBy = document.getElementById('powered-by-text');
poweredBy.textContent = langTexts.poweredBy;
}
window.addEventListener('DOMContentLoaded', setPageLanguage);
// Example dark mode toggle code – keep if you want, or remove
const svg = Array.from(document.querySelectorAll("svg"));
const button = document.querySelector(".dark-mode-toggle");
const moonSvgString = '<svg fill="black" class="h-6 w-6" viewBox="0 0 35 35" ...> ... </svg>';
const sunSvgString = '<svg class="h-6 w-6" viewBox="0 0 24 24" fill="white" ...> ... </svg>';
window.onload = () => {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
document.body.classList.add("dark");
svg.forEach((el) => el.setAttribute("fill", "white"));
button.innerHTML = sunSvgString;
} else {
button.innerHTML = moonSvgString;
}
button.addEventListener("click", function () {
document.body.classList.toggle("dark");
svg.forEach((el) => {
el.getAttribute("fill") === "black"
? el.setAttribute("fill", "white")
: el.setAttribute("fill", "black");
});
document.body.classList.contains("dark")
? (button.innerHTML = sunSvgString)
: (button.innerHTML = moonSvgString);
});
};
// Listen for messages from the Turnstile iframe
// Listen for messages from the Turnstile iframe
window.addEventListener("message", function (event) {
if (!event.data || !event.data.type) {
return;
}
console.log("data", event.data);
if (event.data.type === "turnstile-success") {
console.log("✅ CAPTCHA passed, unbanning IP locally...");
// Create and submit a POST form to trigger CrowdSec's validation
const form = document.createElement('form');
form.method = 'POST';
form.action = window.location.href;
// Get the validation token generated by the server
const validationToken = document.getElementById('validation-token').value;
// Add the secure validation token (CrowdSec expects this field)
const captchaInput = document.createElement('input');
captchaInput.type = 'hidden';
captchaInput.name = 'cf-turnstile-response'; // Turnstile response field name
captchaInput.value = 'faaaster-validated:' + validationToken; // Secure token format
form.appendChild(captchaInput);
document.body.appendChild(form);
form.submit();
}
if (event.data.type === "turnstile-error") {
// Optionally show an error to user or ask them to retry
console.warn("Turnstile verification failed:", event.data.error);
}
if (event.data.type === "turnstile-expire") {
// The token expired, you might instruct the user or reset the iframe
console.warn("Turnstile token expired");
}
});
</script>
</body>
</html>