upright 0.1.2 → 0.3.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +2 -3
  3. data/README.md +166 -60
  4. data/app/assets/stylesheets/upright/dashboard.css +65 -193
  5. data/app/assets/stylesheets/upright/tables.css +60 -0
  6. data/app/assets/stylesheets/upright/uptime-bars.css +13 -50
  7. data/app/controllers/upright/alertmanager_proxy_controller.rb +1 -1
  8. data/app/controllers/upright/dashboards/probe_statuses_controller.rb +7 -0
  9. data/app/controllers/upright/probe_results_controller.rb +1 -0
  10. data/app/controllers/upright/sessions_controller.rb +1 -0
  11. data/app/helpers/upright/application_helper.rb +10 -0
  12. data/app/helpers/upright/dashboards_helper.rb +12 -0
  13. data/app/helpers/upright/probe_results_helper.rb +3 -10
  14. data/app/javascript/upright/controllers/auto_refresh_controller.js +16 -0
  15. data/app/models/concerns/upright/playwright/form_authentication.rb +0 -3
  16. data/app/models/concerns/upright/playwright/lifecycle.rb +3 -2
  17. data/app/models/concerns/upright/probe_result/stale_cleanup.rb +23 -0
  18. data/app/models/concerns/upright/probeable.rb +7 -1
  19. data/app/models/upright/http/request.rb +1 -1
  20. data/app/models/upright/playwright/storage_state.rb +12 -3
  21. data/app/models/upright/probe_result.rb +6 -1
  22. data/app/models/upright/probes/http_probe.rb +6 -2
  23. data/app/models/upright/probes/status/probe.rb +24 -0
  24. data/app/models/upright/probes/status/site_status.rb +71 -0
  25. data/app/models/upright/probes/status.rb +54 -0
  26. data/app/models/upright/probes/uptime.rb +4 -4
  27. data/app/models/upright/traceroute/ip_metadata_lookup.rb +1 -2
  28. data/app/views/layouts/upright/_header.html.erb +2 -1
  29. data/app/views/upright/dashboards/_uptime_bars.html.erb +2 -2
  30. data/app/views/upright/dashboards/_uptime_probe_row.html.erb +7 -5
  31. data/app/views/upright/dashboards/probe_statuses/_matrix.html.erb +48 -0
  32. data/app/views/upright/dashboards/probe_statuses/show.html.erb +17 -0
  33. data/app/views/upright/dashboards/uptimes/show.html.erb +1 -1
  34. data/app/views/upright/probe_results/index.html.erb +7 -4
  35. data/app/views/upright/sites/index.html.erb +1 -1
  36. data/config/ci.rb +2 -0
  37. data/config/credentials/development.key +1 -0
  38. data/config/credentials/test.key +1 -0
  39. data/config/routes.rb +1 -0
  40. data/lib/generators/upright/install/install_generator.rb +52 -2
  41. data/lib/generators/upright/install/templates/http_probes.yml +1 -0
  42. data/lib/generators/upright/install/templates/recurring.yml +25 -0
  43. data/lib/generators/upright/install/templates/smtp_probes.yml +3 -0
  44. data/lib/generators/upright/install/templates/traceroute_probes.yml +12 -0
  45. data/lib/generators/upright/install/templates/upright.rb +4 -1
  46. data/lib/generators/upright/install/templates/upright.rules.yml +5 -5
  47. data/lib/upright/configuration.rb +14 -0
  48. data/lib/upright/engine.rb +7 -0
  49. data/lib/upright/geohash.rb +46 -0
  50. data/lib/upright/metrics.rb +2 -2
  51. data/lib/upright/probe_type_registry.rb +33 -0
  52. data/lib/upright/site.rb +1 -3
  53. data/lib/upright/version.rb +1 -1
  54. data/lib/upright.rb +6 -1
  55. metadata +17 -17
@@ -1,7 +1,7 @@
1
- <div class="uptime-bars__row">
2
- <%= link_to upright.site_root_url(subdomain: Upright.sites.first.code, probe_type: probe.type, probe_name: probe.name), class: "uptime-bars__probe" do %>
1
+ <div class="data-table__row uptime-bars__row">
2
+ <%= link_to upright.site_root_url(subdomain: current_or_default_site.code, probe_type: probe.type, probe_name: probe.name), class: "data-table__probe" do %>
3
3
  <%= probe_type_icon(probe.type) %>
