@arnaudw38/nodebb-plugin-spam-be-gone 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -26,7 +26,7 @@ Anti-spam plugin for **NodeBB 4.x** with **Cloudflare Turnstile** (Turnstile-onl
26
26
  From your NodeBB root directory:
27
27
 
28
28
  ```bash
29
- npm install nodebb-plugin-spam-be-gone
29
+ npm install @arnaudw38/nodebb-plugin-spam-be-gone
30
30
  ./nodebb build
31
31
  ./nodebb restart
32
32
  ```
package/library.js CHANGED
@@ -199,7 +199,7 @@ Plugin.addCaptcha = async (data) => {
199
199
  if (turnstile) {
200
200
  data.templateData.turnstileArgs = turnstile;
201
201
  addChallenge(data.templateData, turnstile.addLoginTurnstile, {
202
- label: 'Turnstile',
202
+ label: 'Vérification de sécurité',
203
203
  html: `<div id="${turnstile.targetId}"></div>`,
204
204
  styleName: pluginData.nbbId,
205
205
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arnaudw38/nodebb-plugin-spam-be-gone",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Anti-spam plugin for NodeBB 4.x using Akismet, StopForumSpam, ProjectHoneyPot, and Cloudflare Turnstile (Turnstile-only fork)",
5
5
  "main": "library.js",
6
6
  "scripts": {},
@@ -5,6 +5,10 @@
5
5
  $(function () {
6
6
  var pluginName = 'spam-be-gone';
7
7
  var turnstileScriptUrl = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit';
8
+ var turnstileState = {
9
+ widgets: {},
10
+ loginBindingsAttached: false,
11
+ };
8
12
 
9
13
  function getTurnstileArgs() {
10
14
  return ajaxify.data && ajaxify.data.turnstileArgs;
@@ -25,6 +29,67 @@ $(function () {
25
29
  });
26
30
  }
27
31
 
32
+ function getCurrentTurnstileResponse() {
33
+ var input = document.querySelector('input[name="cf-turnstile-response"]');
34
+ return input && input.value;
35
+ }
36
+
37
+ function resetTurnstileWidget(targetId) {
38
+ if (typeof turnstile === 'undefined') {
39
+ return;
40
+ }
41
+ var widgetId = turnstileState.widgets[targetId];
42
+ if (widgetId === undefined || widgetId === null) {
43
+ return;
44
+ }
45
+ try {
46
+ turnstile.reset(widgetId);
47
+ } catch (err) {
48
+ // Ignore reset errors when the widget is already destroyed by navigation.
49
+ }
50
+ }
51
+
52
+ function scheduleLoginTurnstileReset(targetId) {
53
+ // Turnstile tokens are single-use. On failed login, NodeBB keeps the user on the
54
+ // same page, so we refresh the widget shortly after submit to allow a retry.
55
+ [1200, 3000].forEach(function (delay) {
56
+ window.setTimeout(function () {
57
+ if (ajaxify.data && ajaxify.data.template && ajaxify.data.template.name && ajaxify.data.template.name !== 'login') {
58
+ return;
59
+ }
60
+ if (!document.getElementById(targetId)) {
61
+ return;
62
+ }
63
+ if (getCurrentTurnstileResponse()) {
64
+ resetTurnstileWidget(targetId);
65
+ }
66
+ }, delay);
67
+ });
68
+ }
69
+
70
+ function bindLoginRetryReset(targetId) {
71
+ if (turnstileState.loginBindingsAttached) {
72
+ return;
73
+ }
74
+ turnstileState.loginBindingsAttached = true;
75
+
76
+ $(document)
77
+ .off('submit.spamBeGoneTurnstileLogin')
78
+ .on('submit.spamBeGoneTurnstileLogin', 'form[action="/login"], form[data-action="login"], #login', function () {
79
+ scheduleLoginTurnstileReset(targetId);
80
+ })
81
+ .off('click.spamBeGoneTurnstileLogin')
82
+ .on('click.spamBeGoneTurnstileLogin', '[component="login/submit"]', function () {
83
+ scheduleLoginTurnstileReset(targetId);
84
+ })
85
+ .off('input.spamBeGoneTurnstileLogin change.spamBeGoneTurnstileLogin')
86
+ .on('input.spamBeGoneTurnstileLogin change.spamBeGoneTurnstileLogin', 'input[name="username"], input[name="password"]', function () {
87
+ if (document.getElementById(targetId) && getCurrentTurnstileResponse()) {
88
+ resetTurnstileWidget(targetId);
89
+ }
90
+ });
91
+ }
92
+
28
93
  function renderTurnstileIfNeeded(isLoginPage) {
29
94
  var args = getTurnstileArgs();
30
95
  if (!args || (isLoginPage && !args.addLoginTurnstile)) {
@@ -37,10 +102,18 @@ $(function () {
37
102
  return;
38
103
  }
39
104
  var target = document.getElementById(args.targetId);
40
- if (!target || target.dataset.turnstileRendered === '1') {
105
+ if (!target) {
106
+ return;
107
+ }
108
+
109
+ if (target.dataset.turnstileRendered === '1') {
110
+ if (isLoginPage) {
111
+ bindLoginRetryReset(args.targetId);
112
+ }
41
113
  return;
42
114
  }
43
- turnstile.render('#' + args.targetId, {
115
+
116
+ var widgetId = turnstile.render('#' + args.targetId, {
44
117
  sitekey: args.siteKey,
45
118
  theme: args.theme || 'auto',
46
119
  size: args.size || 'normal',
@@ -55,8 +128,18 @@ $(function () {
55
128
  'error-callback': function () {
56
129
  require(['alerts'], function (alerts) { alerts.error('[[spam-be-gone:captcha-not-verified]]'); });
57
130
  },
131
+ 'expired-callback': function () {
132
+ resetTurnstileWidget(args.targetId);
133
+ },
134
+ 'timeout-callback': function () {
135
+ resetTurnstileWidget(args.targetId);
136
+ },
58
137
  });
59
138
  target.dataset.turnstileRendered = '1';
139
+ turnstileState.widgets[args.targetId] = widgetId;
140
+ if (isLoginPage) {
141
+ bindLoginRetryReset(args.targetId);
142
+ }
60
143
  })
61
144
  .catch(function () {
62
145
  require(['alerts'], function (alerts) { alerts.error('Failed to load Cloudflare Turnstile'); });