nodebb-plugin-phone-verification 1.2.4 → 2.0.0

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 CHANGED
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const crypto = require('crypto');
4
- const https = require('https'); // הוספנו עבור SSL
4
+ const https = require('https');
5
+ const nconf = require.main.require('nconf');
5
6
 
6
7
  // NodeBB modules
7
8
  let db;
@@ -24,12 +25,12 @@ const IP_BLOCK_HOURS = 24;
24
25
 
25
26
  // ==================== הגדרות ברירת מחדל ====================
26
27
  const defaultSettings = {
27
- voiceServerUrl: 'https://www.call2all.co.il/ym/api/RunCampaign',
28
+ // עודכן לכתובת הצינתוקים
29
+ voiceServerUrl: 'https://www.call2all.co.il/ym/api/RunTzintuk',
28
30
  voiceServerApiKey: '',
29
31
  voiceServerEnabled: false,
30
32
  blockUnverifiedUsers: false,
31
- voiceTtsMode: '1',
32
- voiceMessageTemplate: 'הקוד שלך לאתר {siteTitle} הוא {code} אני חוזר. הקוד הוא {code}'
33
+ // הוסרו הגדרות TTS שאינן רלוונטיות לצינתוק
33
34
  };
34
35
 
35
36
  // ==================== פונקציות עזר ====================
@@ -46,20 +47,10 @@ plugin.normalizePhone = function (phone) {
46
47
  return phone.replace(/[-\s]/g, '');
47
48
  };
48
49
 
49
- plugin.generateVerificationCode = function () {
50
- const randomBytes = crypto.randomBytes(3);
51
- const number = randomBytes.readUIntBE(0, 3) % 1000000;
52
- return number.toString().padStart(6, '0');
53
- };
54
-
55
50
  plugin.hashCode = function (code) {
56
51
  return crypto.createHash('sha256').update(code).digest('hex');
57
52
  };
58
53
 
59
- plugin.formatCodeForSpeech = function (code) {
60
- return code.split('').join(' ');
61
- };
62
-
63
54
  // ==================== בדיקת הרשאות ====================
64
55
 
