solidstats 1.0.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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +85 -0
  3. data/README.md +35 -0
  4. data/app/assets/javascripts/solidstats/application.js +257 -0
  5. data/app/assets/javascripts/solidstats/dashboard.js +225 -0
  6. data/app/assets/javascripts/solidstats/gem_metadata.js +554 -0
  7. data/app/assets/stylesheets/solidstats/application.css +6 -1
  8. data/app/assets/stylesheets/solidstats/components/action_button.css +99 -0
  9. data/app/assets/stylesheets/solidstats/components/dashboard.css +151 -0
  10. data/app/assets/stylesheets/solidstats/components/dashboard_header.css +93 -0
  11. data/app/assets/stylesheets/solidstats/components/dashboard_layout.css +97 -0
  12. data/app/assets/stylesheets/solidstats/components/gem_metadata.css +1403 -0
  13. data/app/assets/stylesheets/solidstats/components/navigation.css +80 -0
  14. data/app/assets/stylesheets/solidstats/components/quick_navigation.css +54 -0
  15. data/app/assets/stylesheets/solidstats/components/security.css +332 -0
  16. data/app/assets/stylesheets/solidstats/components/status_badge.css +58 -0
  17. data/app/assets/stylesheets/solidstats/components/summary_card.css +66 -0
  18. data/app/assets/stylesheets/solidstats/components/tab_navigation.css +95 -0
  19. data/app/components/solidstats/base_component.rb +88 -0
  20. data/app/components/solidstats/code_quality/code_quality_section_component.html.erb +0 -0
  21. data/app/components/solidstats/code_quality/code_quality_section_component.rb +0 -0
  22. data/app/components/solidstats/code_quality/section_component.html.erb +45 -0
  23. data/app/components/solidstats/code_quality/section_component.rb +34 -0
  24. data/app/components/solidstats/dashboard_header_component.html.erb +39 -0
  25. data/app/components/solidstats/dashboard_header_component.rb +33 -0
  26. data/app/components/solidstats/previews/action_button_component_preview/button_vs_link.html.erb +6 -0
  27. data/app/components/solidstats/previews/action_button_component_preview/sizes.html.erb +6 -0
  28. data/app/components/solidstats/previews/action_button_component_preview/variants.html.erb +6 -0
  29. data/app/components/solidstats/previews/action_button_component_preview/with_icons.html.erb +6 -0
  30. data/app/components/solidstats/previews/action_button_component_preview.rb +64 -0
  31. data/app/components/solidstats/previews/navigation_component_preview.rb +74 -0
  32. data/app/components/solidstats/previews/stats_overview_component_preview.rb +100 -0
  33. data/app/components/solidstats/previews/status_badge_component_preview/sizes.html.erb +6 -0
  34. data/app/components/solidstats/previews/status_badge_component_preview/statuses.html.erb +6 -0
  35. data/app/components/solidstats/previews/status_badge_component_preview/with_icons.html.erb +6 -0
  36. data/app/components/solidstats/previews/status_badge_component_preview.rb +49 -0
  37. data/app/components/solidstats/previews/summary_card_component_preview/clickable.html.erb +9 -0
  38. data/app/components/solidstats/previews/summary_card_component_preview/dashboard_layout.html.erb +9 -0
  39. data/app/components/solidstats/previews/summary_card_component_preview/statuses.html.erb +6 -0
  40. data/app/components/solidstats/previews/summary_card_component_preview/value_formats.html.erb +6 -0
  41. data/app/components/solidstats/previews/summary_card_component_preview.rb +67 -0
  42. data/app/components/solidstats/quick_navigation_component.html.erb +8 -0
  43. data/app/components/solidstats/quick_navigation_component.rb +21 -0
  44. data/app/components/solidstats/security/gem_impact_analysis_component.html.erb +44 -0
  45. data/app/components/solidstats/security/gem_impact_analysis_component.rb +45 -0
  46. data/app/components/solidstats/security/overview_component.html.erb +21 -0
  47. data/app/components/solidstats/security/overview_component.rb +104 -0
  48. data/app/components/solidstats/security/section_component.html.erb +26 -0
  49. data/app/components/solidstats/security/section_component.rb +52 -0
  50. data/app/components/solidstats/security/timeline_component.html.erb +39 -0
  51. data/app/components/solidstats/security/timeline_component.rb +43 -0
  52. data/app/components/solidstats/tasks_section_component.html.erb +17 -0
  53. data/app/components/solidstats/tasks_section_component.rb +22 -0
  54. data/app/components/solidstats/ui/action_button_component.html.erb +6 -0
  55. data/app/components/solidstats/ui/action_button_component.rb +71 -0
  56. data/app/components/solidstats/ui/dashboard_layout_component.html.erb +19 -0
  57. data/app/components/solidstats/ui/dashboard_layout_component.rb +85 -0
  58. data/app/components/solidstats/ui/navigation_component.html.erb +34 -0
  59. data/app/components/solidstats/ui/navigation_component.rb +72 -0
  60. data/app/components/solidstats/ui/stats_overview_component.html.erb +14 -0
  61. data/app/components/solidstats/ui/stats_overview_component.rb +78 -0
  62. data/app/components/solidstats/ui/status_badge_component.html.erb +6 -0
  63. data/app/components/solidstats/ui/status_badge_component.rb +42 -0
  64. data/app/components/solidstats/ui/summary_card_component.html.erb +12 -0
  65. data/app/components/solidstats/ui/summary_card_component.rb +63 -0
  66. data/app/components/solidstats/ui/tab_navigation_component.html.erb +22 -0
  67. data/app/components/solidstats/ui/tab_navigation_component.rb +79 -0
  68. data/app/controllers/solidstats/dashboard_controller.rb +22 -0
  69. data/app/controllers/solidstats/gem_metadata_controller.rb +12 -0
  70. data/app/helpers/solidstats/application_helper.rb +42 -0
  71. data/app/services/solidstats/gem_metadata/fetcher_service.rb +136 -0
  72. data/app/services/solidstats/log_size_monitor_service.rb +94 -0
  73. data/app/views/layouts/solidstats/application.html.erb +2 -1
  74. data/app/views/solidstats/dashboard/_log_monitor.html.erb +759 -0
  75. data/app/views/solidstats/dashboard/index.html.erb +67 -1323
  76. data/app/views/solidstats/gem_metadata/_panel.html.erb +419 -0
  77. data/config/routes.rb +7 -0
  78. data/lib/generators/solidstats/feature/feature_generator.rb +170 -0
  79. data/lib/generators/solidstats/feature/templates/component.html.erb +84 -0
  80. data/lib/generators/solidstats/feature/templates/component.rb.erb +103 -0
  81. data/lib/generators/solidstats/feature/templates/component.scss +243 -0
  82. data/lib/generators/solidstats/feature/templates/component_test.rb.erb +183 -0
  83. data/lib/generators/solidstats/feature/templates/controller.rb.erb +44 -0
  84. data/lib/generators/solidstats/feature/templates/controller_test.rb.erb +111 -0
  85. data/lib/generators/solidstats/feature/templates/detail_view.html.erb +755 -0
  86. data/lib/generators/solidstats/feature/templates/preview.rb.erb +107 -0
  87. data/lib/generators/solidstats/feature/templates/service.rb.erb +132 -0
  88. data/lib/generators/solidstats/feature/templates/service_test.rb.erb +109 -0
  89. data/lib/generators/solidstats/install_generator.rb +109 -0
  90. data/lib/generators/solidstats/templates/initializer.rb +112 -0
  91. data/lib/solidstats/asset_compatibility.rb +238 -0
  92. data/lib/solidstats/asset_manifest.rb +205 -0
  93. data/lib/solidstats/engine.rb +114 -9
  94. data/lib/solidstats/version.rb +1 -1
  95. data/lib/solidstats.rb +299 -2
  96. data/lib/tasks/solidstats_install.rake +122 -2
  97. metadata +99 -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
