puny-monitor 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef7da3ad71ee116621cfd11bc930e6ab5624aad481afaa024e6e09790916cde4
4
- data.tar.gz: d590745c16a3ca947e1a1af8b91931cbf9daec21e9f1703a308b8be7e92e5bf5
3
+ metadata.gz: 5563bcd36e5c112a41100ba5138ca04eb9c2d8ad4cdf7782f9b3847d4d7cc44c
4
+ data.tar.gz: dcab8591a17194d2a92e0ff23f052d6680348d3925a36f45cef69caf8e5c2130
5
5
  SHA512:
6
- metadata.gz: 345ed87c689aa7c153009f8a3f21fa33778ed97cf1db0a3e60948717ee0906886c4d9eb037a6b4602567354a34c89378f9fa57fb7b1f4a3596024f1580f2b687
7
- data.tar.gz: af40c578d0c669f4216e0eaaa456154835819934c79f6befb40ea9200e888eba3854bef9594278644718ab242bc0c9804c862f8bdbca02d1df323ebeae6892e3
6
+ metadata.gz: e1f2e834bb72b0918f50d80c7a842d8bcdf685d3caddcb28e7aea60808836e90371330cfc2f31d8f479b4414f6db85bdd2883c17dce6cb44db6a47351245a7d5
7
+ data.tar.gz: ba927bfbd53adfe1aab34ed2b2c0e5cb7452a6128a99cdf61c52d1e6ab2876b59673c24357cf5d6a75defd1830d5a1ad700c2b2124fdbf019d805b796e76bf31
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.2.0] - 2025-08-18
4
+
5
+ - Add HTTP authentication
6
+ - Upgrade to Ruby 3.4
7
+
8
+ ## [0.1.0] - 2024-09-15
9
+
10
+ - Initial release
data/README.md CHANGED
@@ -33,6 +33,20 @@ docker run --rm \
33
33
 
