cyclid-ui 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 (98) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +178 -0
  3. data/README.md +20 -0
  4. data/app/cyclid_ui.rb +124 -0
  5. data/app/cyclid_ui/config.rb +64 -0
  6. data/app/cyclid_ui/controllers/auth.rb +102 -0
  7. data/app/cyclid_ui/controllers/base.rb +41 -0
  8. data/app/cyclid_ui/controllers/health.rb +96 -0
  9. data/app/cyclid_ui/controllers/organization.rb +64 -0
  10. data/app/cyclid_ui/controllers/user.rb +40 -0
  11. data/app/cyclid_ui/helpers.rb +55 -0
  12. data/app/cyclid_ui/memcache.rb +45 -0
  13. data/app/cyclid_ui/models/user.rb +91 -0
  14. data/app/cyclid_ui/templates/footer.mustache +8 -0
  15. data/app/cyclid_ui/templates/job.mustache +21 -0
  16. data/app/cyclid_ui/templates/job_info.mustache +40 -0
  17. data/app/cyclid_ui/templates/layout.mustache +102 -0
  18. data/app/cyclid_ui/templates/login.mustache +78 -0
  19. data/app/cyclid_ui/templates/organization.mustache +150 -0
  20. data/app/cyclid_ui/templates/user.mustache +97 -0
  21. data/app/cyclid_ui/views/job.rb +25 -0
  22. data/app/cyclid_ui/views/layout.rb +52 -0
  23. data/app/cyclid_ui/views/login.rb +25 -0
  24. data/app/cyclid_ui/views/organization.rb +25 -0
  25. data/app/cyclid_ui/views/user.rb +25 -0
  26. data/bin/cyclid-ui-assets +17 -0
  27. data/lib/cyclid_ui/app.rb +4 -0
  28. data/public/images/LICENSE +3 -0
  29. data/public/images/cyclid-logo-large.png +0 -0
  30. data/public/images/favicon16.png +0 -0
  31. data/public/images/favicon32.png +0 -0
  32. data/public/images/favicon48.png +0 -0
  33. data/public/images/favicon64.png +0 -0
  34. data/public/images/favicon96.png +0 -0
  35. data/public/js/api.js +34 -0
  36. data/public/js/cyclid.js +32 -0
  37. data/public/js/job.js +215 -0
  38. data/public/js/organization.js +345 -0
  39. data/public/js/user.js +145 -0
  40. data/public/vendor/bootstrap/css/bootstrap-theme.css +587 -0
  41. data/public/vendor/bootstrap/css/bootstrap-theme.css.map +1 -0
  42. data/public/vendor/bootstrap/css/bootstrap-theme.min.css +6 -0
  43. data/public/vendor/bootstrap/css/bootstrap-theme.min.css.map +1 -0
  44. data/public/vendor/bootstrap/css/bootstrap.css +6760 -0
  45. data/public/vendor/bootstrap/css/bootstrap.css.map +1 -0
  46. data/public/vendor/bootstrap/css/bootstrap.min.css +6 -0
  47. data/public/vendor/bootstrap/css/bootstrap.min.css.map +1 -0
  48. data/public/vendor/bootstrap/css/custom.css +193 -0
  49. data/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  50. data/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
  51. data/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  52. data/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  53. data/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
  54. data/public/vendor/bootstrap/js/bootstrap.js +2363 -0
  55. data/public/vendor/bootstrap/js/bootstrap.min.js +7 -0
  56. data/public/vendor/bootstrap/js/npm.js +13 -0
  57. data/public/vendor/font-awesome/HELP-US-OUT.txt +7 -0
  58. data/public/vendor/font-awesome/css/font-awesome.css +2199 -0
  59. data/public/vendor/font-awesome/css/font-awesome.min.css +4 -0
  60. data/public/vendor/font-awesome/fonts/FontAwesome.otf +0 -0
  61. data/public/vendor/font-awesome/fonts/fontawesome-webfont.eot +0 -0
  62. data/public/vendor/font-awesome/fonts/fontawesome-webfont.svg +685 -0
  63. data/public/vendor/font-awesome/fonts/fontawesome-webfont.ttf +0 -0
  64. data/public/vendor/font-awesome/fonts/fontawesome-webfont.woff +0 -0
  65. data/public/vendor/font-awesome/fonts/fontawesome-webfont.woff2 +0 -0
  66. data/public/vendor/font-awesome/less/animated.less +34 -0
  67. data/public/vendor/font-awesome/less/bordered-pulled.less +25 -0
  68. data/public/vendor/font-awesome/less/core.less +12 -0
  69. data/public/vendor/font-awesome/less/fixed-width.less +6 -0
  70. data/public/vendor/font-awesome/less/font-awesome.less +18 -0
  71. data/public/vendor/font-awesome/less/icons.less +733 -0
  72. data/public/vendor/font-awesome/less/larger.less +13 -0
  73. data/public/vendor/font-awesome/less/list.less +19 -0
  74. data/public/vendor/font-awesome/less/mixins.less +60 -0
  75. data/public/vendor/font-awesome/less/path.less +15 -0
  76. data/public/vendor/font-awesome/less/rotated-flipped.less +20 -0
  77. data/public/vendor/font-awesome/less/screen-reader.less +5 -0
  78. data/public/vendor/font-awesome/less/stacked.less +20 -0
  79. data/public/vendor/font-awesome/less/variables.less +744 -0
  80. data/public/vendor/font-awesome/scss/_animated.scss +34 -0
  81. data/public/vendor/font-awesome/scss/_bordered-pulled.scss +25 -0
  82. data/public/vendor/font-awesome/scss/_core.scss +12 -0
  83. data/public/vendor/font-awesome/scss/_fixed-width.scss +6 -0
  84. data/public/vendor/font-awesome/scss/_icons.scss +733 -0
  85. data/public/vendor/font-awesome/scss/_larger.scss +13 -0
  86. data/public/vendor/font-awesome/scss/_list.scss +19 -0
  87. data/public/vendor/font-awesome/scss/_mixins.scss +60 -0
  88. data/public/vendor/font-awesome/scss/_path.scss +15 -0
  89. data/public/vendor/font-awesome/scss/_rotated-flipped.scss +20 -0
  90. data/public/vendor/font-awesome/scss/_screen-reader.scss +5 -0
  91. data/public/vendor/font-awesome/scss/_stacked.scss +20 -0
  92. data/public/vendor/font-awesome/scss/_variables.scss +744 -0
  93. data/public/vendor/font-awesome/scss/font-awesome.scss +18 -0
  94. data/public/vendor/jquery-2.2.4.min.js +4 -0
  95. data/public/vendor/jquery.md5.js +269 -0
  96. data/public/vendor/js.cookie.js +151 -0
  97. data/public/vendor/mustache.min.js +1 -0
  98. metadata +322 -0
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2016 Liqwyd Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'sinatra/base'
17
+ require 'sinatra/cookies'
18
+ require 'sinatra/flash'
19
+ require 'mustache'
20
+ require 'mustache/sinatra'
21
+
22
+ module Cyclid
23
+ module UI
24
+ module Controllers
25
+ # Base class for all Sinatra controllers
26
+ class Base < Sinatra::Base
27
+ register Mustache::Sinatra
28
+
29
+ set :mustache, templates: File.expand_path('../../templates/', __FILE__),
30
+
31
+ views: File.expand_path('../../views/', __FILE__),
32
+
33
+ namespace: Cyclid::UI
34
+
35
+ register Sinatra::Flash
36
+
37
+ helpers Sinatra::Cookies, Helpers, AuthHelpers
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2016 Liqwyd Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ require 'sinatra-health-check'
16
+ require 'memcached'
17
+
18
+ # Top level module for all of the core Cyclid code.
19
+ module Cyclid
20
+ module UI
21
+ module Controllers
22
+ # Controller for all Health related API endpoints
23
+ class Health < Base
24
+ def initialize(_app)
25
+ super
26
+ @checker = SinatraHealthCheck::Checker.new(logger: Cyclid.logger,
27
+ timeout: 0)
28
+
29
+ # Add internal health checks
30
+ @checker.systems[:memcache] = Cyclid::UI::Health::Memcache
31
+ @checker.systems[:api] = Cyclid::UI::Health::API
32
+ end
33
+
34
+ # Return either 200 (healthy) or 503 (unhealthy) based on the status of
35
+ # the healthchecks. This is intended to be used by things like load
36
+ # balancers and active monitors.
37
+ get '/health/status' do
38
+ @checker.healthy? ? 200 : 503
39
+ end
40
+
41
+ # Return verbose information on the status of the individual checks;
42
+ # note that this method always returns 200 with a message body, so it is
43
+ # not suitable for general health checks unless the caller intends to
44
+ # parse the message body for the health status.
45
+ get '/health/info' do
46
+ @checker.status.to_json
47
+ end
48
+ end
49
+ end
50
+
51
+ # Healthchecks
52
+ module Health
53
+ # Internal Memcache connection health check
54
+ module Memcache
55
+ # Check if Memcache is available
56
+ def self.status
57
+ connected = begin
58
+ memcache = Memcached.new(Cyclid.config.memcached)
59
+ memcache.stats
60
+ true
61
+ rescue Memcached::SomeErrorsWereReported
62
+ false
63
+ end
64
+
65
+ if connected
66
+ SinatraHealthCheck::Status.new(:ok, 'memcache connection is okay')
67
+ else
68
+ SinatraHealthCheck::Status.new(:warning, 'memcache is not available')
69
+ end
70
+ end
71
+ end
72
+
73
+ # Internal API connection health check
74
+ module API
75
+ # Check if we can connect to the Cyclid API
76
+ def self.status
77
+ connected = begin
78
+ client = Client::Tilapia.new(auth: Client::AUTH_NONE,
79
+ log_level: Logger::DEBUG,
80
+ server: Cyclid.config.server_api.host,
81
+ port: Cyclid.config.server_api.port)
82
+ client.health_ping
83
+ rescue
84
+ false
85
+ end
86
+
87
+ if connected
88
+ SinatraHealthCheck::Status.new(:ok, 'API connection is okay')
89
+ else
90
+ SinatraHealthCheck::Status.new(:error, 'API is not available')
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2016 Liqwyd Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ module Cyclid
17
+ module UI
18
+ module Controllers
19
+ # Controller for Organization related endpoints (including Jobs)
20
+ class Organization < Base
21
+ get '/:name' do
22
+ authenticate!
23
+
24
+ # Build breadcrumbs
25
+ name = params[:name]
26
+
27
+ @crumbs = []
28
+ @crumbs << { 'name' => name.capitalize }
29
+
30
+ @organization = name
31
+ @linkback_url = "/#{name}"
32
+
33
+ api_server = Cyclid.config.client_api
34
+ @organization_url = "#{api_server}/organizations/#{params[:name]}"
35
+ @current_user = current_user
36
+
37
+ mustache :organization
38
+ end
39
+
40
+ get '/:name/job/:id' do
41
+ authenticate!
42
+
43
+ # Build breadcrumbs
44
+ name = params[:name]
45
+ id = params[:id]
46
+
47
+ @crumbs = []
48
+ @crumbs << { 'url' => "/#{name}", 'name' => name.capitalize }
49
+ @crumbs << { 'name' => "Job ##{id}" }
50
+
51
+ @organization = name
52
+ @linkback_url = "/#{name}"
53
+
54
+ api_server = Cyclid.config.client_api
55
+ @job_url = "#{api_server}/organizations/#{params[:name]}/jobs/#{params[:id]}"
56
+ @job_id = id
57
+ @current_user = current_user
58
+
59
+ mustache :job
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2016 Liqwyd Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ module Cyclid
17
+ module UI
18
+ module Controllers
19
+ # Sinatra controller for user related endpoints
20
+ class User < Base
21
+ get '/user/:username' do
22
+ authenticate!
23
+
24
+ # Build breadcrumbs
25
+ username = params[:username]
26
+
27
+ @crumbs = []
28
+ @crumbs << { 'name' => 'User' }
29
+ @crumbs << { 'url' => "/user/#{username}", 'name' => username }
30
+
31
+ @api_url = Cyclid.config.client_api
32
+ @user_url = "#{@api_url}/users/#{params[:username]}"
33
+ @current_user = current_user
34
+
35
+ mustache :user
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2016 Liqwyd Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ module Cyclid
17
+ module UI
18
+ # Various helper methods for Sinatra controllers
19
+ module Helpers
20
+ # Raw CSRF token
21
+ def csrf_token(rack_env)
22
+ Rack::Csrf.csrf_token(rack_env)
23
+ end
24
+
25
+ # CSRF HTML forms tag
26
+ def csrf_tag(rack_env)
27
+ Rack::Csrf.csrf_tag(rack_env)
28
+ end
29
+
30
+ # Standard unauthenticated 401 message
31
+ def halt_with_401
32
+ flash[:login_error] = 'Invalid username or password'
33
+ halt 401, flash.now[:login_error]
34
+ end
35
+ end
36
+
37
+ # Sinatra Warden AuthN/AuthZ helpers
38
+ module AuthHelpers
39
+ # Return the current Warden scope
40
+ def warden
41
+ env['warden']
42
+ end
43
+
44
+ # Call the Warden authenticate! method
45
+ def authenticate!
46
+ warden.authenticate!
47
+ end
48
+
49
+ # Current User object from the session
50
+ def current_user
51
+ warden.user
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+ # Modified from https://gist.github.com/mralex/956592
3
+ #
4
+ # Original licensed under MIT: https://github.com/gioext/sinatra-memcache
5
+ require 'memcached'
6
+
7
+ module Cyclid
8
+ module UI
9
+ # Simple Memcache caching layer on top of the Memcached client. Keys are
10
+ # retrieved or set via. the cache method; if the key exists it is returned,
11
+ # if the key does not exist the given block is called and its output is
12
+ # stored in Memcached.
13
+ class Memcache
14
+ attr_reader :client, :server, :expiry
15
+
16
+ def initialize(args)
17
+ @server = args[:server]
18
+ @expiry = args[:expiry] || 3600
19
+ end
20
+
21
+ def cache(key)
22
+ begin
23
+ output = memcached.get(key)
24
+ rescue Memcached::NotFound
25
+ output = yield
26
+ memcached.set(key, output, @expiry)
27
+ end
28
+ output
29
+ end
30
+
31
+ def expire(key)
32
+ memcached.delete key
33
+ true
34
+ rescue Memcached::NotFound
35
+ false
36
+ end
37
+
38
+ private
39
+
40
+ def memcached
41
+ @client ||= Memcached.new(@server)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+ # Copyright 2016 Liqwyd Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ module Cyclid
17
+ module UI
18
+ module Models
19
+ # A User object. This is really no more than a simple wrapper around the
20
+ # Cyclid user data that is returned from the API, with the added layer
21
+ # of Memecached caching of the object to avoid API calls.
22
+ class User
23
+ attr_reader :username, :email, :organizations, :id
24
+
25
+ def initialize(args = {})
26
+ @username = args['username'] || nil
27
+ @email = args['email'] || nil
28
+ @organizations = args['organizations'] || []
29
+ @id = args['id'] || nil
30
+ end
31
+
32
+ def to_hash
33
+ { 'username' => @username,
34
+ 'email' => @email,
35
+ 'organizations' => @organizations,
36
+ 'id' => @id }
37
+ end
38
+
39
+ # Try to find the user object in Memcached; if it does not exist,
40
+ # fallback to the API. If the API returns the user data, it will be
41
+ # cached into Memcache for future use.
42
+ #
43
+ # If we have to fall back to the API we assume that the username is
44
+ # valid and either the HTTP Basic password or an API token are available
45
+ # and valid.
46
+ def self.get(args)
47
+ username = args[:username] || args['username']
48
+ memcache = Memcache.new(server: Cyclid.config.memcached)
49
+
50
+ user_data = begin
51
+ memcache.cache username do
52
+ user_fetch(args)
53
+ end
54
+ rescue Memcached::ServerIsMarkedDead => ex
55
+ Cyclid.logger.fatal "cannot connect to memcached: #{ex}"
56
+ # Fall back to a direct API connection
57
+ user_fetch(args)
58
+ end
59
+
60
+ new(user_data)
61
+ end
62
+
63
+ def self.user_fetch(args)
64
+ username = args[:username] || args['username']
65
+ password = args[:password] || args['password']
66
+ token = args[:token] || args['token']
67
+
68
+ auth_method = token.nil? ? Client::AUTH_BASIC : Client::AUTH_TOKEN
69
+
70
+ user_data = nil
71
+ begin
72
+ Cyclid.logger.debug "api=#{Cyclid.config.server_api.inspect}"
73
+ client = Client::Tilapia.new(auth: auth_method,
74
+ server: Cyclid.config.server_api.host,
75
+ port: Cyclid.config.server_api.port,
76
+ username: username,
77
+ password: password,
78
+ token: token)
79
+ user_data = client.user_get(username)
80
+ Cyclid.logger.debug "got #{user_data}"
81
+ rescue StandardError => ex
82
+ Cyclid.logger.fatal "failed to get user details: #{ex}"
83
+ raise ex
84
+ end
85
+
86
+ user_data
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,8 @@
1
+ <div id="footer">
2
+ <div class="container">
3
+ <div class="row">
4
+ <div class="col-md-2"><p class="text-left">&copy;2016 Cyclid</p></div>
5
+ <div class="col-md-1 col-md-offset-9"><a class="text-right" href="http://cyclid.io">cyclid.io</a></div>
6
+ </div>
7
+ </div>
8
+ </div>