65
56
  plugin.checkPostingPermissions = async function (data) {
@@ -75,7 +66,8 @@ plugin.checkPostingPermissions = async function (data) {
75
66
  const phoneData = await plugin.getUserPhone(uid);
76
67
  if (!phoneData || !phoneData.phoneVerified) {
77
68
  const userSlug = await User.getUserField(uid, 'userslug');
78
- const editUrl = userSlug ? `/user/${userSlug}/edit` : '/user/me/edit';
69
+ const relativePath = nconf.get('relative_path');
70
+ const editUrl = userSlug ? `${relativePath}/user/${userSlug}/edit` : `${relativePath}/user/me/edit`;
79
71
  throw new Error(`חובה לאמת מספר טלפון כדי להמשיך את הפעילות בפורום.<br/>אנא גש ל<a href="${editUrl}">הגדרות הפרופיל שלך</a>.`);
80
72
  }
81
73
  return data;
@@ -94,63 +86,48 @@ plugin.checkVotingPermissions = async function (data) {
94
86
  const phoneData = await plugin.getUserPhone(uid);
95
87
  if (!phoneData || !phoneData.phoneVerified) {
96
88
  const userSlug = await User.getUserField(uid, 'userslug');
97
- const editUrl = userSlug ? `/user/${userSlug}/edit` : '/user/me/edit';
89
+ const relativePath = nconf.get('relative_path');
90
+ const editUrl = userSlug ? `${relativePath}/user/${userSlug}/edit` : `${relativePath}/user/me/edit`;
98
91
  throw new Error(`חובה לאמת מספר טלפון כדי להמשיך את הפעילות בפורום.<br/>אנא גש ל<a href="${editUrl}">הגדרות הפרופיל שלך</a>.`);
99
92
  }
100
93
  return data;
101
94
  };
102
95
 
103
96
  plugin.checkMessagingPermissions = async function (data) {
104
- // --- תיקון: בדיקת כל הווריאציות האפשריות (כולל fromuid באותיות קטנות) ---
105
97
  const uid = data.fromuid || data.fromUid || data.uid;
106
98
 
107
- // לוג לבדיקה שזה עובד עכשיו
108
- // console.log(`[Phone-Verify] Checking message from UID: ${uid}`);
109
-
110
- // אם עדיין לא מצאנו מזהה משתמש, מחזירים את המידע כמו שהוא
111
99
  if (!uid || parseInt(uid, 10) === 0) return data;
112
100
 
113
101
  const settings = await plugin.getSettings();
114
- // אם ההגדרות כבויות - משחררים
115
102
  if (!settings.blockUnverifiedUsers) return data;
116
103
 
117
- // מנהלים מורשים תמיד
118
104
  const isAdmin = await User.isAdministrator(uid);
119
105
  if (isAdmin) return data;
120
106
 
121
- // משתמשים מאומתים מורשים תמיד
122
107
  const phoneData = await plugin.getUserPhone(uid);
123
108
  if (phoneData && phoneData.phoneVerified) {
124
109
  return data;
125
110
  }
126
111
 
127
- // --- המשתמש לא מאומת -> בדיקת שאר המשתתפים ---
128
-
129
112
  const Messaging = require.main.require('./src/messaging');
130
113
 
131
- // וידוא שיש לנו roomId
132
114
  if (!data.roomId) return data;
133
115
 
134
- // שליפת המשתתפים בחדר
135
116
  const roomUids = await Messaging.getUidsInRoom(data.roomId, 0, -1);
136
117
 
137
- // סינון השולח עצמו
138
118
  const targetUids = roomUids.filter(id => parseInt(id, 10) !== parseInt(uid, 10));
139
119
 
140
- // הכנת הקישור לפרופיל
141
120
  const userSlug = await User.getUserField(uid, 'userslug');
142
- const editUrl = userSlug ? `/user/${userSlug}/edit` : '/user/me/edit';
121
+ const relativePath = nconf.get('relative_path');
122
+ const editUrl = userSlug ? `${relativePath}/user/${userSlug}/edit` : `${relativePath}/user/me/edit`;
143
123
  const errorMsg = `חובה לאמת מספר טלפון כדי לשלוח הודעות.<br/>אנא גש ל<a href="${editUrl}">הגדרות הפרופיל שלך</a>.`;
144
124
 
145
- // אם אין אף אחד אחר בשיחה (מדבר לעצמו) - חוסמים
146
125
  if (targetUids.length === 0) {
147
126
  throw new Error(errorMsg);
148
127
  }
149
128
 
150
- // בדיקה שכל המשתתפים האחרים הם מנהלים
151
129
  for (const targetUid of targetUids) {
152
130
  const isTargetAdmin = await User.isAdministrator(targetUid);
153
- // אם נמצא אפילו משתתף אחד שאינו מנהל -> חסימה
154
131
  if (!isTargetAdmin) {
155
132
  throw new Error(errorMsg);
156
133
  }
@@ -159,44 +136,48 @@ plugin.checkMessagingPermissions = async function (data) {
159
136
  return data;
160
137
  };
161
138
 
162
- // ==================== שליחת שיחה קולית ====================
139
+ // ==================== שליחת צינתוק (Tzintuk) ====================
163
140
 
164
- plugin.sendVoiceCall = async function (phone, code) {
141
+ plugin.sendTzintuk = async function (phone) {
165
142
  const settings = await plugin.getSettings();
166
- if (!meta) meta = require.main.require('./src/meta');
167
- const siteTitle = meta.config.title || 'האתר';
168
143
 
169
144
  if (!settings.voiceServerEnabled || !settings.voiceServerApiKey) {
170
145
  return { success: false, error: 'VOICE_SERVER_DISABLED', message: 'שרת השיחות לא מוגדר' };
171
146
  }
172
147
 
173
148
  try {
174
- const spokenCode = plugin.formatCodeForSpeech(code);
175
- let messageText = settings.voiceMessageTemplate || defaultSettings.voiceMessageTemplate;
176
- messageText = messageText.replace(/{code}/g, spokenCode).replace(/{siteTitle}/g, siteTitle);
177
-
178
- const phonesData = {};
179
- phonesData[phone] = { name: 'משתמש', moreinfo: messageText, blocked: false };
180
-
181
149
  const baseUrl = settings.voiceServerUrl || defaultSettings.voiceServerUrl;
150
+
151
+ // פרמטרים לצינתוק עם זיהוי רנדומלי
182
152
  const params = new URLSearchParams({
183
- ttsMode: settings.voiceTtsMode || defaultSettings.voiceTtsMode,
184
- phones: JSON.stringify(phonesData),
185
- token: settings.voiceServerApiKey
153
+ token: settings.voiceServerApiKey,
154
+ phones: phone,
155
+ callerId: 'RAND', // חובה עבור קבלת קוד אימות אוטומטי
156
+ // tzintukTimeOut: '9' // אופציונלי
186
157
  });
187
158
 
188
159
  const url = `${baseUrl}?${params.toString()}`;
189
160
 
190
- // עקיפת בעיות SSL (אופציונלי)
191
161
  const agent = new https.Agent({ rejectUnauthorized: false });
192
162
 
193
163
  const response = await fetch(url, { method: 'GET', agent: agent });
194
164
 
195
165
  if (!response.ok) return { success: false, error: 'VOICE_SERVER_ERROR', message: 'שגיאה בשרת השיחות' };
166
+
196
167
  const result = await response.json();
197
168
 
198
- if (result.responseStatus === 'OK' || result.responseStatus === 'WAITING') {
199
- return { success: true, result };
169
+ // בדיקת תקינות התגובה וקיום verifyCode
170
+ if (result.responseStatus === 'OK' && result.verifyCode) {
171
+ return {
172
+ success: true,
173
+ code: result.verifyCode, // מחזירים את הקוד שהתקבל מהשרת
174
+ message: 'השיחה נשלחה בהצלחה'
175
+ };
176
+ } else if (result.errors && Object.keys(result.errors).length > 0) {
177
+ // טיפול בשגיאות ספציפיות
178
+ const errorKey = Object.keys(result.errors)[0];
179
+ const errorMsg = result.errors[errorKey];
180
+ return { success: false, error: 'API_ERROR', message: `שגיאה: ${errorMsg}` };
200
181
  } else {
201
182
  return { success: false, error: 'VOICE_SERVER_ERROR', message: result.message || 'שגיאה בשליחת השיחה' };
202
183
  }
@@ -235,11 +216,11 @@ plugin.verifyCode = async function (phone, code) {
235
216
  if (!db) return { success: false, error: 'DB_ERROR' };
236
217
  const data = await db.getObject(key);
237
218
 
238
- if (!data) return { success: false, error: 'CODE_NOT_FOUND', message: 'לא נמצא קוד אימות' };
219
+ if (!data) return { success: false, error: 'CODE_NOT_FOUND', message: 'לא נמצאה בקשת אימות' };
239
220
  if (data.blockedUntil && parseInt(data.blockedUntil, 10) > now) {
240
221
  return { success: false, error: 'PHONE_BLOCKED', message: 'המספר חסום זמנית' };
241
222
  }
242
- if (parseInt(data.expiresAt, 10) < now) return { success: false, error: 'CODE_EXPIRED', message: 'הקוד פג תוקף' };
223
+ if (parseInt(data.expiresAt, 10) < now) return { success: false, error: 'CODE_EXPIRED', message: 'הזמן לאימות עבר' };
243
224
 
244
225
  if (plugin.hashCode(code) === data.hashedCode) {
245
226
  await db.delete(key);
@@ -300,7 +281,6 @@ plugin.clearVerifiedPhone = async function (phone) {
300
281
  plugin.savePhoneToUser = async function (uid, phone, verified = true, forceOverride = false) {
301
282
  if (!db || !User) return { success: false };
302
283
 
303
- // 1. מקרה של אימות ללא טלפון
304
284
  if (!phone) {
305
285
  await User.setUserFields(uid, {
306
286
  phoneVerified: verified ? 1 : 0,
@@ -317,25 +297,18 @@ plugin.savePhoneToUser = async function (uid, phone, verified = true, forceOverr
317
297
  const normalizedPhone = plugin.normalizePhone(phone);
318
298
  const existingUid = await db.sortedSetScore('phone:uid', normalizedPhone);
319
299
 
320
- // 2. בדיקת כפילות
321
300
  if (existingUid) {
322
- // אם המספר שייך למשתמש אחר
323
301
  if (parseInt(existingUid, 10) !== parseInt(uid, 10)) {
324
302
  if (forceOverride) {
325
- // === תיקון: דריסה בכוח (למנהלים) ===
326
- // מחיקת המספר מהמשתמש הישן
327
303
  console.log(`[phone-verification] Force overwriting phone ${normalizedPhone} from user ${existingUid} to ${uid}`);
328
304
 
329
- // הסרה מה-Set של המשתמש הישן
330
305
  await User.setUserFields(existingUid, {
331
306
  [PHONE_FIELD_KEY]: '',
332
307
  phoneVerified: 0,
333
308
  phoneVerifiedAt: 0
334
309
  });
335
310
  await db.sortedSetRemove('users:phone', existingUid);
336
- // (הערה: לא צריך להסיר מ-phone:uid כי אנחנו דורסים אותו מייד למטה)
337
311
  } else {
338
- // אם זה לא מנהל - זרוק שגיאה
339
312
  return { success: false, error: 'PHONE_EXISTS', message: 'המספר כבר רשום למשתמש אחר' };
340
313
  }
341
314
  }
@@ -348,7 +321,6 @@ plugin.savePhoneToUser = async function (uid, phone, verified = true, forceOverr
348
321
  phoneVerifiedAt: verified ? now : 0
349
322
  });
350
323
 
351
- // עדכון/דריסה של הרשומה ב-DB
352
324
  await db.sortedSetAdd('phone:uid', uid, normalizedPhone);
353
325
  await db.sortedSetAdd('users:phone', now, uid);
354
326
  return { success: true };
@@ -406,19 +378,16 @@ plugin.checkRegistration = async function (data) {
406
378
  const existingUid = await plugin.findUserByPhone(normalizedPhone);
407
379
 
408
380
  if (existingUid) {
409
- // אם המשתמש מחובר ומנסה לעדכן, או אם המספר תפוס על ידי מישהו אחר
410
381
  if (!req.uid || parseInt(existingUid, 10) !== parseInt(req.uid, 10)) {
411
382
  throw new Error('מספר הטלפון כבר רשום במערכת למשתמש אחר');
412
383
  }
413
384
  }
414
385
 
415
- // אם הגעת לכאן, הנתונים תקינים.
416
- // ב-Hook של checkRegistration, פשוט מחזירים את data כדי להמשיך ברישום.
417
386
  return data;
418
387
 
419
388
  } catch (err) {
420
389
  console.error('[phone-verification] Registration check error:', err);
421
- throw err; // NodeBB יציג את השגיאה הזו למשתמש בטופס הרישום
390
+ throw err;
422
391
  }
423
392
  };
424
393
 
@@ -478,7 +447,6 @@ plugin.init = async function (params) {
478
447
  // --- SOCKET.IO EVENTS ---
479
448
  SocketPlugins.call2all = {};
480
449
 
481
- // 1. פונקציה חדשה: מציאת משתמש לפי שם
482
450
  SocketPlugins.call2all.getUidByUsername = async function (socket, data) {
483
451
  if (!data || !data.username) throw new Error('נא לספק שם משתמש');
484
452
  const uid = await User.getUidByUsername(data.username);
@@ -486,7 +454,6 @@ plugin.init = async function (params) {
486
454
  return uid;
487
455
  };
488
456
 
489
- // 2. הוספת משתמש מאומת (מתוקן)
490
457
  SocketPlugins.call2all.adminAddVerifiedUser = async function (socket, data) {
491
458
  if (!data || !data.uid) throw new Error('חסר מזהה משתמש');
492
459
  const isAdmin = await User.isAdministrator(socket.uid);
@@ -503,7 +470,6 @@ plugin.init = async function (params) {
503
470
  if (!result.success) throw new Error(result.message);
504
471
  };
505
472
 
506
- // 3. אימות ידני
507
473
  SocketPlugins.call2all.adminVerifyUser = async function (socket, data) {
508
474
  if (!data || !data.uid) throw new Error('שגיאה');
509
475
  const isAdmin = await User.isAdministrator(socket.uid);
@@ -513,7 +479,6 @@ plugin.init = async function (params) {
513
479
  await db.sortedSetAdd('users:phone', Date.now(), data.uid);
514
480
  };
515
481
 
516
- // 4. ביטול אימות
517
482
  SocketPlugins.call2all.adminUnverifyUser = async function (socket, data) {
518
483
  if (!data || !data.uid) throw new Error('שגיאה');
519
484
  const isAdmin = await User.isAdministrator(socket.uid);
@@ -522,7 +487,6 @@ plugin.init = async function (params) {
522
487
  await User.setUserFields(data.uid, { phoneVerified: 0, phoneVerifiedAt: 0 });
523
488
  };
524
489
 
525
- // 5. מחיקת טלפון
526
490
  SocketPlugins.call2all.adminDeleteUserPhone = async function (socket, data) {
527
491
  if (!data || !data.uid) throw new Error('שגיאה');
528
492
  const isAdmin = await User.isAdministrator(socket.uid);
@@ -573,22 +537,18 @@ plugin.getSettings = async function () {
573
537
  return {
574
538
  voiceServerUrl: settings.voiceServerUrl || defaultSettings.voiceServerUrl,
575
539
  voiceServerApiKey: settings.voiceServerApiKey || '',
576
- voiceServerEnabled: isTrue(settings.voiceServerEnabled), // בדיקה מורחבת
577
- blockUnverifiedUsers: isTrue(settings.blockUnverifiedUsers), // בדיקה מורחבת
578
- voiceTtsMode: settings.voiceTtsMode || '1',
579
- voiceMessageTemplate: settings.voiceMessageTemplate || defaultSettings.voiceMessageTemplate
540
+ voiceServerEnabled: isTrue(settings.voiceServerEnabled),
541
+ blockUnverifiedUsers: isTrue(settings.blockUnverifiedUsers)
580
542
  };
581
543
  };
582
544
 
583
545
  plugin.saveSettings = async function (settings) {
584
546
  if (!meta) return false;
585
547
  await meta.settings.set('phone-verification', {
586
- voiceServerUrl: settings.voiceServerUrl || '',
548
+ voiceServerUrl: settings.voiceServerUrl || defaultSettings.voiceServerUrl,
587
549
  voiceServerApiKey: settings.voiceServerApiKey || '',
588
550
  voiceServerEnabled: settings.voiceServerEnabled ? 'true' : 'false',
589
- blockUnverifiedUsers: settings.blockUnverifiedUsers ? 'true' : 'false',
590
- voiceTtsMode: settings.voiceTtsMode || '1',
591
- voiceMessageTemplate: settings.voiceMessageTemplate || defaultSettings.voiceMessageTemplate
551
+ blockUnverifiedUsers: settings.blockUnverifiedUsers ? 'true' : 'false'
592
552
  });
593
553
  return true;
594
554
  };
@@ -616,7 +576,9 @@ plugin.apiAdminTestCall = async function (req, res) {
616
576
  try {
617
577
  const { phoneNumber } = req.body;
618
578
  if (!phoneNumber) return res.json({ success: false, message: 'חסר טלפון' });
619
- const result = await plugin.sendVoiceCall(plugin.normalizePhone(phoneNumber), '123456');
579
+
580
+ // בצינתוק, השרת מחזיר את הקוד. בבדיקה אנו רק מוודאים שיצא שיחה
581
+ const result = await plugin.sendTzintuk(plugin.normalizePhone(phoneNumber));
620
582
  res.json(result);
621
583
  } catch (err) { res.json({ success: false }); }
622
584
  };
@@ -635,17 +597,25 @@ plugin.apiSendCode = async function (req, res) {
635
597
  if (!plugin.validatePhoneNumber(clean)) return res.json({ success: false, error: 'INVALID' });
636
598
 
637
599
  const existingUid = await plugin.findUserByPhone(clean);
638
- // בדיקת כפילות: אם המספר שייך למשתמש אחר
639
600
  if (existingUid && (!req.uid || parseInt(existingUid) !== parseInt(req.uid))) {
640
601
  return res.json({ success: false, error: 'EXISTS', message: 'המספר תפוס' });
641
602
  }
642
603
 
643
- const code = plugin.generateVerificationCode();
644
- await plugin.saveVerificationCode(clean, code);
645
- const result = await plugin.sendVoiceCall(clean, code);
604
+ // --- שינוי: שליחת בקשה לשרת כדי לקבל את הקוד (Caller ID) ---
605
+ const result = await plugin.sendTzintuk(clean);
646
606
 
647
- res.json({ success: true, message: result.success ? 'שיחה נשלחה' : 'קוד נוצר', voiceCallSent: result.success });
648
- } catch (err) { res.json({ success: false }); }
607
+ if (result.success && result.code) {
608
+ // שמירת הקוד שהתקבל מהשרת
609
+ await plugin.saveVerificationCode(clean, result.code);
610
+ res.json({ success: true, message: 'צינתוק נשלח, נא להזין את 4 הספרות האחרונות', method: 'tzintuk' });
611
+ } else {
612
+ res.json({ success: false, message: result.message || 'תקלה בשליחת הצינתוק' });
613
+ }
614
+
615
+ } catch (err) {
616
+ console.error(err);
617
+ res.json({ success: false });
618
+ }
649
619
  };
650
620
 
651
621
  plugin.apiVerifyCode = async function (req, res) {
@@ -679,19 +649,28 @@ plugin.apiInitiateCall = async function (req, res) {
679
649
  }
680
650
  }
681
651
 
682
- const code = plugin.generateVerificationCode();
683
- const saveResult = await plugin.saveVerificationCode(normalizedPhone, code);
652
+ // --- שינוי: שליחת צינתוק במקום יצירת קוד מקומי ---
653
+ const result = await plugin.sendTzintuk(normalizedPhone);
654
+
655
+ if (!result.success || !result.code) {
656
+ return res.json(result);
657
+ }
658
+
659
+ const saveResult = await plugin.saveVerificationCode(normalizedPhone, result.code);
684
660
 
685
661
  if (!saveResult.success) {
686
662
  return res.json(saveResult);
687
663
  }
688
664
 
689
- res.json({ success: true, phone: normalizedPhone, code: code, expiresAt: saveResult.expiresAt });
665
+ // שימו לב: לא מחזירים את הקוד לקליינט בייצור (אלא אם רוצים דיבוג)
666
+ // כאן הקליינט צריך לבקש מהמשתמש את 4 הספרות האחרונות
667
+ res.json({ success: true, phone: normalizedPhone, expiresAt: saveResult.expiresAt });
690
668
 
691
669
  } catch (err) {
692
670
  res.json({ success: false, error: 'SERVER_ERROR', message: 'אירעה שגיאה' });
693
671
  }
694
672
  };
673
+
695
674
  plugin.apiGetUserPhoneProfile = async function (req, res) {
696
675
  try {
697
676
  const uid = await User.getUidByUserslug(req.params.userslug);
@@ -790,4 +769,4 @@ plugin.userDelete = async function (data) {
790
769
  } catch (e) {}
791
770
  };
792
771
 
793
- module.exports = plugin;
772
+ module.exports = plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-phone-verification",
3
- "version": "1.2.4",
3
+ "version": "2.0.0",
4
4
  "description": "אימות מספר טלפון נייד בתהליך ההרשמה לפורום NodeBB",
5
5
  "main": "library.js",
6
6
  "repository": {
package/plugin.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "id": "nodebb-plugin-phone-verification",
3
3
  "name": "Phone Verification",
4
4
  "description": "אימות מספר טלפון נייד בתהליך ההרשמה לפורום ובפרופיל המשתמש",
5
- "version": "1.2.3",
5
+ "version": "2.0.0",
6
6
  "library": "./library.js",
7
7
  "hooks": [
8
8
  { "hook": "filter:register.check", "method": "checkRegistration" },
@@ -1,17 +1,14 @@
1
1
  'use strict';
2
2
 
3
- /* globals $, app, socket, config */
4
3
 
5
4
  define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], function(Settings, bootbox, alerts) {
6
5
  var ACP = {};
7
6
 
8
7
  ACP.init = function() {
9
- // הגדרת משתנים בראש הפונקציה כדי שיהיו זמינים לכולם בתוכה
10
8
  var usersTbody = $('#users-tbody');
11
9
  var paginationUl = $('#users-pagination');
12
10
  var currentPage = 1;
13
11
 
14
- // 1. טעינת הגדרות
15
12
  Settings.load('phone-verification', $('#voice-settings-form'));
16
13
 
17
14
  $('#save-settings-btn').on('click', function(e) {
@@ -21,11 +18,10 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
21
18
  });
22
19
  });
23
20
 
24
- // --- פונקציות עזר (הועברו פנימה כדי להכיר את המשתנים) ---
25
21
 
26
22
  function renderPagination(curr, total) {
27
23
  currentPage = curr;
28
- paginationUl.empty(); // כעת הפונקציה מכירה את paginationUl
24
+ paginationUl.empty();
29
25
  if(total <= 1) return;
30
26
  for(var i=1; i<=total; i++) {
31
27
  var active = i === curr ? 'active' : '';
@@ -35,13 +31,11 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
35
31
 
36
32
  function buildUserRow(user) {
37
33
  var displayName = user.username || ('משתמש ' + user.uid);
38
- // מניעת XSS בסיסית לשם המשתמש
39
34
  var safeName = $('<div>').text(displayName).html();
40
- var userLink = '/admin/manage/users?searchBy=uid&query=' + user.uid + '&page=1&sortBy=lastonline';
35
+ var userLink = config.relative_path + '/admin/manage/users?searchBy=uid&query=' + user.uid + '&page=1&sortBy=lastonline';
41
36
 
42
37
  var statusBadge = user.phoneVerified ? '<span class="label label-success">מאומת</span>' : '<span class="label label-warning">ממתין</span>';
43
38
 
44
- // מניעת XSS לטלפון
45
39
  var safePhone = user.phone ? $('<div>').text(user.phone).html() : '<span class="text-muted">-- ללא --</span>';
46
40
  var dateStr = user.phoneVerifiedAt ? new Date(user.phoneVerifiedAt).toLocaleDateString('he-IL') : '-';
47
41
 
@@ -65,7 +59,7 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
65
59
  page = page || 1;
66
60
  usersTbody.html('<tr><td colspan="6" class="text-center"><i class="fa fa-spinner fa-spin"></i> טוען נתונים...</td></tr>');
67
61
 
68
- $.get('/api/admin/plugins/phone-verification/users', { page: page }, function(data) {
62
+ $.get(config.relative_path + '/api/admin/plugins/phone-verification/users', { page: page }, function(data) {
69
63
  if (!data || !data.success) {
70
64
  usersTbody.html('<tr><td colspan="6" class="text-center text-danger">שגיאה בטעינת נתונים</td></tr>');
71
65
  return;
@@ -81,17 +75,14 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
81
75
  });
82
76
  }
83
77
 
84
- // --- הפעלת ברירת מחדל ---
85
78
  loadUsers(1);
86
79
 
87
- // --- אירועים (Event Listeners) ---
88
80
 
89
81
  paginationUl.on('click', 'a.page-link', function(e) {
90
82
  e.preventDefault();
91
83
  loadUsers($(this).data('page'));
92
84
  });
93
85
 
94
- // הוספה ידנית
95
86
  $('#btn-add-manual-user').on('click', function() {
96
87
  bootbox.prompt("הזן את <b>שם המשתמש</b> שברצונך להוסיף לרשימת המאומתים:", function(username) {
97
88
  if (!username) return;
@@ -103,7 +94,6 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
103
94
  title: "הזן מספר טלפון עבור " + username + " (אופציונלי)",
104
95
  inputType: 'text',
105
96
  callback: function(phone) {
106
- // תיקון קריטי: אם לחצו ביטול, עצור
107
97
  if (phone === null) return;
108
98
 
109
99
  var confirmMsg = "<h4>סיכום פעולה</h4>" +
@@ -130,9 +120,16 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
130
120
  var phone = $('#test-phone').val();
131
121
  if(!phone) return alerts.error('נא להזין מספר לבדיקה');
132
122
 
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);
123
+ $.post(config.relative_path + '/api/admin/plugins/phone-verification/test-call', { phoneNumber: phone, _csrf: config.csrf_token }, function(res) {
124
+ if(res.success) {
125
+ // עדכון: הצגת הקוד שהתקבל מהשרת למנהל
126
+ var msg = res.message || 'הצינתוק יצא בהצלחה';
127
+ if (res.code) {
128
+ msg += '<br><strong>קוד צפוי (4 ספרות אחרונות): ' + res.code + '</strong>';
129
+ }
130
+ alerts.success(msg);
131
+ }
132
+ else alerts.error(res.message || 'שגיאה בביצוע הבדיקה');
136
133
  });
137
134
  });
138
135
 
@@ -175,7 +172,7 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
175
172
  $('#search-btn').on('click', function() {
176
173
  var phone = $('#phone-search').val();
177
174
  if (!phone) { loadUsers(1); return; }
178
- $.get('/api/admin/plugins/phone-verification/search', { phone: phone }, function(data) {
175
+ $.get(config.relative_path + '/api/admin/plugins/phone-verification/search', { phone: phone }, function(data) {
179
176
  usersTbody.empty();
180
177
  if (data.success && data.found) usersTbody.append(buildUserRow(data.user));
181
178
  else usersTbody.html('<tr><td colspan="6" class="text-center">לא נמצא משתמש</td></tr>');