@go-mailer/jarvis 10.9.2 → 10.9.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.
@@ -1,126 +1,159 @@
1
- const fs = require('fs')
2
- const path = require('path')
3
-
4
- const WalletThrottler = async ({ redisClient = null, RMQBroker, wallet_charge_queue, wallet_refund_queue }) => {
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const WalletThrottler = async ({
5
+ redisClient = null,
6
+ RMQBroker,
7
+ wallet_charge_queue,
8
+ wallet_refund_queue,
9
+ }) => {
5
10
  if (!redisClient) {
6
- throw new Error('Redis client is required for WalletThrottler')
11
+ throw new Error("Redis client is required for WalletThrottler");
7
12
  }
8
13
 
9
- const lua_script = fs.readFileSync(path.join(__dirname, 'throttler.lua'), 'utf-8')
10
- const deduct_sha = await redisClient.scriptLoad(lua_script)
11
-
12
- const deductCredits = async ({ tenant_id, message_type = 'transactional', wallet_type, amount, min_allowed = 0 }) => {
13
- const balance_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}`
14
- const usage_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}:usage`
15
-
16
- let result
14
+ const lua_script = fs.readFileSync(
15
+ path.join(__dirname, "throttler.lua"),
16
+ "utf-8",
17
+ );
18
+ const deduct_sha = await redisClient.scriptLoad(lua_script);
19
+
20
+ const deductCredits = async ({
21
+ tenant_id,
22
+ resource_id,
23
+ resource_type,
24
+ wallet_type,
25
+ amount,
26
+ min_allowed = 0,
27
+ }) => {
28
+ const balance_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}`;
29
+ const usage_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}:${resource_type}:${resource_id}:usage`;
30
+
31
+ let result;
17
32
  try {
18
33
  result = await redisClient.evalSha(deduct_sha, {
19
34
  keys: [balance_key, usage_key],
20
- arguments: [amount.toString(), min_allowed.toString(), message_type]
21
- })
35
+ arguments: [amount.toString(), min_allowed.toString()],
36
+ });
22
37
  } catch (err) {
23
38
  result = await redisClient.eval(lua_script, {
24
39
  keys: [balance_key, usage_key],
25
- arguments: [amount.toString(), min_allowed.toString(), message_type]
26
- })
40
+ arguments: [amount.toString(), min_allowed.toString()],
41
+ });
27
42
  }
28
43
 
29
- const [success, status, newBalance] = result
44
+ const [success, status, newBalance] = result;
30
45
  if (success === 1) {
31
- return { success: true, newBalance: parseInt(newBalance, 10) }
46
+ return { success: true, newBalance: parseInt(newBalance, 10) };
32
47
  }
33
48
 
34
- if (status === 'no_balance') {
35
- return { success: false, reason: 'no_wallet' }
49
+ if (status === "no_balance") {
50
+ return { success: false, reason: "no_wallet" };
36
51
  }
37
52
 
38
- return { success: false, reason: 'insufficient', currentBalance: parseInt(newBalance, 10) }
39
- }
53
+ return {
54
+ success: false,
55
+ reason: "insufficient",
56
+ currentBalance: parseInt(newBalance, 10),
57
+ };
58
+ };
40
59
 
41
60
  const deductUsedCreditsFromDB = async () => {
42
61
  if (!RMQBroker) {
43
- throw new Error('RMQBroker is required for WalletThrottler')
62
+ throw new Error("RMQBroker is required for WalletThrottler");
44
63
  }
45
64
 
46
65
  if (!wallet_charge_queue) {
47
- throw new Error('wallet_charge_queue is required for WalletThrottler')
66
+ throw new Error("wallet_charge_queue is required for WalletThrottler");
48
67
  }
49
68
 
50
- const used_keys = await redisClient.keys('wallet:tenant:*:wallet:*:usage')
69
+ const used_keys = await redisClient.keys(
70
+ "wallet:tenant:*:wallet:*:*:*:usage",
71
+ );
51
72
  for (const usage_key of used_keys) {
52
- const usage = await redisClient.get(usage_key)
53
- const [, , tenant_id, , wallet_type] = usage_key.split(':')
54
- if (usage && parseInt(usage, 10) === 0) continue
73
+ const usage = await redisClient.get(usage_key);
74
+ const [, , tenant_id, , wallet_type, resource_type, resource_id] =
75
+ usage_key.split(":");
76
+ if (usage && parseInt(usage, 10) === 0) continue;
55
77
 
56
- await redisClient.multi().set(usage_key, 0).exec()
78
+ await redisClient.multi().set(usage_key, 0).exec();
57
79
  RMQBroker.publish(
58
80
  wallet_charge_queue,
59
- JSON.stringify({ tenant_id, resource_id: 0, resource_type: wallet_type, wallet_type, cost: parseInt(usage, 10), timestamp: Date.now() })
60
- )
61
-
62
- console.log(`Synced ${usage} used credits for ${tenant_id}`)
81
+ JSON.stringify({
82
+ tenant_id,
83
+ resource_id,
84
+ resource_type,
85
+ wallet_type,
86
+ cost: parseInt(usage, 10),
87
+ timestamp: Date.now(),
88
+ }),
89
+ );
90
+
91
+ console.log(`Synced ${usage} used credits for ${tenant_id}`);
63
92
  }
