dbwatcher 1.1.0 → 1.1.2

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -2
  3. data/app/assets/javascripts/dbwatcher/components/changes_table_hybrid.js +12 -22
  4. data/app/assets/javascripts/dbwatcher/components/dashboard.js +325 -0
  5. data/app/assets/stylesheets/dbwatcher/application.css +394 -41
  6. data/app/assets/stylesheets/dbwatcher/application.scss +4 -0
  7. data/app/assets/stylesheets/dbwatcher/components/_badges.scss +68 -23
  8. data/app/assets/stylesheets/dbwatcher/components/_compact_table.scss +83 -26
  9. data/app/assets/stylesheets/dbwatcher/components/_diagrams.scss +3 -3
  10. data/app/assets/stylesheets/dbwatcher/components/_navigation.scss +9 -0
  11. data/app/assets/stylesheets/dbwatcher/components/_tabulator.scss +248 -0
  12. data/app/assets/stylesheets/dbwatcher/vendor/_tabulator_overrides.scss +37 -0
  13. data/app/controllers/dbwatcher/api/v1/system_info_controller.rb +180 -0
  14. data/app/controllers/dbwatcher/dashboard/system_info_controller.rb +64 -0
  15. data/app/controllers/dbwatcher/dashboard_controller.rb +17 -0
  16. data/app/controllers/dbwatcher/sessions_controller.rb +3 -19
  17. data/app/helpers/dbwatcher/application_helper.rb +43 -11
  18. data/app/helpers/dbwatcher/diagram_helper.rb +0 -88
  19. data/app/views/dbwatcher/dashboard/_layout.html.erb +27 -0
  20. data/app/views/dbwatcher/dashboard/_overview.html.erb +188 -0
  21. data/app/views/dbwatcher/dashboard/_system_info.html.erb +22 -0
  22. data/app/views/dbwatcher/dashboard/_system_info_content.html.erb +389 -0
  23. data/app/views/dbwatcher/dashboard/index.html.erb +8 -177
  24. data/app/views/dbwatcher/sessions/_changes.html.erb +91 -0
  25. data/app/views/dbwatcher/sessions/_layout.html.erb +23 -0
  26. data/app/views/dbwatcher/sessions/index.html.erb +107 -87
  27. data/app/views/dbwatcher/sessions/show.html.erb +10 -4
  28. data/app/views/dbwatcher/tables/index.html.erb +32 -40
  29. data/app/views/layouts/dbwatcher/application.html.erb +100 -48
  30. data/config/routes.rb +23 -6
  31. data/lib/dbwatcher/configuration.rb +18 -1
  32. data/lib/dbwatcher/services/base_service.rb +2 -0
  33. data/lib/dbwatcher/services/diagram_analyzers/model_association_analyzer.rb +177 -138
  34. data/lib/dbwatcher/services/diagram_data/dataset.rb +2 -0
  35. data/lib/dbwatcher/services/mermaid_syntax/class_diagram_builder.rb +13 -9
  36. data/lib/dbwatcher/services/mermaid_syntax/class_diagram_helper.rb +3 -1
  37. data/lib/dbwatcher/services/mermaid_syntax/sanitizer.rb +17 -1
  38. data/lib/dbwatcher/services/system_info/database_info_collector.rb +263 -0
  39. data/lib/dbwatcher/services/system_info/machine_info_collector.rb +387 -0
  40. data/lib/dbwatcher/services/system_info/runtime_info_collector.rb +328 -0
  41. data/lib/dbwatcher/services/system_info/system_info_collector.rb +114 -0
  42. data/lib/dbwatcher/storage/concerns/error_handler.rb +6 -6
  43. data/lib/dbwatcher/storage/session.rb +5 -0
  44. data/lib/dbwatcher/storage/system_info_storage.rb +242 -0
  45. data/lib/dbwatcher/storage.rb +12 -0
  46. data/lib/dbwatcher/version.rb +1 -1
  47. data/lib/dbwatcher.rb +15 -1
  48. metadata +20 -15
  49. data/app/helpers/dbwatcher/component_helper.rb +0 -29
  50. data/app/views/dbwatcher/sessions/_changes_tab.html.erb +0 -265
  51. data/app/views/dbwatcher/sessions/_tab_navigation.html.erb +0 -12
  52. data/app/views/dbwatcher/sessions/changes.html.erb +0 -21
  53. data/app/views/dbwatcher/sessions/components/changes/_filters.html.erb +0 -44
  54. data/app/views/dbwatcher/sessions/components/changes/_table_list.html.erb +0 -96
  55. data/app/views/dbwatcher/sessions/diagrams.html.erb +0 -21
  56. data/app/views/dbwatcher/sessions/shared/_layout.html.erb +0 -8
  57. data/app/views/dbwatcher/sessions/shared/_navigation.html.erb +0 -35
  58. data/app/views/dbwatcher/sessions/shared/_session_header.html.erb +0 -25
  59. data/app/views/dbwatcher/sessions/summary.html.erb +0 -21
  60. /data/app/views/dbwatcher/sessions/{_diagrams_tab.html.erb → _diagrams.html.erb} +0 -0
  61. /data/app/views/dbwatcher/sessions/{_summary_tab.html.erb → _summary.html.erb} +0 -0
