telebit-relay.js/lib/device-tracker.js

164 lines
5.5 KiB
JavaScript
Raw Permalink Normal View History

'use strict';
var Devices = module.exports;
2018-11-04 21:22:07 +00:00
// TODO enumerate store's keys and device's keys for documentation
2018-08-21 02:58:04 +00:00
Devices.addPort = function (store, serverport, newDevice) {
// TODO make special
return Devices.add(store, serverport, newDevice, true);
};
Devices.add = function (store, servername, newDevice, isPort) {
if (isPort) {
if (!store._ports) { store._ports = {}; }
}
2018-08-21 02:58:04 +00:00
// add domain (also handles ports at the moment)
if (!store._domains) { store._domains = {}; }
if (!store._domains[servername]) { store._domains[servername] = []; }
store._domains[servername].push(newDevice);
Devices.touch(store, servername);
2018-08-21 02:58:04 +00:00
// add device
// TODO only use a device id
var devId = newDevice.id || servername;
2018-08-21 03:12:53 +00:00
if (!newDevice.__servername) {
newDevice.__servername = servername;
}
2018-08-21 02:58:04 +00:00
if (!store._devices) { store._devices = {}; }
if (!store._devices[devId]) {
store._devices[devId] = newDevice;
2018-08-21 02:58:04 +00:00
if (!store._devices[devId].domainsMap) { store._devices[devId].domainsMap = {}; }
if (!store._devices[devId].domainsMap[servername]) { store._devices[devId].domainsMap[servername] = true; }
}
};
Devices.alias = function (store, servername, alias) {
if (!store._domains[servername]) {
store._domains[servername] = [];
}
if (!store._domains[servername]._primary) {
store._domains[servername]._primary = servername;
}
if (!store._domains[servername].aliases) {
store._domains[servername].aliases = {};
}
store._domains[alias] = store._domains[servername];
store._domains[servername].aliases[alias] = true;
};
Devices.remove = function (store, servername, device) {
// Check if this domain has an active device
var devices = store._domains[servername] || [];
var index = devices.indexOf(device);
if (index < 0) {
console.warn('attempted to remove non-present device', device.deviceId, 'from', servername);
return null;
}
// unlink this domain from this device
2018-08-21 02:58:04 +00:00
var domainsMap = store._devices[devices[index].id || servername].domainsMap;
delete domainsMap[servername];
/*
// remove device if no domains remain
// nevermind, a device can hang around in limbo for a bit
if (!Object.keys(domains).length) {
delete store._devices[devices[index].id || servername];
}
*/
// unlink this device from this domain
return devices.splice(index, 1)[0];
};
Devices.close = function (store, device) {
2018-08-21 03:12:53 +00:00
var dev = store._devices[device.id || device.__servername];
// because we're actually using names rather than don't have reliable deviceIds yet
if (!dev) {
2018-08-21 03:12:53 +00:00
Object.keys(store._devices).some(function (key) {
if (store._devices[key].socketId === device.socketId) {
2018-08-21 03:12:53 +00:00
// TODO double check that all domains are removed
delete store._devices[key];
return true;
}
});
}
};
2018-08-21 02:58:04 +00:00
Devices.bySocket = function (store, socketId) {
var dev;
Object.keys(store._devices).some(function (k) {
if (store._devices[k].socketId === socketId) {
dev = store._devices[k];
return dev;
}
});
return dev;
};
Devices.list = function (store, servername) {
2018-08-21 02:58:04 +00:00
console.log('[dontkeepme] servername', servername);
// efficient lookup first
if (store._domains[servername] && store._domains[servername].length) {
2018-08-21 02:58:04 +00:00
// aliases have ._primary which is the name of the original
return store._domains[servername]._primary && store._domains[store._domains[servername]._primary] || store._domains[servername];
}
// There wasn't an exact match so check any of the wildcard domains, sorted longest
// first so the one with the biggest natural match with be found first.
var deviceList = [];
Object.keys(store._domains).filter(function (pattern) {
return pattern[0] === '*' && store._domains[pattern].length;
}).sort(function (a, b) {
return b.length - a.length;
}).some(function (pattern) {
var subPiece = pattern.slice(1);
if (subPiece === servername.slice(-subPiece.length)) {
console.log('[Devices.list] "'+servername+'" matches "'+pattern+'"');
deviceList = store._domains[pattern];
// Devices.alias(store, '*.example.com', 'sub.example.com'
// '*.example.com' retrieves a reference to 'example.com'
// and this reference then also referenced by 'sub.example.com'
// Hence this O(n) check is replaced with the O(1) check above
Devices.alias(store, pattern, servername);
return true;
}
});
return deviceList;
};
/*
Devices.active = function (store, id) {
var dev = store._devices[id];
return !!dev;
};
*/
Devices.exist = function (store, servername) {
if (Devices.list(store, servername).length) {
Devices.touch(store, servername);
return true;
}
return false;
};
Devices.next = function (store, servername) {
var devices = Devices.list(store, servername);
var device;
if (devices._index >= devices.length) {
devices._index = 0;
}
device = devices[devices._index || 0];
devices._index = (devices._index || 0) + 1;
if (device) { Devices.touch(store, servername); }
return device;
};
2018-11-04 21:22:07 +00:00
Devices.touchDevice = function (store, device) {
// TODO use device.id (which will be pubkey thumbprint) and store._devices[id].domainsMap
Object.keys(device.domainsMap).forEach(function (servername) {
Devices.touch(store, servername);
});
};
Devices.touch = function (store, servername) {
if (!store._recency) { store._recency = {}; }
store._recency[servername] = Date.now();
};
Devices.lastSeen = function (store, servername) {
if (!store._recency) { store._recency = {}; }
return store._recency[servername] || 0;
};