4
- <span class="probe__name"><%= probe.name %></span>
4
+ <span class="data-table__probe-name"><%= probe.name %></span>
5
5
  <% end %>
6
6
  <div class="uptime-bars__days">
7
7
  <% dates.each do |date| %>
@@ -11,8 +11,10 @@
11
11
  <% else %>
12
12
  <% uptime_percent = uptime * 100 %>
13
13
  <% downtime_minutes = ((1 - uptime) * 24 * 60).round %>
14
- <div class="uptime-bar uptime-bar--<%= uptime_label(uptime_percent) %>"
15
- title="<%= uptime_bar_tooltip(date, uptime_percent, downtime_minutes) %>"></div>
14
+ <%= link_to "", upright.site_root_url(subdomain: current_or_default_site.code, probe_type: probe.type, probe_name: probe.name, date: date.iso8601),
15
+ class: "uptime-bar uptime-bar--#{uptime_label(uptime_percent)}",
16
+ title: uptime_bar_tooltip(date, uptime_percent, downtime_minutes),
17
+ aria: { label: uptime_bar_tooltip(date, uptime_percent, downtime_minutes) } %>
16
18
  <% end %>
17
19
  <% end %>
18
20
  </div>
@@ -0,0 +1,48 @@
1
+ <% if probes.empty? %>
2
+ <div class="no-data-message">
3
+ No probe data available for the selected type.
4
+ </div>
5
+ <% else %>
6
+ <div class="scrollable-x">
7
+ <div class="data-table" style="--cols: <%= sites.size %>">
8
+ <div class="data-table__header probe-status__header">
9
+ <div class="sticky-left">Probe</div>
10
+ <% sites.each do |site| %>
11
+ <div class="text-center"><%= site_name(site) %></div>
12
+ <% end %>
13
+ </div>
14
+ <% probes.each do |probe| %>
15
+ <div class="data-table__row probe-status__row <%= "probe-status-row--down" if probe.any_down? %>">
16
+ <div class="sticky-left">
17
+ <%= link_to upright.site_root_url(subdomain: Upright.sites.first.code, probe_type: probe.type, probe_name: probe.name), class: "data-table__probe" do %>
18
+ <%= probe_type_icon(probe.type) %>
19
+ <span><%= probe.name %></span>
20
+ <% end %>
21
+ </div>
22
+ <% sites.each do |site| %>
23
+ <% status = probe.status_for_site(site.code) %>
24
+ <div class="text-center <%= probe_status_css_class(status) %>" data-label="<%= site_name(site) %>">
25
+ <%= link_to upright.site_root_url(subdomain: site.code, probe_type: probe.type, probe_name: probe.name), class: "probe-status-cell-link" do %>
26
+ <% if status.nil? %>
27
+ <span class="probe-status-indicator probe-status-indicator--unknown">—</span>
28
+ <% elsif status.stale? %>
29
+ <span class="probe-status-indicator probe-status-indicator--stale">STALE</span>
30
+ <% elsif status.up? %>
31
+ <span class="probe-status-indicator probe-status-indicator--up">UP</span>
32
+ <% else %>
33
+ <span class="probe-status-indicator probe-status-indicator--down">
34
+ <% if status.down_since_known? %>
35
+ DOWN for <%= time_ago_in_words(status.down_since) %>
36
+ <% else %>
37
+ DOWN
38
+ <% end %>
39
+ </span>
40
+ <% end %>
41
+ <% end %>
42
+ </div>
43
+ <% end %>
44
+ </div>
45
+ <% end %>
46
+ </div>
47
+ </div>
48
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <div class="dashboard probe-status-dashboard">
2
+ <header class="dashboard-header">
3
+ <h1>Probe Status</h1>
4
+
5
+ <div class="dashboard-filters">
6
+ <%= form_with url: dashboards_probe_status_path, method: :get, data: { controller: "form", turbo_frame: :probe_status_matrix } do %>
7
+ <%= collection_select nil, :probe_type, Upright.probe_types, :type, :name,
8
+ { selected: @probe_type },
9
+ { class: "input--select", data: { action: "change->form#submit" } } %>
10
+ <% end %>
11
+ </div>
12
+ </header>
13
+
14
+ <turbo-frame id="probe_status_matrix" data-controller="auto-refresh" data-auto-refresh-interval-value="60000">
15
+ <%= render "matrix", probes: @probes, sites: @sites %>
16
+ </turbo-frame>
17
+ </div>
@@ -4,7 +4,7 @@
4
4
 
