diff --git a/lib/db/api.js b/lib/db/api.js index 2b3548df..f9a17b2b 100644 --- a/lib/db/api.js +++ b/lib/db/api.js @@ -46,6 +46,7 @@ dbapi.saveDevice = function(serial, device) { serial: serial , present: true , provider: device.provider + , owner: null , status: device.status , statusChangedAt: r.now() , createdAt: r.now() @@ -65,6 +66,18 @@ dbapi.saveDeviceStatus = function(serial, status) { }) } +dbapi.setDeviceOwner = function(serial, owner) { + return db.run(r.table('devices').get(serial).update({ + owner: owner + })) +} + +dbapi.unsetDeviceOwner = function(serial, owner) { + return db.run(r.table('devices').get(serial).update({ + owner: null + })) +} + dbapi.setDeviceAbsent = function(serial) { return db.run(r.table('devices').get(serial).update({ present: false diff --git a/lib/roles/app.js b/lib/roles/app.js index 6386c9d8..0d261e57 100644 --- a/lib/roles/app.js +++ b/lib/roles/app.js @@ -169,7 +169,7 @@ module.exports = function(options) { io.on('connection', function(socket) { var channels = [] - , group = socket.handshake.user.group + , user = socket.handshake.user var messageListener = wirerouter() .on(wire.JoinGroupMessage, function(channel, message) { @@ -200,9 +200,9 @@ module.exports = function(options) { groupRouter.on(wireutil.global, messageListener) // User's private group - channels.push(group) - sub.subscribe(group) - groupRouter.on(group, messageListener) + channels.push(user.group) + sub.subscribe(user.group) + groupRouter.on(user.group, messageListener) // Clean up all listeners and subscriptions socket.on('disconnect', function() { @@ -214,24 +214,33 @@ module.exports = function(options) { }) socket.on('group.invite', function(data) { - push.send([wireutil.global, wireutil.makeGroupMessage( - group - , options.groupTimeout - , data - )]) + push.send([ + wireutil.global + , wireutil.envelope(new wire.GroupMessage( + new wire.OwnerMessage( + user.email + , user.name + , user.group + ) + , options.groupTimeout + , wireutil.toDeviceRequirements(data) + )) + ]) }) socket.on('group.kick', function(data) { - push.send([group, wireutil.makeUngroupMessage( - group - , data - )]) + push.send([ + user.group + , wireutil.envelope(new wire.UngroupMessage( + wireutil.toDeviceRequirements(data) + )) + ]) }) function touchSender(klass) { return function(data) { push.send([ - group + user.group , wireutil.envelope(new klass( data.x , data.y @@ -243,7 +252,7 @@ module.exports = function(options) { function keySender(klass) { return function(data) { push.send([ - group + user.group , wireutil.envelope(new klass( data.key )) diff --git a/lib/roles/device.js b/lib/roles/device.js index 42b686a1..e7a6d7f1 100644 --- a/lib/roles/device.js +++ b/lib/roles/device.js @@ -25,8 +25,8 @@ module.exports = function(options) { , identity = Object.create(null) , display = Object.create(null) , vendor = Object.create(null) + , owner = null , solo = wireutil.makePrivateChannel() - , group = null , channels = new ChannelManager() , vitals = new Vitals() , ports = { @@ -83,7 +83,7 @@ module.exports = function(options) { // Unsubscribe from temporary channels when they timeout channels.on('timeout', function(channel) { log.info('Channel "%s" timed out', channel) - if (channel === group) { + if (channel === owner.group) { leaveGroup() } }) @@ -342,7 +342,7 @@ module.exports = function(options) { .on(wire.GroupMessage, function(channel, message) { if (!isGrouped() && devutil.matchesRequirements(identity, message.requirements)) { - joinGroup(message.channel, message.timeout) + joinGroup(message.owner, message.timeout) } channels.keepalive(channel) }) @@ -436,23 +436,37 @@ module.exports = function(options) { } function isGrouped() { - return !!group + return !!owner } - function joinGroup(channel, timeout) { - log.info('Subscribing to group channel "%s"', channel) - channels.register(channel, timeout) - sub.subscribe(channel) - push.send([channel, wireutil.makeJoinGroupMessage(options.serial)]) - group = channel + function joinGroup(newOwner, timeout) { + log.info('Now owned by "%s"', newOwner.email) + log.info('Subscribing to group channel "%s"', newOwner.group) + channels.register(newOwner.group, timeout) + sub.subscribe(newOwner.group) + push.send([ + wireutil.global + , wireutil.envelope(new wire.JoinGroupMessage( + options.serial + , newOwner + )) + ]) + owner = newOwner } function leaveGroup() { - log.info('Unsubscribing from group channel "%s"', group) - channels.unregister(group) - sub.unsubscribe(group) - push.send([group, wireutil.makeLeaveGroupMessage(options.serial)]) - group = null + log.info('No longer owned by "%s"', owner.email) + log.info('Unsubscribing from group channel "%s"', owner.group) + channels.unregister(owner.group) + sub.unsubscribe(owner.group) + push.send([ + wireutil.global + , wireutil.envelope(new wire.LeaveGroupMessage( + options.serial + , owner + )) + ]) + owner = null } function selfDestruct() { diff --git a/lib/roles/processor.js b/lib/roles/processor.js index aa604fd6..f13a9eeb 100644 --- a/lib/roles/processor.js +++ b/lib/roles/processor.js @@ -45,9 +45,11 @@ module.exports = function(options) { appDealer.send([channel, data]) }) .on(wire.JoinGroupMessage, function(channel, message, data) { + dbapi.setDeviceOwner(message.serial, message.owner) appDealer.send([channel, data]) }) .on(wire.LeaveGroupMessage, function(channel, message, data) { + dbapi.unsetDeviceOwner(message.serial, message.owner) appDealer.send([channel, data]) }) .on(wire.DeviceLogMessage, function(channel, message, data) { diff --git a/lib/wire/util.js b/lib/wire/util.js index a31ddf1e..9e683ddc 100644 --- a/lib/wire/util.js +++ b/lib/wire/util.js @@ -16,6 +16,16 @@ var wireutil = { , offline: 'OFFLINE' }[type]] } +, toDeviceRequirements: function(requirements) { + return Object.keys(requirements).map(function(name) { + var item = requirements[name] + return new wire.DeviceRequirement( + name + , item.value + , wire.RequirementType[item.match.toUpperCase()] + ) + }) + } , envelope: function(message) { return new wire.Envelope(message.$code, message.encode()).encodeNB() } @@ -30,43 +40,6 @@ var wireutil = { , entry.identifier )) } -, makeGroupMessage: function(channel, timeout, requirements) { - return wireutil.envelope(new wire.GroupMessage( - channel - , timeout - , Object.keys(requirements).map(function(name) { - var item = requirements[name] - return new wire.DeviceRequirement( - name - , item.value - , wire.RequirementType[item.match.toUpperCase()] - ) - }) - )) - } -, makeUngroupMessage: function(channel, requirements) { - return wireutil.envelope(new wire.UngroupMessage( - channel - , Object.keys(requirements).map(function(name) { - var item = requirements[name] - return new wire.DeviceRequirement( - name - , item.value - , wire.RequirementType[item.match.toUpperCase()] - ) - }) - )) - } -, makeJoinGroupMessage: function(serial) { - return wireutil.envelope(new wire.JoinGroupMessage( - serial - )) - } -, makeLeaveGroupMessage: function(serial) { - return wireutil.envelope(new wire.LeaveGroupMessage( - serial - )) - } , makeDevicePokeMessage: function(serial, channel) { return wireutil.envelope(new wire.DevicePokeMessage( serial diff --git a/lib/wire/wire.proto b/lib/wire/wire.proto index bf656bb0..04f70375 100644 --- a/lib/wire/wire.proto +++ b/lib/wire/wire.proto @@ -124,23 +124,30 @@ message DeviceRequirement { required RequirementType type = 3; } +message OwnerMessage { + required string email = 1; + required string name = 2; + required string group = 3; +} + message GroupMessage { - required string channel = 1; + required OwnerMessage owner = 1; required uint32 timeout = 2; repeated DeviceRequirement requirements = 3; } message UngroupMessage { - required string channel = 1; repeated DeviceRequirement requirements = 2; } message JoinGroupMessage { required string serial = 1; + required OwnerMessage owner = 2; } message LeaveGroupMessage { required string serial = 1; + required OwnerMessage owner = 2; } // Input diff --git a/res/app/scripts/services/deviceService.js b/res/app/scripts/services/deviceService.js index acf37748..da42f15e 100644 --- a/res/app/scripts/services/deviceService.js +++ b/res/app/scripts/services/deviceService.js @@ -51,6 +51,16 @@ define(['./module', 'oboe'], function(mod, oboe) { modify(get(data), data) }) + socket.on('group.join', function(data) { + modify(get(data), data) + }) + + socket.on('group.leave', function(data) { + modify(get(data), { + owner: null + }) + }) + oboe('/api/v1/devices') .node('devices[*]', function(device) { // We want to skip other arguments diff --git a/res/app/views/partials/deviceList.jade b/res/app/views/partials/deviceList.jade index 06a27b43..57e015cc 100644 --- a/res/app/views/partials/deviceList.jade +++ b/res/app/views/partials/deviceList.jade @@ -2,6 +2,6 @@ h1 Devices ul li(ng-repeat='device in devices track by device.serial') - span {{ device.serial }} + span {{ device.serial }} {{ device.present ? 'present' : 'absent' }} {{ device.owner.email }} button(ng-click="invite(device)") invite button(ng-click="kick(device)") kick