@@ -29,6 +29,7 @@
29
29
  <%= javascript_include_tag "dbwatcher/components/changes_table_hybrid" %>
30
30
  <%= javascript_include_tag "dbwatcher/components/diagrams" %>
31
31
  <%= javascript_include_tag "dbwatcher/components/summary" %>
32
+ <%= javascript_include_tag "dbwatcher/components/dashboard" %>
32
33
 
33
34
  <!-- DBWatcher Services -->
34
35
  <%= javascript_include_tag "dbwatcher/core/alpine_store" %>
@@ -56,9 +57,9 @@
56
57
  }
57
58
  });
58
59
 
59
- // Fallback initialization with better error handling
60
+ // Improved initialization with safety checks
60
61
  document.addEventListener('DOMContentLoaded', function() {
61
- // Small delay to ensure Alpine is available
62
+ // Prevent immediate DOM access conflicts
62
63
  setTimeout(() => {
63
64
  try {
64
65
  if (window.DBWatcher && !DBWatcher.initialized) {
@@ -68,16 +69,22 @@
68
69
  } catch (error) {
69
70
  console.error('❌ Error during DBWatcher fallback initialization:', error);
70
71
  }
71
- }, 100);
72
+ }, 150);
72
73
 
73
- // Plugin verification
74
+ // Plugin verification with safety checks
74
75
  setTimeout(() => {
75
- if (window.Alpine && !window.Alpine.directive('collapse')) {
76
- console.warn('⚠️ Alpine.js Collapse plugin may not be properly loaded');
77
- } else if (window.Alpine && window.Alpine.directive('collapse')) {
78
- console.log(' Alpine.js Collapse plugin verified');
76
+ try {
77
+ if (window.Alpine && typeof window.Alpine.directive === 'function') {
78
+ if (!window.Alpine.directive('collapse')) {
79
+ console.warn('⚠️ Alpine.js Collapse plugin may not be properly loaded');
80
+ } else {
81
+ console.log('✅ Alpine.js Collapse plugin verified');
82
+ }
83
+ }
84
+ } catch (error) {
85
+ console.warn('⚠️ Alpine.js plugin verification failed:', error);
79
86
  }
80
- }, 200);
87
+ }, 300);
81
88
  });
82
89
  </script>
83
90
 
@@ -129,7 +136,7 @@
129
136
 
130
137
  <!-- Navigation -->
131
138
  <nav class="flex-1 py-2 overflow-y-auto">
132
- <%= link_to root_path, class: "sidebar-item #{current_page?(root_path) ? 'active' : ''}" do %>
139
+ <%= link_to root_path, class: "sidebar-item #{current_page?(root_path) || (params[:tab] == 'system_info') ? 'active' : ''}" do %>
133
140
  <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
134
141
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
135
142
  d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"/>
@@ -160,6 +167,8 @@
160
167
  </svg>
161
168
  <span x-show="!sidebarCollapsed">SQL Logs</span>
162
169
  <% end %>
170
+
171
+
163
172
  </nav>
164
173
 
165
174
  <!-- Actions -->
@@ -176,6 +185,42 @@
176
185
  <span x-show="!sidebarCollapsed" class="text-xs">Clear All</span>
177
186
  <% end %>
178
187
  </div>