5
5
  <div class="dashboard-filters">
6
6
  <%= form_with url: dashboards_uptime_path, method: :get, data: { controller: "form", turbo_frame: :uptime_bars } do %>
7
- <%= collection_select nil, :probe_type, Upright::Probeable::TYPES, :itself, :titleize,
7
+ <%= collection_select nil, :probe_type, Upright.probe_types, :type, :name,
8
8
  { selected: @probe_type },
9
9
  { class: "input--select", data: { action: "change->form#submit" } } %>
10
10
  <% end %>
@@ -2,10 +2,9 @@
2
2
  <aside>
3
3
  <ul>
4
4
  <li><%= type_filter_link "All" %></li>
5
- <li><%= type_filter_link "HTTP", "http" %></li>
6
- <li><%= type_filter_link "Playwright", "playwright" %></li>
7
- <li><%= type_filter_link "SMTP", "smtp" %></li>
8
- <li><%= type_filter_link "Traceroute", "traceroute" %></li>
5
+ <% Upright.probe_types.each do |probe_type| %>
6
+ <li><%= type_filter_link probe_type.name, probe_type.type %></li>
7
+ <% end %>
9
8
  </ul>
10
9
  </aside>
11
10
 
@@ -16,6 +15,7 @@
16
15
  <div class="filters">
17
16
  <%= form_with url: site_root_path, method: :get, data: { controller: "form" } do |f| %>
18
17
  <%= hidden_field_tag :probe_type, params[:probe_type] if params[:probe_type].present? %>
18
+ <%= hidden_field_tag :date, params[:date] if params[:date].present? %>
19
19
  <label for="probe_name">Name</label>
20
20
  <%= select_tag :probe_name,
21
21
  options_for_select([["All", ""]] + @probe_names, params[:probe_name]),
@@ -26,6 +26,9 @@
26
26
  options_for_select([["All", ""]] + Upright::ProbeResult.statuses.keys.map { |s| [s.titleize, s] }, params[:status]),
27
27
  class: "input--select",
28
28
  data: { action: "change->form#submit" } %>
29
+ <% if params[:date].present? %>
30
+ <%= link_to "Clear date filter", site_root_path(probe_type: params[:probe_type], probe_name: params[:probe_name], status: params[:status]), class: "filter-clear" %>
31
+ <% end %>
29
32
  <% end %>
30
33
  </div>
31
34
 
@@ -14,7 +14,7 @@
14
14
  <% @sites.each do |site| %>
15
15
  <tr>
16
16
  <td><%= link_to site.host, site.url %></td>
17
- <td><%= site.city %></td>
17
+ <td><%= site_name(site) %></td>
18
18
  </tr>
19
19
  <% end %>
20
20
  </tbody>
data/config/ci.rb CHANGED
@@ -3,5 +3,7 @@
3
3
  CI.run do
4
4
  step "Style: Ruby", "bin/rubocop"
5
5
  step "Security: Brakeman code analysis", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
6
+ step "Lint: GitHub Actions (actionlint)", "actionlint"
7
+ step "Lint: GitHub Actions (zizmor)", "zizmor ."
6
8
  step "Tests: Rails", "bin/rails test"
7
9
  end
@@ -0,0 +1 @@
1
+ 40451408e038d0fe36a8f4aed0cef429
@@ -0,0 +1 @@
1
+ 40451408e038d0fe36a8f4aed0cef429
data/config/routes.rb CHANGED
@@ -10,6 +10,7 @@ Upright::Engine.routes.draw do
10
10
 
11
11
  namespace :dashboards do
12
12
  resource :uptime, only: :show
13
+ resource :probe_status, only: :show
13
14
  end
14
15
 
15
16
  scope :framed do
@@ -5,6 +5,15 @@ module Upright
5
5
 