64
- }
93
+ };
65
94
 
66
95
  const loadCache = async ({ tenant_id, wallet_type, balance }) => {
67
- const balance_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}`
68
- const usage_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}:usage`
96
+ const balance_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}`;
69
97
 
70
98
  // Load balance from DB if missing
71
- const redis_balance = await redisClient.get(balance_key)
99
+ const redis_balance = await redisClient.get(balance_key);
72
100
  if (redis_balance === null) {
73
- await redisClient.set(balance_key, balance)
101
+ await redisClient.set(balance_key, balance);
74
102
  }
75
-
76
- // Check & sync any dangling used immediately
77
- const used = parseInt((await redisClient.get(usage_key)) || '0', 10)
78
- if (used > 0) {
79
- RMQBroker.publish(
80
- wallet_charge_queue,
81
- JSON.stringify({ tenant_id, resource_id: 0, resource_type: wallet_type, wallet_type, cost: used, timestamp: Date.now() })
82
- )
83
-
84
- await redisClient.set(usage_key, 0) // reset after sync
85
- await redisClient.decrBy(balance_key, used) // update Redis balance too
86
- console.log(`Synced dangling used ${used} for ${tenant_id} on startup`)
87
- }
88
- }
89
-
90
- const refundCredits = async ({ tenant_id, wallet_type, amount, resource_id }) => {
91
- const balance_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}`
92
- const usage_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}:usage`
103
+ };
104
+
105
+ const refundCredits = async ({
106
+ tenant_id,
107
+ wallet_type,
108
+ amount,
109
+ resource_id,
110
+ resource_type,
111
+ syncWithDb = true,
112
+ }) => {
113
+ const balance_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}`;
114
+ const usage_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}:${resource_type}:${resource_id}:usage`;
93
115
 
94
116
  // Atomic refund in Redis
95
117
  await redisClient
96
118
  .multi()
97
119
  .incrBy(balance_key, amount)
98
120
  .decrBy(usage_key, amount) // reduce tracked usage
99
- .exec()
121
+ .exec();
100
122
 
101
123
  // Async DB refund
102
- RMQBroker.publish(
103
- wallet_refund_queue,
104
- JSON.stringify({
105
- tenant_id,
106
- amount: 0,
107
- units: amount,
108
- resource_id,
109
- type: wallet_type
110
- })
111
- )
112
- console.log(`Refunded ${amount} credits for tenant ${tenant_id} on wallet ${wallet_type}`)
113
- }
124
+ if (syncWithDb) {
125
+ RMQBroker.publish(
126
+ wallet_refund_queue,
127
+ JSON.stringify({
128
+ tenant_id,
129
+ amount: 0,
130
+ units: amount,
131
+ resource_id,
132
+ resource_type,
133
+ type: wallet_type,
134
+ }),
135
+ );
136
+ }
137
+ console.log(
138
+ `Refunded ${amount} credits for tenant ${tenant_id} on wallet ${wallet_type}`,
139
+ );
140
+ };
114
141
 
115
142
  const topUpCredits = async ({ tenant_id, wallet_type, amount = 0 }) => {
116
- const balance_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}`
143
+ const balance_key = `wallet:tenant:${tenant_id}:wallet:${wallet_type}`;
117
144
 
118
145
  // Increment Redis (instant across all instances)
119
- await redisClient.incrBy(balance_key, amount)
120
- console.log(`Top-up ${amount} for ${tenant_id} on wallet ${wallet_type}`)
121
- }
122
-
123
- return { deductCredits, deductUsedCreditsFromDB, loadCache, refundCredits, topUpCredits }
124
- }
125
-
126
- module.exports = { WalletThrottler }
146
+ await redisClient.incrBy(balance_key, amount);
147
+ console.log(`Top-up ${amount} for ${tenant_id} on wallet ${wallet_type}`);
148
+ };
149
+
150
+ return {
151
+ deductCredits,
152
+ deductUsedCreditsFromDB,
153
+ loadCache,
154
+ refundCredits,
155
+ topUpCredits,
156
+ };
157
+ };
158
+
159
+ module.exports = { WalletThrottler };
@@ -8,7 +8,6 @@ local balanceKey = KEYS[1]
8
8
  local usedKey = KEYS[2]
9
9
  local amount = tonumber(ARGV[1])
10
10
  local minAllowed = tonumber(ARGV[2]) or 0
11
- local msgType = ARGV[3]
12
11
 
13
12
  local balance = redis.call('GET', balanceKey)
14
13
  if not balance then
@@ -25,8 +24,6 @@ end
25
24
  redis.call('SET', balanceKey, balance - amount)
26
25
 
27
26
  -- Track usage
28
- if msgType ~= "campaign" then
29
- redis.call('INCRBY', usedKey, amount)
30
- end
27
+ redis.call('INCRBY', usedKey, amount)
31
28
 
32
29
  return {1, "success", balance - amount}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@go-mailer/jarvis",
3
- "version": "10.9.2",
3
+ "version": "10.9.4",
4
4
  "main": "index.js",
5
5
  "repository": "git@github.com:go-mailer-ltd/jarvis-node.git",
6
6
  "author": "Nathan Oguntuberu <nateoguns.work@gmail.com>",