vines-services 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. data/LICENSE +19 -0
  2. data/README +40 -0
  3. data/Rakefile +130 -0
  4. data/bin/vines-services +95 -0
  5. data/conf/config.rb +25 -0
  6. data/lib/vines/services/command/init.rb +209 -0
  7. data/lib/vines/services/command/restart.rb +14 -0
  8. data/lib/vines/services/command/start.rb +30 -0
  9. data/lib/vines/services/command/stop.rb +20 -0
  10. data/lib/vines/services/command/views.rb +26 -0
  11. data/lib/vines/services/component.rb +26 -0
  12. data/lib/vines/services/config.rb +105 -0
  13. data/lib/vines/services/connection.rb +120 -0
  14. data/lib/vines/services/controller/attributes_controller.rb +19 -0
  15. data/lib/vines/services/controller/base_controller.rb +99 -0
  16. data/lib/vines/services/controller/disco_info_controller.rb +61 -0
  17. data/lib/vines/services/controller/labels_controller.rb +17 -0
  18. data/lib/vines/services/controller/members_controller.rb +44 -0
  19. data/lib/vines/services/controller/messages_controller.rb +66 -0
  20. data/lib/vines/services/controller/probes_controller.rb +45 -0
  21. data/lib/vines/services/controller/services_controller.rb +81 -0
  22. data/lib/vines/services/controller/subscriptions_controller.rb +39 -0
  23. data/lib/vines/services/controller/systems_controller.rb +45 -0
  24. data/lib/vines/services/controller/transfers_controller.rb +58 -0
  25. data/lib/vines/services/controller/uploads_controller.rb +62 -0
  26. data/lib/vines/services/controller/users_controller.rb +127 -0
  27. data/lib/vines/services/core_ext/blather.rb +46 -0
  28. data/lib/vines/services/core_ext/couchrest.rb +33 -0
  29. data/lib/vines/services/indexer.rb +195 -0
  30. data/lib/vines/services/priority_queue.rb +94 -0
  31. data/lib/vines/services/roster.rb +70 -0
  32. data/lib/vines/services/storage/couchdb/fragment.rb +23 -0
  33. data/lib/vines/services/storage/couchdb/service.rb +170 -0
  34. data/lib/vines/services/storage/couchdb/system.rb +141 -0
  35. data/lib/vines/services/storage/couchdb/upload.rb +66 -0
  36. data/lib/vines/services/storage/couchdb/user.rb +137 -0
  37. data/lib/vines/services/storage/couchdb/vcard.rb +13 -0
  38. data/lib/vines/services/storage/couchdb.rb +157 -0
  39. data/lib/vines/services/storage.rb +33 -0
  40. data/lib/vines/services/throttle.rb +26 -0
  41. data/lib/vines/services/version.rb +7 -0
  42. data/lib/vines/services/vql/compiler.rb +94 -0
  43. data/lib/vines/services/vql/vql.citrus +115 -0
  44. data/lib/vines/services/vql/vql.rb +186 -0
  45. data/lib/vines/services.rb +71 -0
  46. data/test/config_test.rb +242 -0
  47. data/test/priority_queue_test.rb +23 -0
  48. data/test/storage/couchdb_test.rb +30 -0
  49. data/test/vql/compiler_test.rb +96 -0
  50. data/test/vql/vql_test.rb +233 -0
  51. data/web/coffeescripts/api.coffee +51 -0
  52. data/web/coffeescripts/commands.coffee +18 -0
  53. data/web/coffeescripts/files.coffee +315 -0
  54. data/web/coffeescripts/init.coffee +21 -0
  55. data/web/coffeescripts/services.coffee +356 -0
  56. data/web/coffeescripts/setup.coffee +503 -0
  57. data/web/coffeescripts/systems.coffee +371 -0
  58. data/web/images/default-service.png +0 -0
  59. data/web/images/linux.png +0 -0
  60. data/web/images/mac.png +0 -0
  61. data/web/images/run.png +0 -0
  62. data/web/images/windows.png +0 -0
  63. data/web/index.html +17 -0
  64. data/web/stylesheets/common.css +52 -0
  65. data/web/stylesheets/files.css +218 -0
  66. data/web/stylesheets/services.css +181 -0
  67. data/web/stylesheets/setup.css +117 -0
  68. data/web/stylesheets/systems.css +142 -0
  69. metadata +230 -0
