2017-09-15 01:09:02 +08:00
|
|
|
const _ = require("lodash");
|
|
|
|
const async = require("async");
|
|
|
|
const redis = require("redis");
|
|
|
|
|
|
|
|
const config = require("./config.js");
|
|
|
|
const Logger = require("./logger.js");
|
|
|
|
|
|
|
|
// The database of mappings. Uses the internal ID as key because it is unique
|
|
|
|
// unlike the external ID.
|
|
|
|
// Used always from memory, but saved to redis for persistence.
|
|
|
|
//
|
|
|
|
// Format:
|
|
|
|
// {
|
|
|
|
// internalMeetingID: {
|
|
|
|
// id: @id
|
|
|
|
// externalMeetingID: @externalMeetingID
|
|
|
|
// internalMeetingID: @internalMeetingID
|
|
|
|
// lastActivity: @lastActivity
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// Format on redis:
|
|
|
|
// * a SET "...:mappings" with all ids (not meeting ids, the object id)
|
|
|
|
// * a HASH "...:mapping:<id>" for each mapping with all its attributes
|
|
|
|
const db = {};
|
|
|
|
let nextID = 1;
|
|
|
|
|
|
|
|
// A simple model to store mappings for user extIDs.
|
|
|
|
module.exports = class UserMapping {
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
this.id = null;
|
|
|
|
this.externalUserID = null;
|
|
|
|
this.internalUserID = null;
|
|
|
|
this.meetingId = null;
|
2018-10-25 10:49:56 +08:00
|
|
|
this.user = null;
|
2017-09-15 01:09:02 +08:00
|
|
|
this.redisClient = config.redis.client;
|
|
|
|
}
|
|
|
|
|
|
|
|
save(callback) {
|
2018-10-25 10:50:51 +08:00
|
|
|
db[this.internalUserID] = this;
|
|
|
|
|
2017-09-15 01:09:02 +08:00
|
|
|
this.redisClient.hmset(config.redis.keys.userMap(this.id), this.toRedis(), (error, reply) => {
|
|
|
|
if (error != null) { Logger.error("[UserMapping] error saving mapping to redis:", error, reply); }
|
|
|
|
this.redisClient.sadd(config.redis.keys.userMaps, this.id, (error, reply) => {
|
|
|
|
if (error != null) { Logger.error("[UserMapping] error saving mapping ID to the list of mappings:", error, reply); }
|
|
|
|
|
|
|
|
(typeof callback === 'function' ? callback(error, db[this.internalUserID]) : undefined);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
destroy(callback) {
|
|
|
|
this.redisClient.srem(config.redis.keys.userMaps, this.id, (error, reply) => {
|
|
|
|
if (error != null) { Logger.error("[UserMapping] error removing mapping ID from the list of mappings:", error, reply); }
|
|
|
|
this.redisClient.del(config.redis.keys.userMap(this.id), error => {
|
|
|
|
if (error != null) { Logger.error("[UserMapping] error removing mapping from redis:", error); }
|
|
|
|
|
|
|
|
if (db[this.internalUserID]) {
|
|
|
|
delete db[this.internalUserID];
|
|
|
|
(typeof callback === 'function' ? callback(error, true) : undefined);
|
|
|
|
} else {
|
|
|
|
(typeof callback === 'function' ? callback(error, false) : undefined);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
toRedis() {
|
|
|
|
const r = {
|
|
|
|
"id": this.id,
|
|
|
|
"internalUserID": this.internalUserID,
|
|
|
|
"externalUserID": this.externalUserID,
|
2018-10-25 10:49:56 +08:00
|
|
|
"meetingId": this.meetingId,
|
|
|
|
"user": this.user
|
2017-09-15 01:09:02 +08:00
|
|
|
};
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
fromRedis(redisData) {
|
|
|
|
this.id = parseInt(redisData.id);
|
|
|
|
this.externalUserID = redisData.externalUserID;
|
|
|
|
this.internalUserID = redisData.internalUserID;
|
|
|
|
this.meetingId = redisData.meetingId;
|
2018-10-25 10:49:56 +08:00
|
|
|
this.user = redisData.user;
|
2017-09-15 01:09:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
print() {
|
|
|
|
return JSON.stringify(this.toRedis());
|
|
|
|
}
|
|
|
|
|
2018-10-25 10:49:56 +08:00
|
|
|
static addOrUpdateMapping(internalUserID, externalUserID, meetingId, user, callback) {
|
2017-09-15 01:09:02 +08:00
|
|
|
let mapping = new UserMapping();
|
|
|
|
mapping.id = nextID++;
|
|
|
|
mapping.internalUserID = internalUserID;
|
|
|
|
mapping.externalUserID = externalUserID;
|
|
|
|
mapping.meetingId = meetingId;
|
2018-10-25 10:49:56 +08:00
|
|
|
mapping.user = user;
|
2017-09-15 01:09:02 +08:00
|
|
|
mapping.save(function(error, result) {
|
|
|
|
Logger.info(`[UserMapping] added user mapping to the list ${internalUserID}:`, mapping.print());
|
|
|
|
(typeof callback === 'function' ? callback(error, result) : undefined);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static removeMapping(internalUserID, callback) {
|
|
|
|
return (() => {
|
|
|
|
let result = [];
|
|
|
|
for (let internal in db) {
|
|
|
|
var mapping = db[internal];
|
|
|
|
if (mapping.internalUserID === internalUserID) {
|
|
|
|
result.push(mapping.destroy( (error, result) => {
|
|
|
|
Logger.info(`[UserMapping] removing user mapping from the list ${internalUserID}:`, mapping.print());
|
|
|
|
return (typeof callback === 'function' ? callback(error, result) : undefined);
|
|
|
|
}));
|
|
|
|
} else {
|
|
|
|
result.push(undefined);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
})();
|
|
|
|
}
|
|
|
|
|
|
|
|
static removeMappingMeetingId(meetingId, callback) {
|
|
|
|
return (() => {
|
|
|
|
let result = [];
|
|
|
|
for (let internal in db) {
|
|
|
|
var mapping = db[internal];
|
|
|
|
if (mapping.meetingId === meetingId) {
|
|
|
|
result.push(mapping.destroy( (error, result) => {
|
|
|
|
Logger.info(`[UserMapping] removing user mapping from the list ${mapping.internalUserID}:`, mapping.print());
|
|
|
|
}));
|
|
|
|
} else {
|
|
|
|
result.push(undefined);
|
|
|
|
}
|
|
|
|
}
|
2017-11-02 03:05:55 +08:00
|
|
|
return (typeof callback === 'function' ? callback() : undefined);
|
2017-09-15 01:09:02 +08:00
|
|
|
})();
|
|
|
|
}
|
|
|
|
|
2018-10-25 10:49:56 +08:00
|
|
|
static getUser(internalUserID) {
|
|
|
|
if (db[internalUserID]){
|
|
|
|
return db[internalUserID].user;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-15 01:09:02 +08:00
|
|
|
static getExternalUserID(internalUserID) {
|
|
|
|
if (db[internalUserID]){
|
|
|
|
return db[internalUserID].externalUserID;
|
|
|
|
}
|
|
|
|
}
|
2017-09-15 01:27:09 +08:00
|
|
|
|
2017-09-15 01:09:02 +08:00
|
|
|
static allSync() {
|
|
|
|
let arr = Object.keys(db).reduce(function(arr, id) {
|
|
|
|
arr.push(db[id]);
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
, []);
|
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initializes global methods for this model.
|
|
|
|
static initialize(callback) {
|
|
|
|
UserMapping.resync(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets all mappings from redis to populate the local database.
|
|
|
|
// Calls `callback()` when done.
|
|
|
|
static resync(callback) {
|
|
|
|
let client = config.redis.client;
|
|
|
|
let tasks = [];
|
|
|
|
|
|
|
|
return client.smembers(config.redis.keys.userMaps, (error, mappings) => {
|
|
|
|
if (error != null) { Logger.error("[UserMapping] error getting list of mappings from redis:", error); }
|
|
|
|
|
|
|
|
mappings.forEach(id => {
|
|
|
|
tasks.push(done => {
|
|
|
|
client.hgetall(config.redis.keys.userMap(id), function(error, mappingData) {
|
|
|
|
if (error != null) { Logger.error("[UserMapping] error getting information for a mapping from redis:", error); }
|
|
|
|
|
|
|
|
if (mappingData != null) {
|
|
|
|
let mapping = new UserMapping();
|
|
|
|
mapping.fromRedis(mappingData);
|
|
|
|
mapping.save(function(error, hook) {
|
|
|
|
if (mapping.id >= nextID) { nextID = mapping.id + 1; }
|
|
|
|
done(null, mapping);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
done(null, null);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return async.series(tasks, function(errors, result) {
|
|
|
|
mappings = _.map(UserMapping.allSync(), m => m.print());
|
|
|
|
Logger.info("[UserMapping] finished resync, mappings registered:", mappings);
|
|
|
|
return (typeof callback === 'function' ? callback() : undefined);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|