188
+
189
+ <!-- Gem Info Section -->
190
+ <div class="mt-auto pt-4 border-t border-gray-700">
191
+ <div class="px-3 py-2">
192
+ <div class="text-xs text-gray-400 mb-2 font-medium" x-show="!sidebarCollapsed">
193
+ dbwatcher v<%= Dbwatcher::VERSION %>
194
+ </div>
195
+ <div class="flex items-center gap-2 text-xs text-gray-400" x-show="!sidebarCollapsed">
196
+ <a href="https://github.com/patrick204nqh/dbwatcher"
197
+ target="_blank"
198
+ rel="noopener noreferrer"
199
+ class="hover:text-blue-400 transition-colors duration-200 flex items-center gap-1"
200
+ title="View on GitHub">
201
+ <svg class="w-3 h-3" fill="currentColor" viewBox="0 0 24 24">
202
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
203
+ </svg>
204
+ <span>GitHub</span>
205
+ </a>
206
+ <span class="text-gray-600">•</span>
207
+ <a href="https://rubydoc.info/gems/dbwatcher/<%= Dbwatcher::VERSION %>"
208
+ target="_blank"
209
+ rel="noopener noreferrer"
210
+ class="hover:text-red-400 transition-colors duration-200 flex items-center gap-1"
211
+ title="View Documentation">
212
+ <svg class="w-3 h-3" fill="currentColor" viewBox="0 0 24 24">
213
+ <path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/>
214
+ </svg>
215
+ <span>Docs</span>
216
+ </a>
217
+ </div>
218
+ <!-- Collapsed state - just show version -->
219
+ <div class="text-xs text-gray-400 text-center" x-show="sidebarCollapsed" title="DBWatcher v<%= Dbwatcher::VERSION %>">
220
+ v<%= Dbwatcher::VERSION %>
221
+ </div>
222
+ </div>
223
+ </div>
179
224
  </div>
180
225
  </aside>
181
226
 
@@ -211,50 +256,57 @@
211
256
  <!-- Initialize DBWatcher system -->
212
257
  <script>
