solidstats 1.1.0 → 2.0.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/CHANGELOG.md +68 -0
- data/README.md +33 -0
- data/app/assets/javascripts/solidstats/application.js +257 -0
- data/app/assets/javascripts/solidstats/dashboard.js +225 -0
- data/app/assets/javascripts/solidstats/gem_metadata.js +554 -0
- data/app/assets/stylesheets/solidstats/application.css +6 -1
- data/app/assets/stylesheets/solidstats/components/action_button.css +99 -0
- data/app/assets/stylesheets/solidstats/components/dashboard.css +151 -0
- data/app/assets/stylesheets/solidstats/components/dashboard_header.css +93 -0
- data/app/assets/stylesheets/solidstats/components/dashboard_layout.css +97 -0
- data/app/assets/stylesheets/solidstats/components/gem_metadata.css +1403 -0
- data/app/assets/stylesheets/solidstats/components/navigation.css +80 -0
- data/app/assets/stylesheets/solidstats/components/quick_navigation.css +54 -0
- data/app/assets/stylesheets/solidstats/components/security.css +332 -0
- data/app/assets/stylesheets/solidstats/components/status_badge.css +58 -0
- data/app/assets/stylesheets/solidstats/components/summary_card.css +66 -0
- data/app/assets/stylesheets/solidstats/components/tab_navigation.css +95 -0
- data/app/components/solidstats/base_component.rb +88 -0
- data/app/components/solidstats/code_quality/code_quality_section_component.html.erb +0 -0
- data/app/components/solidstats/code_quality/code_quality_section_component.rb +0 -0
- data/app/components/solidstats/code_quality/section_component.html.erb +45 -0
- data/app/components/solidstats/code_quality/section_component.rb +34 -0
- data/app/components/solidstats/dashboard_header_component.html.erb +39 -0
- data/app/components/solidstats/dashboard_header_component.rb +33 -0
- data/app/components/solidstats/previews/action_button_component_preview/button_vs_link.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview/sizes.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview/variants.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview/with_icons.html.erb +6 -0
- data/app/components/solidstats/previews/action_button_component_preview.rb +64 -0
- data/app/components/solidstats/previews/navigation_component_preview.rb +74 -0
- data/app/components/solidstats/previews/stats_overview_component_preview.rb +100 -0
- data/app/components/solidstats/previews/status_badge_component_preview/sizes.html.erb +6 -0
- data/app/components/solidstats/previews/status_badge_component_preview/statuses.html.erb +6 -0
- data/app/components/solidstats/previews/status_badge_component_preview/with_icons.html.erb +6 -0
- data/app/components/solidstats/previews/status_badge_component_preview.rb +49 -0
- data/app/components/solidstats/previews/summary_card_component_preview/clickable.html.erb +9 -0
- data/app/components/solidstats/previews/summary_card_component_preview/dashboard_layout.html.erb +9 -0
- data/app/components/solidstats/previews/summary_card_component_preview/statuses.html.erb +6 -0
- data/app/components/solidstats/previews/summary_card_component_preview/value_formats.html.erb +6 -0
- data/app/components/solidstats/previews/summary_card_component_preview.rb +67 -0
- data/app/components/solidstats/quick_navigation_component.html.erb +8 -0
- data/app/components/solidstats/quick_navigation_component.rb +21 -0
- data/app/components/solidstats/security/gem_impact_analysis_component.html.erb +44 -0
- data/app/components/solidstats/security/gem_impact_analysis_component.rb +45 -0
- data/app/components/solidstats/security/overview_component.html.erb +21 -0
- data/app/components/solidstats/security/overview_component.rb +104 -0
- data/app/components/solidstats/security/section_component.html.erb +26 -0
- data/app/components/solidstats/security/section_component.rb +52 -0
- data/app/components/solidstats/security/timeline_component.html.erb +39 -0
- data/app/components/solidstats/security/timeline_component.rb +43 -0
- data/app/components/solidstats/tasks_section_component.html.erb +17 -0
- data/app/components/solidstats/tasks_section_component.rb +22 -0
- data/app/components/solidstats/ui/action_button_component.html.erb +6 -0
- data/app/components/solidstats/ui/action_button_component.rb +71 -0
- data/app/components/solidstats/ui/dashboard_layout_component.html.erb +19 -0
- data/app/components/solidstats/ui/dashboard_layout_component.rb +85 -0
- data/app/components/solidstats/ui/navigation_component.html.erb +34 -0
- data/app/components/solidstats/ui/navigation_component.rb +72 -0
- data/app/components/solidstats/ui/stats_overview_component.html.erb +14 -0
- data/app/components/solidstats/ui/stats_overview_component.rb +78 -0
- data/app/components/solidstats/ui/status_badge_component.html.erb +6 -0
- data/app/components/solidstats/ui/status_badge_component.rb +42 -0
- data/app/components/solidstats/ui/summary_card_component.html.erb +12 -0
- data/app/components/solidstats/ui/summary_card_component.rb +63 -0
- data/app/components/solidstats/ui/tab_navigation_component.html.erb +22 -0
- data/app/components/solidstats/ui/tab_navigation_component.rb +79 -0
- data/app/controllers/solidstats/dashboard_controller.rb +8 -5
- data/app/controllers/solidstats/gem_metadata_controller.rb +12 -0
- data/app/helpers/solidstats/application_helper.rb +42 -0
- data/app/services/solidstats/gem_metadata/fetcher_service.rb +136 -0
- data/app/services/solidstats/log_size_monitor_service.rb +10 -10
- data/app/views/layouts/solidstats/application.html.erb +2 -1
- data/app/views/solidstats/dashboard/index.html.erb +67 -1337
- data/app/views/solidstats/gem_metadata/_panel.html.erb +419 -0
- data/config/routes.rb +5 -2
- data/lib/generators/solidstats/feature/feature_generator.rb +170 -0
- data/lib/generators/solidstats/feature/templates/component.html.erb +84 -0
- data/lib/generators/solidstats/feature/templates/component.rb.erb +103 -0
- data/lib/generators/solidstats/feature/templates/component.scss +243 -0
- data/lib/generators/solidstats/feature/templates/component_test.rb.erb +183 -0
- data/lib/generators/solidstats/feature/templates/controller.rb.erb +44 -0
- data/lib/generators/solidstats/feature/templates/controller_test.rb.erb +111 -0
- data/lib/generators/solidstats/feature/templates/detail_view.html.erb +755 -0
- data/lib/generators/solidstats/feature/templates/preview.rb.erb +107 -0
- data/lib/generators/solidstats/feature/templates/service.rb.erb +132 -0
- data/lib/generators/solidstats/feature/templates/service_test.rb.erb +109 -0
- data/lib/generators/solidstats/install_generator.rb +109 -0
- data/lib/generators/solidstats/templates/initializer.rb +112 -0
- data/lib/solidstats/asset_compatibility.rb +238 -0
- data/lib/solidstats/asset_manifest.rb +205 -0
- data/lib/solidstats/engine.rb +114 -9
- data/lib/solidstats/version.rb +1 -1
- data/lib/solidstats.rb +299 -2
- data/lib/tasks/solidstats_install.rake +122 -2
- metadata +97 -2
@@ -0,0 +1,136 @@
|
|
1
|
+
require "net/http"
|
2
|
+
|
3
|
+
module Solidstats
|
4
|
+
module GemMetadata
|
5
|
+
class FetcherService
|
6
|
+
GemMeta = Struct.new(:name, :version, :current_version, :released, :info, :runtime, keyword_init: true)
|
7
|
+
|
8
|
+
def self.call(gem_names = nil, force_refresh = false)
|
9
|
+
new.call(gem_names, force_refresh)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(gem_names = nil, force_refresh = false)
|
13
|
+
gem_names = get_gem_names(gem_names)
|
14
|
+
fetch_metadata(gem_names, force_refresh)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def get_gem_names(gem_names)
|
20
|
+
return gem_names if gem_names.present?
|
21
|
+
|
22
|
+
gemfile_lock = Rails.root.join("Gemfile.lock")
|
23
|
+
return [] unless File.exist?(gemfile_lock)
|
24
|
+
|
25
|
+
lockfile = File.read(gemfile_lock)
|
26
|
+
parser = Bundler::LockfileParser.new(lockfile)
|
27
|
+
parser.specs.map(&:name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_current_versions
|
31
|
+
gemfile_lock = Rails.root.join("Gemfile.lock")
|
32
|
+
return {} unless File.exist?(gemfile_lock)
|
33
|
+
|
34
|
+
lockfile = File.read(gemfile_lock)
|
35
|
+
parser = Bundler::LockfileParser.new(lockfile)
|
36
|
+
|
37
|
+
parser.specs.each_with_object({}) do |spec, versions|
|
38
|
+
versions[spec.name] = spec.version.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def fetch_metadata(gem_names, force_refresh)
|
43
|
+
current_versions = get_current_versions
|
44
|
+
|
45
|
+
gem_names.map do |name|
|
46
|
+
fetch_gem_data(name, current_versions[name], force_refresh)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def fetch_gem_data(name, current_version, force_refresh)
|
51
|
+
cached_data = fetch_from_cache(name) unless force_refresh
|
52
|
+
|
53
|
+
if cached_data
|
54
|
+
# Update current version if it changed since caching
|
55
|
+
if current_version && cached_data.current_version != current_version
|
56
|
+
cached_data.current_version = current_version
|
57
|
+
cache_data(name, cached_data)
|
58
|
+
end
|
59
|
+
return cached_data
|
60
|
+
end
|
61
|
+
|
62
|
+
begin
|
63
|
+
data = fetch_from_api(name)
|
64
|
+
meta = GemMeta.new(
|
65
|
+
name: data["name"],
|
66
|
+
version: data["version"],
|
67
|
+
current_version: current_version,
|
68
|
+
released: data["version_created_at"] ? Time.parse(data["version_created_at"]) : nil,
|
69
|
+
info: data["info"],
|
70
|
+
runtime: data["dependencies"] && data["dependencies"]["runtime"] || []
|
71
|
+
)
|
72
|
+
cache_data(name, meta)
|
73
|
+
meta
|
74
|
+
rescue => e
|
75
|
+
Rails.logger.error("Failed to fetch gem metadata for #{name}: #{e.message}")
|
76
|
+
GemMeta.new(name: name, current_version: current_version, info: "(API unavailable)")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def fetch_from_api(name)
|
81
|
+
uri = URI.parse("https://rubygems.org/api/v1/gems/#{name}.json")
|
82
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
83
|
+
http.use_ssl = true
|
84
|
+
http.open_timeout = 5
|
85
|
+
http.read_timeout = 5
|
86
|
+
|
87
|
+
response = http.get(uri.request_uri)
|
88
|
+
|
89
|
+
if response.code == "200"
|
90
|
+
JSON.parse(response.body)
|
91
|
+
else
|
92
|
+
raise "API request failed with status #{response.code}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def fetch_from_cache(name)
|
97
|
+
cache_file = cache_path(name)
|
98
|
+
return nil unless File.exist?(cache_file)
|
99
|
+
|
100
|
+
data = JSON.parse(File.read(cache_file))
|
101
|
+
cache_time = File.mtime(cache_file)
|
102
|
+
|
103
|
+
# Return nil if cache is older than 24 hours
|
104
|
+
return nil if Time.now - cache_time > 24.hours
|
105
|
+
|
106
|
+
GemMeta.new(
|
107
|
+
name: data["name"],
|
108
|
+
version: data["version"],
|
109
|
+
current_version: data["current_version"],
|
110
|
+
released: data["released"] ? Time.parse(data["released"]) : nil,
|
111
|
+
info: data["info"],
|
112
|
+
runtime: data["runtime"] || []
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
def cache_data(name, meta)
|
117
|
+
FileUtils.mkdir_p(File.dirname(cache_path(name)))
|
118
|
+
|
119
|
+
data = {
|
120
|
+
"name" => meta.name,
|
121
|
+
"version" => meta.version,
|
122
|
+
"current_version" => meta.current_version,
|
123
|
+
"released" => meta.released&.iso8601,
|
124
|
+
"info" => meta.info,
|
125
|
+
"runtime" => meta.runtime
|
126
|
+
}
|
127
|
+
|
128
|
+
File.write(cache_path(name), data.to_json)
|
129
|
+
end
|
130
|
+
|
131
|
+
def cache_path(name)
|
132
|
+
Rails.root.join("tmp", "cache", "gem_metadata", "#{name}.json")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -6,7 +6,7 @@ module Solidstats
|
|
6
6
|
def collect_data
|
7
7
|
log_files = scan_log_directory
|
8
8
|
total_size_bytes = log_files.sum { |file| file[:size_bytes] }
|
9
|
-
|
9
|
+
|
10
10
|
# Create aggregated data
|
11
11
|
{
|
12
12
|
log_dir_path: log_directory,
|
@@ -24,11 +24,11 @@ module Solidstats
|
|
24
24
|
if filename.present?
|
25
25
|
# Truncate specific log file
|
26
26
|
# Ensure filename has .log extension
|
27
|
-
filename = "#{filename}.log" unless filename.end_with?(
|
28
|
-
|
27
|
+
filename = "#{filename}.log" unless filename.end_with?(".log")
|
28
|
+
|
29
29
|
file_path = File.join(log_directory, filename)
|
30
30
|
if File.exist?(file_path)
|
31
|
-
File.open(file_path,
|
31
|
+
File.open(file_path, "w") { |f| f.truncate(0) }
|
32
32
|
{ success: true, message: "Log file '#{filename}' truncated successfully" }
|
33
33
|
else
|
34
34
|
{ success: false, message: "Log file '#{filename}' not found" }
|
@@ -36,7 +36,7 @@ module Solidstats
|
|
36
36
|
else
|
37
37
|
# Truncate all log files
|
38
38
|
scan_log_directory.each do |log_file|
|
39
|
-
File.open(log_file[:path],
|
39
|
+
File.open(log_file[:path], "w") { |f| f.truncate(0) }
|
40
40
|
end
|
41
41
|
{ success: true, message: "All log files truncated successfully" }
|
42
42
|
end
|
@@ -50,16 +50,16 @@ module Solidstats
|
|
50
50
|
def log_directory
|
51
51
|
Rails.root.join("log").to_s
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
def scan_log_directory
|
55
55
|
log_files = []
|
56
|
-
|
56
|
+
|
57
57
|
# Get all files in the log directory
|
58
58
|
Dir.glob(File.join(log_directory, "*.log")).each do |file_path|
|
59
59
|
if File.file?(file_path)
|
60
60
|
size_bytes = File.size(file_path)
|
61
61
|
filename = File.basename(file_path)
|
62
|
-
|
62
|
+
|
63
63
|
log_files << {
|
64
64
|
filename: filename,
|
65
65
|
path: file_path,
|
@@ -70,7 +70,7 @@ module Solidstats
|
|
70
70
|
}
|
71
71
|
end
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
# Sort by size (largest first)
|
75
75
|
log_files.sort_by { |file| -file[:size_bytes] }
|
76
76
|
end
|
@@ -81,7 +81,7 @@ module Solidstats
|
|
81
81
|
|
82
82
|
def calculate_status(size_bytes)
|
83
83
|
size_mb = bytes_to_mb(size_bytes)
|
84
|
-
|
84
|
+
|
85
85
|
if size_mb >= DANGER_THRESHOLD
|
86
86
|
:danger
|
87
87
|
elsif size_mb >= WARNING_THRESHOLD
|