Rewrite to vanilla JS
This commit is contained in:
parent
a53fada7e1
commit
0759b43efc
@ -23,6 +23,8 @@ SOFTWARE.
|
||||
"""
|
||||
|
||||
import time
|
||||
import json
|
||||
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from functools import wraps
|
||||
@ -75,10 +77,13 @@ def hoptcha_protected(
|
||||
|
||||
if isinstance(key, str):
|
||||
key_func = BUILTIN_KEYS.get(key)
|
||||
|
||||
if not key_func:
|
||||
raise ValueError(f"Unknown key: {key}")
|
||||
|
||||
elif callable(key):
|
||||
key_func = key
|
||||
|
||||
else:
|
||||
raise TypeError("key must be a string or callable")
|
||||
|
||||
@ -101,7 +106,19 @@ def hoptcha_protected(
|
||||
attempts = cache.get(cache_key, 0)
|
||||
|
||||
if attempts >= threshold:
|
||||
token = request.POST.get("captcha_token") or request.GET.get("captcha_token")
|
||||
token = (
|
||||
request.POST.get("captcha_token") or
|
||||
request.GET.get("captcha_token")
|
||||
)
|
||||
|
||||
# Try extracting from JSON body if not found yet
|
||||
if not token and request.content_type == "application/json":
|
||||
try:
|
||||
body = json.loads(request.body)
|
||||
token = body.get("captcha_token")
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
pass # Malformed or empty JSON
|
||||
|
||||
if not token or not verify_token(token):
|
||||
return response(request) if response else JsonResponse({
|
||||
"error": "CAPTCHA",
|
||||
|
@ -1,11 +1,6 @@
|
||||
// ==================== HOPTCHA CLIENT ====================
|
||||
|
||||
(function(window, $) {
|
||||
if (!$) {
|
||||
console.error("Hoptcha requires jQuery. Please include it before this script.");
|
||||
return;
|
||||
}
|
||||
// ==================== HOPTCHA CLIENT (Vanilla JS) ====================
|
||||
|
||||
(function (window) {
|
||||
/**
|
||||
* Handle messages from the iframe.
|
||||
*/
|
||||
@ -23,14 +18,17 @@
|
||||
* Renders the Hoptcha iframe inside #hoptcha-container.
|
||||
* @param {string} url - The Hoptcha URL endpoint.
|
||||
*/
|
||||
window.renderCaptchaStep = function(url) {
|
||||
$('#hoptcha-container').html(`
|
||||
<iframe
|
||||
id="captcha-iframe"
|
||||
src=${url}
|
||||
style="width: 100%; height: 250px; border: none; border-radius: 12px;"
|
||||
></iframe>
|
||||
`);
|
||||
window.renderCaptchaStep = function (url) {
|
||||
const container = document.getElementById("hoptcha-container");
|
||||
if (container) {
|
||||
container.innerHTML = `
|
||||
<iframe
|
||||
id="captcha-iframe"
|
||||
src="${url}"
|
||||
style="width: 100%; height: 250px; border: none; border-radius: 12px;"
|
||||
></iframe>
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -39,13 +37,30 @@
|
||||
* @param {object} payload - Payload to send, must include `captcha_token`.
|
||||
* @param {function} onSuccess - Success handler.
|
||||
* @param {function} [onError] - Error fallback.
|
||||
* @param {function} [onCaptcha] - Optional custom render callback.
|
||||
*/
|
||||
window.hoptchaPost = function(url, payload, onSuccess, onError, onCaptcha) {
|
||||
$.post(url, payload, function(data) {
|
||||
window.hoptchaPost = function (url, payload, onSuccess, onError, onCaptcha) {
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.json().then(data => {
|
||||
throw { status: response.status, data };
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (onSuccess) onSuccess(data);
|
||||
}).fail(xhr => {
|
||||
const error = xhr.responseJSON ? xhr.responseJSON.error : 'Something went wrong.';
|
||||
const captcha_url = xhr.responseJSON?.url;
|
||||
})
|
||||
.catch(err => {
|
||||
const error = err.data?.error || 'Something went wrong.';
|
||||
const captcha_url = err.data?.url;
|
||||
|
||||
if (!captcha_url) {
|
||||
if (onError) onError(error);
|
||||
@ -54,7 +69,7 @@
|
||||
}
|
||||
|
||||
// Register callback
|
||||
window._captchaSuccessCallback = function(token) {
|
||||
window._captchaSuccessCallback = function (token) {
|
||||
payload.captcha_token = token;
|
||||
window.hoptchaPost(url, payload, onSuccess, onError, onCaptcha);
|
||||
};
|
||||
@ -63,5 +78,4 @@
|
||||
render(captcha_url);
|
||||
});
|
||||
};
|
||||
|
||||
})(window, window.jQuery);
|
||||
})(window);
|
||||
|
@ -4,7 +4,6 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hoptcha Demo</title>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="{% static 'django_hoptcha/hoptcha.js' %}"></script>
|
||||
<style>
|
||||
body {
|
||||
@ -73,26 +72,39 @@
|
||||
|
||||
<script>
|
||||
function closeCaptcha() {
|
||||
$('#custom-captcha-modal').hide();
|
||||
const modal = document.getElementById("custom-captcha-modal");
|
||||
if (modal) modal.style.display = "none";
|
||||
}
|
||||
|
||||
$("#demo-form").submit(function(e) {
|
||||
e.preventDefault();
|
||||
const formData = $(this).serializeArray();
|
||||
let payload = {};
|
||||
formData.forEach(item => payload[item.name] = item.value);
|
||||
payload["captcha_token"] = "init";
|
||||
const demoForm = document.getElementById("demo-form");
|
||||
|
||||
hoptchaPost('/submit/', payload, function(data) {
|
||||
closeCaptcha();
|
||||
alert(data.success);
|
||||
}, function(error) {
|
||||
alert(error);
|
||||
}, function renderCustomCaptcha(url) {
|
||||
$('#custom-captcha-frame').attr('src', url);
|
||||
$('#custom-captcha-modal').show();
|
||||
if (demoForm) {
|
||||
demoForm.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(demoForm);
|
||||
const payload = {};
|
||||
|
||||
for (const [key, value] of formData.entries()) {
|
||||
payload[key] = value;
|
||||
}
|
||||
|
||||
payload["captcha_token"] = "init";
|
||||
|
||||
hoptchaPost('/submit/', payload, function (data) {
|
||||
closeCaptcha();
|
||||
alert(data.success);
|
||||
}, function (error) {
|
||||
alert(error);
|
||||
}, function renderCustomCaptcha(url) {
|
||||
const frame = document.getElementById("custom-captcha-frame");
|
||||
const modal = document.getElementById("custom-captcha-modal");
|
||||
|
||||
if (frame) frame.src = url;
|
||||
if (modal) modal.style.display = "block";
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -22,6 +22,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import render
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
@ -30,10 +32,16 @@ from django_hoptcha.decorators import hoptcha_protected
|
||||
@csrf_exempt
|
||||
@hoptcha_protected(threshold=3, timeout=300)
|
||||
def protected_form_submit(request):
|
||||
name = request.POST.get("name")
|
||||
if not name:
|
||||
return JsonResponse({"error": "Name is required."}, status=400)
|
||||
return JsonResponse({"success": f"Hello, {name}!"})
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
name = data.get('name', '')
|
||||
|
||||
if not name:
|
||||
return JsonResponse({"error": "Name is required."}, status=400)
|
||||
return JsonResponse({"success": f"Hello, {name}!"})
|
||||
|
||||
except Exception as e:
|
||||
return JsonResponse({"error": str(e)}, status=500)
|
||||
|
||||
def form_index(request):
|
||||
return render(request, "templates/form.html")
|
||||
|
Loading…
x
Reference in New Issue
Block a user