6
6
  desc "Install Upright engine into your application"
7
7
 
8
+ def set_ruby_version
9
+ ruby_version = RUBY_VERSION
10
+ create_file ".ruby-version", "#{ruby_version}\n"
11
+ create_file "mise.toml", <<~TOML
12
+ [tools]
13
+ ruby = "#{ruby_version}"
14
+ TOML
15
+ end
16
+
8
17
  def copy_initializers
9
18
  template "upright.rb", "config/initializers/upright.rb"
10
19
  template "omniauth.rb", "config/initializers/omniauth.rb"
@@ -19,6 +28,7 @@ module Upright
19
28
  empty_directory "probes/authenticators"
20
29
  template "http_probes.yml", "probes/http_probes.yml"
21
30
  template "smtp_probes.yml", "probes/smtp_probes.yml"
31
+ template "traceroute_probes.yml", "probes/traceroute_probes.yml"
22
32
  end
23
33
 
24
34
  def copy_observability_configs
@@ -42,23 +52,63 @@ module Upright
42
52
  template "puma.rb", "config/puma.rb"
43
53
  end
44
54
 
55
+ def install_solid_queue
56
+ rails_command "solid_queue:install"
57
+ end
58
+
59
+ def add_queue_database
60
+ gsub_file "config/database.yml",
61
+ "development:\n <<: *default\n database: storage/development.sqlite3",
62
+ "development:\n primary:\n <<: *default\n database: storage/development.sqlite3\n queue:\n <<: *default\n database: storage/development_queue.sqlite3\n migrations_paths: db/queue_migrate"
63
+
64
+ gsub_file "config/database.yml",
65
+ "test:\n <<: *default\n database: storage/test.sqlite3",
66
+ "test:\n primary:\n <<: *default\n database: storage/test.sqlite3\n queue:\n <<: *default\n database: storage/test_queue.sqlite3\n migrations_paths: db/queue_migrate"
67
+ end
68
+
69
+ def copy_recurring_config
70
+ template "recurring.yml", "config/recurring.yml", force: true
71
+ end
72
+
73
+ def add_jobs_to_procfile
74
+ procfile = File.join(destination_root, "Procfile.dev")
75
+ if File.exist?(procfile)
76
+ unless File.read(procfile).include?("jobs:")
77
+ append_to_file "Procfile.dev", "jobs: OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES bin/rails solid_queue:start\n"
78
+ end
79
+ else
80
+ create_file "Procfile.dev", <<~PROCFILE
81
+ web: bin/rails server -b '0.0.0.0' -p ${PORT:-3000}
82
+ jobs: OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES bin/rails solid_queue:start
83
+ PROCFILE
84
+ end
85
+ end
86
+
45
87
  def add_routes
46
88
  route 'mount Upright::Engine => "/", as: :upright'
47
89
  end
48
90
 
91
+ def install_active_storage
92
+ rails_command "active_storage:install"
93
+ end
94
+
95
+ def configure_javascript
96
+ append_to_file "app/javascript/application.js", 'import "upright/application"'
97
+ end
98
+
49
99
  def show_post_install_message
50
100
  say ""
51
101
  say "Upright has been installed!", :green
52
102
  say ""
53
103
  say "Next steps:"
54
- say " 1. Run migrations: bin/rails db:migrate"
104
+ say " 1. Prepare the database: bin/rails db:prepare"
55
105
  say " 2. Configure your servers in config/deploy.yml"
56
106
  say " 3. Configure sites in config/sites.yml"
57
107
  say " 4. Add probes in probes/*.yml"
58
108
  say " 5. Set ADMIN_PASSWORD env var (default: upright)"
59
109
  say ""
60
110
  say "For production, review config/initializers/upright.rb and update:"
61
- say " config.hostname = \"honcho-upright.com\""
111
+ say " config.hostname = \"example.com\""
62
112
  say ""
63
113
  say "Start dev services (Prometheus, Alertmanager, Playwright):"
64
114
  say " docker compose up -d"
@@ -7,6 +7,7 @@
7
7
  #
8
8
  # Optional fields:
9
9
  # - expected_status: HTTP status code to expect (default: 200-399)