@@ -0,0 +1,94 @@
1
+ module Solidstats
2
+ class LogSizeMonitorService
3
+ WARNING_THRESHOLD = 25 # In megabytes
4
+ DANGER_THRESHOLD = 50 # In megabytes
5
+
6
+ def collect_data
7
+ log_files = scan_log_directory
8
+ total_size_bytes = log_files.sum { |file| file[:size_bytes] }
9
+
10
+ # Create aggregated data
11
+ {
12
+ log_dir_path: log_directory,
13
+ total_size_bytes: total_size_bytes,
14
+ total_size_mb: bytes_to_mb(total_size_bytes),
15
+ status: calculate_status(total_size_bytes),
16
+ logs_count: log_files.size,
17
+ log_files: log_files,
18
+ created_at: Time.now.iso8601
19
+ }
20
+ end
21
+
22
+ def truncate_log(filename = nil)
23
+ begin
24
+ if filename.present?
25
+ # Truncate specific log file
26
+ # Ensure filename has .log extension
27
+ filename = "#{filename}.log" unless filename.end_with?(".log")
28
+
29
+ file_path = File.join(log_directory, filename)
30
+ if File.exist?(file_path)
31
+ File.open(file_path, "w") { |f| f.truncate(0) }
32
+ { success: true, message: "Log file '#{filename}' truncated successfully" }
33
+ else
34
+ { success: false, message: "Log file '#{filename}' not found" }
35
+ end
36
+ else
37
+ # Truncate all log files
38
+ scan_log_directory.each do |log_file|
39
+ File.open(log_file[:path], "w") { |f| f.truncate(0) }
40
+ end
41
+ { success: true, message: "All log files truncated successfully" }
42
+ end
43
+ rescue => e
44
+ { success: false, message: "Failed to truncate log file: #{e.message}" }
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def log_directory
51
+ Rails.root.join("log").to_s
52
+ end
53
+
54
+ def scan_log_directory
55
+ log_files = []
56
+
57
+ # Get all files in the log directory
58
+ Dir.glob(File.join(log_directory, "*.log")).each do |file_path|
59
+ if File.file?(file_path)
60
+ size_bytes = File.size(file_path)
61
+ filename = File.basename(file_path)
62
+
63
+ log_files << {
64
+ filename: filename,
65
+ path: file_path,
66
+ size_bytes: size_bytes,
67
+ size_mb: bytes_to_mb(size_bytes),
68
+ status: calculate_status(size_bytes),
69
+ last_modified: File.mtime(file_path)
70
+ }
71
+ end
72
+ end
73
+
74
+ # Sort by size (largest first)
75
+ log_files.sort_by { |file| -file[:size_bytes] }
76
+ end
77
+
78
+ def bytes_to_mb(bytes)
79
+ (bytes.to_f / (1024 * 1024)).round(2)
80
+ end
81
+
82
+ def calculate_status(size_bytes)
83
+ size_mb = bytes_to_mb(size_bytes)
84
+
85
+ if size_mb >= DANGER_THRESHOLD
86
+ :danger
87
+ elsif size_mb >= WARNING_THRESHOLD
88
+ :warning
89
+ else
90
+ :ok
91
+ end
92
+ end
93
+ end
94
+ end
@@ -7,7 +7,8 @@
7
7
 
8
8
  <%= yield :head %>
9
9
 
10
- <%= stylesheet_link_tag "solidstats/application", media: "all" %>
10
+ <%= solidstats_styles %>
11
+ <%= solidstats_scripts %>
11
12
  </head>
12
13
  <body>
13
14