@@ -0,0 +1,503 @@
1
+ class SetupPage
2
+ SERVICES = 'http://getvines.com/protocol/services'
3
+ SYSTEMS = 'http://getvines.com/protocol/systems'
4
+ USERS = 'http://getvines.com/protocol/users'
5
+
6
+ constructor: (@session) ->
7
+ @api = new Api @session
8
+ @layout = null
9
+ @selected = null
10
+ @services = []
11
+ @users = []
12
+
13
+ findSystem: (name) ->
14
+ @api.get SYSTEMS, {name: name}, (result) =>
15
+ this.drawSystemInfo(result)
16
+
17
+ findServices: ->
18
+ @api.get SERVICES, {}, (result) =>
19
+ @services = result.rows
20
+ this.drawServices()
21
+
22
+ drawServices: ->
23
+ return if $('#setup-page #services').length == 0
24
+ $('#services').empty()
25
+ for service in @services
26
+ node = $("""
27
+ <li>
28
+ <input id='service-#{service.id}' type='checkbox' value='#{service.id}'/>
29
+ <label for='service-#{service.id}'></label>
30
+ </li>
31
+ """).appendTo '#services'
32
+ $('label', node).text service.name
33
+ $('#services input[type="checkbox"]').val @selected.services if @selected
34
+ if @selected && !@selected.permissions['services']
35
+ $('#services input[type="checkbox"]').prop 'disabled', true
36
+
37
+ findUsers: ->
38
+ @api.get USERS, {}, (result) =>
39
+ @users = result.rows
40
+ this.drawUsers()
41
+
42
+ drawUsers: ->
43
+ return if $('#setup-page #users').length == 0
44
+ $('#users').empty()
45
+ systems = $('#systems-nav').hasClass 'selected'
46
+ for user in @users
47
+ this.userNode(user) if user.system == systems
48
+
49
+ userNode: (user) ->
50
+ node = $("""
51
+ <li data-name="" data-jid="" id="#{user.jid}">
52
+ <span class="text"></span>
53
+ <span class="jid"></span>
54
+ </li>
55
+ """).appendTo '#users'
56
+
57
+ name = this.userName(user)
58
+ $('.text', node).text name
59
+ $('.jid', node).text user.jid
60
+ node.attr 'data-name', name
61
+ node.attr 'data-jid', user.jid
62
+ node.click (event) => this.selectUser event.currentTarget
63
+ node
64
+
65
+ userName: (user) ->
66
+ user.name || user.jid.split('@')[0]
67
+
68
+ selectUser: (node) ->
69
+ jid = $(node).attr 'data-jid'
70
+ name = $(node).attr 'data-name'
71
+
72
+ $('#users li').removeClass 'selected'
73
+ $(node).addClass 'selected'
74
+
75
+ $('#remove-user-msg').html "Are you sure you want to remove " +
76
+ "<strong>#{name}</strong>?"
77
+ $('#remove-user-form .buttons').fadeIn 200
78
+ @api.get USERS, jid: jid, (result) =>
79
+ @selected = result
80
+ if result.system
81
+ this.drawSystemEditor result
82
+ else
83
+ this.drawUserEditor result
84
+
85
+ removeUser: ->
86
+ this.toggleForm '#remove-user-form'
87
+ selected = $("#users li[data-jid='#{@selected.jid}']")
88
+ @api.remove USERS, @selected.jid, (result) =>
89
+ selected.fadeOut 200, =>
90
+ @users = (u for u in @users when u.jid != @selected.jid)
91
+ selected.remove()
92
+ @selected = null
93
+ if $('#users-nav').hasClass 'selected'
94
+ this.drawUserBlankSlate()
95
+ else
96
+ this.drawSystemBlankSlate()
97
+ false
98
+
99
+ selectTask: (event) ->
100
+ @selected = null
101
+ $('#setup li').removeClass 'selected secondary'
102
+ $(event.currentTarget).addClass 'selected secondary'
103
+ switch $(event.currentTarget).attr('id')
104
+ when 'users-nav'
105
+ $('#beta-header').text 'Users'
106
+ this.drawUsers()
107
+ this.drawUserBlankSlate()
108
+ when 'systems-nav'
109
+ $('#beta-header').text 'Systems'
110
+ this.drawUsers()
111
+ this.drawSystemBlankSlate()
112
+
113
+ toggleForm: (form, fn) ->
114
+ form = $(form)
115
+ $('form.overlay').each ->
116
+ $(this).hide() unless this.id == form.attr 'id'
117
+ if form.is ':hidden'
118
+ fn() if fn
119
+ form.fadeIn 100
120
+ else
121
+ form.fadeOut 100, ->
122
+ form[0].reset()
123
+ fn() if fn
124
+
125
+ validateUser: ->
126
+ $('#user-name-error').empty()
127
+ $('#password-error').empty()
128
+ valid = true
129
+
130
+ node = $.trim $('#user-name').val()
131
+ password1 = $.trim $('#password1').val()
132
+ password2 = $.trim $('#password2').val()
133
+
134
+ if @selected # exisiting user
135
+ if password2.length > 0 && password2.length < 8
136
+ $('#password-error').text 'Password must be at least 8 characters.'
137
+ valid = false
138
+
139
+ else # new user
140
+ if node == ''
141
+ $('#user-name-error').text 'User name is required.'
142
+ valid = false
143
+
144
+ if node.match /[\s"&'\/:<>@]/
145
+ $('#user-name-error').text 'User name contains forbidden characters.'
146
+ valid = false
147
+
148
+ if password1.length == 0 || password2.length == 0
149
+ $('#password-error').text 'Password is required.'
150
+ valid = false
151
+
152
+ if password1 != password2
153
+ $('#password-error').text 'Passwords must match.'
154
+ valid = false
155
+
156
+ if password2.length < 8
157
+ $('#password-error').text 'Password must be at least 8 characters.'
158
+ valid = false
159
+
160
+ valid
161
+
162
+ saveUser: ->
163
+ return unless this.validateUser()
164
+ user =
165
+ jid: $('#jid').val()
166
+ username: $('#user-name').val()
167
+ name: $('#name').val()
168
+ password1: $('#password1').val()
169
+ password2: $('#password2').val()
170
+ services: $('#services :checked').map(-> $(this).val()).get()
171
+ permissions:
172
+ systems: $('#perm-systems').prop('checked')
173
+ services: $('#perm-services').prop('checked')
174
+ files: $('#perm-files').prop('checked')
175
+ users: $('#perm-users').prop('checked')
176
+
177
+ @api.save USERS, user, (result) =>
178
+ new Notification 'User saved successfully'
179
+ $('#jid').val result.jid
180
+ node = $("#users li[data-jid='#{result.jid}']")
181
+ if node.length == 0
182
+ @users.push result
183
+ node = this.userNode result
184
+ this.selectUser node
185
+ else
186
+ $('.text', node).text this.userName(result)
187
+ false
188
+
189
+ validateSystem: ->
190
+ $('#user-name-error').empty()
191
+ valid = true
192
+ node = $.trim $('#user-name').val()
193
+ unless @selected # new user
194
+ if node == ''
195
+ $('#user-name-error').text 'Hostname is required.'
196
+ valid = false
197
+
198
+ if node.match /[\s"&'\/:<>@]/
199
+ $('#user-name-error').text 'Hostname contains forbidden characters.'
200
+ valid = false
201
+ valid
202
+
203
+ saveSystem: ->
204
+ return unless this.validateSystem()
205
+ user =
206
+ jid: $('#jid').val()
207
+ username: $('#user-name').val()
208
+ password1: $('#password1').val()
209
+ password2: $('#password1').val()
210
+ system: true
211
+
212
+ @api.save USERS, user, (result) =>
213
+ new Notification 'System saved successfully'
214
+ $('#jid').val result.jid
215
+ node = $("#users li[data-jid='#{result.jid}']")
216
+ if node.length == 0
217
+ @users.push result
218
+ node = this.userNode result
219
+ this.selectUser node
220
+ else
221
+ $('.text', node).text result.name
222
+ false
223
+
224
+ rand: ->
225
+ Math.floor(Math.random() * 16)
226
+
227
+ token: ->
228
+ (this.rand().toString(16) for i in [0..127]).join('')
229
+
230
+ drawUserBlankSlate: ->
231
+ $('#charlie').empty()
232
+ $("""
233
+ <form id="blank-slate">
234
+ <p>
235
+ Select a user account to edit or add a new user.
236
+ </p>
237
+ <input type="submit" id="blank-slate-add" value="Add User"/>
238
+ </form>
239
+ """).appendTo '#charlie'
240
+ $('#blank-slate').submit =>
241
+ this.drawUserEditor()
242
+ false
243
+
244
+ drawSystemBlankSlate: ->
245
+ $('#charlie').empty()
246
+ $("""
247
+ <form id="blank-slate">
248
+ <p>
249
+ Systems need a user account before they can connect and
250
+ authenticate with the chat server.
251
+ </p>
252
+ <input type="submit" id="blank-slate-add" value="Add System"/>
253
+ </form>
254
+ """).appendTo '#charlie'
255
+ $('#blank-slate').submit =>
256
+ this.drawSystemEditor()
257
+ false
258
+
259
+ draw: ->
260
+ unless @session.connected()
261
+ window.location.hash = ''
262
+ return
263
+
264
+ $('body').attr 'id', 'setup-page'
265
+ $('#container').hide().empty()
266
+ $("""
267
+ <div id="alpha" class="sidebar column y-fill">
268
+ <h2>Setup</h2>
269
+ <ul id="setup" class="selectable scroll y-fill">
270
+ <li id="users-nav" class='selected secondary'>
271
+ <span class="text">Users</span>
272
+ </li>
273
+ <li id="systems-nav">
274
+ <span class="text">Systems</span>
275
+ </li>
276
+ </ul>
277
+ <div id="alpha-controls" class="controls"></div>
278
+ </div>
279
+ <div id="beta" class="sidebar column y-fill">
280
+ <h2><span id="beta-header">Users</span> <div id="search-users-icon"></div></h2>
281
+ <div id="search-users-form"></div>
282
+ <ul id="users" class="selectable scroll y-fill"></ul>
283
+ <form id="remove-user-form" class="overlay" style="display:none;">
284
+ <h2>Remove User</h2>
285
+ <p id="remove-user-msg">Select a user to delete.</p>
286
+ <fieldset class="buttons" style="display:none;">
287
+ <input id="remove-user-cancel" type="button" value="Cancel"/>
288
+ <input id="remove-user-ok" type="submit" value="Remove"/>
289
+ </fieldset>
290
+ </form>
291
+ <div id="beta-controls" class="controls">
292
+ <div id="add-user"></div>
293
+ <div id="remove-user"></div>
294
+ </div>
295
+ </div>
296
+ <div id="charlie" class="primary column x-fill y-fill"></div>
297
+ """).appendTo '#container'
298
+ this.drawUserBlankSlate()
299
+
300
+ $('#setup li').click (event) => this.selectTask event
301
+
302
+ this.findUsers()
303
+ this.findServices()
304
+
305
+ $('#container').show()
306
+ @layout = this.resize()
307
+
308
+ new Button '#add-user', ICONS.plus
309
+ new Button '#remove-user', ICONS.minus
310
+
311
+ $('#add-user').click =>
312
+ if $('#users-nav').hasClass 'selected'
313
+ this.drawUserEditor()
314
+ else
315
+ this.drawSystemEditor()
316
+
317
+ $('#remove-user').click => this.toggleForm '#remove-user-form'
318
+ $('#remove-user-cancel').click => this.toggleForm '#remove-user-form'
319
+ $('#remove-user-form').submit => this.removeUser()
320
+
321
+ fn = =>
322
+ @layout.resize()
323
+ @layout.resize() # not sure why two are needed
324
+
325
+ new Filter
326
+ list: '#users'
327
+ icon: '#search-users-icon'
328
+ form: '#search-users-form'
329
+ attrs: ['data-jid']
330
+ open: fn
331
+ close: fn
332
+
333
+ drawUserEditor: (user) ->
334
+ unless user
335
+ @selected = null
336
+ $('#users li').removeClass 'selected'
337
+
338
+ $('#charlie').empty()
339
+ $("""
340
+ <form id="editor-form" class="sections y-fill scroll">
341
+ <div>
342
+ <section>
343
+ <h2>User</h2>
344
+ <fieldset id="jid-fields">
345
+ <input id="jid" type="hidden" value=""/>
346
+ <label for="name">Real Name</label>
347
+ <input id="name" type="text" maxlength="1024"/>
348
+ </fieldset>
349
+ </section>
350
+ <section>
351
+ <h2>Password</h2>
352
+ <fieldset>
353
+ <label id="password1-label" for="password1">Current Password</label>
354
+ <input id="password1" type="password" maxlength="1024"/>
355
+ <label id="password2-label" for="password2">New Password</label>
356
+ <input id="password2" type="password" maxlength="1024"/>
357
+ <p id="password-error" class="error"></p>
358
+ </fieldset>
359
+ </section>
360
+ <section>
361
+ <h2>Permissions</h2>
362
+ <fieldset>
363
+ <label>Manage</label>
364
+ <ul id="permissions">
365
+ <li>
366
+ <input id="perm-systems" type="checkbox" value="systems"/>
367
+ <label for="perm-systems">Systems</label>
368
+ </li>
369
+ <li>
370
+ <input id="perm-services" type="checkbox" value="services"/>
371
+ <label for="perm-services">Services</label>
372
+ </li>
373
+ <li>
374
+ <input id="perm-users" type="checkbox" value="users"/>
375
+ <label for="perm-users">Users</label>
376
+ </li>
377
+ <li>
378
+ <input id="perm-files" type="checkbox" value="files"/>
379
+ <label for="perm-files">Files</label>
380
+ </li>
381
+ </ul>
382
+ </fieldset>
383
+ </section>
384
+ <section>
385
+ <h2>Services</h2>
386
+ <fieldset>
387
+ <label>Access To</label>
388
+ <ul id="services" class="scroll"></ul>
389
+ </fieldset>
390
+ </section>
391
+ </div>
392
+ </form>
393
+ <form id="editor-buttons">
394
+ <input id="save" type="submit" value="Save"/>
395
+ </form>
396
+ """).appendTo '#charlie'
397
+
398
+ if user
399
+ $("""
400
+ <label>Account Name</label>
401
+ <p>#{user.jid}</p>
402
+ """).prependTo '#jid-fields'
403
+
404
+ $('#name').focus()
405
+
406
+ $('#jid').val user.jid
407
+ $('#name').val user.name
408
+ $('#user-name').val user.jid.split('@')[0]
409
+ for name in 'services systems files users'.split(' ')
410
+ $("#perm-#{name}").prop('checked', true) if user.permissions[name]
411
+ $("#perm-#{name}").prop('disabled', true) if @session.bareJid() == user.jid
412
+ else
413
+ $("""
414
+ <label for="user-name">User Name</label>
415
+ <input id="user-name" type="text" maxlength="1023"/>
416
+ <p id="user-name-error" class="error"></p>
417
+ """).prependTo '#jid-fields'
418
+ $('#password1-label').text 'Password'
419
+ $('#password2-label').text 'Password Again'
420
+ $('#user-name').focus()
421
+
422
+ this.drawServices() if @services.length > 0
423
+
424
+ @layout.resize()
425
+ $('#editor-form').submit => this.saveUser()
426
+ $('#editor-buttons').submit => this.saveUser()
427
+
428
+ drawSystemEditor: (user) ->
429
+ unless user
430
+ @selected = null
431
+ $('#users li').removeClass 'selected'
432
+
433
+ $('#charlie').empty()
434
+ $("""
435
+ <form id="editor-form" class="sections y-fill scroll">
436
+ <div>
437
+ <section>
438
+ <h2>System</h2>
439
+ <fieldset id="jid-fields">
440
+ <input id="jid" type="hidden" value=""/>
441
+ <label id="password1-label" for="password1">Authentication Token</label>
442
+ <div id="token-container">
443
+ <input id="password1" type="text" readonly placeholder="Press Generate to create a new token"/>
444
+ <input id="new-token" type="button" value="Generate"/>
445
+ </div>
446
+ </fieldset>
447
+ </section>
448
+ <section id="info">
449
+ <h2>Info</h2>
450
+ <fieldset>
451
+ <label>Platform</label>
452
+ <p id="info-platform">-</p>
453
+ <label>Hostname</label>
454
+ <p id="info-fqdn">-</p>
455
+ <label>IP Address</label>
456
+ <p id="info-ip">-</p>
457
+ <label>MAC Address</label>
458
+ <p id="info-mac">-</p>
459
+ </fieldset>
460
+ </section>
461
+ </div>
462
+ </form>
463
+ <form id="editor-buttons">
464
+ <input id="save" type="submit" value="Save"/>
465
+ </form>
466
+ """).appendTo '#charlie'
467
+
468
+ $('#new-token').click => $('#password1').val this.token()
469
+
470
+ this.findSystem(user.jid.split('@')[0]) if user
471
+
472
+ if user
473
+ $("""
474
+ <label>Account Name</label>
475
+ <p>#{user.jid}</p>
476
+ """).prependTo '#jid-fields'
477
+ $('#jid').val user.jid
478
+ $('#user-name').val user.jid.split('@')[0]
479
+ else
480
+ $("""
481
+ <label for="user-name">Hostname</label>
482
+ <input id="user-name" type="text" maxlength="1023"/>
483
+ <p id="user-name-error" class="error"></p>
484
+ """).prependTo '#jid-fields'
485
+ $('#user-name').focus()
486
+ $('#password1').val this.token()
487
+
488
+ @layout.resize()
489
+ $('#editor-form').submit => this.saveSystem()
490
+ $('#editor-buttons').submit => this.saveSystem()
491
+
492
+ drawSystemInfo: (system) ->
493
+ $('#info-platform').text system.platform
494
+ $('#info-fqdn').text system.fqdn
495
+ $('#info-ip').text system.ipaddress
496
+ $('#info-mac').text system.macaddress
497
+
498
+ resize: ->
499
+ a = $ '#alpha'
500
+ b = $ '#beta'
501
+ c = $ '#charlie'
502
+ new Layout ->
503
+ c.css 'left', a.outerWidth() + b.outerWidth()