mission_control-web 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +20 -0
- data/README.md +122 -15
- data/Rakefile +21 -3
- data/app/assets/config/mission_control_web_manifest.js +2 -0
- data/app/assets/stylesheets/mission_control/web/application.css +15 -0
- data/app/assets/stylesheets/mission_control/web/vendor/bulma.min.css +1 -0
- data/app/controllers/concerns/mission_control/web/application_scoped.rb +17 -0
- data/app/controllers/mission_control/web/application_controller.rb +4 -0
- data/app/controllers/mission_control/web/errors_controller.rb +6 -0
- data/app/controllers/mission_control/web/routes_controller.rb +52 -0
- data/app/helpers/mission_control/web/applications_helper.rb +9 -0
- data/app/helpers/mission_control/web/routes_helper.rb +9 -0
- data/app/javascript/mission_control/web/application.js +1 -0
- data/app/models/concerns/mission_control/web/route/applications.rb +17 -0
- data/app/models/mission_control/web/application/routes.rb +24 -0
- data/app/models/mission_control/web/application.rb +34 -0
- data/app/models/mission_control/web/application_record.rb +3 -0
- data/app/models/mission_control/web/current.rb +8 -0
- data/app/models/mission_control/web/route.rb +14 -0
- data/app/views/layouts/mission_control/web/application.html.erb +23 -0
- data/app/views/mission_control/web/application/_current_application_selector.html.erb +15 -0
- data/app/views/mission_control/web/application/_flash.html.erb +3 -0
- data/app/views/mission_control/web/application/_form_errors.html.erb +11 -0
- data/app/views/mission_control/web/application/_navbar.html.erb +14 -0
- data/app/views/mission_control/web/errors/disallowed.html.erb +8 -0
- data/app/views/mission_control/web/routes/_form.html.erb +26 -0
- data/app/views/mission_control/web/routes/_route.html.erb +13 -0
- data/app/views/mission_control/web/routes/edit.html.erb +8 -0
- data/app/views/mission_control/web/routes/index.html.erb +27 -0
- data/app/views/mission_control/web/routes/new.html.erb +8 -0
- data/app/views/mission_control/web/routes/show.html.erb +42 -0
- data/config/importmap.rb +2 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20221025093008_create_mission_control_web_routes.rb +14 -0
- data/lib/generators/mission_control/web/install/admin_generator.rb +38 -0
- data/lib/generators/mission_control/web/install/middleware_generator.rb +26 -0
- data/lib/generators/mission_control/web/install_generator.rb +6 -0
- data/lib/mission_control/web/action_dispatch_request.rb +7 -0
- data/lib/mission_control/web/bulma_form_builder.rb +50 -0
- data/lib/mission_control/web/configuration.rb +21 -0
- data/lib/mission_control/web/engine.rb +45 -0
- data/lib/mission_control/web/errors/resource_not_found.rb +2 -0
- data/lib/mission_control/web/request_filter.rb +17 -0
- data/lib/mission_control/web/request_filter_middleware.rb +26 -0
- data/lib/mission_control/web/routes_cache.rb +52 -0
- data/lib/mission_control/web/version.rb +1 -3
- data/lib/mission_control/web.rb +18 -4
- data/lib/tasks/mission_control/web_tasks.rake +4 -0
- metadata +147 -8
- data/LICENSE.txt +0 -21
- data/mission_control-web.gemspec +0 -35
- data/sig/mission_control/web.rbs +0 -6
@@ -0,0 +1,24 @@
|
|
1
|
+
module MissionControl::Web::Application::Routes
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
def routes
|
5
|
+
MissionControl::Web::Route.where(application_id: id)
|
6
|
+
end
|
7
|
+
|
8
|
+
def route_was_updated(route)
|
9
|
+
routes_cache.put(route)
|
10
|
+
end
|
11
|
+
|
12
|
+
def route_was_deleted(route)
|
13
|
+
routes_cache.remove(route)
|
14
|
+
end
|
15
|
+
|
16
|
+
def route_disabled?(path)
|
17
|
+
routes_cache.disabled?(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def routes_cache
|
22
|
+
@routes_cache ||= MissionControl::Web::RoutesCache.new(self)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class MissionControl::Web::Application
|
2
|
+
include ActiveModel::Model
|
3
|
+
include Routes
|
4
|
+
|
5
|
+
attr_accessor :name, :redis
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def all
|
9
|
+
MissionControl::Web.configuration.administered_applications.map { |app| new(**app) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def find(id)
|
13
|
+
all.find { |application| application.id == id }
|
14
|
+
end
|
15
|
+
|
16
|
+
def find!(id)
|
17
|
+
find(id) or raise MissionControl::Web::Errors::ResourceNotFound
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_or_initialize_by_name(name)
|
21
|
+
find(name.parameterize) || new(name: name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def default
|
25
|
+
all.first or raise MissionControl::Web::Errors::ResourceNotFound
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def id
|
30
|
+
name.parameterize
|
31
|
+
end
|
32
|
+
|
33
|
+
alias to_param id
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class MissionControl::Web::Route < ApplicationRecord
|
2
|
+
include Applications
|
3
|
+
|
4
|
+
validates :name, :pattern, presence: true
|
5
|
+
validates :pattern, uniqueness: { scope: :application_id }
|
6
|
+
|
7
|
+
def application
|
8
|
+
@application ||= MissionControl::Web::Application.find!(application_id)
|
9
|
+
end
|
10
|
+
|
11
|
+
def disabled?
|
12
|
+
!enabled?
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Mission Control - Web</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<%= stylesheet_link_tag "mission_control/web/application", "data-turbo-track": "reload" %>
|
9
|
+
<%= javascript_importmap_tags %>
|
10
|
+
</head>
|
11
|
+
|
12
|
+
<body>
|
13
|
+
<div class="container">
|
14
|
+
<section class="section">
|
15
|
+
<%= render "navbar" %>
|
16
|
+
<%= render "flash" %>
|
17
|
+
|
18
|
+
<%= yield %>
|
19
|
+
|
20
|
+
</section>
|
21
|
+
</div>
|
22
|
+
</body>
|
23
|
+
</html>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<% if selectable_applications.any? %>
|
2
|
+
<div class="application-selector navbar-item has-dropdown is-hoverable">
|
3
|
+
<a class="navbar-link">
|
4
|
+
<%= MissionControl::Web::Current.application.name %>
|
5
|
+
</a>
|
6
|
+
|
7
|
+
<div class="navbar-dropdown">
|
8
|
+
<% selectable_applications.each do |application| %>
|
9
|
+
<%= link_to application_routes_path(application), class: "navbar-item" do %>
|
10
|
+
<span><%= application.name %></span>
|
11
|
+
<% end %>
|
12
|
+
<% end %>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% if model.errors.any? %>
|
2
|
+
<div class="notification content">
|
3
|
+
<h3 class="subtitle is-6"><%= pluralize(model.errors.count, "error") %> prohibited this <%= model.class.name.demodulize %> from being saved:</h3>
|
4
|
+
|
5
|
+
<ul>
|
6
|
+
<% model.errors.each do |error| %>
|
7
|
+
<li><%= error.full_message %></li>
|
8
|
+
<% end %>
|
9
|
+
</ul>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<nav class="navbar" role="navigation" aria-label="main navigation">
|
2
|
+
<div class="navbar-brand">
|
3
|
+
<h1 class="title">Mission Control - Web</h1>
|
4
|
+
</div>
|
5
|
+
|
6
|
+
<div class="navbar-menu is-active">
|
7
|
+
<div class="navbar-start">
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="navbar-end">
|
11
|
+
<%= render "current_application_selector" %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
</nav>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<%= form_with(model: [ route.application, route] ) do |form| %>
|
2
|
+
<%= render partial: "form_errors", locals: { model: route } %>
|
3
|
+
|
4
|
+
<div class="field">
|
5
|
+
<%= form.label :name %>
|
6
|
+
<%= form.text_field :name %>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="field">
|
10
|
+
<%= form.label :pattern %>
|
11
|
+
<%= form.text_field :pattern %>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<div class="field">
|
15
|
+
<%= form.label_check_box :enabled, "Traffic allowed" %>
|
16
|
+
<%= form.check_box :enabled %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div class="field is-grouped">
|
20
|
+
<%= form.submit_primary %>
|
21
|
+
|
22
|
+
<%= form.div_control do %>
|
23
|
+
<%= link_to "Cancel", application_routes_path(route.application), class: "button is-link is-light" %>
|
24
|
+
<% end %>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<div class="level">
|
2
|
+
<div class="level-left">
|
3
|
+
<h2 class="subtitle level-item"><%= @application.name %> - Routes</h2>
|
4
|
+
</div>
|
5
|
+
<div class="level-right">
|
6
|
+
<%= link_to "New Route", new_application_route_path(@application), class: "button is-primary level-item" %>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="content">
|
11
|
+
<% if @routes.any? %>
|
12
|
+
<table class="table">
|
13
|
+
<thead>
|
14
|
+
<tr>
|
15
|
+
<th>Name</th>
|
16
|
+
<th>Pattern</th>
|
17
|
+
<th>Traffic Status</th>
|
18
|
+
</tr>
|
19
|
+
</thead>
|
20
|
+
<tbody>
|
21
|
+
<%= render @routes, application: @application %>
|
22
|
+
</tbody>
|
23
|
+
</table>
|
24
|
+
<% else %>
|
25
|
+
<p>No Routes have yet been configured.</p>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<nav class="breadcrumb" aria-label="breadcrumbs">
|
2
|
+
<ul>
|
3
|
+
<li><%= link_to @application.name, application_routes_path(@application) %></li>
|
4
|
+
<li class="is-active"><%= link_to @route.name, [ @application, @route ] %></li>
|
5
|
+
</ul>
|
6
|
+
</nav>
|
7
|
+
|
8
|
+
<div class="my-4">
|
9
|
+
<div class="columns">
|
10
|
+
<div class="column is-one-fifth">
|
11
|
+
<p class="has-text-weight-semibold">Name</p>
|
12
|
+
</div>
|
13
|
+
<div class="column">
|
14
|
+
<p><%= @route.name %></p>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div class="columns">
|
19
|
+
<div class="column is-one-fifth">
|
20
|
+
<p class="has-text-weight-semibold">Pattern</p>
|
21
|
+
</div>
|
22
|
+
<div class="column">
|
23
|
+
<p><%= @route.pattern %></p>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div class="columns">
|
28
|
+
<div class="column is-one-fifth">
|
29
|
+
<p class="has-text-weight-semibold">Traffic Status</p>
|
30
|
+
</div>
|
31
|
+
<div class="column">
|
32
|
+
<p><%= route_status_tag @route.enabled %></p>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
|
37
|
+
<div class="level">
|
38
|
+
<div class="level-left">
|
39
|
+
<%= link_to "Edit", edit_application_route_path(@application, @route), class: "button level-item" %>
|
40
|
+
<%= button_to "Delete", [ @application, @route ], method: :delete, class: "button is-danger level-item" %>
|
41
|
+
</div>
|
42
|
+
</div>
|
data/config/importmap.rb
ADDED
data/config/routes.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateMissionControlWebRoutes < ActiveRecord::Migration[7.0]
|
2
|
+
def up
|
3
|
+
create_table :mission_control_web_routes do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.string :pattern, null: false
|
6
|
+
t.boolean :enabled, default: true
|
7
|
+
t.string :application_id, null: false, index: true
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :mission_control_web_routes, [ :application_id, :pattern ], unique: true
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module MissionControl::Web::Install
|
2
|
+
class AdminGenerator < Rails::Generators::Base
|
3
|
+
INITIALIZER_FILE_PATH = "config/initializers/mission_control_web.rb"
|
4
|
+
|
5
|
+
def create_initializer_file
|
6
|
+
create_file INITIALIZER_FILE_PATH, <<~RUBY, skip: true
|
7
|
+
Rails.application.configure do
|
8
|
+
end
|
9
|
+
RUBY
|
10
|
+
end
|
11
|
+
|
12
|
+
def insert_admin_configuration
|
13
|
+
initializer = <<~RUBY
|
14
|
+
config.mission_control.web.middleware_enabled = false
|
15
|
+
|
16
|
+
# Admin
|
17
|
+
config.mission_control.web.administered_applications = [
|
18
|
+
{ name: "My App", redis: Redis.new(url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0")) }
|
19
|
+
]
|
20
|
+
RUBY
|
21
|
+
|
22
|
+
insert_into_file INITIALIZER_FILE_PATH, indent(initializer), after: "Rails.application.configure do\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_engine_routes
|
26
|
+
route "mount MissionControl::Web::Engine => '/mission_control-web'"
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy_migrations
|
30
|
+
rake "mission_control_web:install:migrations"
|
31
|
+
end
|
32
|
+
|
33
|
+
def run_migrations
|
34
|
+
say "Running migrations..."
|
35
|
+
rails_command "db:migrate"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module MissionControl::Web::Install
|
2
|
+
class MissionControl::Web::Install::MiddlewareGenerator < Rails::Generators::Base
|
3
|
+
INITIALIZER_FILE_PATH = "config/initializers/mission_control_web.rb"
|
4
|
+
|
5
|
+
def create_initializer_file
|
6
|
+
create_file INITIALIZER_FILE_PATH, <<~RUBY, skip: true
|
7
|
+
Rails.application.configure do
|
8
|
+
end
|
9
|
+
RUBY
|
10
|
+
end
|
11
|
+
|
12
|
+
def insert_middleware_configuration
|
13
|
+
initializer = <<~RUBY
|
14
|
+
# Middleware
|
15
|
+
config.mission_control.web.host_application_name = "My App"
|
16
|
+
config.mission_control.web.redis = Redis.new(url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0"))
|
17
|
+
RUBY
|
18
|
+
|
19
|
+
insert_into_file INITIALIZER_FILE_PATH, indent(initializer), after: "Rails.application.configure do\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
def toggle_middleware_enabled
|
23
|
+
gsub_file INITIALIZER_FILE_PATH, "middleware_enabled = false", "middleware_enabled = true"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Inspired by https://medium.com/@dyanagi/a-bulma-form-builder-for-ruby-on-rails-applications-aef780808bab
|
2
|
+
|
3
|
+
class MissionControl::Web::BulmaFormBuilder < ActionView::Helpers::FormBuilder
|
4
|
+
def label_default(method, text = nil, options = {}, &block)
|
5
|
+
label(method, text, merge_class(options, "label"), &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def label_check_box(method, text = nil, options = {}, &block)
|
9
|
+
label(method, text, merge_class(options, "checkbox"), &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def text_field(method, options = {})
|
13
|
+
super(method, merge_class(options, "input"))
|
14
|
+
end
|
15
|
+
|
16
|
+
def submit(value = nil, options = {})
|
17
|
+
div_control do
|
18
|
+
super(value, merge_class(options, "button"))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def submit_primary(value = nil, options = {})
|
23
|
+
submit(value, merge_class(options, "is-primary"))
|
24
|
+
end
|
25
|
+
|
26
|
+
def div_control
|
27
|
+
@template.content_tag(:div, class: "control") do
|
28
|
+
yield
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def select_with_label(method, choices = nil, options = {}, html_options = {}, &block)
|
33
|
+
label_default(method) + select(method, choices, options, html_options, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def select(method, choices = nil, options = {}, html_options = {}, &block)
|
37
|
+
label(method, class: "select") do
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def merge_class_attribute_value(options, value)
|
44
|
+
new_options = options.clone
|
45
|
+
new_options[:class] = [ value, new_options[:class] ].join(" ")
|
46
|
+
new_options
|
47
|
+
end
|
48
|
+
|
49
|
+
alias_method :merge_class, :merge_class_attribute_value
|
50
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class MissionControl::Web::Configuration
|
2
|
+
include ActiveModel::Attributes, ActiveModel::Dirty
|
3
|
+
|
4
|
+
attribute :host_application_name, :string
|
5
|
+
attribute :middleware_enabled, :boolean, default: true
|
6
|
+
attribute :routes_cache_ttl, :integer, default: 10.seconds
|
7
|
+
attribute :base_controller_class, :string, default: "::ApplicationController"
|
8
|
+
|
9
|
+
attr_writer :redis, :administered_applications
|
10
|
+
attr_accessor :errors_controller
|
11
|
+
|
12
|
+
alias :middleware_enabled? :middleware_enabled
|
13
|
+
|
14
|
+
def redis
|
15
|
+
@redis || raise("Redis client not configured. Configure with MissionControl::Web.configuration.redis = Redis.new")
|
16
|
+
end
|
17
|
+
|
18
|
+
def administered_applications
|
19
|
+
@administered_applications || []
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "importmap-rails"
|
2
|
+
require "turbo-rails"
|
3
|
+
|
4
|
+
module MissionControl
|
5
|
+
module Web
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
isolate_namespace MissionControl::Web
|
8
|
+
|
9
|
+
config.mission_control = ActiveSupport::OrderedOptions.new unless config.try(:mission_control)
|
10
|
+
config.mission_control.web = ActiveSupport::OrderedOptions.new
|
11
|
+
|
12
|
+
initializer "mission_control-web.config" do
|
13
|
+
config.mission_control.web.each do |key, value|
|
14
|
+
MissionControl::Web.configuration.public_send("#{key}=", value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
initializer "mission_control-web.request" do
|
19
|
+
ActiveSupport.on_load :action_dispatch_request do
|
20
|
+
include MissionControl::Web::ActionDispatchRequest
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
initializer "mission_control-web.add_middleware", after: "mission_control-web.config" do |app|
|
25
|
+
if MissionControl::Web.configuration.middleware_enabled?
|
26
|
+
app.middleware.use MissionControl::Web::RequestFilterMiddleware
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
initializer "mission_control-web.assets" do |app|
|
31
|
+
app.config.assets.paths << root.join("app/javascript")
|
32
|
+
app.config.assets.precompile += %w[ mission_control_web_manifest ]
|
33
|
+
end
|
34
|
+
|
35
|
+
initializer "mission_control-web.importmap", before: "importmap" do |app|
|
36
|
+
app.config.importmap.paths << root.join("config/importmap.rb")
|
37
|
+
app.config.importmap.cache_sweepers << root.join("app/javascript")
|
38
|
+
end
|
39
|
+
|
40
|
+
config.after_initialize do
|
41
|
+
MissionControl::Web.configuration.host_application_name ||= ::Rails.application.class.module_parent.to_s
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MissionControl::Web
|
2
|
+
class RequestFilter
|
3
|
+
attr_reader :request
|
4
|
+
|
5
|
+
def initialize(request)
|
6
|
+
@request = request
|
7
|
+
end
|
8
|
+
|
9
|
+
def action
|
10
|
+
request.fetch_header("mission_control.action") do
|
11
|
+
if MissionControl::Web.host_application.route_disabled?(request.path)
|
12
|
+
:disallowed
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module MissionControl::Web
|
2
|
+
class RequestFilterMiddleware
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
filter(env) || @app.call(env)
|
9
|
+
end
|
10
|
+
|
11
|
+
def filter(env)
|
12
|
+
return unless MissionControl::Web.configuration.middleware_enabled?
|
13
|
+
|
14
|
+
request = ActionDispatch::Request.new(env)
|
15
|
+
|
16
|
+
if action = request.mission_control.action
|
17
|
+
errors_controller.action(action).call(request.env)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def errors_controller
|
23
|
+
MissionControl::Web.configuration.errors_controller || MissionControl::Web::ErrorsController
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class MissionControl::Web::RoutesCache
|
2
|
+
delegate :redis, to: MissionControl::Web
|
3
|
+
|
4
|
+
def initialize(application)
|
5
|
+
@application = application
|
6
|
+
end
|
7
|
+
|
8
|
+
def put(route)
|
9
|
+
remove(route)
|
10
|
+
|
11
|
+
if route.disabled?
|
12
|
+
add(route)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove(route)
|
17
|
+
redis.srem redis_key, [ route.pattern_previously_was.to_s, route.pattern.to_s ]
|
18
|
+
end
|
19
|
+
|
20
|
+
def disabled?(path)
|
21
|
+
all_disabled_patterns.any? { |pattern| Regexp.new(pattern) =~ path }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
attr_reader :application
|
26
|
+
|
27
|
+
def add(route)
|
28
|
+
redis.sadd redis_key, route.pattern.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
def redis_key
|
32
|
+
:"mission_control_web_#{application.id}_disabled_patterns"
|
33
|
+
end
|
34
|
+
|
35
|
+
def all_disabled_patterns
|
36
|
+
memoize(ttl: MissionControl::Web.configuration.routes_cache_ttl) do
|
37
|
+
# Using Redis client rather than Kredis as request interception with a middleware is performance-critical.
|
38
|
+
redis.smembers redis_key
|
39
|
+
end
|
40
|
+
rescue Redis::BaseConnectionError
|
41
|
+
[]
|
42
|
+
end
|
43
|
+
|
44
|
+
def memoize(ttl:)
|
45
|
+
if !@patterns.nil? && @patterns_expires_at > Time.now
|
46
|
+
@patterns
|
47
|
+
else
|
48
|
+
@patterns_expires_at = Time.now + ttl
|
49
|
+
@patterns = yield
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|