stoplight-admin 0.3.6 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +19 -5
- data/lib/sinatra/stoplight_admin.rb +3 -160
- data/lib/stoplight_admin/actions/action.rb +22 -0
- data/lib/stoplight_admin/actions/home.rb +25 -0
- data/lib/stoplight_admin/actions/lock.rb +21 -0
- data/lib/stoplight_admin/actions/lock_all_green.rb +16 -0
- data/lib/stoplight_admin/actions/lock_green.rb +21 -0
- data/lib/stoplight_admin/actions/lock_red.rb +21 -0
- data/lib/stoplight_admin/actions/stats.rb +8 -0
- data/lib/stoplight_admin/actions/unlock.rb +21 -0
- data/lib/stoplight_admin/application/helpers.rb +17 -0
- data/lib/stoplight_admin/application.rb +63 -0
- data/lib/stoplight_admin/dependencies.rb +61 -0
- data/lib/stoplight_admin/lights_repository/light.rb +64 -0
- data/lib/stoplight_admin/lights_repository.rb +71 -0
- data/lib/stoplight_admin/lights_stats.rb +75 -0
- data/lib/stoplight_admin/views/index.erb +120 -0
- data/lib/stoplight_admin/views/layout.erb +55 -0
- data/lib/stoplight_admin.rb +21 -0
- metadata +40 -75
- data/lib/sinatra/views/index.haml +0 -79
- data/lib/sinatra/views/layout.haml +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c61065004c1c254f44f10415c852818e962b82ce7d2b7e9d451bd2b998487144
|
4
|
+
data.tar.gz: 3162abbe1ad6f0e0aad93fbade2b35fae6e7df7ddfa2b61a161bc7d339da9c9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b53b0093a1ac59c7d21352a6ea0ecd7ac3de163d000ab701cf92eb19250f9fdf803cccb1df30b6bf55d721ff12a9ca25d28239ce16c223ded9e9e42f99cf72b3
|
7
|
+
data.tar.gz: e709637df523e89529e507159bff0bffc63b364539ae1e2fe7873b5525f2aded28833d9d45ee6bfc26199b58f8115c43edf0b3fcb283b68487cf444d3becbe50
|
data/README.md
CHANGED
@@ -6,6 +6,9 @@ A simple administration interface for [stoplight][2]. Monitor the
|
|
6
6
|
status, failures, and invocations of your stoplights. Change
|
7
7
|
stoplight colors, or lock them in either red or green state.
|
8
8
|
|
9
|
+
The stoplight-admin version 0.4 and above is only compatible with Stoplight 4.1. If you have Stoplight version
|
10
|
+
below 4.0, use [stoplight-admin 0.3.6].
|
11
|
+
|
9
12
|
## Configuration
|
10
13
|
|
11
14
|
This project is packaged as a Ruby gem so that you can easily embed it
|
@@ -32,9 +35,19 @@ example using a local Redis data store:
|
|
32
35
|
``` rb
|
33
36
|
# app.rb
|
34
37
|
|
38
|
+
require 'bundler/inline'
|
39
|
+
|
40
|
+
gemfile do
|
41
|
+
source 'https://rubygems.org'
|
42
|
+
|
43
|
+
gem 'redis'
|
44
|
+
gem 'stoplight-admin'
|
45
|
+
gem 'webrick'
|
46
|
+
end
|
47
|
+
|
35
48
|
require 'redis'
|
36
49
|
require 'sinatra'
|
37
|
-
require '
|
50
|
+
require 'stoplight_admin/application'
|
38
51
|
|
39
52
|
redis = Redis.new(url: 'redis://localhost:6379')
|
40
53
|
set :data_store, Stoplight::DataStore::Redis.new(redis)
|
@@ -65,10 +78,10 @@ Add something like this to your `config/routes.rb`:
|
|
65
78
|
|
66
79
|
``` rb
|
67
80
|
require 'redis'
|
68
|
-
require '
|
81
|
+
require 'stoplight_admin/application'
|
69
82
|
|
70
|
-
class
|
71
|
-
register
|
83
|
+
class StoplightAdminApp < Sinatra::Base
|
84
|
+
register StoplightAdmin::Application
|
72
85
|
|
73
86
|
redis = Redis.new # Uses REDIS_URL environment variable.
|
74
87
|
data_store = Stoplight::DataStore::Redis.new(redis)
|
@@ -76,7 +89,7 @@ class StoplightAdmin < Sinatra::Base
|
|
76
89
|
end
|
77
90
|
|
78
91
|
Rails.application.routes.draw do
|
79
|
-
mount
|
92
|
+
mount StoplightAdminApp => '/stoplights'
|
80
93
|
end
|
81
94
|
```
|
82
95
|
|
@@ -93,3 +106,4 @@ Stoplight is brought to you by [@camdez][4] and [@tfausak][5] from
|
|
93
106
|
[6]: https://github.com/OrgSync
|
94
107
|
[7]: https://badge.fury.io/rb/stoplight-admin.svg
|
95
108
|
[8]: https://rubygems.org/gems/stoplight-admin
|
109
|
+
[stoplight-admin 0.3.6]: https://github.com/bolshakov/stoplight-admin/releases/tag/v0.3.6
|
@@ -1,161 +1,4 @@
|
|
1
|
-
|
1
|
+
require "stoplight_admin/application"
|
2
2
|
|
3
|
-
|
4
|
-
require
|
5
|
-
require 'sinatra/json'
|
6
|
-
require 'stoplight'
|
7
|
-
|
8
|
-
module Sinatra
|
9
|
-
module StoplightAdmin
|
10
|
-
module Helpers
|
11
|
-
COLORS = [
|
12
|
-
GREEN = Stoplight::Color::GREEN,
|
13
|
-
YELLOW = Stoplight::Color::YELLOW,
|
14
|
-
RED = Stoplight::Color::RED
|
15
|
-
].freeze
|
16
|
-
|
17
|
-
def data_store
|
18
|
-
return @data_store if defined?(@data_store)
|
19
|
-
@data_store = Stoplight::Light.default_data_store = settings.data_store
|
20
|
-
end
|
21
|
-
|
22
|
-
def lights
|
23
|
-
data_store
|
24
|
-
.names
|
25
|
-
.map { |name| light_info(name) }
|
26
|
-
.sort_by { |light| light_sort_key(light) }
|
27
|
-
end
|
28
|
-
|
29
|
-
def light_info(light)
|
30
|
-
l = Stoplight::Light.new(light) {}
|
31
|
-
color = l.color
|
32
|
-
failures, state = l.data_store.get_all(l)
|
33
|
-
|
34
|
-
{
|
35
|
-
name: light,
|
36
|
-
color: color,
|
37
|
-
failures: failures,
|
38
|
-
locked: locked?(state)
|
39
|
-
}
|
40
|
-
end
|
41
|
-
|
42
|
-
def light_sort_key(light)
|
43
|
-
[-COLORS.index(light[:color]),
|
44
|
-
light[:name]]
|
45
|
-
end
|
46
|
-
|
47
|
-
def locked?(state)
|
48
|
-
[Stoplight::State::LOCKED_GREEN,
|
49
|
-
Stoplight::State::LOCKED_RED]
|
50
|
-
.include?(state)
|
51
|
-
end
|
52
|
-
|
53
|
-
def stat_params(ls)
|
54
|
-
h = {
|
55
|
-
count_red: 0, count_yellow: 0, count_green: 0,
|
56
|
-
percent_red: 0, percent_yellow: 0, percent_green: 0
|
57
|
-
}
|
58
|
-
return h if (size = ls.size).zero?
|
59
|
-
|
60
|
-
h[:count_red] = ls.count { |l| l[:color] == RED }
|
61
|
-
h[:count_yellow] = ls.count { |l| l[:color] == YELLOW }
|
62
|
-
h[:count_green] = size - h[:count_red] - h[:count_yellow]
|
63
|
-
|
64
|
-
h[:percent_red] = (100.0 * h[:count_red] / size).ceil
|
65
|
-
h[:percent_yellow] = (100.0 * h[:count_yellow] / size).ceil
|
66
|
-
h[:percent_green] = 100.0 - h[:percent_red] - h[:percent_yellow]
|
67
|
-
|
68
|
-
h
|
69
|
-
end
|
70
|
-
|
71
|
-
def lock(light)
|
72
|
-
l = Stoplight::Light.new(light) {}
|
73
|
-
new_state =
|
74
|
-
case l.color
|
75
|
-
when Stoplight::Color::GREEN
|
76
|
-
Stoplight::State::LOCKED_GREEN
|
77
|
-
else
|
78
|
-
Stoplight::State::LOCKED_RED
|
79
|
-
end
|
80
|
-
|
81
|
-
data_store.set_state(l, new_state)
|
82
|
-
end
|
83
|
-
|
84
|
-
def unlock(light)
|
85
|
-
l = Stoplight::Light.new(light) {}
|
86
|
-
data_store.set_state(l, Stoplight::State::UNLOCKED)
|
87
|
-
end
|
88
|
-
|
89
|
-
def green(light)
|
90
|
-
l = Stoplight::Light.new(light) {}
|
91
|
-
if data_store.get_state(l) == Stoplight::State::LOCKED_RED
|
92
|
-
new_state = Stoplight::State::LOCKED_GREEN
|
93
|
-
data_store.set_state(l, new_state)
|
94
|
-
end
|
95
|
-
|
96
|
-
data_store.clear_failures(l)
|
97
|
-
end
|
98
|
-
|
99
|
-
def red(light)
|
100
|
-
l = Stoplight::Light.new(light) {}
|
101
|
-
data_store.set_state(l, Stoplight::State::LOCKED_RED)
|
102
|
-
end
|
103
|
-
|
104
|
-
def with_lights
|
105
|
-
[*params[:names]]
|
106
|
-
.map { |l| CGI.unescape(l) }
|
107
|
-
.each { |l| yield(l) }
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def self.registered(app)
|
112
|
-
app.helpers StoplightAdmin::Helpers
|
113
|
-
|
114
|
-
app.set :data_store, nil
|
115
|
-
app.set :views, File.join(File.dirname(__FILE__), 'views')
|
116
|
-
|
117
|
-
app.get '/' do
|
118
|
-
ls = lights
|
119
|
-
stats = stat_params(ls)
|
120
|
-
|
121
|
-
haml :index, locals: stats.merge(lights: ls)
|
122
|
-
end
|
123
|
-
|
124
|
-
app.get '/stats' do
|
125
|
-
ls = lights
|
126
|
-
stats = stat_params(ls)
|
127
|
-
|
128
|
-
json({stats: stats, lights: ls})
|
129
|
-
end
|
130
|
-
|
131
|
-
app.post '/lock' do
|
132
|
-
with_lights { |l| lock(l) }
|
133
|
-
redirect to('/')
|
134
|
-
end
|
135
|
-
|
136
|
-
app.post '/unlock' do
|
137
|
-
with_lights { |l| unlock(l) }
|
138
|
-
redirect to('/')
|
139
|
-
end
|
140
|
-
|
141
|
-
app.post '/green' do
|
142
|
-
with_lights { |l| green(l) }
|
143
|
-
redirect to('/')
|
144
|
-
end
|
145
|
-
|
146
|
-
app.post '/red' do
|
147
|
-
with_lights { |l| red(l) }
|
148
|
-
redirect to('/')
|
149
|
-
end
|
150
|
-
|
151
|
-
app.post '/green_all' do
|
152
|
-
data_store.names
|
153
|
-
.reject { |l| Stoplight::Light.new(l) {}.color == Stoplight::Color::GREEN }
|
154
|
-
.each { |l| green(l) }
|
155
|
-
redirect to('/')
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
register StoplightAdmin
|
161
|
-
end
|
3
|
+
warn "[DEPRECATED] requiring `sinatra/stoplight_admin` is deprecated. " \
|
4
|
+
"Please require `stoplight_admin/application` instead."
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# @abstract
|
6
|
+
class Action
|
7
|
+
# @!attribute lights_repository
|
8
|
+
# @return [StoplightAdmin::LightsRepository]
|
9
|
+
attr_reader :lights_repository
|
10
|
+
private :lights_repository
|
11
|
+
|
12
|
+
# @return lights_repository [StoplightAdmin::LightsRepository]
|
13
|
+
def initialize(lights_repository:)
|
14
|
+
@lights_repository = lights_repository
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(params)
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
class Home < Action
|
6
|
+
# @!attribute lights_stats
|
7
|
+
# @return [Class<StoplightAdmin::LightsStats>]
|
8
|
+
attr_reader :lights_stats
|
9
|
+
private :lights_stats
|
10
|
+
|
11
|
+
# @param lights_stats [Class<StoplightAdmin::LightsStats>]
|
12
|
+
def initialize(lights_stats:, **deps)
|
13
|
+
super(**deps)
|
14
|
+
@lights_stats = lights_stats
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [(StoplightAdmin::LightsRepository::Light)]
|
18
|
+
def call(*)
|
19
|
+
lights = lights_repository.all
|
20
|
+
stats = lights_stats.call(lights)
|
21
|
+
[lights, stats]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# This action locks light
|
6
|
+
class Lock < Action
|
7
|
+
# @param params [Hash] query parameters
|
8
|
+
# @return [void]
|
9
|
+
def call(params)
|
10
|
+
light_names(params).each do |name|
|
11
|
+
lights_repository.lock(name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private def light_names(params)
|
16
|
+
Array(params[:names])
|
17
|
+
.map { |name| CGI.unescape(name) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# This action locks all lights green
|
6
|
+
class LockAllGreen < Action
|
7
|
+
# @return [void]
|
8
|
+
def call(*)
|
9
|
+
lights_repository
|
10
|
+
.with_color(RED, YELLOW)
|
11
|
+
.map(&:name)
|
12
|
+
.each { |name| lights_repository.lock(name, GREEN) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# This action locks light with the specific name green
|
6
|
+
class LockGreen < Action
|
7
|
+
# @param params [Hash] query parameters
|
8
|
+
# @return [void]
|
9
|
+
def call(params)
|
10
|
+
light_names(params).each do |name|
|
11
|
+
lights_repository.lock(name, GREEN)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private def light_names(params)
|
16
|
+
Array(params[:names])
|
17
|
+
.map { |name| CGI.unescape(name) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# This action locks light with the specific name red
|
6
|
+
class LockRed < Action
|
7
|
+
# @param params [Hash] query parameters
|
8
|
+
# @return [void]
|
9
|
+
def call(params)
|
10
|
+
light_names(params).each do |name|
|
11
|
+
lights_repository.lock(name, RED)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private def light_names(params)
|
16
|
+
Array(params[:names])
|
17
|
+
.map { |name| CGI.unescape(name) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# This action unlocks light
|
6
|
+
class Unlock < Action
|
7
|
+
# @param params [Hash] query parameters
|
8
|
+
# @return [void]
|
9
|
+
def call(params)
|
10
|
+
light_names(params).each do |name|
|
11
|
+
lights_repository.unlock(name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private def light_names(params)
|
16
|
+
Array(params[:names])
|
17
|
+
.map { |name| CGI.unescape(name) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Application
|
5
|
+
module Helpers
|
6
|
+
COLORS = [
|
7
|
+
GREEN = Stoplight::Color::GREEN,
|
8
|
+
YELLOW = Stoplight::Color::YELLOW,
|
9
|
+
RED = Stoplight::Color::RED
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
def dependencies
|
13
|
+
StoplightAdmin::Dependencies.new(data_store: settings.data_store)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sinatra"
|
4
|
+
require "sinatra/base"
|
5
|
+
require "sinatra/json"
|
6
|
+
require "stoplight_admin"
|
7
|
+
|
8
|
+
module StoplightAdmin
|
9
|
+
module Application
|
10
|
+
def self.registered(app)
|
11
|
+
app.helpers Application::Helpers
|
12
|
+
|
13
|
+
app.set :data_store, nil
|
14
|
+
app.set :views, File.join(File.dirname(__FILE__), "views")
|
15
|
+
|
16
|
+
app.get "/" do
|
17
|
+
lights, stats = dependencies.home_action.call
|
18
|
+
|
19
|
+
erb :index, locals: stats.merge(lights: lights)
|
20
|
+
end
|
21
|
+
|
22
|
+
app.get "/stats" do
|
23
|
+
lights, stats = dependencies.stats_action.call
|
24
|
+
|
25
|
+
json({stats: stats, lights: lights.map(&:as_json)})
|
26
|
+
end
|
27
|
+
|
28
|
+
app.post "/lock" do
|
29
|
+
dependencies.lock_action.call(params)
|
30
|
+
|
31
|
+
redirect to("/")
|
32
|
+
end
|
33
|
+
|
34
|
+
app.post "/unlock" do
|
35
|
+
dependencies.unlock_action.call(params)
|
36
|
+
|
37
|
+
redirect to("/")
|
38
|
+
end
|
39
|
+
|
40
|
+
app.post "/green" do
|
41
|
+
dependencies.green_action.call(params)
|
42
|
+
|
43
|
+
redirect to("/")
|
44
|
+
end
|
45
|
+
|
46
|
+
app.post "/red" do
|
47
|
+
dependencies.red_action.call(params)
|
48
|
+
|
49
|
+
redirect to("/")
|
50
|
+
end
|
51
|
+
|
52
|
+
app.post "/green_all" do
|
53
|
+
dependencies.green_all_action.call
|
54
|
+
|
55
|
+
redirect to("/")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
StoplightAdmin.loader!.eager_load
|
62
|
+
|
63
|
+
register StoplightAdmin::Application
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
class Dependencies
|
5
|
+
# @!attribute data_store
|
6
|
+
# @return [Stoplight::DataStore::Base]
|
7
|
+
attr_reader :data_store
|
8
|
+
private :data_store
|
9
|
+
|
10
|
+
# @param data_store [Stoplight::DataStore::Base]
|
11
|
+
def initialize(data_store:)
|
12
|
+
@data_store = data_store
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [StoplightAdmin::LightsRepository]
|
16
|
+
def lights_repository
|
17
|
+
StoplightAdmin::LightsRepository.new(data_store: data_store)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [StoplightAdmin::Actions::Home]
|
21
|
+
def home_action
|
22
|
+
StoplightAdmin::Actions::Home.new(
|
23
|
+
lights_repository: lights_repository,
|
24
|
+
lights_stats: StoplightAdmin::LightsStats
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [StoplightAdmin::Actions::Stats]
|
29
|
+
def stats_action
|
30
|
+
StoplightAdmin::Actions::Stats.new(
|
31
|
+
lights_repository: lights_repository,
|
32
|
+
lights_stats: StoplightAdmin::LightsStats
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [StoplightAdmin::Actions::Lock]
|
37
|
+
def lock_action
|
38
|
+
StoplightAdmin::Actions::Lock.new(lights_repository: lights_repository)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [StoplightAdmin::Actions::Unlock]
|
42
|
+
def unlock_action
|
43
|
+
StoplightAdmin::Actions::Unlock.new(lights_repository: lights_repository)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [StoplightAdmin::Actions::LockGreen]
|
47
|
+
def green_action
|
48
|
+
StoplightAdmin::Actions::LockGreen.new(lights_repository: lights_repository)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [StoplightAdmin::Actions::LockRed]
|
52
|
+
def red_action
|
53
|
+
StoplightAdmin::Actions::LockRed.new(lights_repository: lights_repository)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [StoplightAdmin::Actions::LockAllGreen]
|
57
|
+
def green_all_action
|
58
|
+
StoplightAdmin::Actions::LockAllGreen.new(lights_repository: lights_repository)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
class LightsRepository
|
5
|
+
class Light
|
6
|
+
COLORS = [
|
7
|
+
GREEN = Stoplight::Color::GREEN,
|
8
|
+
YELLOW = Stoplight::Color::YELLOW,
|
9
|
+
RED = Stoplight::Color::RED
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
# @!attribute name
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
# @!attribute color
|
17
|
+
# @return [String]
|
18
|
+
attr_reader :color
|
19
|
+
|
20
|
+
# @!attribute state
|
21
|
+
# @return [String]
|
22
|
+
attr_reader :state
|
23
|
+
|
24
|
+
# @!attribute failures
|
25
|
+
# @return [<Stoplight::Failure>]
|
26
|
+
attr_reader :failures
|
27
|
+
|
28
|
+
# @param name [String]
|
29
|
+
# @param color [String]
|
30
|
+
# @param state [String]
|
31
|
+
# @param failures [<Stoplight::Failure>]
|
32
|
+
def initialize(name:, color:, state:, failures:)
|
33
|
+
@name = name
|
34
|
+
@color = color
|
35
|
+
@state = state
|
36
|
+
@failures = failures
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Boolean]
|
40
|
+
def locked?
|
41
|
+
!unlocked?
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Boolean]
|
45
|
+
def unlocked?
|
46
|
+
state == Stoplight::State::UNLOCKED
|
47
|
+
end
|
48
|
+
|
49
|
+
def as_json
|
50
|
+
{
|
51
|
+
name: name,
|
52
|
+
color: color,
|
53
|
+
failures: failures,
|
54
|
+
locked: locked?
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [Array]
|
59
|
+
def default_sort_key
|
60
|
+
[-COLORS.index(color), name]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
class LightsRepository
|
5
|
+
# @!attribute data_store
|
6
|
+
# @return [Stoplight::DataStore::Base]
|
7
|
+
attr_reader :data_store
|
8
|
+
private :data_store
|
9
|
+
|
10
|
+
# @param data_store [Stoplight::DataStore::Base]
|
11
|
+
def initialize(data_store:)
|
12
|
+
@data_store = data_store
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [<StoplightAdmin::LightsRepository::Light>]
|
16
|
+
def all
|
17
|
+
data_store
|
18
|
+
.names
|
19
|
+
.map { |name| load_light(name) }
|
20
|
+
.sort_by(&:default_sort_key)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param colors <String>] colors name
|
24
|
+
# @return [<StoplightAdmin::LightsRepository::Light>] lights with the requested colors
|
25
|
+
#
|
26
|
+
def with_color(*colors)
|
27
|
+
requested_colors = Array(colors)
|
28
|
+
|
29
|
+
all.select do |light|
|
30
|
+
requested_colors.include?(light.color)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param name [String] locks light by its name
|
35
|
+
# @param color [String, nil] locks to this color. When nil is given, locks to the current
|
36
|
+
# color
|
37
|
+
# @return [void]
|
38
|
+
def lock(name, color = nil)
|
39
|
+
light = build_light(name)
|
40
|
+
|
41
|
+
case color || light.color
|
42
|
+
when Stoplight::Color::GREEN
|
43
|
+
light.lock(Stoplight::Color::GREEN)
|
44
|
+
else
|
45
|
+
light.lock(Stoplight::Color::RED)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param name [String] unlocks light by its name
|
50
|
+
# @return [void]
|
51
|
+
def unlock(name)
|
52
|
+
build_light(name).unlock
|
53
|
+
end
|
54
|
+
|
55
|
+
private def load_light(name)
|
56
|
+
light = build_light(name)
|
57
|
+
failures, state = data_store.get_all(light)
|
58
|
+
|
59
|
+
Light.new(
|
60
|
+
name: name,
|
61
|
+
color: light.color,
|
62
|
+
state: state,
|
63
|
+
failures: failures
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
private def build_light(name)
|
68
|
+
Stoplight(name).with_data_store(data_store).build
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
class LightsStats
|
5
|
+
EMPTY_STATS = {
|
6
|
+
count_red: 0, count_yellow: 0, count_green: 0,
|
7
|
+
percent_red: 0, percent_yellow: 0, percent_green: 0
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
# @!attribute lights
|
11
|
+
# @return [<StoplightAdmin::LightsRepository::Light>]
|
12
|
+
attr_reader :lights
|
13
|
+
private :lights
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def call(lights)
|
17
|
+
new(lights).call
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param lights [<StoplightAdmin::LightsRepository::Light>]
|
22
|
+
def initialize(lights)
|
23
|
+
@lights = lights
|
24
|
+
end
|
25
|
+
|
26
|
+
def call
|
27
|
+
return EMPTY_STATS if size.zero?
|
28
|
+
|
29
|
+
EMPTY_STATS.merge(
|
30
|
+
count_red: count_red,
|
31
|
+
count_yellow: count_yellow,
|
32
|
+
count_green: count_green,
|
33
|
+
percent_red: percent_red,
|
34
|
+
percent_yellow: percent_yellow,
|
35
|
+
percent_green: percent_green
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
private def count_red
|
40
|
+
count_lights(RED)
|
41
|
+
end
|
42
|
+
|
43
|
+
private def percent_red
|
44
|
+
percent_lights(RED)
|
45
|
+
end
|
46
|
+
|
47
|
+
private def count_green
|
48
|
+
count_lights(GREEN)
|
49
|
+
end
|
50
|
+
|
51
|
+
private def percent_green
|
52
|
+
percent_lights(GREEN)
|
53
|
+
end
|
54
|
+
|
55
|
+
private def count_yellow
|
56
|
+
count_lights(YELLOW)
|
57
|
+
end
|
58
|
+
|
59
|
+
private def percent_yellow
|
60
|
+
percent_lights(YELLOW)
|
61
|
+
end
|
62
|
+
|
63
|
+
private def count_lights(color)
|
64
|
+
lights.count { |l| l.color == color }
|
65
|
+
end
|
66
|
+
|
67
|
+
private def percent_lights(color)
|
68
|
+
(100 * count_lights(color).fdiv(size)).ceil
|
69
|
+
end
|
70
|
+
|
71
|
+
private def size
|
72
|
+
lights.size
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
<div class="jumbotron hidden-xs hidden-sm">
|
2
|
+
<h1>🚦 All of the Lights</h1>
|
3
|
+
<p>Cop lights, flashlights, spotlights, strobe lights…</p>
|
4
|
+
</div>
|
5
|
+
|
6
|
+
<div class="main">
|
7
|
+
<% if lights.empty? %>
|
8
|
+
<div class="alert alert-warning" role="alert">
|
9
|
+
<strong>No lights found.</strong>
|
10
|
+
Ensure that your Stoplight data store is properly configured and that your Stoplight blocks have been run.
|
11
|
+
</div>
|
12
|
+
<% else %>
|
13
|
+
<div class="well">
|
14
|
+
<form method="post" action="<%= url('/green_all') %>">
|
15
|
+
<button class="btn-lg btn btn-success"
|
16
|
+
type="submit"
|
17
|
+
<%= count_red + count_yellow > 0 ? nil : "disabled" %>>
|
18
|
+
Greenify All
|
19
|
+
</button>
|
20
|
+
</form>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div class="progress">
|
24
|
+
<div class="progress-bar progress-bar-danger" style="width: <%= percent_red %>%">
|
25
|
+
<span><%= count_red %></span>
|
26
|
+
</div>
|
27
|
+
<div class="progress-bar progress-bar-warning" style="width: <%= percent_yellow %>%">
|
28
|
+
<span><%= count_yellow %></span>
|
29
|
+
</div>
|
30
|
+
<div class="progress-bar progress-bar-success" style="width: <%= percent_green %>%">
|
31
|
+
<span><%= count_green %></span>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div class="lights">
|
36
|
+
<table class="table table-hover">
|
37
|
+
<thead class="hidden-xs">
|
38
|
+
<tr>
|
39
|
+
<th>Status</th>
|
40
|
+
<th>
|
41
|
+
<abbr title="Locked lights will not change color automatically.">Locked?</abbr>
|
42
|
+
</th>
|
43
|
+
<th>Name</th>
|
44
|
+
<th>
|
45
|
+
<abbr title="Exceptions which caused the light to turn red.">Failures</abbr>
|
46
|
+
</th>
|
47
|
+
</tr>
|
48
|
+
</thead>
|
49
|
+
<tbody>
|
50
|
+
<% lights.each do |l| %>
|
51
|
+
<tr>
|
52
|
+
<td class="indicator">
|
53
|
+
<% if l.color == GREEN %>
|
54
|
+
<form method="post" action="<%= url('/red') %>">
|
55
|
+
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
|
56
|
+
<button type="submit" class="btn btn-success">
|
57
|
+
<span>G</span><span class="hidden-xs">REEN</span>
|
58
|
+
</button>
|
59
|
+
</form>
|
60
|
+
<% elsif l.color == YELLOW %>
|
61
|
+
<form method="post" action="<%= url('/green') %>">
|
62
|
+
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
|
63
|
+
<button type="submit" class="btn btn-warning">
|
64
|
+
<span>Y</span><span class="hidden-xs">ELLOW</span>
|
65
|
+
</button>
|
66
|
+
</form>
|
67
|
+
<% else %>
|
68
|
+
<form method="post" action="<%= url('/green') %>">
|
69
|
+
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
|
70
|
+
<button type="submit" class="btn btn-danger">
|
71
|
+
<span>R</span><span class="hidden-xs">ED</span>
|
72
|
+
</button>
|
73
|
+
</form>
|
74
|
+
<% end %>
|
75
|
+
</td>
|
76
|
+
<td class="locked">
|
77
|
+
<% if l.locked? %>
|
78
|
+
<form method="post" action="<%= url('/unlock') %>">
|
79
|
+
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
|
80
|
+
<button type="submit" class="btn btn-link">
|
81
|
+
<span class="glyphicon glyphicon-lock"></span>
|
82
|
+
</button>
|
83
|
+
</form>
|
84
|
+
<% else %>
|
85
|
+
<form method="post" action="<%= url('/lock') %>">
|
86
|
+
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
|
87
|
+
<button type="submit" class="btn btn-link">
|
88
|
+
<span class="glyphicon glyphicon-minus"></span>
|
89
|
+
</button>
|
90
|
+
</form>
|
91
|
+
<% end %>
|
92
|
+
</td>
|
93
|
+
<td class="name">
|
94
|
+
<%= l.name %>
|
95
|
+
</td>
|
96
|
+
<td class="failures">
|
97
|
+
<% if l.failures %>
|
98
|
+
<ul>
|
99
|
+
<% l.failures.each do |failure| %>
|
100
|
+
<li>
|
101
|
+
<span class="error">
|
102
|
+
<%= ERB::Util.html_escape(failure.error_class) %>
|
103
|
+
\:
|
104
|
+
<%= ERB::Util.html_escape(failure.error_message) %>
|
105
|
+
</span>
|
106
|
+
<span class="timestamp">
|
107
|
+
<%= ERB::Util.html_escape(failure.time) %>
|
108
|
+
</span>
|
109
|
+
</li>
|
110
|
+
<% end %>
|
111
|
+
</ul>
|
112
|
+
<% end %>
|
113
|
+
</td>
|
114
|
+
</tr>
|
115
|
+
<% end %>
|
116
|
+
</tbody>
|
117
|
+
</table>
|
118
|
+
</div>
|
119
|
+
<% end %>
|
120
|
+
</div>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
+
|
7
|
+
<title>Stoplight Admin</title>
|
8
|
+
|
9
|
+
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
|
10
|
+
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css">
|
11
|
+
<link rel="shortcut icon" href="//i.imgur.com/qvfoNuh.png">
|
12
|
+
|
13
|
+
<style>
|
14
|
+
td.indicator {
|
15
|
+
font-size: 200%;
|
16
|
+
}
|
17
|
+
|
18
|
+
td.name {
|
19
|
+
font-weight: bold;
|
20
|
+
}
|
21
|
+
|
22
|
+
td.locked button {
|
23
|
+
font-size: 200%;
|
24
|
+
}
|
25
|
+
|
26
|
+
td.failures span.error {
|
27
|
+
font-weight: bold;
|
28
|
+
}
|
29
|
+
|
30
|
+
td.failures ul {
|
31
|
+
padding: 0;
|
32
|
+
list-style-type: none;
|
33
|
+
}
|
34
|
+
|
35
|
+
.btn-group form {
|
36
|
+
display: inline-block;
|
37
|
+
}
|
38
|
+
</style>
|
39
|
+
</head>
|
40
|
+
<body>
|
41
|
+
<div class="navbar navbar-inverse navbar-static-top" role="navigation">
|
42
|
+
<div class="container">
|
43
|
+
<div class="navbar-header">
|
44
|
+
<a class="navbar-brand" href="<%= url('/') %>">Stoplight Admin</a>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
|
49
|
+
<div class="container" role="main">
|
50
|
+
<%= yield %>
|
51
|
+
</div>
|
52
|
+
|
53
|
+
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
|
54
|
+
</body>
|
55
|
+
</html>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "stoplight"
|
2
|
+
require "zeitwerk"
|
3
|
+
|
4
|
+
module StoplightAdmin
|
5
|
+
COLORS = [
|
6
|
+
GREEN = Stoplight::Color::GREEN,
|
7
|
+
YELLOW = Stoplight::Color::YELLOW,
|
8
|
+
RED = Stoplight::Color::RED
|
9
|
+
].freeze
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def loader!
|
13
|
+
@loader ||= Zeitwerk::Loader.for_gem.tap do |loader|
|
14
|
+
loader.ignore("#{__dir__}/sinatra")
|
15
|
+
loader.setup
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
StoplightAdmin.loader!
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stoplight-admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cameron Desautels
|
@@ -9,36 +9,8 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-08-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: haml
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - "~>"
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: '5.0'
|
21
|
-
type: :runtime
|
22
|
-
prerelease: false
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - "~>"
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: '5.0'
|
28
|
-
- !ruby/object:Gem::Dependency
|
29
|
-
name: sinatra-contrib
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
31
|
-
requirements:
|
32
|
-
- - "~>"
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: 2.2.3
|
35
|
-
type: :runtime
|
36
|
-
prerelease: false
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - "~>"
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version: 2.2.3
|
42
14
|
- !ruby/object:Gem::Dependency
|
43
15
|
name: redis
|
44
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -54,75 +26,53 @@ dependencies:
|
|
54
26
|
- !ruby/object:Gem::Version
|
55
27
|
version: '3.2'
|
56
28
|
- !ruby/object:Gem::Dependency
|
57
|
-
name:
|
29
|
+
name: zeitwerk
|
58
30
|
requirement: !ruby/object:Gem::Requirement
|
59
31
|
requirements:
|
60
32
|
- - ">="
|
61
33
|
- !ruby/object:Gem::Version
|
62
|
-
version: '
|
34
|
+
version: '0'
|
63
35
|
type: :runtime
|
64
36
|
prerelease: false
|
65
37
|
version_requirements: !ruby/object:Gem::Requirement
|
66
38
|
requirements:
|
67
39
|
- - ">="
|
68
40
|
- !ruby/object:Gem::Version
|
69
|
-
version: '
|
41
|
+
version: '0'
|
70
42
|
- !ruby/object:Gem::Dependency
|
71
|
-
name:
|
43
|
+
name: sinatra-contrib
|
72
44
|
requirement: !ruby/object:Gem::Requirement
|
73
45
|
requirements:
|
74
|
-
- - "
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '2.4'
|
77
|
-
type: :development
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - "~>"
|
46
|
+
- - ">="
|
82
47
|
- !ruby/object:Gem::Version
|
83
|
-
version:
|
84
|
-
-
|
85
|
-
name: pry-byebug
|
86
|
-
requirement: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
- - "~>"
|
48
|
+
version: 2.2.3
|
49
|
+
- - "<"
|
89
50
|
- !ruby/object:Gem::Version
|
90
|
-
version: 3.
|
91
|
-
type: :
|
51
|
+
version: 3.2.0
|
52
|
+
type: :runtime
|
92
53
|
prerelease: false
|
93
54
|
version_requirements: !ruby/object:Gem::Requirement
|
94
55
|
requirements:
|
95
|
-
- - "
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: 3.10.0
|
98
|
-
- !ruby/object:Gem::Dependency
|
99
|
-
name: rake
|
100
|
-
requirement: !ruby/object:Gem::Requirement
|
101
|
-
requirements:
|
102
|
-
- - "~>"
|
56
|
+
- - ">="
|
103
57
|
- !ruby/object:Gem::Version
|
104
|
-
version:
|
105
|
-
|
106
|
-
prerelease: false
|
107
|
-
version_requirements: !ruby/object:Gem::Requirement
|
108
|
-
requirements:
|
109
|
-
- - "~>"
|
58
|
+
version: 2.2.3
|
59
|
+
- - "<"
|
110
60
|
- !ruby/object:Gem::Version
|
111
|
-
version:
|
61
|
+
version: 3.2.0
|
112
62
|
- !ruby/object:Gem::Dependency
|
113
|
-
name:
|
63
|
+
name: stoplight
|
114
64
|
requirement: !ruby/object:Gem::Requirement
|
115
65
|
requirements:
|
116
|
-
- - "
|
66
|
+
- - ">="
|
117
67
|
- !ruby/object:Gem::Version
|
118
|
-
version:
|
119
|
-
type: :
|
68
|
+
version: '4.1'
|
69
|
+
type: :runtime
|
120
70
|
prerelease: false
|
121
71
|
version_requirements: !ruby/object:Gem::Requirement
|
122
72
|
requirements:
|
123
|
-
- - "
|
73
|
+
- - ">="
|
124
74
|
- !ruby/object:Gem::Version
|
125
|
-
version:
|
75
|
+
version: '4.1'
|
126
76
|
description: A simple administration interface for Stoplight.
|
127
77
|
email:
|
128
78
|
- camdez@gmail.com
|
@@ -134,8 +84,23 @@ files:
|
|
134
84
|
- LICENSE.md
|
135
85
|
- README.md
|
136
86
|
- lib/sinatra/stoplight_admin.rb
|
137
|
-
- lib/
|
138
|
-
- lib/
|
87
|
+
- lib/stoplight_admin.rb
|
88
|
+
- lib/stoplight_admin/actions/action.rb
|
89
|
+
- lib/stoplight_admin/actions/home.rb
|
90
|
+
- lib/stoplight_admin/actions/lock.rb
|
91
|
+
- lib/stoplight_admin/actions/lock_all_green.rb
|
92
|
+
- lib/stoplight_admin/actions/lock_green.rb
|
93
|
+
- lib/stoplight_admin/actions/lock_red.rb
|
94
|
+
- lib/stoplight_admin/actions/stats.rb
|
95
|
+
- lib/stoplight_admin/actions/unlock.rb
|
96
|
+
- lib/stoplight_admin/application.rb
|
97
|
+
- lib/stoplight_admin/application/helpers.rb
|
98
|
+
- lib/stoplight_admin/dependencies.rb
|
99
|
+
- lib/stoplight_admin/lights_repository.rb
|
100
|
+
- lib/stoplight_admin/lights_repository/light.rb
|
101
|
+
- lib/stoplight_admin/lights_stats.rb
|
102
|
+
- lib/stoplight_admin/views/index.erb
|
103
|
+
- lib/stoplight_admin/views/layout.erb
|
139
104
|
homepage: https://github.com/bolshakov/stoplight-admin
|
140
105
|
licenses:
|
141
106
|
- MIT
|
@@ -148,14 +113,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
148
113
|
requirements:
|
149
114
|
- - ">="
|
150
115
|
- !ruby/object:Gem::Version
|
151
|
-
version:
|
116
|
+
version: 3.0.0
|
152
117
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
118
|
requirements:
|
154
119
|
- - ">="
|
155
120
|
- !ruby/object:Gem::Version
|
156
121
|
version: '0'
|
157
122
|
requirements: []
|
158
|
-
rubygems_version: 3.
|
123
|
+
rubygems_version: 3.4.18
|
159
124
|
signing_key:
|
160
125
|
specification_version: 4
|
161
126
|
summary: A simple administration interface for Stoplight.
|
@@ -1,79 +0,0 @@
|
|
1
|
-
.jumbotron.hidden-xs.hidden-sm
|
2
|
-
%h1 🚦 All of the Lights
|
3
|
-
%p Cop lights, flashlights, spotlights, strobe lights…
|
4
|
-
|
5
|
-
.main
|
6
|
-
- if lights.empty?
|
7
|
-
.alert.alert-warning{role: 'alert'}
|
8
|
-
%strong No lights found.
|
9
|
-
Ensure that your Stoplight data store is properly configured and that your Stoplight blocks have been run.
|
10
|
-
- else
|
11
|
-
.well
|
12
|
-
%form{method: 'post', action: url('/green_all')}
|
13
|
-
%button.btn-lg.btn.btn-success{type: 'submit', disabled: count_red + count_yellow > 0 ? nil : 'disabled'} Greenify All
|
14
|
-
|
15
|
-
.progress
|
16
|
-
.progress-bar.progress-bar-danger{style: "width: #{percent_red}%"}
|
17
|
-
.span= count_red
|
18
|
-
.progress-bar.progress-bar-warning{style: "width: #{percent_yellow}%"}
|
19
|
-
.span= count_yellow
|
20
|
-
.progress-bar.progress-bar-success{style: "width: #{percent_green}%"}
|
21
|
-
.span= count_green
|
22
|
-
|
23
|
-
.lights
|
24
|
-
%table.table.table-hover
|
25
|
-
%thead.hidden-xs
|
26
|
-
%tr
|
27
|
-
%th Status
|
28
|
-
%th
|
29
|
-
%abbr{title: 'Locked lights will not change color automatically.'} Locked?
|
30
|
-
%th Name
|
31
|
-
%th
|
32
|
-
%abbr{title: 'Exceptions which caused the light to turn red.'} Failures
|
33
|
-
%tbody
|
34
|
-
- lights.each do |l|
|
35
|
-
%tr
|
36
|
-
%td.indicator
|
37
|
-
- if l[:color] == GREEN
|
38
|
-
%form{method: 'post', action: url('/red')}
|
39
|
-
%input{type: 'hidden', name: 'names', value: CGI.escape(l[:name])}
|
40
|
-
%button{type: 'submit', class: 'btn btn-success'}
|
41
|
-
G
|
42
|
-
%span.hidden-xs> REEN
|
43
|
-
- elsif l[:color] == YELLOW
|
44
|
-
%form{method: 'post', action: url('/green')}
|
45
|
-
%input{type: 'hidden', name: 'names', value: CGI.escape(l[:name])}
|
46
|
-
%button{type: 'submit', class: 'btn btn-warning'}
|
47
|
-
Y
|
48
|
-
%span.hidden-xs> ELLOW
|
49
|
-
- else
|
50
|
-
%form{method: 'post', action: url('/green')}
|
51
|
-
%input{type: 'hidden', name: 'names', value: CGI.escape(l[:name])}
|
52
|
-
%button{type: 'submit', class: 'btn btn-danger'}
|
53
|
-
R
|
54
|
-
%span.hidden-xs> ED
|
55
|
-
%td.locked
|
56
|
-
- if l[:locked]
|
57
|
-
%form{method: 'post', action: url('/unlock')}
|
58
|
-
%input{type: 'hidden', name: 'names', value: CGI.escape(l[:name])}
|
59
|
-
%button{type: 'submit', class: 'btn btn-link'}
|
60
|
-
%span{class: 'glyphicon glyphicon-lock'}
|
61
|
-
- else
|
62
|
-
%form{method: 'post', action: url('/lock')}
|
63
|
-
%input{type: 'hidden', name: 'names', value: CGI.escape(l[:name])}
|
64
|
-
%button{type: 'submit', class: 'btn btn-link'}
|
65
|
-
%span{class: 'glyphicon glyphicon-minus'}
|
66
|
-
%td.name
|
67
|
-
= l[:name]
|
68
|
-
%td.failures
|
69
|
-
- if l[:failures]
|
70
|
-
%ul
|
71
|
-
- l[:failures].each do |failure|
|
72
|
-
%li
|
73
|
-
%span.error
|
74
|
-
&= failure.error_class
|
75
|
-
\:
|
76
|
-
&= failure.error_message
|
77
|
-
@
|
78
|
-
%span.timestamp
|
79
|
-
&= failure.time
|
@@ -1,48 +0,0 @@
|
|
1
|
-
!!! 5
|
2
|
-
%html{lang: 'en'}
|
3
|
-
%head
|
4
|
-
%meta{charset: 'utf-8'}
|
5
|
-
%meta{name: 'viewport', content: 'width=device-width, initial-scale=1'}
|
6
|
-
|
7
|
-
%title Stoplight Admin
|
8
|
-
|
9
|
-
%link{rel: 'stylesheet', href: '//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css'}
|
10
|
-
%link{rel: 'stylesheet', href: '//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css'}
|
11
|
-
%link{rel: 'shortcut icon', href: '//i.imgur.com/qvfoNuh.png'}
|
12
|
-
|
13
|
-
:css
|
14
|
-
td.indicator {
|
15
|
-
font-size: 200%;
|
16
|
-
}
|
17
|
-
|
18
|
-
td.name {
|
19
|
-
font-weight: bold;
|
20
|
-
}
|
21
|
-
|
22
|
-
td.locked button {
|
23
|
-
font-size: 200%;
|
24
|
-
}
|
25
|
-
|
26
|
-
td.failures span.error {
|
27
|
-
font-weight: bold;
|
28
|
-
}
|
29
|
-
|
30
|
-
td.failures ul {
|
31
|
-
padding: 0;
|
32
|
-
list-style-type: none;
|
33
|
-
}
|
34
|
-
|
35
|
-
.btn-group form {
|
36
|
-
display: inline-block;
|
37
|
-
}
|
38
|
-
|
39
|
-
%body
|
40
|
-
.navbar.navbar-inverse.navbar-static-top{role: 'navigation'}
|
41
|
-
.container
|
42
|
-
.navbar-header
|
43
|
-
%a{class: 'navbar-brand', href: url('/')} Stoplight Admin
|
44
|
-
|
45
|
-
.container{role: 'main'}
|
46
|
-
= yield
|
47
|
-
|
48
|
-
%script{src: '//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js'}
|