nodebb-plugin-niki-loyalty 1.2.3 → 1.2.4

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.
Files changed (2) hide show
  1. package/library.js +121 -1
  2. package/package.json +1 -1
package/library.js CHANGED
@@ -411,7 +411,127 @@ Plugin.adminGetUsers = async function (socket, data) {
411
411
 
412
412
  // Soket'e kaydet (Client: socket.emit('plugins.niki.getUsers', ...))
413
413
  SocketPlugins.niki = {
414
- getUsers: Plugin.adminGetUsers
414
+ getUsers: Plugin.adminGetUsers,
415
+ scanQR: Plugin.socketScanQR,
416
+ getKasaHistory: Plugin.socketKasaHistory,
417
+ managePoints: Plugin.adminManagePoints
418
+ };
419
+
420
+ // =========================
421
+ // 🔌 YENİ SOCKET FONKSİYONLARI (POS & ADMIN)
422
+ // =========================
423
+
424
+ // 1) QR SCAN (Socket Versiyonu)
425
+ Plugin.socketScanQR = async function (socket, data) {
426
+ const uid = socket.uid;
427
+ if (!uid) throw new Error('Giriş yapmalısınız.');
428
+
429
+ const isAdmin = await user.isAdministrator(uid);
430
+ const isMod = await user.isGlobalModerator(uid);
431
+ if (!isAdmin && !isMod) throw new Error('Yetkisiz Erişim');
432
+
433
+ const token = data.token;
434
+ if (!token) throw new Error('Geçersiz Token');
435
+
436
+ const custUid = await db.get(`niki:qr:${token}`);
437
+ if (!custUid) throw new Error('QR Kod Geçersiz veya Süresi Dolmuş');
438
+
439
+ const pts = parseFloat((await user.getUserField(custUid, 'niki_points')) || 0);
440
+
441
+ let selectedReward = null;
442
+ if (!TEST_MODE_UNLIMITED) {
443
+ for (const r of REWARDS) {
444
+ if (pts >= r.cost) { selectedReward = r; break; }
445
+ }
446
+ if (!selectedReward) throw new Error('Puan Yetersiz');
447
+ } else { selectedReward = REWARDS[0]; }
448
+
449
+ if (!TEST_MODE_UNLIMITED) {
450
+ await user.decrementUserFieldBy(custUid, 'niki_points', selectedReward.cost);
451
+ }
452
+ await db.delete(`niki:qr:${token}`);
453
+
454
+ const cData = await user.getUserFields(custUid, ['username', 'picture', 'userslug']);
455
+ await addUserLog(custUid, 'spend', selectedReward.cost, selectedReward.name);
456
+ await addKasaLog(uid, cData.username, custUid, selectedReward.name, selectedReward.cost);
457
+
458
+ return { success: true, customer: cData, rewardName: selectedReward.name, cost: selectedReward.cost };
459
+ };
460
+
461
+ // 2) KASA HISTORY (Socket Versiyonu)
462
+ Plugin.socketKasaHistory = async function (socket, data) {
463
+ const uid = socket.uid;
464
+ if (!uid) throw new Error('Giriş yapmalısınız.');
465
+
466
+ const isAdmin = await user.isAdministrator(uid);
467
+ const isMod = await user.isGlobalModerator(uid);
468
+ if (!isAdmin && !isMod) throw new Error('Yetkisiz Erişim');
469
+
470
+ const raw = await db.getListRange('niki:kasa:history', 0, -1);
471
+ const rows = (raw || []).map(safeParseMaybeJson).filter(Boolean).reverse();
472
+
473
+ const uids = rows.map(r => parseInt(r.cuid, 10)).filter(n => Number.isFinite(n) && n > 0);
474
+ const users = await user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'icon:bgColor']);
475
+ const userMap = {};
476
+ (users || []).forEach(u => userMap[u.uid] = u);
477
+
478
+ const rp = nconf.get('relative_path') || '';
479
+ return rows.map(r => {
480
+ const u = userMap[r.cuid] || {};
481
+ return {
482
+ ...r,
483
+ cust: u.username || r.cust || 'Bilinmeyen',
484
+ picture: u.picture || '',
485
+ iconBg: u['icon:bgColor'] || '#4b5563',
486
+ profileUrl: u.userslug ? `${rp}/user/${u.userslug}` : '',
487
+ reward: r.reward || 'İşlem'
488
+ };
489
+ });
490
+ };
491
+
492
+ // 3) MANUEL PUAN YÖNETİMİ (Güvenli)
493
+ Plugin.adminManagePoints = async function (socket, data) {
494
+ // data = { targetUid, action: 'add'|'remove', amount, reason }
495
+ const uid = socket.uid;
496
+ if (!uid) throw new Error('Giriş yapmalısınız.');
497
+
498
+ // KESİN YETKİ KONTROLÜ (Sadece Administrator)
499
+ const isAdmin = await user.isAdministrator(uid);
500
+ if (!isAdmin) {
501
+ console.warn(`[NIKI SECURITY] Yetkisiz puan değiştirme denemesi! Actor: ${uid}`);
502
+ throw new Error('BU İŞLEM İÇİN YETKİNİZ YOK! (Olay Loglandı)');
503
+ }
504
+
505
+ const targetUid = data.targetUid;
506
+ const amount = Math.abs(parseFloat(data.amount));
507
+ const action = data.action;
508
+ const reason = data.reason || 'Manuel Düzenleme';
509
+
510
+ if (!targetUid || !amount || amount <= 0) throw new Error('Geçersiz veri.');
511
+
512
+ const exists = await user.exists(targetUid);
513
+ if (!exists) throw new Error('Kullanıcı bulunamadı.');
514
+
515
+ if (action === 'add') {
516
+ await user.incrementUserFieldBy(targetUid, 'niki_points', amount);
517
+ } else if (action === 'remove') {
518
+ await user.decrementUserFieldBy(targetUid, 'niki_points', amount);
519
+ } else {
520
+ throw new Error('Geçersiz işlem türü.');
521
+ }
522
+
523
+ // GÜVENLİK LOGU
524
+ const adminUserData = await user.getUserFields(uid, ['username']);
525
+ const logMsg = `Admin (${adminUserData.username}) tarafından ${action === 'add' ? '+' : '-'}${amount} puan. Sebep: ${reason}`;
526
+
527
+ await addUserLog(targetUid, 'admin_adjust', amount, logMsg);
528
+
529
+ // Denetim Logu
530
+ const auditLog = { ts: Date.now(), adminUid: uid, adminName: adminUserData.username, targetUid: targetUid, action: action, amount: amount, reason: reason };
531
+ await db.listAppend('niki:audit:admin_points', JSON.stringify(auditLog));
532
+
533
+ const newPoints = await user.getUserField(targetUid, 'niki_points');
534
+ return { success: true, newPoints: parseFloat(newPoints) };
415
535
  };
416
536
 
417
537
  module.exports = Plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-niki-loyalty",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "Niki The Cat Coffee Loyalty System - Earn points while studying on IEU Forum.",
5
5
  "main": "library.js",
6
6
  "nbbpm": {