213
258
  document.addEventListener('DOMContentLoaded', function() {
214
- // Initialize DBWatcher if available
215
- if (window.DBWatcher) {
216
- // Ensure BaseComponent is available
217
- if (!window.DBWatcher.BaseComponent && typeof DBWatcher.BaseComponent !== 'function') {
218
- window.DBWatcher.BaseComponent = function(config = {}) {
219
- return {
220
- init() {
221
- if (this.componentInit) {
222
- try {
223
- this.componentInit();
224
- } catch (error) {
225
- console.error("Error during component initialization:", error);
259
+ // Add delay to prevent timing conflicts
260
+ setTimeout(() => {
261
+ try {
262
+ // Initialize DBWatcher if available
263
+ if (window.DBWatcher) {
264
+ // Ensure BaseComponent is available
265
+ if (!window.DBWatcher.BaseComponent && typeof DBWatcher.BaseComponent !== 'function') {
266
+ window.DBWatcher.BaseComponent = function(config = {}) {
267
+ return {
268
+ init() {
269
+ if (this.componentInit) {
270
+ try {
271
+ this.componentInit();
272
+ } catch (error) {
273
+ console.error("Error during component initialization:", error);
274
+ }
275
+ }
226
276
  }
277
+ };
278
+ };
279
+ console.log('Added fallback BaseComponent');
280
+ }
281
+
282
+ // Register legacy components with Alpine directly if ComponentRegistry isn't available
283
+ if (!window.DBWatcher.ComponentRegistry) {
284
+ console.log('ComponentRegistry not available, using direct Alpine registration');
285
+
286
+ // Ensure Alpine is available with safety checks
287
+ if (window.Alpine && typeof window.Alpine.data === 'function') {
288
+ // Direct registration of components with Alpine
289
+ if (!window.Alpine.data('changesTable') && window.DBWatcher.components && window.DBWatcher.components.changesTable) {
290
+ window.Alpine.data('changesTable', (config = {}) => {
291
+ const component = window.DBWatcher.components.changesTable(config);
292
+ return component;
293
+ });
294
+ console.log('Registered changesTable component with Alpine');
227
295
  }
228
296
  }
229
- };
230
- };
231
- console.log('Added fallback BaseComponent');
232
- }
233
-
234
- // Register legacy components with Alpine directly if ComponentRegistry isn't available
235
- if (!window.DBWatcher.ComponentRegistry) {
236
- console.log('ComponentRegistry not available, using direct Alpine registration');
237
-
238
- // Ensure Alpine is available
239
- if (window.Alpine) {
240
- // Direct registration of components with Alpine
241
- if (!window.Alpine.data('changesTable') && window.DBWatcher.components && window.DBWatcher.components.changesTable) {
242
- window.Alpine.data('changesTable', (config = {}) => {
243
- const component = window.DBWatcher.components.changesTable(config);
244
- return component;
245
- });
246
- console.log('Registered changesTable component with Alpine');
247
297
  }
248
- }
249
- }
250
298
 
251
- console.log('✅ DBWatcher initialized (fallback)');
252
- }
299
+ console.log('✅ DBWatcher initialized (fallback)');
300
+ }
253
301
 
254
- // Verify Alpine.js plugins
255
- if (window.Alpine && window.Alpine.directive && window.Alpine.directive('collapse')) {
256
- console.log('✅ Alpine.js Collapse plugin verified');
257
- }
302
+ // Verify Alpine.js plugins with safety checks
303
+ if (window.Alpine && typeof window.Alpine.directive === 'function' && window.Alpine.directive('collapse')) {
304
+ console.log('✅ Alpine.js Collapse plugin verified');
305
+ }
306
+ } catch (error) {
307
+ console.error('❌ Error during DBWatcher system initialization:', error);
308
+ }
309
+ }, 200);
258
310
  });
259
311
  </script>
260
312
  </body>
data/config/routes.rb CHANGED
@@ -3,16 +3,20 @@
3
3
  Dbwatcher::Engine.routes.draw do
4
4
  root to: "dashboard#index"
5
5
 
6
- # Dashboard clear all action
6
+ # Dashboard actions
7
7
  delete :clear_all, to: "dashboard#clear_all"
8
8
 
9
- resources :sessions do
10
- member do
11
- get :changes
12
- get :summary
13
- get :diagrams
9
+ # Dashboard system info actions
10
+ namespace :dashboard do
11
+ resources :system_info, only: [] do
12
+ collection do
13
+ post :refresh
14
+ delete :clear_cache
15
+ end
14
16
  end
17
+ end
15
18
 
19
+ resources :sessions do
16
20
  collection do
17
21
  delete :clear
18
22
  end
@@ -32,6 +36,19 @@ Dbwatcher::Engine.routes.draw do
32
36
  get :diagram_types
33
37
  end
34
38
  end
39
+
40
+ # System information API routes
41
+ resources :system_info, only: [:index] do
42
+ collection do
43
+ post :refresh
44
+ get :machine
45
+ get :database
46
+ get :runtime
47
+ get :summary
48
+ delete :clear_cache
49
+ get :cache_status
50
+ end
51
+ end
35
52
  end
36
53
  end
37
54
 
@@ -21,6 +21,11 @@ module Dbwatcher
21
21
  :diagram_preserve_table_case, :diagram_direction, :diagram_cardinality_format,
22
22
  :diagram_show_attribute_count, :diagram_show_method_count
23
23
 
24
+ # System information configuration
25
+ attr_accessor :collect_system_info, :system_info_refresh_interval,
26
+ :collect_sensitive_env_vars, :system_info_cache_duration,
27
+ :system_info_include_performance_metrics
28
+
24
29
  # Initialize with default values
25
30
  def initialize
26
31
  # Storage configuration defaults
@@ -30,7 +35,7 @@ module Dbwatcher
30
35
  @auto_clean_after_days = 7
31
36
 
32
37
  # Query tracking configuration defaults
33
- @track_queries = true
38
+ @track_queries = false
34
39
  @slow_query_threshold = 200 # milliseconds
35
40
  @max_query_logs_per_day = 1000
36
41
 
@@ -39,6 +44,9 @@ module Dbwatcher
39
44
 
40
45
  # Initialize diagram configuration with defaults
41
46
  initialize_diagram_config
47
+
48
+ # Initialize system information configuration with defaults
49
+ initialize_system_info_config
42
50
  end
43
51
 
44
52
  # Initialize diagram configuration with default values
@@ -56,6 +64,15 @@ module Dbwatcher
56
64
  @diagram_show_method_count = true
57
65
  end
58
66
 
67
+ # Initialize system information configuration with default values
68
+ def initialize_system_info_config
69
+ @collect_system_info = true
70
+ @system_info_refresh_interval = 5 * 60 # 5 minutes in seconds
71
+ @collect_sensitive_env_vars = false
72
+ @system_info_cache_duration = 60 * 60 # 1 hour in seconds
73
+ @system_info_include_performance_metrics = true
74
+ end
75
+
59
76
  # Validate configuration
60
77
  #
61
78
  # @return [Boolean] true if configuration is valid
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../logging"
4
+
3
5
  module Dbwatcher
4
6
  module Services
5
7
  # Base class for all service objects