10
+ # - alert_severity: Alert severity when probe fails: medium, high (default), critical
10
11
  # - basic_auth_credentials: Key in Rails credentials for basic auth
11
12
  # - proxy: Key in Rails credentials for proxy settings
12
13
 
@@ -0,0 +1,25 @@
1
+ development:
2
+ http_probes:
3
+ schedule: every minute
4
+ command: "Upright::Probes::HTTPProbe.check_and_record_all_later"
5
+
6
+ smtp_probes:
7
+ schedule: every minute
8
+ command: "Upright::Probes::SMTPProbe.check_and_record_all_later"
9
+
10
+ production:
11
+ http_probes:
12
+ schedule: "*/30 * * * * *"
13
+ command: "Upright::Probes::HTTPProbe.check_and_record_all_later"
14
+
15
+ smtp_probes:
16
+ schedule: "*/30 * * * * *"
17
+ command: "Upright::Probes::SMTPProbe.check_and_record_all_later"
18
+
19
+ clear_solid_queue_finished_jobs:
20
+ command: "SolidQueue::Job.clear_finished_in_batches(sleep_between_batches: 0.3)"
21
+ schedule: every hour at minute 12
22
+
23
+ cleanup_stale_probe_results:
24
+ command: "Upright::ProbeResult.cleanup_stale"
25
+ schedule: every hour at minute 30
@@ -4,6 +4,9 @@
4
4
  # Required fields:
5
5
  # - name: Unique identifier for the probe
6
6
  # - host: SMTP server hostname
7
+ #
8
+ # Optional fields:
9
+ # - alert_severity: Alert severity when probe fails: medium, high (default), critical
7
10
 
8
11
  # - name: Example Mail Server
9
12
  # host: mail.example.com
@@ -0,0 +1,12 @@
1
+ # Traceroute Probe Definitions
2
+ # Add your traceroute probes here
3
+ #
4
+ # Required fields:
5
+ # - name: Unique identifier for the probe
6
+ # - host: Hostname or IP address to trace
7
+ #
8
+ # Optional fields:
9
+ # - alert_severity: Alert severity when probe fails: medium, high (default), critical
10
+
11
+ # - name: Example Service
12
+ # host: example.com
@@ -3,7 +3,7 @@
3
3
  Upright.configure do |config|
4
4
  config.service_name = "<%= Rails.application.class.module_parent_name.underscore %>"
5
5
  config.user_agent = "<%= Rails.application.class.module_parent_name.underscore %>/1.0"
6
- config.hostname = Rails.env.local? ? "<%= Rails.application.class.module_parent_name.downcase %>.localhost" : "<%= Rails.application.class.module_parent_name.downcase %>.com"
6
+ config.hostname = Rails.env.local? ? "<%= Rails.application.class.module_parent_name.underscore.dasherize %>.localhost" : "<%= Rails.application.class.module_parent_name.underscore.dasherize %>.com"
7
7
 
8
8
  # Playwright browser server URL
9
9
  # config.playwright_server_url = ENV["PLAYWRIGHT_SERVER_URL"]
@@ -18,4 +18,7 @@ Upright.configure do |config|
18
18
  # client_id: ENV["OIDC_CLIENT_ID"],
19
19
  # client_secret: ENV["OIDC_CLIENT_SECRET"]
20
20
  # }
21
+
22
+ # Register custom probe types (built-in types: http, playwright, smtp, traceroute)
23
+ # config.probe_types.register :ftp_file, name: "FTP File", icon: "📂"
21
24
  end
@@ -8,9 +8,9 @@ groups:
8
8
  # Fraction of regions reporting DOWN (0.0 to 1.0)
9
9
  - record: upright:probe_down_fraction
10
10
  expr: |
11
- count by (name, type, probe_target) (upright_probe_up == 0)
11
+ count by (name, type, probe_target, alert_severity) (upright_probe_up == 0)
12
12
  /
13
- count by (name, type, probe_target) (upright_probe_up)
13
+ count by (name, type, probe_target, alert_severity) (upright_probe_up)
14
14
 
15
15
  # Daily uptime percentage (0.0 to 1.0)
16
16
  # Uptime = percentage of time in past day when majority of sites reported UP