34
34
  Visit [localhost:4567](http://localhost:4567) to check your system data. To see how to deploy Puny Monitor in a production environment see [Deployment].
35
35
 
36
+ ## Authentication
37
+
38
+ Puny monitor supports Basic HTTP Authentication. To enable authentication set both the `PUNY_USERNAME` and `PUNY_PASSWORD` environment variables. For example, using with `docker run`:
39
+
40
+ ```
41
+ docker run --rm \
42
+ -v=/:/host:ro,rslave -v=puny-data:/puny-monitor/db \
43
+ -e HOST_PATH=/host \
44
+ -e PUNY_USERNAME=admin \
45
+ -e PUNY_PASSWORD=secret \
46
+ -p 4567:4567 \
47
+ hschne/puny-monitor:latest
48
+ ```
49
+
36
50
  ## Deployment
37
51
 
38
52
  Puny Monitor was made with [Kamal](https://kamal-deploy.org/) and [Ruby on Rails](https://rubyonrails.org/) in mind. It is recommended that you deploy it as an accessory to your application. Add the following lines to `config/deploy.yml`:
@@ -81,7 +95,14 @@ To put it simply, Puny Monitor replicates [Digital Ocean's Monitoring](https://w
81
95
 
82
96
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
83
97
 
84
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
98
+ To install this gem onto your local machine, run `bundle exec rake install`. To run Puny Monitor locally use
99
+ ```
100
+ bundle exec rackup
101
+ ```
102
+
103
+ Alternatively, you may use the various docker helpers defined in the [Rakefile].
104
+
105
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
85
106
 
86
107
  ## Contributing
87
108
 
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PunyMonitor
4
+ module Authorization
5
+ def self.included(base)
6
+ return unless authorize?
7
+
8
+ base.use Rack::Auth::Basic, 'Puny Monitor' do |username, password|
9
+ username == self.username && password == self.password
10
+ end
11
+ end
12
+
13
+ def self.username
14
+ @username ||= ENV.fetch('PUNY_USERNAME', nil)
15
+ end
16
+
17
+ def self.password
18
+ @password ||= ENV.fetch('PUNY_PASSWORD', nil)
19
+ end
20
+
21
+ def self.authorize?
22
+ puts username
23
+ puts password
24
+ username && password
25
+ end
26
+ end
27
+ end
@@ -4,8 +4,8 @@ class Bandwidth < ActiveRecord::Base
4
4
  class << self
5
5
  def average_usage(start_time, group_by)
6
6
  [
7
- { name: "Incoming Mbps", data: average_for_period(:incoming_mbps, start_time, group_by) },
8
- { name: "Outgoing Mbps", data: average_for_period(:outgoing_mbps, start_time, group_by) }
7
+ {name: "Incoming Mbps", data: average_for_period(:incoming_mbps, start_time, group_by)},
8
+ {name: "Outgoing Mbps", data: average_for_period(:outgoing_mbps, start_time, group_by)}
9
9
  ]
10
10
  end
11
11
 
@@ -3,9 +3,9 @@
3
3
  class CpuLoad < ActiveRecord::Base
4
4
  def self.average_load(start_time, end_time, group_by)
5
5
  [
6
- { name: "1 minute", data: average_for_period(:one_minute, start_time, end_time, group_by) },
7
- { name: "5 minutes", data: average_for_period(:five_minutes, start_time, end_time, group_by) },
8
- { name: "15 minutes", data: average_for_period(:fifteen_minutes, start_time, end_time, group_by) }
6
+ {name: "1 minute", data: average_for_period(:one_minute, start_time, end_time, group_by)},
7
+ {name: "5 minutes", data: average_for_period(:five_minutes, start_time, end_time, group_by)},
8
+ {name: "15 minutes", data: average_for_period(:fifteen_minutes, start_time, end_time, group_by)}
9
9
  ]
10
10
  end
11
11
 
@@ -4,8 +4,8 @@ class DiskIO < ActiveRecord::Base
4
4
  class << self
5
5
  def average_io(start_time, group_by)
6
6
  [
7
- { name: "Read MB/s", data: average_for_period(:read_mb_per_sec, start_time, group_by) },
8
- { name: "Write MB/s", data: average_for_period(:write_mb_per_sec, start_time, group_by) }
7
+ {name: "Read MB/s", data: average_for_period(:read_mb_per_sec, start_time, group_by)},
8
+ {name: "Write MB/s", data: average_for_period(:write_mb_per_sec, start_time, group_by)}
9
9
  ]
10
10
  end
11
11
 
data/app/puny_monitor.rb CHANGED
@@ -1,16 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rufus-scheduler"
4
- require_relative "scheduler"
5
- require_relative "../lib/system_utils"
3
+ require 'rufus-scheduler'
4
+ require_relative 'scheduler'
5
+ require_relative 'authorization'
6
+ require_relative '../lib/system_utils'
6
7
 
7
8
  module PunyMonitor
8
9
  class App < Sinatra::Base
10
+ # include Authorization
11
+
9
12
  configure do
10
13
  register Sinatra::ActiveRecordExtension
11
14
  @scheduler = Rufus::Scheduler.new
12
- @scheduler.every("5s") { Scheduler.collect_data }
13
- @scheduler.every("1h") { Scheduler.cleanup_old_data }
15
+ @scheduler.every('5s') { Scheduler.collect_data }
16
+ @scheduler.every('1h') { Scheduler.cleanup_old_data }
14
17
  end
15
18
 
16
19
  configure :development do
@@ -18,43 +21,43 @@ module PunyMonitor
18
21
  end
19
22
 
20
23
  set :erb, layout: :layout
21
- set :public_folder, File.join(__dir__, "..", "public")
22
- set :database_file, "../config/database.yml"
24
+ set :public_folder, File.join(__dir__, '..', 'public')
25
+ set :database_file, '../config/database.yml'
23
26
 
24
- get "/" do
27
+ get '/' do
25
28
  erb :index, locals: { params:, logo: }
26
29
  end
27
30
 
28
- get "/up" do
31
+ get '/up' do
29
32
  200
30
33
  end
31
34
 
32
- get "/data/cpu_usage" do
35
+ get '/data/cpu_usage' do
33
36
  content_type :json
34
37
  CpuUsage.average_usage(start_time, group_by).to_json
35
38
  end
36
39
 
37
- get "/data/cpu_load" do
40
+ get '/data/cpu_load' do
38
41
  content_type :json
39
42
  CpuLoad.average_load(start_time, Time.now, group_by).to_json
40
43
  end
41
44
 
42
- get "/data/memory_usage" do
45
+ get '/data/memory_usage' do
43
46
  content_type :json
44
47
  MemoryUsage.average_usage(start_time, group_by).to_json
45
48
  end
46
49
 
47
- get "/data/filesystem_usage" do
50
+ get '/data/filesystem_usage' do
48
51
  content_type :json
49
52
  FilesystemUsage.average_usage(start_time, group_by).to_json
50
53
  end
51
54
 
52
- get "/data/disk_io" do
55
+ get '/data/disk_io' do
53
56
  content_type :json
54
57
  DiskIO.average_io(start_time, group_by).to_json
55
58
  end
56
59
 
57
- get "/data/bandwidth" do
60
+ get '/data/bandwidth' do
58
61
  content_type :json
59
62
  Bandwidth.average_usage(start_time, group_by).to_json
60
63
  end
@@ -63,28 +66,28 @@ module PunyMonitor
63
66
 
64
67
  def logo
65
68
  @logo ||= begin
66
- file = File.open("public/icon.svg")
69
+ file = File.open('public/icon.svg')
67
70
  file.read
68
71
  end
69
72
  end
70
73
 
71
74
  def duration
72
- params[:duration] || "1d"
75
+ params[:duration] || '1d'
73
76
  end
74
77
 
75
78
  def start_time
76
79
  case duration
77
- when "1h" then 1.hour.ago
78
- when "3d" then 3.days.ago
79
- when "1w" then 1.week.ago
80
- when "1m" then 1.month.ago
80
+ when '1h' then 1.hour.ago
81
+ when '3d' then 3.days.ago
82
+ when '1w' then 1.week.ago
83
+ when '1m' then 1.month.ago
81
84
  else 1.day.ago
82
85
  end
83
86
  end
84
87
 
85
88
  def group_by
86
89
  case duration
87
- when "1h", "1d" then :minute
90
+ when '1h', '1d' then :minute
88
91
  else :hour
89
92
  end
90
93
  end
data/app/scheduler.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../lib/system_utils"
4
- require "debug"
3
+ require_relative '../lib/system_utils'
5
4
 
6
5
  module PunyMonitor
7
6
  class Scheduler
@@ -25,7 +24,7 @@ module PunyMonitor
25
24
  def cleanup_old_data
26
25
  one_month_ago = 1.month.ago
27
26
  [CpuUsage, CpuLoad, MemoryUsage, FilesystemUsage, DiskIO, Bandwidth].each do |model|
28
- model.where("created_at < ?", one_month_ago).delete_all
27
+ model.where('created_at < ?', one_month_ago).delete_all
29
28
  end
30
29
  end
31
30
  end
data/app/views/layout.erb CHANGED
@@ -13,13 +13,16 @@
13
13
  <link rel="icon" href="favicon.ico" sizes="32x32">
14
14
  <link rel="icon" href="icon.svg" type="image/svg+xml">
15
15
 
16
- <script
17
- src="https://cdn.jsdelivr.net/npm/chartkick@4.2.0/dist/chartkick.min.js"
18
- ></script>
19
- <script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script>
20
- <script
21
- src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@2.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"
22
- ></script>
16
+ <script type="importmap">
17
+ {
18
+ "imports": {
19
+ "chartkick": "./javascript/chartkick.js",
20
+ "Chart.bundle": "./javascript/Chart.bundle.js"
21
+ }
22
+ }
23
+ </script>
24
+ <script type="module" src="javascript/index.js"></script>
25
+
23
26
  <link
24
27
  rel="preload"
25
28
  href="fonts/Rubik.woff2"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PunyMonitor
4
- VERSION = "0.1.0"
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/system_utils.rb CHANGED
@@ -13,9 +13,9 @@ class SystemUtils
13
13
  idle = current_cpu[:idle] + current_cpu[:iowait]
14
14
 
15
15
  prev_non_idle = prev_cpu[:user] + prev_cpu[:nice] + prev_cpu[:system] +
16
- prev_cpu[:irq] + prev_cpu[:softirq] + prev_cpu[:steal]
16
+ prev_cpu[:irq] + prev_cpu[:softirq] + prev_cpu[:steal]
17
17
  non_idle = current_cpu[:user] + current_cpu[:nice] + current_cpu[:system] +
18
- current_cpu[:irq] + current_cpu[:softirq] + current_cpu[:steal]
18
+ current_cpu[:irq] + current_cpu[:softirq] + current_cpu[:steal]
19
19
 
20
20
  prev_total = prev_idle + prev_non_idle
21
21
  total = idle + non_idle
@@ -29,8 +29,8 @@ class SystemUtils
29
29
 
30
30
  def cpu_load_average
31
31
  File.read("#{proc_path}/loadavg").split.take(3)
32
- .map(&:to_f)
33
- .map { |value| value.round(2) }
32
+ .map(&:to_f)
33
+ .map { |value| value.round(2) }
34
34
  end
35
35
 
36
36
  def memory_usage_percent
@@ -111,16 +111,16 @@ class SystemUtils
111
111
 
112
112
  def read_disk_stats
113
113
  primary_disk = File.read("#{proc_path}/partitions")
114
- .lines
115
- .drop(2)
116
- .first
117
- .split
118
- .last
114
+ .lines
115
+ .drop(2)
116
+ .first
117
+ .split
118
+ .last
119
119
 
120
120
  stats = File.read("#{proc_path}/diskstats")
121
- .lines
122
- .map(&:split)
123
- .find { |line| line[2] == primary_disk }
121
+ .lines
122
+ .map(&:split)
123
+ .find { |line| line[2] == primary_disk }
124
124
 
125
125
  {
126
126
  read_sectors: stats[5].to_i,
@@ -130,14 +130,14 @@ class SystemUtils
130
130
 
131
131
  def read_network_stats
132
132
  primary_interface = File.read("#{proc_path}/net/route")
133
- .lines
134
- .drop(1)
135
- .find { |line| line.split[1] == "00000000" }
136
- &.split&.first
133
+ .lines
134
+ .drop(1)
135
+ .find { |line| line.split[1] == "00000000" }
136
+ &.split&.first
137
137
  stats = File.read("#{proc_path}/net/dev")
138
- .lines
139
- .map(&:split)
140
- .find { |line| line[0].chomp(":") == primary_interface }
138
+ .lines
139
+ .map(&:split)
140
+ .find { |line| line[0].chomp(":") == primary_interface }
141
141
 
142
142
  {
143
143
  rx_bytes: stats[1].to_i,