nodebb-plugin-phone-verification 1.2.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/library.js +758 -0
- package/package.json +30 -0
- package/plugin.json +32 -0
- package/static/lib/admin.js +187 -0
- package/static/lib/main.js +456 -0
- package/templates/admin/plugins/phone-verification.tpl +163 -0
- package/templates/admin/settings/phone-verification.tpl +163 -0
- package/test/helpers.test.js +158 -0
- package/test/phone-storage.test.js +216 -0
- package/test/verification.test.js +173 -0
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nodebb-plugin-phone-verification",
|
|
3
|
+
"version": "1.2.2",
|
|
4
|
+
"description": "אימות מספר טלפון נייד בתהליך ההרשמה לפורום NodeBB",
|
|
5
|
+
"main": "library.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": ""
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"nodebb",
|
|
12
|
+
"plugin",
|
|
13
|
+
"phone",
|
|
14
|
+
"verification",
|
|
15
|
+
"registration"
|
|
16
|
+
],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"nbbpm": {
|
|
20
|
+
"compatibility": "^3.0.0 || ^4.0.0"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"fast-check": "^3.14.0",
|
|
25
|
+
"mocha": "^10.2.0"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "mocha --recursive test/"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/plugin.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "nodebb-plugin-phone-verification",
|
|
3
|
+
"name": "Phone Verification",
|
|
4
|
+
"description": "אימות מספר טלפון נייד בתהליך ההרשמה לפורום ובפרופיל המשתמש",
|
|
5
|
+
"version": "1.2.2",
|
|
6
|
+
"library": "./library.js",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{ "hook": "filter:register.check", "method": "checkRegistration" },
|
|
9
|
+
{ "hook": "action:user.create", "method": "userCreated" },
|
|
10
|
+
{ "hook": "filter:admin.header.build", "method": "addAdminNavigation" },
|
|
11
|
+
{ "hook": "static:app.load", "method": "init" },
|
|
12
|
+
{ "hook": "filter:script.load", "method": "loadScript" },
|
|
13
|
+
{ "hook": "filter:user.whitelistFields", "method": "whitelistFields" },
|
|
14
|
+
{ "hook": "filter:user.account", "method": "addPhoneToAccount" },
|
|
15
|
+
{ "hook": "filter:post.create", "method": "checkPostingPermissions" },
|
|
16
|
+
{ "hook": "filter:topic.create", "method": "checkPostingPermissions" },
|
|
17
|
+
{ "hook": "action:user.delete", "method": "userDelete" },
|
|
18
|
+
{ "hook": "filter:post.upvote", "method": "checkVotingPermissions" },
|
|
19
|
+
{ "hook": "filter:post.downvote", "method": "checkVotingPermissions" },
|
|
20
|
+
{ "hook": "filter:post.unvote", "method": "checkVotingPermissions" },
|
|
21
|
+
{ "hook": "filter:messaging.save", "method": "checkMessagingPermissions"}
|
|
22
|
+
],
|
|
23
|
+
"modules": {
|
|
24
|
+
"../client/phone-verification.js": "static/lib/main.js",
|
|
25
|
+
"../admin/plugins/phone-verification.js": "static/lib/admin.js"
|
|
26
|
+
},
|
|
27
|
+
"scripts": [ "static/lib/main.js" ],
|
|
28
|
+
"acpScripts": [ "static/lib/admin.js" ],
|
|
29
|
+
"templates": "templates",
|
|
30
|
+
"staticDirs": { "static": "static" },
|
|
31
|
+
"nbbpm": { "compatibility": "^2.0.0 || ^3.0.0 || ^4.0.0" }
|
|
32
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* globals $, app, socket, config */
|
|
4
|
+
|
|
5
|
+
define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], function(Settings, bootbox, alerts) {
|
|
6
|
+
var ACP = {};
|
|
7
|
+
|
|
8
|
+
ACP.init = function() {
|
|
9
|
+
// הגדרת משתנים בראש הפונקציה כדי שיהיו זמינים לכולם בתוכה
|
|
10
|
+
var usersTbody = $('#users-tbody');
|
|
11
|
+
var paginationUl = $('#users-pagination');
|
|
12
|
+
var currentPage = 1;
|
|
13
|
+
|
|
14
|
+
// 1. טעינת הגדרות
|
|
15
|
+
Settings.load('phone-verification', $('#voice-settings-form'));
|
|
16
|
+
|
|
17
|
+
$('#save-settings-btn').on('click', function(e) {
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
Settings.save('phone-verification', $('#voice-settings-form'), function() {
|
|
20
|
+
alerts.success('ההגדרות נשמרו בהצלחה!');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// --- פונקציות עזר (הועברו פנימה כדי להכיר את המשתנים) ---
|
|
25
|
+
|
|
26
|
+
function renderPagination(curr, total) {
|
|
27
|
+
currentPage = curr;
|
|
28
|
+
paginationUl.empty(); // כעת הפונקציה מכירה את paginationUl
|
|
29
|
+
if(total <= 1) return;
|
|
30
|
+
for(var i=1; i<=total; i++) {
|
|
31
|
+
var active = i === curr ? 'active' : '';
|
|
32
|
+
paginationUl.append('<li class="' + active + '"><a href="#" class="page-link" data-page="' + i + '">' + i + '</a></li>');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function buildUserRow(user) {
|
|
37
|
+
var displayName = user.username || ('משתמש ' + user.uid);
|
|
38
|
+
// מניעת XSS בסיסית לשם המשתמש
|
|
39
|
+
var safeName = $('<div>').text(displayName).html();
|
|
40
|
+
var userLink = '/admin/manage/users?searchBy=uid&query=' + user.uid + '&page=1&sortBy=lastonline';
|
|
41
|
+
|
|
42
|
+
var statusBadge = user.phoneVerified ? '<span class="label label-success">מאומת</span>' : '<span class="label label-warning">ממתין</span>';
|
|
43
|
+
|
|
44
|
+
// מניעת XSS לטלפון
|
|
45
|
+
var safePhone = user.phone ? $('<div>').text(user.phone).html() : '<span class="text-muted">-- ללא --</span>';
|
|
46
|
+
var dateStr = user.phoneVerifiedAt ? new Date(user.phoneVerifiedAt).toLocaleDateString('he-IL') : '-';
|
|
47
|
+
|
|
48
|
+
var btnVerify = '<button class="btn btn-xs btn-success verify-user-btn" data-uid="' + user.uid + '" data-name="' + safeName + '" title="אמת"><i class="fa fa-check"></i></button>';
|
|
49
|
+
var btnUnverify = '<button class="btn btn-xs btn-warning unverify-user-btn" data-uid="' + user.uid + '" data-name="' + safeName + '" title="בטל"><i class="fa fa-ban"></i></button>';
|
|
50
|
+
var btnDelete = '<button class="btn btn-xs btn-danger delete-phone-btn" data-uid="' + user.uid + '" data-name="' + safeName + '" title="מחק"><i class="fa fa-trash"></i></button>';
|
|
51
|
+
|
|
52
|
+
var actionBtn = user.phoneVerified ? btnUnverify : btnVerify;
|
|
53
|
+
|
|
54
|
+
return '<tr>' +
|
|
55
|
+
'<td>' + user.uid + '</td>' +
|
|
56
|
+
'<td><a href="' + userLink + '" target="_blank"><strong>' + safeName + '</strong></a></td>' +
|
|
57
|
+
'<td dir="ltr">' + safePhone + '</td>' +
|
|
58
|
+
'<td>' + dateStr + '</td>' +
|
|
59
|
+
'<td>' + statusBadge + '</td>' +
|
|
60
|
+
'<td class="text-right"><div class="btn-group">' + actionBtn + btnDelete + '</div></td>' +
|
|
61
|
+
'</tr>';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function loadUsers(page) {
|
|
65
|
+
page = page || 1;
|
|
66
|
+
usersTbody.html('<tr><td colspan="6" class="text-center"><i class="fa fa-spinner fa-spin"></i> טוען נתונים...</td></tr>');
|
|
67
|
+
|
|
68
|
+
$.get('/api/admin/plugins/phone-verification/users', { page: page }, function(data) {
|
|
69
|
+
if (!data || !data.success) {
|
|
70
|
+
usersTbody.html('<tr><td colspan="6" class="text-center text-danger">שגיאה בטעינת נתונים</td></tr>');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (data.users.length === 0) {
|
|
74
|
+
usersTbody.html('<tr><td colspan="6" class="text-center">אין משתמשים להצגה</td></tr>');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
usersTbody.empty();
|
|
78
|
+
data.users.forEach(function(user) { usersTbody.append(buildUserRow(user)); });
|
|
79
|
+
$('#total-users').text(data.total);
|
|
80
|
+
renderPagination(data.page, data.totalPages);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// --- הפעלת ברירת מחדל ---
|
|
85
|
+
loadUsers(1);
|
|
86
|
+
|
|
87
|
+
// --- אירועים (Event Listeners) ---
|
|
88
|
+
|
|
89
|
+
paginationUl.on('click', 'a.page-link', function(e) {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
loadUsers($(this).data('page'));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// הוספה ידנית
|
|
95
|
+
$('#btn-add-manual-user').on('click', function() {
|
|
96
|
+
bootbox.prompt("הזן את <b>שם המשתמש</b> שברצונך להוסיף לרשימת המאומתים:", function(username) {
|
|
97
|
+
if (!username) return;
|
|
98
|
+
|
|
99
|
+
socket.emit('plugins.call2all.getUidByUsername', { username: username }, function(err, uid) {
|
|
100
|
+
if (err) return alerts.error(err.message || 'משתמש לא נמצא');
|
|
101
|
+
|
|
102
|
+
bootbox.prompt({
|
|
103
|
+
title: "הזן מספר טלפון עבור " + username + " (אופציונלי)",
|
|
104
|
+
inputType: 'text',
|
|
105
|
+
callback: function(phone) {
|
|
106
|
+
// תיקון קריטי: אם לחצו ביטול, עצור
|
|
107
|
+
if (phone === null) return;
|
|
108
|
+
|
|
109
|
+
var confirmMsg = "<h4>סיכום פעולה</h4>" +
|
|
110
|
+
"<b>שם משתמש:</b> " + username + "<br/>" +
|
|
111
|
+
"<b>מספר טלפון:</b> " + (phone ? phone : "ללא מספר (יוגדר כמאומת)") + "<br/><br/>" +
|
|
112
|
+
"האם אתה בטוח שברצונך להמשיך?";
|
|
113
|
+
|
|
114
|
+
bootbox.confirm(confirmMsg, function(result) {
|
|
115
|
+
if (result) {
|
|
116
|
+
socket.emit('plugins.call2all.adminAddVerifiedUser', { uid: uid, phone: phone }, function(err) {
|
|
117
|
+
if (err) return alerts.error(err.message);
|
|
118
|
+
alerts.success('המשתמש ' + username + ' הוגדר כמאומת בהצלחה!');
|
|
119
|
+
loadUsers(1);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
$('#test-call-btn').on('click', function() {
|
|
130
|
+
var phone = $('#test-phone').val();
|
|
131
|
+
if(!phone) return alerts.error('נא להזין מספר לבדיקה');
|
|
132
|
+
|
|
133
|
+
$.post('/api/admin/plugins/phone-verification/test-call', { phoneNumber: phone, _csrf: config.csrf_token }, function(res) {
|
|
134
|
+
if(res.success) alerts.success(res.message);
|
|
135
|
+
else alerts.error(res.message);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
$('#users-table').on('click', '.verify-user-btn', function() {
|
|
140
|
+
var uid = $(this).data('uid');
|
|
141
|
+
var name = $(this).data('name');
|
|
142
|
+
bootbox.confirm("האם לאמת ידנית את " + name + "?", function(res) {
|
|
143
|
+
if(res) socket.emit('plugins.call2all.adminVerifyUser', { uid: uid }, function(err) {
|
|
144
|
+
if(err) return alerts.error(err.message);
|
|
145
|
+
alerts.success('המשתמש ' + name + ' אומת!');
|
|
146
|
+
loadUsers(currentPage);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
$('#users-table').on('click', '.unverify-user-btn', function() {
|
|
152
|
+
var uid = $(this).data('uid');
|
|
153
|
+
var name = $(this).data('name');
|
|
154
|
+
bootbox.confirm("האם לבטל את האימות ל-" + name + "?", function(res) {
|
|
155
|
+
if(res) socket.emit('plugins.call2all.adminUnverifyUser', { uid: uid }, function(err) {
|
|
156
|
+
if(err) return alerts.error(err.message);
|
|
157
|
+
alerts.success('האימות בוטל!');
|
|
158
|
+
loadUsers(currentPage);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
$('#users-table').on('click', '.delete-phone-btn', function() {
|
|
164
|
+
var uid = $(this).data('uid');
|
|
165
|
+
var name = $(this).data('name');
|
|
166
|
+
bootbox.confirm("האם למחוק את הטלפון של " + name + "?", function(res) {
|
|
167
|
+
if(res) socket.emit('plugins.call2all.adminDeleteUserPhone', { uid: uid }, function(err) {
|
|
168
|
+
if(err) return alerts.error(err.message);
|
|
169
|
+
alerts.success('נמחק!');
|
|
170
|
+
loadUsers(currentPage);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
$('#search-btn').on('click', function() {
|
|
176
|
+
var phone = $('#phone-search').val();
|
|
177
|
+
if (!phone) { loadUsers(1); return; }
|
|
178
|
+
$.get('/api/admin/plugins/phone-verification/search', { phone: phone }, function(data) {
|
|
179
|
+
usersTbody.empty();
|
|
180
|
+
if (data.success && data.found) usersTbody.append(buildUserRow(data.user));
|
|
181
|
+
else usersTbody.html('<tr><td colspan="6" class="text-center">לא נמצא משתמש</td></tr>');
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
return ACP;
|
|
187
|
+
});
|