@@ -148,7 +148,7 @@ groups:
148
148
  upright: "https://app.<%= app_domain %>/?probe_type={{ $labels.type }}&status=fail&probe_name={{ $labels.name | urlquery }}"
149
149
  expr: upright:probe_down_fraction{type="http"} > 0.5
150
150
  labels:
151
- severity: page
151
+ severity: "{{ $labels.alert_severity }}"
152
152
  group: upright
153
153
 
154
154
  - alert: UprightHTTPProbeDegraded
@@ -187,7 +187,7 @@ groups:
187
187
  upright: "https://app.<%= app_domain %>/?probe_type={{ $labels.type }}&status=fail&probe_name={{ $labels.name | urlquery }}"
188
188
  expr: upright:probe_down_fraction{type="smtp"} > 0.5
189
189
  labels:
190
- severity: page
190
+ severity: "{{ $labels.alert_severity }}"
191
191
  group: upright
192
192
 
193
193
  - alert: UprightSMTPProbeDegraded
@@ -226,7 +226,7 @@ groups:
226
226
  upright: "https://app.<%= app_domain %>/?probe_type={{ $labels.type }}&status=fail&probe_name={{ $labels.name | urlquery }}"
227
227
  expr: upright:probe_down_fraction{type="playwright"} > 0.5
228
228
  labels:
229
- severity: page
229
+ severity: "{{ $labels.alert_severity }}"
230
230
  group: upright
231
231
 
232
232
  - alert: UprightPlaywrightProbeDegraded
@@ -29,6 +29,14 @@ class Upright::Configuration
29
29
  attr_accessor :prometheus_url
30
30
  attr_accessor :alert_webhook_url
31
31
 
32
+ # Probe types
33
+ attr_reader :probe_types
34
+
35
+ # Probe result cleanup
36
+ attr_accessor :stale_success_threshold
37
+ attr_accessor :stale_failure_threshold
38
+ attr_accessor :failure_retention_limit
39
+
32
40
  def initialize
33
41
  @service_name = "upright"
34
42
  @user_agent = "Upright/1.0"
@@ -41,11 +49,17 @@ class Upright::Configuration
41
49
  @probes_path = nil
42
50
  @authenticators_path = nil
43
51
 
52
+ @probe_types = Upright::ProbeTypeRegistry.new
53
+
44
54
  @playwright_server_url = ENV["PLAYWRIGHT_SERVER_URL"]
45
55
  @otel_endpoint = ENV["OTEL_EXPORTER_OTLP_ENDPOINT"]
46
56
 
47
57
  @auth_provider = :static_credentials
48
58
  @auth_options = {}
59
+
60
+ @stale_success_threshold = 24.hours
61
+ @stale_failure_threshold = 30.days
62
+ @failure_retention_limit = 20_000
49
63
  end
50
64
 
51
65
  def global_subdomain
@@ -56,6 +56,13 @@ class Upright::Engine < ::Rails::Engine
56
56
  end
57
57
  end
58
58
 
59
+ initializer "upright.probe_types", before: :load_config_initializers do
60
+ Upright.config.probe_types.register :http, name: "HTTP", icon: "🌐"
61
+ Upright.config.probe_types.register :playwright, name: "Playwright", icon: "🎭"
62
+ Upright.config.probe_types.register :smtp, name: "SMTP", icon: "✉️"
63
+ Upright.config.probe_types.register :traceroute, name: "Traceroute", icon: "🛤️"
64
+ end
65
+
59
66
  initializer "upright.frozen_record" do
60
67
  FrozenRecord::Base.base_path = Upright.configuration.frozen_record_path
61
68
  end
