vines-services 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +5 -5
- data/Rakefile +13 -11
- data/bin/vines-services +0 -1
- data/lib/vines/services/command/init.rb +156 -139
- data/lib/vines/services/controller/messages_controller.rb +8 -3
- data/lib/vines/services/controller/users_controller.rb +11 -11
- data/lib/vines/services/indexer.rb +1 -1
- data/lib/vines/services/priority_queue.rb +12 -12
- data/lib/vines/services/roster.rb +85 -46
- data/lib/vines/services/storage/couchdb/service.rb +1 -1
- data/lib/vines/services/storage/couchdb/system.rb +1 -1
- data/lib/vines/services/storage/couchdb/upload.rb +1 -1
- data/lib/vines/services/storage/couchdb/user.rb +13 -3
- data/lib/vines/services/storage/couchdb.rb +8 -12
- data/lib/vines/services/version.rb +1 -1
- data/test/priority_queue_test.rb +1 -1
- data/web/coffeescripts/api.coffee +6 -0
- data/web/coffeescripts/files.coffee +65 -39
- data/web/coffeescripts/services.coffee +5 -2
- data/web/coffeescripts/setup.coffee +35 -12
- data/web/coffeescripts/systems.coffee +115 -228
- data/web/javascripts/api.js +69 -0
- data/web/javascripts/app.js +2 -0
- data/web/javascripts/commands.js +28 -0
- data/web/javascripts/files.js +424 -0
- data/web/javascripts/init.js +27 -0
- data/web/javascripts/services.js +404 -0
- data/web/javascripts/setup.js +485 -0
- data/web/javascripts/systems.js +342 -0
- data/web/stylesheets/app.css +714 -0
- data/web/stylesheets/services.css +4 -14
- data/web/stylesheets/setup.css +1 -2
- data/web/stylesheets/systems.css +124 -108
- metadata +95 -92
@@ -0,0 +1,404 @@
|
|
1
|
+
var ServicesPage;
|
2
|
+
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
3
|
+
ServicesPage = (function() {
|
4
|
+
var ATTRS, MEMBERS, SERVICES, SYSTEMS, USERS;
|
5
|
+
SERVICES = 'http://getvines.com/protocol/services';
|
6
|
+
MEMBERS = 'http://getvines.com/protocol/services/members';
|
7
|
+
SYSTEMS = 'http://getvines.com/protocol/systems';
|
8
|
+
ATTRS = 'http://getvines.com/protocol/systems/attributes';
|
9
|
+
USERS = 'http://getvines.com/protocol/users';
|
10
|
+
function ServicesPage(session) {
|
11
|
+
this.session = session;
|
12
|
+
this.api = new Api(this.session);
|
13
|
+
this.selectedService = null;
|
14
|
+
this.validateTimeout = null;
|
15
|
+
this.layout = null;
|
16
|
+
this.users = [];
|
17
|
+
}
|
18
|
+
ServicesPage.prototype.deleteService = function(event) {
|
19
|
+
var selected;
|
20
|
+
this.drawBlankSlate();
|
21
|
+
this.toggleForm('#remove-contact-form');
|
22
|
+
selected = $("#services li[data-id='" + this.selectedService.id + "']");
|
23
|
+
this.api.remove(SERVICES, this.selectedService.id, __bind(function(result) {
|
24
|
+
return selected.fadeOut(200, function() {
|
25
|
+
selected.remove();
|
26
|
+
return this.selectedService = null;
|
27
|
+
});
|
28
|
+
}, this));
|
29
|
+
return false;
|
30
|
+
};
|
31
|
+
ServicesPage.prototype.icon = function(member) {
|
32
|
+
var icon, icons;
|
33
|
+
icons = {
|
34
|
+
darwin: 'mac.png',
|
35
|
+
linux: 'linux.png',
|
36
|
+
windows: 'windows.png'
|
37
|
+
};
|
38
|
+
icon = icons[member.os] || 'run.png';
|
39
|
+
return "images/" + icon;
|
40
|
+
};
|
41
|
+
ServicesPage.prototype.drawMember = function(member) {
|
42
|
+
var node;
|
43
|
+
if (!this.editorVisible()) {
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
node = $("<li>\n <span class=\"icon\"><img src=\"" + (this.icon(member)) + "\"/></span>\n <span class=\"text\"></span>\n</li>").appendTo('#members');
|
47
|
+
return $('.text', node).text(member.name);
|
48
|
+
};
|
49
|
+
ServicesPage.prototype.operators = function() {
|
50
|
+
var node, operator, _i, _len, _ref, _results;
|
51
|
+
_ref = ['like', 'not like', 'starts with', 'ends with', 'is', 'is not', '>', '>=', '<', '<=', 'and', 'or'];
|
52
|
+
_results = [];
|
53
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
54
|
+
operator = _ref[_i];
|
55
|
+
node = $("<li data-selector=\"" + operator + "\">\n " + operator + "\n</li>").appendTo('#operators');
|
56
|
+
_results.push(node.click(__bind(function(event) {
|
57
|
+
var name;
|
58
|
+
$('#syntax').focus();
|
59
|
+
name = $(event.currentTarget).attr('data-selector');
|
60
|
+
$('#syntax').val($('#syntax').val() + (" " + name + " "));
|
61
|
+
return this.validateIn();
|
62
|
+
}, this)));
|
63
|
+
}
|
64
|
+
return _results;
|
65
|
+
};
|
66
|
+
ServicesPage.prototype.selectService = function(node) {
|
67
|
+
var id, name;
|
68
|
+
id = $(node).attr('data-id');
|
69
|
+
name = $(node).attr('data-name');
|
70
|
+
$('#services li').removeClass('selected');
|
71
|
+
$(node).addClass('selected');
|
72
|
+
$('#remove-service-msg').html("Are you sure you want to remove the " + ("<strong>" + name + "</strong> service?"));
|
73
|
+
$('#remove-service-form .buttons').fadeIn(200);
|
74
|
+
return this.api.get(SERVICES, {
|
75
|
+
id: id
|
76
|
+
}, __bind(function(result) {
|
77
|
+
this.selectedService = result;
|
78
|
+
return this.drawEditor(result);
|
79
|
+
}, this));
|
80
|
+
};
|
81
|
+
ServicesPage.prototype.serviceNode = function(service) {
|
82
|
+
var label, node;
|
83
|
+
label = service.size === 1 ? 'system' : 'systems';
|
84
|
+
node = $("<li data-id=\"" + service.id + "\" data-name=\"\" data-size=\"" + service.size + "\">\n <span class=\"text\">" + service.name + "</span>\n <span class=\"count\">" + service.size + " " + label + "</span>\n</li>").appendTo('#services');
|
85
|
+
node.attr('data-name', service.name);
|
86
|
+
$('.text', node).text(service.name);
|
87
|
+
node.click(__bind(function(event) {
|
88
|
+
return this.selectService(event.currentTarget);
|
89
|
+
}, this));
|
90
|
+
return node;
|
91
|
+
};
|
92
|
+
ServicesPage.prototype.findServices = function() {
|
93
|
+
return this.api.get(SERVICES, {}, __bind(function(result) {
|
94
|
+
var row, _i, _len, _ref, _results;
|
95
|
+
_ref = result.rows;
|
96
|
+
_results = [];
|
97
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
98
|
+
row = _ref[_i];
|
99
|
+
_results.push(this.serviceNode(row));
|
100
|
+
}
|
101
|
+
return _results;
|
102
|
+
}, this));
|
103
|
+
};
|
104
|
+
ServicesPage.prototype.findMembers = function(id) {
|
105
|
+
return this.api.get(MEMBERS, {
|
106
|
+
id: id
|
107
|
+
}, __bind(function(result) {
|
108
|
+
var row, _i, _len, _ref, _results;
|
109
|
+
_ref = result.rows;
|
110
|
+
_results = [];
|
111
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
112
|
+
row = _ref[_i];
|
113
|
+
_results.push(this.drawMember(row));
|
114
|
+
}
|
115
|
+
return _results;
|
116
|
+
}, this));
|
117
|
+
};
|
118
|
+
ServicesPage.prototype.findUsers = function(syntax) {
|
119
|
+
return this.api.get(USERS, {}, __bind(function(result) {
|
120
|
+
var row;
|
121
|
+
this.users = (function() {
|
122
|
+
var _i, _len, _ref, _results;
|
123
|
+
_ref = result.rows;
|
124
|
+
_results = [];
|
125
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
126
|
+
row = _ref[_i];
|
127
|
+
if (!row.system) {
|
128
|
+
_results.push(row);
|
129
|
+
}
|
130
|
+
}
|
131
|
+
return _results;
|
132
|
+
})();
|
133
|
+
return this.drawUsers();
|
134
|
+
}, this));
|
135
|
+
};
|
136
|
+
ServicesPage.prototype.attributeNode = function(attribute) {
|
137
|
+
var node;
|
138
|
+
node = $('<li data-name=""></li>').appendTo('#attributes');
|
139
|
+
node.text(attribute);
|
140
|
+
node.attr('data-name', attribute);
|
141
|
+
return node.click(__bind(function(event) {
|
142
|
+
var name;
|
143
|
+
$('#syntax').focus();
|
144
|
+
name = $(event.currentTarget).attr('data-name');
|
145
|
+
$('#syntax').val($('#syntax').val() + (" " + name + " "));
|
146
|
+
return this.validateIn();
|
147
|
+
}, this));
|
148
|
+
};
|
149
|
+
ServicesPage.prototype.findAttributes = function(syntax) {
|
150
|
+
return this.api.get(ATTRS, {}, __bind(function(result) {
|
151
|
+
var row, _i, _len, _ref, _results;
|
152
|
+
_ref = result.rows;
|
153
|
+
_results = [];
|
154
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
155
|
+
row = _ref[_i];
|
156
|
+
_results.push(this.attributeNode(row));
|
157
|
+
}
|
158
|
+
return _results;
|
159
|
+
}, this));
|
160
|
+
};
|
161
|
+
ServicesPage.prototype.validateIn = function(millis) {
|
162
|
+
clearTimeout(this.validateTimeout);
|
163
|
+
return this.validateTimeout = setTimeout((__bind(function() {
|
164
|
+
return this.validate();
|
165
|
+
}, this)), millis || 500);
|
166
|
+
};
|
167
|
+
ServicesPage.prototype.validate = function() {
|
168
|
+
var code, prev;
|
169
|
+
$('#syntax-status').text('');
|
170
|
+
prev = $('#syntax').data('prev');
|
171
|
+
code = $.trim($('#syntax').val());
|
172
|
+
$('#syntax').data('prev', code);
|
173
|
+
if (!(code && code !== prev)) {
|
174
|
+
return;
|
175
|
+
}
|
176
|
+
$('#syntax-status').text('Searching . . .');
|
177
|
+
$('#members').empty();
|
178
|
+
return this.api.get2(MEMBERS, code, __bind(function(result) {
|
179
|
+
var row, _i, _len, _ref, _results;
|
180
|
+
if (result.ok) {
|
181
|
+
$('#syntax-status').text('');
|
182
|
+
_ref = result.rows;
|
183
|
+
_results = [];
|
184
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
185
|
+
row = _ref[_i];
|
186
|
+
_results.push(this.drawMember(row));
|
187
|
+
}
|
188
|
+
return _results;
|
189
|
+
} else {
|
190
|
+
return $('#syntax-status').text(result.error);
|
191
|
+
}
|
192
|
+
}, this));
|
193
|
+
};
|
194
|
+
ServicesPage.prototype.validateForm = function() {
|
195
|
+
var name, valid;
|
196
|
+
$('#name-error').empty();
|
197
|
+
valid = true;
|
198
|
+
name = $.trim($('#name').val());
|
199
|
+
if (name === '') {
|
200
|
+
$('#name-error').text('Name is required.');
|
201
|
+
valid = false;
|
202
|
+
}
|
203
|
+
return valid;
|
204
|
+
};
|
205
|
+
ServicesPage.prototype.save = function() {
|
206
|
+
var accounts, service, u, users;
|
207
|
+
if (!this.validateForm()) {
|
208
|
+
return;
|
209
|
+
}
|
210
|
+
users = $('#users :checked').map(function() {
|
211
|
+
return $(this).val();
|
212
|
+
}).get();
|
213
|
+
accounts = $('#unix-users').val().split(',');
|
214
|
+
accounts = (function() {
|
215
|
+
var _i, _len, _results;
|
216
|
+
_results = [];
|
217
|
+
for (_i = 0, _len = accounts.length; _i < _len; _i++) {
|
218
|
+
u = accounts[_i];
|
219
|
+
if ($.trim(u).length > 0) {
|
220
|
+
_results.push($.trim(u));
|
221
|
+
}
|
222
|
+
}
|
223
|
+
return _results;
|
224
|
+
})();
|
225
|
+
service = {
|
226
|
+
name: $('#name').val(),
|
227
|
+
code: $('#syntax').val(),
|
228
|
+
accounts: accounts,
|
229
|
+
users: users
|
230
|
+
};
|
231
|
+
if ($('#id').val().length > 0) {
|
232
|
+
service['id'] = $('#id').val();
|
233
|
+
}
|
234
|
+
this.api.save(SERVICES, service, __bind(function(result) {
|
235
|
+
var node;
|
236
|
+
new Notification('Service saved successfully');
|
237
|
+
result.size = $('#members').length;
|
238
|
+
$('#id').val(result.id);
|
239
|
+
node = $("#services li[data-id='" + result.id + "']");
|
240
|
+
if (node.length === 0) {
|
241
|
+
node = this.serviceNode(result);
|
242
|
+
return this.selectService(node);
|
243
|
+
} else {
|
244
|
+
return $('.text', node).text(result.name);
|
245
|
+
}
|
246
|
+
}, this));
|
247
|
+
return false;
|
248
|
+
};
|
249
|
+
ServicesPage.prototype.drawBlankSlate = function() {
|
250
|
+
$('#beta').empty();
|
251
|
+
$("<form id=\"blank-slate\">\n <p>\n Services are dynamically updated groups of systems based on\n criteria you define. Send a command to the service and it runs\n on every system in the group.\n </p>\n <input type=\"submit\" id=\"blank-slate-add\" value=\"Add Service\"/>\n</form>").appendTo('#beta');
|
252
|
+
if (!this.api.user.permissions.services) {
|
253
|
+
$('#blank-slate-add').remove();
|
254
|
+
}
|
255
|
+
return $('#blank-slate').submit(__bind(function() {
|
256
|
+
this.drawEditor();
|
257
|
+
return false;
|
258
|
+
}, this));
|
259
|
+
};
|
260
|
+
ServicesPage.prototype.draw = function() {
|
261
|
+
var fn;
|
262
|
+
if (!this.session.connected()) {
|
263
|
+
window.location.hash = '';
|
264
|
+
return;
|
265
|
+
}
|
266
|
+
$('body').attr('id', 'services-page');
|
267
|
+
$('#container').hide().empty();
|
268
|
+
$("<div id=\"alpha\" class=\"sidebar column y-fill\">\n <h2>Services <div id=\"search-services-icon\"></div></h2>\n <div id=\"search-services-form\"></div>\n <ul id=\"services\" class=\"selectable scroll y-fill\"></ul>\n <div id=\"alpha-controls\" class=\"controls\">\n <div id=\"add-service\"></div>\n <div id=\"remove-service\"></div>\n </div>\n <form id=\"remove-service-form\" class=\"overlay\" style=\"display:none;\">\n <h2>Remove Service</h2>\n <p id=\"remove-service-msg\">Select a service in the list above to remove.</p>\n <fieldset class=\"buttons\" style=\"display:none;\">\n <input id=\"remove-service-cancel\" type=\"button\" value=\"Cancel\"/>\n <input id=\"remove-service-ok\" type=\"submit\" value=\"Remove\"/>\n </fieldset>\n </form>\n</div>\n<div id=\"beta\" class=\"primary column x-fill y-fill\"></div>\n<div id=\"charlie\" class=\"sidebar column y-fill\">\n <h2>Operators</h2>\n <ul id=\"operators\"></ul>\n <h2>Attributes <div id=\"search-attributes-icon\"></div></h2>\n <div id=\"search-attributes-form\"></div>\n <ul id=\"attributes\" class=\"y-fill scroll\"></ul>\n</div>").appendTo('#container');
|
269
|
+
new Button('#add-service', ICONS.plus);
|
270
|
+
new Button('#remove-service', ICONS.minus);
|
271
|
+
if (!this.api.user.permissions.services) {
|
272
|
+
$('#alpha-controls div').remove();
|
273
|
+
}
|
274
|
+
this.drawBlankSlate();
|
275
|
+
$('#add-service').click(__bind(function() {
|
276
|
+
return this.drawEditor();
|
277
|
+
}, this));
|
278
|
+
$('#remove-service').click(__bind(function() {
|
279
|
+
return this.toggleForm('#remove-service-form');
|
280
|
+
}, this));
|
281
|
+
$('#remove-service-cancel').click(__bind(function() {
|
282
|
+
return this.toggleForm('#remove-service-form');
|
283
|
+
}, this));
|
284
|
+
$('#remove-service-form').submit(__bind(function() {
|
285
|
+
return this.deleteService();
|
286
|
+
}, this));
|
287
|
+
this.operators();
|
288
|
+
this.findServices();
|
289
|
+
this.findAttributes();
|
290
|
+
this.findUsers();
|
291
|
+
$('#container').show();
|
292
|
+
this.layout = this.resize();
|
293
|
+
fn = __bind(function() {
|
294
|
+
this.layout.resize();
|
295
|
+
return this.layout.resize();
|
296
|
+
}, this);
|
297
|
+
new Filter({
|
298
|
+
list: '#services',
|
299
|
+
icon: '#search-services-icon',
|
300
|
+
form: '#search-services-form',
|
301
|
+
attrs: ['data-name'],
|
302
|
+
open: fn,
|
303
|
+
close: fn
|
304
|
+
});
|
305
|
+
return new Filter({
|
306
|
+
list: '#attributes',
|
307
|
+
icon: '#search-attributes-icon',
|
308
|
+
form: '#search-attributes-form',
|
309
|
+
attrs: ['data-name'],
|
310
|
+
open: fn,
|
311
|
+
close: fn
|
312
|
+
});
|
313
|
+
};
|
314
|
+
ServicesPage.prototype.drawEditor = function(service) {
|
315
|
+
if (!this.pageVisible()) {
|
316
|
+
return;
|
317
|
+
}
|
318
|
+
if (!service) {
|
319
|
+
this.selectedService = null;
|
320
|
+
$('#services li').removeClass('selected');
|
321
|
+
}
|
322
|
+
$('#beta').empty();
|
323
|
+
$("<form id=\"editor-form\" class=\"sections y-fill scroll\">\n <input id=\"id\" type=\"hidden\"/>\n <div>\n <section>\n <h2>Service</h2>\n <fieldset>\n <label for=\"name\">Name</label>\n <input id=\"name\" type=\"text\"/>\n <p id=\"name-error\" class=\"error\"></p>\n <label for=\"syntax\">Criteria</label>\n <textarea id=\"syntax\" placeholder=\"fqdn starts with 'www.' and platform is 'mac_os_x'\"></textarea>\n <p id=\"syntax-status\"></p>\n </fieldset>\n </section>\n <section>\n <h2>Members</h2>\n <fieldset id=\"service-preview\">\n <ul id=\"members\" class=\"scroll\"></ul>\n </fieldset>\n </section>\n <section>\n <h2>Permissions</h2>\n <fieldset>\n <label>Users</label>\n <ul id=\"users\" class=\"scroll\"></ul>\n <label for=\"unix-users\">Unix Accounts</label>\n <input id=\"unix-users\" type=\"text\"/>\n </fieldset>\n </section>\n </div>\n</form>\n<form id=\"editor-buttons\">\n <input id=\"save\" type=\"submit\" value=\"Save\"/>\n</form>").appendTo('#beta');
|
324
|
+
if (service) {
|
325
|
+
this.findMembers(service.id);
|
326
|
+
$('#id').val(service.id);
|
327
|
+
$('#name').val(service.name);
|
328
|
+
$('#syntax').val(service.code);
|
329
|
+
$('#unix-users').val(service.accounts.join(', '));
|
330
|
+
}
|
331
|
+
if (this.users.length > 0) {
|
332
|
+
this.drawUsers();
|
333
|
+
}
|
334
|
+
this.layout.resize();
|
335
|
+
$('#name').focus();
|
336
|
+
$('#syntax').change(__bind(function() {
|
337
|
+
return this.validateIn();
|
338
|
+
}, this));
|
339
|
+
$('#syntax').keyup(__bind(function() {
|
340
|
+
return this.validateIn();
|
341
|
+
}, this));
|
342
|
+
$('#editor-form').submit(__bind(function() {
|
343
|
+
return this.save();
|
344
|
+
}, this));
|
345
|
+
return $('#editor-buttons').submit(__bind(function() {
|
346
|
+
return this.save();
|
347
|
+
}, this));
|
348
|
+
};
|
349
|
+
ServicesPage.prototype.drawUsers = function() {
|
350
|
+
var node, user, _i, _len, _ref;
|
351
|
+
if (!this.editorVisible()) {
|
352
|
+
return;
|
353
|
+
}
|
354
|
+
$('#users').empty();
|
355
|
+
_ref = this.users;
|
356
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
357
|
+
user = _ref[_i];
|
358
|
+
node = $("<li>\n <input id='user-" + user.jid + "' type='checkbox' value='" + user.jid + "'/>\n <label for='user-" + user.jid + "'>" + user.jid + "</label>\n</li>").appendTo('#users');
|
359
|
+
if (user.jid === this.session.bareJid()) {
|
360
|
+
$('input', node).prop('checked', true);
|
361
|
+
}
|
362
|
+
}
|
363
|
+
if (this.selectedService) {
|
364
|
+
return $('#users input[type="checkbox"]').val(this.selectedService.users);
|
365
|
+
}
|
366
|
+
};
|
367
|
+
ServicesPage.prototype.toggleForm = function(form, fn) {
|
368
|
+
form = $(form);
|
369
|
+
$('form.overlay').each(function() {
|
370
|
+
if (this.id !== form.attr('id')) {
|
371
|
+
return $(this).hide();
|
372
|
+
}
|
373
|
+
});
|
374
|
+
if (form.is(':hidden')) {
|
375
|
+
if (fn) {
|
376
|
+
fn();
|
377
|
+
}
|
378
|
+
return form.fadeIn(100);
|
379
|
+
} else {
|
380
|
+
return form.fadeOut(100, function() {
|
381
|
+
form[0].reset();
|
382
|
+
if (fn) {
|
383
|
+
return fn();
|
384
|
+
}
|
385
|
+
});
|
386
|
+
}
|
387
|
+
};
|
388
|
+
ServicesPage.prototype.pageVisible = function() {
|
389
|
+
return $('#services-page').length > 0;
|
390
|
+
};
|
391
|
+
ServicesPage.prototype.editorVisible = function() {
|
392
|
+
return $('#services-page #editor-form').length > 0;
|
393
|
+
};
|
394
|
+
ServicesPage.prototype.resize = function() {
|
395
|
+
var a, b, c;
|
396
|
+
a = $('#alpha');
|
397
|
+
b = $('#beta');
|
398
|
+
c = $('#charlie');
|
399
|
+
return new Layout(function() {
|
400
|
+
return c.css('left', a.width() + b.width());
|
401
|
+
});
|
402
|
+
};
|
403
|
+
return ServicesPage;
|
404
|
+
})();
|