@arnaudw38/nodebb-plugin-spam-be-gone 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/public/js/scripts.js +58 -16
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.8] - 2026-02-25
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Fixed Turnstile login retry reset in themes/login flows where previous click/enter hooks did not reliably trigger the widget reset.
|
|
9
|
+
- Made login retry reset logic target the current Turnstile container dynamically instead of relying on a stale target id.
|
|
10
|
+
- Added a fallback reset trigger when login error alerts are rendered on the login page.
|
|
11
|
+
|
|
5
12
|
## [1.0.7] - 2026-02-25
|
|
6
13
|
|
|
7
14
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arnaudw38/nodebb-plugin-spam-be-gone",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Anti-spam plugin for NodeBB 4.x using Akismet, StopForumSpam API, ProjectHoneyPot, and Cloudflare Turnstile (Turnstile-only fork)",
|
|
5
5
|
"main": "library.js",
|
|
6
6
|
"scripts": {},
|
package/public/js/scripts.js
CHANGED
|
@@ -35,36 +35,48 @@ $(function () {
|
|
|
35
35
|
return input && input.value;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
function getCurrentLoginTurnstileTargetId() {
|
|
39
|
+
var args = getTurnstileArgs();
|
|
40
|
+
return args && args.targetId;
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
function resetTurnstileWidget(targetId) {
|
|
39
44
|
if (typeof turnstile === 'undefined') {
|
|
40
|
-
return;
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
targetId = targetId || getCurrentLoginTurnstileTargetId();
|
|
48
|
+
if (!targetId) {
|
|
49
|
+
return false;
|
|
41
50
|
}
|
|
42
51
|
var widgetId = turnstileState.widgets[targetId];
|
|
43
52
|
if (widgetId === undefined || widgetId === null) {
|
|
44
|
-
return;
|
|
53
|
+
return false;
|
|
45
54
|
}
|
|
46
55
|
try {
|
|
47
56
|
turnstile.reset(widgetId);
|
|
57
|
+
return true;
|
|
48
58
|
} catch (err) {
|
|
49
59
|
// Ignore reset errors when the widget is already destroyed by navigation.
|
|
60
|
+
return false;
|
|
50
61
|
}
|
|
51
62
|
}
|
|
52
63
|
|
|
53
|
-
function scheduleLoginTurnstileReset(
|
|
54
|
-
// Turnstile tokens are single-use. On failed login, NodeBB keeps the user on the
|
|
55
|
-
// same page
|
|
64
|
+
function scheduleLoginTurnstileReset() {
|
|
65
|
+
// Turnstile tokens are single-use. On failed login, NodeBB often keeps the user on the
|
|
66
|
+
// same page and may handle submission via AJAX/click handlers. We try multiple delayed resets.
|
|
56
67
|
turnstileState.loginResetTimers.forEach(function (timerId) {
|
|
57
68
|
window.clearTimeout(timerId);
|
|
58
69
|
});
|
|
59
70
|
turnstileState.loginResetTimers = [];
|
|
60
71
|
|
|
61
|
-
[
|
|
72
|
+
[700, 1400, 2600, 4200].forEach(function (delay) {
|
|
62
73
|
var timerId = window.setTimeout(function () {
|
|
63
74
|
var onLoginPage = !ajaxify.data || !ajaxify.data.tpl_url || ajaxify.data.tpl_url === 'login';
|
|
64
75
|
if (!onLoginPage) {
|
|
65
76
|
return;
|
|
66
77
|
}
|
|
67
|
-
|
|
78
|
+
var targetId = getCurrentLoginTurnstileTargetId();
|
|
79
|
+
if (!targetId || !document.getElementById(targetId)) {
|
|
68
80
|
return;
|
|
69
81
|
}
|
|
70
82
|
resetTurnstileWidget(targetId);
|
|
@@ -73,20 +85,24 @@ $(function () {
|
|
|
73
85
|
});
|
|
74
86
|
}
|
|
75
87
|
|
|
76
|
-
function bindLoginRetryReset(
|
|
88
|
+
function bindLoginRetryReset() {
|
|
77
89
|
if (turnstileState.loginBindingsAttached) {
|
|
78
90
|
return;
|
|
79
91
|
}
|
|
80
92
|
turnstileState.loginBindingsAttached = true;
|
|
81
93
|
|
|
82
94
|
function loginSubmitTrigger() {
|
|
83
|
-
scheduleLoginTurnstileReset(
|
|
95
|
+
scheduleLoginTurnstileReset();
|
|
84
96
|
}
|
|
85
97
|
|
|
86
98
|
// Native capture listeners only (avoid duplicate handlers/race conditions with jQuery delegates).
|
|
87
99
|
document.addEventListener('click', function (ev) {
|
|
88
|
-
var btn = ev.target && ev.target.closest ? ev.target.closest('[component="login/submit"], #login [type="submit"], form[action*="/login"] [type="submit"]') : null;
|
|
89
|
-
if (btn) {
|
|
100
|
+
var btn = ev.target && ev.target.closest ? ev.target.closest('[component="login/submit"], #login [type="submit"], form[action*="/login"] [type="submit"], form[data-action="login"] [type="submit"], button[type="submit"]') : null;
|
|
101
|
+
if (!btn) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
var inLogin = btn.closest && btn.closest('#login, form[action*="/login"], form[data-action="login"], [component="login"]');
|
|
105
|
+
if (inLogin || (ajaxify.data && ajaxify.data.tpl_url === 'login')) {
|
|
90
106
|
loginSubmitTrigger();
|
|
91
107
|
}
|
|
92
108
|
}, true);
|
|
@@ -100,14 +116,40 @@ $(function () {
|
|
|
100
116
|
if (!el || !el.closest) {
|
|
101
117
|
return;
|
|
102
118
|
}
|
|
103
|
-
var inLogin = el.closest('#login, form[action*="/login"], form[data-action="login"]');
|
|
104
|
-
if (inLogin) {
|
|
119
|
+
var inLogin = el.closest('#login, form[action*="/login"], form[data-action="login"], [component="login"]');
|
|
120
|
+
if (inLogin || (ajaxify.data && ajaxify.data.tpl_url === 'login')) {
|
|
105
121
|
loginSubmitTrigger();
|
|
106
122
|
}
|
|
107
123
|
}, true);
|
|
124
|
+
|
|
125
|
+
// Extra safety: reset again when a login error alert appears (covers AJAX flows that do not
|
|
126
|
+
// reliably trigger the expected submit/click path in some themes).
|
|
127
|
+
if (window.MutationObserver) {
|
|
128
|
+
var observer = new MutationObserver(function (mutations) {
|
|
129
|
+
var onLoginPage = !ajaxify.data || !ajaxify.data.tpl_url || ajaxify.data.tpl_url === 'login';
|
|
130
|
+
if (!onLoginPage) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
for (var i = 0; i < mutations.length; i += 1) {
|
|
134
|
+
for (var j = 0; j < mutations[i].addedNodes.length; j += 1) {
|
|
135
|
+
var n = mutations[i].addedNodes[j];
|
|
136
|
+
if (!n || n.nodeType !== 1) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
var hasError = (n.matches && n.matches('.alert-danger, .alert-error, .text-danger, [component="alerts/error"]')) ||
|
|
140
|
+
(n.querySelector && n.querySelector('.alert-danger, .alert-error, .text-danger, [component="alerts/error"]'));
|
|
141
|
+
if (hasError) {
|
|
142
|
+
scheduleLoginTurnstileReset();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
149
|
+
}
|
|
108
150
|
}
|
|
109
151
|
|
|
110
|
-
|
|
152
|
+
function renderTurnstileIfNeeded(isLoginPage) {
|
|
111
153
|
var args = getTurnstileArgs();
|
|
112
154
|
if (!args || (isLoginPage && !args.addLoginTurnstile)) {
|
|
113
155
|
return;
|
|
@@ -125,7 +167,7 @@ $(function () {
|
|
|
125
167
|
|
|
126
168
|
if (target.dataset.turnstileRendered === '1') {
|
|
127
169
|
if (isLoginPage) {
|
|
128
|
-
bindLoginRetryReset(
|
|
170
|
+
bindLoginRetryReset();
|
|
129
171
|
}
|
|
130
172
|
return;
|
|
131
173
|
}
|
|
@@ -155,7 +197,7 @@ $(function () {
|
|
|
155
197
|
target.dataset.turnstileRendered = '1';
|
|
156
198
|
turnstileState.widgets[args.targetId] = widgetId;
|
|
157
199
|
if (isLoginPage) {
|
|
158
|
-
bindLoginRetryReset(
|
|
200
|
+
bindLoginRetryReset();
|
|
159
201
|
}
|
|
160
202
|
})
|
|
161
203
|
.catch(function () {
|