@@ -0,0 +1,46 @@
1
+ module Upright
2
+ module Geohash
3
+ BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz"
4
+
5
+ module_function
6
+
7
+ def decode(geohash)
8
+ bounds = [ [ -90.0, +90.0 ], [ -180.0, +180.0 ] ]
9
+
10
+ geohash.downcase.each_char.with_index do |c, i|
11
+ d = BASE32.index c
12
+
13
+ 5.times do |j|
14
+ bit = (d & (1 << (4 - j))) >> (4 - j)
15
+ k = (~i & 1) ^ (j & 1)
16
+ bounds[k][bit ^ 1] = (bounds[k][0] + bounds[k][1]) / 2
17
+ end
18
+ end
19
+
20
+ bounds.transpose
21
+ end
22
+
23
+ def encode(latitude, longitude, precision = 12)
24
+ mids = [ latitude, longitude ]
25
+ bounds = [ [ -90.0, +90.0 ], [ -180.0, +180.0 ] ]
26
+
27
+ geohash = +""
28
+
29
+ precision.times do |i|
30
+ d = 0
31
+
32
+ 5.times do |j|
33
+ k = (~i & 1) ^ (j & 1)
34
+ mid = (bounds[k][0] + bounds[k][1]) / 2
35
+ bit = mids[k] > mid ? 1 : 0
36
+ bounds[k][bit ^ 1] = mid
37
+ d |= bit << (4 - j)
38
+ end
39
+
40
+ geohash << BASE32[d]
41
+ end
42
+
43
+ geohash
44
+ end
45
+ end
46
+ end
@@ -44,12 +44,12 @@ module Upright::Metrics
44
44
  gauge :probe_duration_seconds,
45
45
  comment: "Duration of each probe",
46
46
  aggregation: :max,
47
- tags: %i[type name probe_target probe_service status]
47
+ tags: %i[type name probe_target probe_service alert_severity status]
48
48
 
49
49
  gauge :probe_up,
50
50
  comment: "Probe status (1 = up, 0 = down)",
51
51
  aggregation: :most_recent,
52
- tags: %i[type name probe_target probe_service]
52
+ tags: %i[type name probe_target probe_service alert_severity]
53
53
 
54
54
  gauge :http_response_status,
55
55
  comment: "HTTP response status code",
@@ -0,0 +1,33 @@
1
+ module Upright
2
+ class ProbeTypeRegistry
3
+ ProbeType = Data.define(:type, :name, :icon)
4
+
5
+ include Enumerable
6
+
7
+ def initialize
8
+ @probe_types = []
9
+ end
10
+
11
+ def register(type, name:, icon:)
12
+ type = type.to_s
13
+ @probe_types.reject! { |pt| pt.type == type }
14
+ @probe_types << ProbeType.new(type:, name:, icon:)
15
+ end
16
+
17
+ def types
18
+ @probe_types.map(&:type)
19
+ end
20
+
21
+ def find(type)
22
+ @probe_types.find { |pt| pt.type == type.to_s }
23
+ end
24
+
25
+ def unregister(type)
26
+ @probe_types.reject! { |pt| pt.type == type.to_s }
27
+ end
28
+
29
+ def each(&block)
30
+ @probe_types.each(&block)
31
+ end
32
+ end
33
+ end
data/lib/upright/site.rb CHANGED
@@ -1,5 +1,3 @@
1
- require "geohash_ruby"
2
-
3
1
  module Upright
4
2
  class Site
5
3
  attr_reader :code, :city, :country, :geohash, :stagger_index
@@ -43,7 +41,7 @@ module Upright
43
41
 
44
42
  private
45
43
  def coordinates
46
- @coordinates ||= Geohash.decode(geohash).first
44
+ @coordinates ||= Upright::Geohash.decode(geohash).first
47
45
  end
48
46
  end
49
47
  end
@@ -1,3 +1,3 @@
1
1
  module Upright
2
- VERSION = "0.1.2"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/upright.rb CHANGED
@@ -14,12 +14,13 @@ require "importmap-rails"
14
14
  require "turbo-rails"
15
15
  require "stimulus-rails"
16
16
  require "geared_pagination"
17
- require "geohash_ruby"
18
17
  require "yabeda/prometheus"
19
18
  require "yabeda/puma/plugin"
20
19
 
21
20
  require "upright/version"
22
21
  require "upright/configuration"
22
+ require "upright/probe_type_registry"
23
+ require "upright/geohash"
23
24
  require "upright/site"
24
25
  require "upright/metrics"
25
26
  require "upright/tracing"
@@ -38,6 +39,10 @@ module Upright
38
39
  yield(configuration)
39
40
  end
40
41
 
42
+ def probe_types
43
+ configuration.probe_types
44
+ end
45
+
41
46
  def sites
42
47
  @sites ||= load_sites
43
48
  end