-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
181 lines (166 loc) · 6.08 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
const debug = require('debug')('jambonz:mw-registrar');
const Emitter = require('events');
const noop = () => {
};
function makeUserKey(aor) {
return `user:${aor}`;
}
class Registrar extends Emitter {
constructor(logger, redisClient) {
super();
if (!logger) {
logger = Object.create(null);
logger.info = logger.debug = logger.error = noop;
}
this.logger = logger;
this.client = redisClient;
//cleanup expired entries
const cleanRateSeconds = 60 * 1000 * 10; //10 minutes
setInterval((rClient) => {
try {
rClient.zremrangebyscore('active-user', 0, Date.now());
} catch (err) {
this.logger.error(err, 'Error cleaning user interval');
}
}, cleanRateSeconds, redisClient);
}
/* for use by test suite only */
_quit() {
this.client.end(true);
}
/**
* Add a registration for a user identified by a sip address-of-record
* @param {String} aor - a sip address-of-record for a user (e.g. daveh@drachtio.org)
* @param {String} obj.contact - the sip address where this user can be reached
* @param {String} obj.sbcAddress - the sip uri address of the sbc that manages the connection to this user
* @param {String} obj.protocol - the transport protocol used between the sbc and the user
* @param {String} expires - number of seconds the registration for this user is active
* @returns {Boolean} true if the registration was successfully added
*/
async add(aor, obj, expires) {
debug(`Registrar#add ${aor} from ${JSON.stringify(obj)} for ${expires}`);
const key = makeUserKey(aor);
try {
obj.expiryTime = Date.now() + (expires * 1000);
const result = await this.client.setex(key, expires, JSON.stringify(obj));
const zResult = await this.client.zadd('active-user', obj.expiryTime, key);
debug({result, zResult, expires, obj}, `Registrar#add - result of adding ${aor}`);
return result === 'OK' && zResult === 1;
} catch (err) {
this.logger.error(err, `Error adding user ${aor}`);
return false;
}
}
/**
* Retrieve the registration details for a user
* @param {String} aor - the address-of-record for the user
* @returns {Object} an object containing the registration details for this user, or null
* if the user does not have an active registration.
*/
async query(aor) {
try {
const key = makeUserKey(aor);
const result = await this.client.get(key);
return JSON.parse(result);
} catch (err) {
this.logger.error({err}, `@jambonz/mw-registrar query: Error retrieving ${aor}`);
}
}
/**
* Remove the registration for a user
* @param {String} aor - the address-of-record for the user
* @returns {Boolean} true if the registration was successfully removed
*/
async remove(aor) {
const key = makeUserKey(aor);
try {
const result = await this.client.del(key);
const sortedSetResult = await this.client.zrem('active-user', key);
debug(`Registrar#remove ${aor} result=${result} expiredKeys=${sortedSetResult}`);
return result === 1 && sortedSetResult === 1;
} catch (err) {
this.logger.error(err, `Error removing aor ${aor}`);
return false;
}
}
//todo
// keys shouldn't be used on a production redis as can degrade performance
// if this method is needed, suggest using scan instead.
// A quick search of repo and it looks like this method is used for tts cache counts.
// Maybe a simple set which holds expiry timestamps for each tts entry, this can easily be trimmed
// on each count invocation before returning the final count. This method could then be removed.
async keys(prefix) {
try {
prefix = prefix || '*';
const result = await this.client.keys(prefix);
debug(`keys ${prefix}: ${JSON.stringify(result)}`);
return result;
} catch (err) {
this.logger.error(err, `Error keys ${prefix}`);
debug(err, `Error keys prefix ${prefix}`);
return null;
}
}
/**
* if param realm exists then returns count of users belonging to a realm,
* otherwise returns count of all registered users
* @param {String} realm - nullable realm (e.g. drachtio.org)
* @returns {int} count of users
*/
async getCountOfUsers(realm) {
const users = await this.getRegisteredUsersForRealm(realm);
debug(`Registrar#getCountOfUsers result=${users.length} realm=${realm}`);
return users.length;
}
/**
* if realm exists then return all user parts belonging to a realm,
* otherwise returns all registered user parts
* @param {String | undefined} realm - realm (e.g. drachtio.org)
* @returns {[String]} Set of userParts
*/
async getRegisteredUsersForRealm(realm) {
try {
//cleanup expired entries
await this.client.zremrangebyscore('active-user', 0, Date.now());
const keyPattern = realm ? `*${realm}` : '*';
const users = new Set();
let idx = 0;
do {
const res = await this.client.zscan('active-user', [idx, 'MATCH', keyPattern, 'COUNT', 100]);
const next = res[0];
const keys = res[1];
debug(next, keys, `Registrar:getCountOfUsers result from scan cursor ${idx} ${realm}`);
keys.forEach((k) => {
const arr = /^user:(.*)@.*$/.exec(k);
if (arr) users.add(arr[1]);
});
idx = parseInt(next);
} while (idx !== 0);
return [...users];
} catch (err) {
debug(err);
this.logger.error(err, 'getRegisteredUsersForRealm: Error retrieving registered users');
}
}
async getRegisteredUsersDetailsForRealm(realm) {
try {
const users = new Set();
const userNames = await this.getRegisteredUsersForRealm(realm);
for (const u of userNames) {
const user = JSON.parse(await this.client.get(`user:${u}@${realm}`));
if (user) {
delete user.sbcAddress;
users.add({
name: u,
...user
});
}
}
return [...users];
} catch (err) {
debug(err);
this.logger.error(err, 'getRegisteredUsersDetailsForRealm: Error retrieving registered users');
}
}
}
module.exports = Registrar;