@arnaudw38/nodebb-plugin-spam-be-gone 1.0.1 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arnaudw38/nodebb-plugin-spam-be-gone",
3
- "version": "1.0.1",
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": {},
package/plugin.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "id": "nodebb-plugin-spam-be-gone",
3
3
  "name": "Spam Be Gone",
4
4
  "description": "Anti-spam using Akismet.com, StopForumSpam.com, ProjectHoneyPot.org and Cloudflare Turnstile",
5
- "url": "https://github.com/aworobel/nodebb-plugin-spam-be-gone",
5
+ "url": "https://github.com/akhoury/nodebb-plugin-spam-be-gone",
6
6
  "scss": [
7
7
  "public/scss/styles.scss"
8
8
  ],
@@ -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'); });