dbwatcher 1.1.3 → 1.1.5

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +79 -26
  3. data/app/assets/images/dbwatcher/apple-touch-icon.png +0 -0
  4. data/app/assets/images/dbwatcher/dbwatcher-social-preview.png +0 -0
  5. data/app/assets/images/dbwatcher/dbwatcher-tranparent_512x512.png +0 -0
  6. data/app/assets/images/dbwatcher/dbwatcher_512x512.png +0 -0
  7. data/app/assets/images/dbwatcher/favicon-96x96.png +0 -0
  8. data/app/assets/images/dbwatcher/favicon.ico +0 -0
  9. data/app/assets/images/dbwatcher/favicon.svg +3 -0
  10. data/app/assets/images/dbwatcher/site.webmanifest +21 -0
  11. data/app/assets/images/dbwatcher/web-app-manifest-192x192.png +0 -0
  12. data/app/assets/images/dbwatcher/web-app-manifest-512x512.png +0 -0
  13. data/app/assets/stylesheets/dbwatcher/application.css +38 -4
  14. data/app/assets/stylesheets/dbwatcher/components/_tabulator.scss +57 -13
  15. data/app/controllers/dbwatcher/api/v1/sessions_controller.rb +14 -18
  16. data/app/controllers/dbwatcher/api/v1/system_info_controller.rb +1 -1
  17. data/app/controllers/dbwatcher/dashboard_controller.rb +1 -1
  18. data/app/views/dbwatcher/dashboard/_overview.html.erb +8 -7
  19. data/app/views/dbwatcher/sessions/index.html.erb +42 -59
  20. data/app/views/layouts/dbwatcher/application.html.erb +22 -6
  21. data/lib/dbwatcher/configuration.rb +51 -74
  22. data/lib/dbwatcher/logging.rb +23 -1
  23. data/lib/dbwatcher/services/diagram_analyzers/concerns/activerecord_introspection.rb +60 -0
  24. data/lib/dbwatcher/services/diagram_analyzers/concerns/association_scope_filtering.rb +60 -0
  25. data/lib/dbwatcher/services/diagram_analyzers/inferred_relationship_analyzer.rb +62 -36
  26. data/lib/dbwatcher/services/diagram_analyzers/model_analysis/association_extractor.rb +224 -0
  27. data/lib/dbwatcher/services/diagram_analyzers/model_analysis/dataset_builder.rb +226 -0
  28. data/lib/dbwatcher/services/diagram_analyzers/model_analysis/model_discovery.rb +161 -0
  29. data/lib/dbwatcher/services/diagram_analyzers/model_association_analyzer.rb +27 -514
  30. data/lib/dbwatcher/services/diagram_data/attribute.rb +22 -83
  31. data/lib/dbwatcher/services/diagram_data/base.rb +129 -0
  32. data/lib/dbwatcher/services/diagram_data/entity.rb +23 -72
  33. data/lib/dbwatcher/services/diagram_data/relationship.rb +15 -66
  34. data/lib/dbwatcher/services/diagram_generator.rb +35 -69
  35. data/lib/dbwatcher/services/diagram_strategies/base_diagram_strategy.rb +23 -9
  36. data/lib/dbwatcher/services/diagram_strategies/class_diagram_strategy.rb +16 -22
  37. data/lib/dbwatcher/services/diagram_strategies/diagram_strategy_helpers.rb +33 -0
  38. data/lib/dbwatcher/services/diagram_strategies/erd_diagram_strategy.rb +20 -25
  39. data/lib/dbwatcher/services/diagram_strategies/flowchart_diagram_strategy.rb +20 -25
  40. data/lib/dbwatcher/services/diagram_strategies/standard_diagram_strategy.rb +80 -0
  41. data/lib/dbwatcher/services/diagram_system.rb +14 -1
  42. data/lib/dbwatcher/services/mermaid_syntax/base_builder.rb +2 -0
  43. data/lib/dbwatcher/services/mermaid_syntax/erd_builder.rb +2 -2
  44. data/lib/dbwatcher/services/mermaid_syntax/sanitizer.rb +4 -14
  45. data/lib/dbwatcher/services/mermaid_syntax_builder.rb +10 -8
  46. data/lib/dbwatcher/services/system_info/runtime_info_collector.rb +7 -7
  47. data/lib/dbwatcher/services/system_info/system_info_collector.rb +3 -3
  48. data/lib/dbwatcher/services/timeline_data_service/entry_builder.rb +23 -1
  49. data/lib/dbwatcher/storage/session_storage.rb +2 -2
  50. data/lib/dbwatcher/storage.rb +1 -1
  51. data/lib/dbwatcher/version.rb +1 -1
  52. metadata +20 -2
@@ -35,24 +35,39 @@
35
35
  position: sticky;
36
36
  left: 0;
37
37
  z-index: 20;
38
- background: #f3f3f3;
38
+ background: #f3f3f3 !important;
39
+ background-color: #f3f3f3 !important;
39
40
  box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1);
41
+ will-change: transform;
42
+ transform: translateZ(0);
43
+ opacity: 1 !important;
44
+ transition: opacity 0.1s ease-out;
40
45
  }
41
46
 
42
47
  &.sticky-left-1 {
43
48
  position: sticky;
44
49
  left: 60px;
45
50
  z-index: 19;
46
- background: #f3f3f3;
51
+ background: #f3f3f3 !important;
52
+ background-color: #f3f3f3 !important;
47
53
  box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1);
54
+ will-change: transform;
55
+ transform: translateZ(0);
56
+ opacity: 1 !important;
57
+ transition: opacity 0.1s ease-out;
48
58
  }
49
59
 
50
60
  &.sticky-left-2 {
51
61
  position: sticky;
52
62
  left: 108px;
53
63
  z-index: 18;
54
- background: #f3f3f3;
64
+ background: #f3f3f3 !important;
65
+ background-color: #f3f3f3 !important;
55
66
  box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1);
67
+ will-change: transform;
68
+ transform: translateZ(0);
69
+ opacity: 1 !important;
70
+ transition: opacity 0.1s ease-out;
56
71
  }
57
72
  }
58
73
  }
@@ -115,25 +130,40 @@
115
130
  &.sticky-left-0 {
116
131
  position: sticky;
117
132
  left: 0;
118
- background: white;
133
+ background: white !important;
134
+ background-color: white !important;
119
135
  z-index: 5;
120
136
  box-shadow: 2px 0 4px rgba(0, 0, 0, 0.05);
137
+ will-change: transform;
138
+ transform: translateZ(0);
139
+ opacity: 1 !important;
140
+ transition: opacity 0.1s ease-out;
121
141
  }
122
142
 
123
143
  &.sticky-left-1 {
124
144
  position: sticky;
125
145
  left: 60px;
126
- background: white;
146
+ background: white !important;
147
+ background-color: white !important;
127
148
  z-index: 4;
128
149
  box-shadow: 2px 0 4px rgba(0, 0, 0, 0.05);
150
+ will-change: transform;
151
+ transform: translateZ(0);
152
+ opacity: 1 !important;
153
+ transition: opacity 0.1s ease-out;
129
154
  }
130
155
 
131
156
  &.sticky-left-2 {
132
157
  position: sticky;
133
158
  left: 108px;
134
- background: white;
159
+ background: white !important;
160
+ background-color: white !important;
135
161
  z-index: 3;
136
162
  box-shadow: 2px 0 4px rgba(0, 0, 0, 0.05);
163
+ will-change: transform;
164
+ transform: translateZ(0);
165
+ opacity: 1 !important;
166
+ transition: opacity 0.1s ease-out;
137
167
  }
138
168
  }
139
169
 
@@ -143,7 +173,9 @@
143
173
  &.sticky-left-0,
144
174
  &.sticky-left-1,
145
175
  &.sticky-left-2 {
146
- background: #f9fafb;
176
+ background: #f9fafb !important;
177
+ background-color: #f9fafb !important;
178
+ opacity: 1 !important;
147
179
  }
148
180
  }
149
181
  }
@@ -151,38 +183,50 @@
151
183
  // Operation-specific sticky cell backgrounds
152
184
  &.operation-insert .tabulator-cell {
153
185
  &.sticky-left-0, &.sticky-left-1, &.sticky-left-2 {
154
- background-color: rgba(16, 185, 129, 0.05);
186
+ background: rgba(16, 185, 129, 0.05) !important;
187
+ background-color: rgba(16, 185, 129, 0.05) !important;
188
+ opacity: 1 !important;
155
189
  }
156
190
  }
157
191
 
158
192
  &.operation-update .tabulator-cell {
159
193
  &.sticky-left-0, &.sticky-left-1, &.sticky-left-2 {
160
- background-color: rgba(108, 173, 223, 0.05);
194
+ background: rgba(108, 173, 223, 0.05) !important;
195
+ background-color: rgba(108, 173, 223, 0.05) !important;
196
+ opacity: 1 !important;
161
197
  }
162
198
  }
163
199
 
164
200
  &.operation-delete .tabulator-cell {
165
201
  &.sticky-left-0, &.sticky-left-1, &.sticky-left-2 {
166
- background-color: rgba(239, 68, 68, 0.05);
202
+ background: rgba(239, 68, 68, 0.05) !important;
203
+ background-color: rgba(239, 68, 68, 0.05) !important;
204
+ opacity: 1 !important;
167
205
  }
168
206
  }
169
207
 
170
208
  // Operation-specific sticky cell hover backgrounds
171
209
  &.operation-insert:hover .tabulator-cell {
172
210
  &.sticky-left-0, &.sticky-left-1, &.sticky-left-2 {
173
- background-color: rgba(16, 185, 129, 0.1);
211
+ background: rgba(16, 185, 129, 0.1) !important;
212
+ background-color: rgba(16, 185, 129, 0.1) !important;
213
+ opacity: 1 !important;
174
214
  }
175
215
  }
176
216
 
177
217
  &.operation-update:hover .tabulator-cell {
178
218
  &.sticky-left-0, &.sticky-left-1, &.sticky-left-2 {
179
- background-color: rgba(108, 173, 223, 0.1);
219
+ background: rgba(108, 173, 223, 0.1) !important;
220
+ background-color: rgba(108, 173, 223, 0.1) !important;
221
+ opacity: 1 !important;
180
222
  }
181
223
  }
182
224
 
183
225
  &.operation-delete:hover .tabulator-cell {
184
226
  &.sticky-left-0, &.sticky-left-1, &.sticky-left-2 {
185
- background-color: rgba(239, 68, 68, 0.1);
227
+ background: rgba(239, 68, 68, 0.1) !important;
228
+ background-color: rgba(239, 68, 68, 0.1) !important;
229
+ opacity: 1 !important;
186
230
  }
187
231
  }
188
232
  }
@@ -8,32 +8,23 @@ module Dbwatcher
8
8
 
9
9
  def tables_data
10
10
  Rails.logger.info "API::V1::SessionsController#tables_data: Getting tables for session #{@session.id}"
11
-
12
- # Paginated, filtered tables data
13
- # Convert ActionController::Parameters to a hash before passing to service
14
- service = Dbwatcher::Services::Api::TablesDataService.new(@session, filter_params.to_h)
11
+ service = Dbwatcher::Services::Api::TablesDataService.new(@session, tables_data_params)
15
12
  render json: service.call
16
13
  end
17
14
 
18
15
  def summary_data
19
16
  Rails.logger.info "API::V1::SessionsController#summary_data: Getting summary for session #{@session.id}"
20
-
21
- # Aggregated summary statistics
22
17
  service = Dbwatcher::Services::Api::SummaryDataService.new(@session)
23
18
  render json: service.call
24
19
  end
25
20
 
26
21
  def diagram_data
27
22
  Rails.logger.info "API::V1::SessionsController#diagram_data: Getting diagram for session #{@session.id}"
28
-
29
- # Generated diagram content with caching
30
- # Convert ActionController::Parameters to a hash before passing to service
31
- diagram_params = params.to_unsafe_h
32
23
  service = Dbwatcher::Services::Api::DiagramDataService.new(@session, params[:type], diagram_params)
33
24
  result = service.call
34
25
 
35
26
  if result[:error]
36
- render json: { error: result[:error] }, status: :unprocessable_entity
27
+ render_error(result[:error])
37
28
  else
38
29
  render json: result
39
30
  end
@@ -41,13 +32,11 @@ module Dbwatcher
41
32
 
42
33
  def timeline_data
43
34
  Rails.logger.info "API::V1::SessionsController#timeline_data: Getting timeline for session #{@session.id}"
44
-
45
- # Timeline data processed from session changes
46
35
  service = Dbwatcher::Services::TimelineDataService.new(@session)
47
36
  result = service.call
48
37
 
49
38
  if result[:errors].any?
50
- render json: { error: result[:errors].first[:message] }, status: :unprocessable_entity
39
+ render_error(result[:errors].first[:message])
51
40
  else
52
41
  render json: result
53
42
  end
@@ -55,7 +44,6 @@ module Dbwatcher
55
44
 
56
45
  def diagram_types
57
46
  Rails.logger.info "API::V1::SessionsController#diagram_types: Getting available diagram types"
58
-
59
47
  render json: {
60
48
  types: Dbwatcher::Services::Api::DiagramDataService.available_types_with_metadata,
61
49
  default_type: "database_tables"
@@ -66,11 +54,19 @@ module Dbwatcher
66
54
 
67
55
  def find_session
68
56
  @session = Storage.sessions.find(params[:id])
69
- render json: { error: "Session not found" }, status: :not_found unless @session
57
+ render_error("Session not found", :not_found) unless @session
58
+ end
59
+
60
+ def tables_data_params
61
+ params.permit(:id, :table, :operation, :page, :per_page, session: {}).to_h
62
+ end
63
+
64
+ def diagram_params
65
+ params.permit(:type, :format, :include_columns, :show_relationships, session: {}).to_h
70
66
  end
71
67
 
72
- def filter_params
73
- params.permit(:table, :operation, :page, :per_page)
68
+ def render_error(message, status = :unprocessable_entity)
69
+ render json: { error: message }, status: status
74
70
  end
75
71
  end
76
72
  end
@@ -166,7 +166,7 @@ module Dbwatcher
166
166
  #
167
167
  # @return [void]
168
168
  def ensure_system_info_enabled
169
- return if Dbwatcher.configuration.collect_system_info
169
+ return if Dbwatcher.configuration.system_info
170
170
 
171
171
  render json: {
172
172
  error: "System information collection is disabled",
@@ -10,7 +10,7 @@ module Dbwatcher
10
10
  @active_tab = params[:tab] || "overview"
11
11
 
12
12
  # Add system information if enabled
13
- return unless Dbwatcher.configuration.collect_system_info
13
+ return unless Dbwatcher.configuration.system_info
14
14
 
15
15
  @system_info_summary = system_info_storage.summary
16
16
  @system_info = system_info_storage.cached_info
@@ -118,10 +118,11 @@
118
118
  </div>
119
119
  <% else %>
120
120
  <div class="p-8 text-center text-gray-500">
121
- <svg class="w-8 h-8 mx-auto text-gray-400 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
122
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
123
- </svg>
121
+ <%= image_tag "dbwatcher/dbwatcher-tranparent_512x512.png",
122
+ alt: "DBWatcher Logo",
123
+ class: "w-12 h-12 mx-auto opacity-30 mb-2" %>
124
124
  <p class="text-sm">No recent sessions</p>
125
+ <p class="text-xs text-gray-400 mt-1">Start tracking with <code>?dbwatch=true</code></p>
125
126
  </div>
126
127
  <% end %>
127
128
  </div>
@@ -175,11 +176,11 @@
175
176
  </div>
176
177
  <% else %>
177
178
  <div class="p-8 text-center text-gray-500">
178
- <svg class="w-8 h-8 mx-auto text-gray-400 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
179
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z"/>
180
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5a2 2 0 012-2h2a2 2 0 012 2v2H8V5z"/>
181
- </svg>
179
+ <%= image_tag "dbwatcher/dbwatcher-tranparent_512x512.png",
180
+ alt: "DBWatcher Logo",
181
+ class: "w-12 h-12 mx-auto opacity-30 mb-2" %>
182
182
  <p class="text-sm">No active tables</p>
183
+ <p class="text-xs text-gray-400 mt-1">Tables will appear when data changes</p>
183
184
  </div>
184
185
  <% end %>
185
186
  </div>
@@ -41,46 +41,39 @@
41
41
  </div>
42
42
 
43
43
  <!-- Content Area -->
44
- <div class="flex-1 overflow-auto p-4">
45
-
46
- <% if @sessions.empty? %>
47
- <div class="flex items-center justify-center h-full text-gray-500">
48
- <div class="text-center">
49
- <svg class="mx-auto h-8 w-8 text-gray-400 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
50
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1"
51
- d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
52
- </svg>
53
- <p class="text-xs">No tracking sessions yet</p>
54
- <p class="text-xs text-gray-400">Start tracking with <code class="bg-gray-200 px-1 rounded">Dbwatcher.track { ... }</code></p>
44
+ <div class="flex-1 overflow-auto">
45
+ <% if @sessions.empty? %>
46
+ <div class="p-8 text-center text-gray-500">
47
+ <%= image_tag "dbwatcher/dbwatcher-tranparent_512x512.png",
48
+ alt: "DBWatcher Logo",
49
+ class: "mx-auto w-12 h-12 opacity-30 mb-3" %>
50
+ <p class="text-sm font-medium text-gray-600 mb-2">No tracking sessions yet</p>
51
+ <p class="text-xs text-gray-400">Start tracking with <code class="bg-gray-100 px-1 rounded text-xs">Dbwatcher.track { ... }</code></p>
52
+ <p class="text-xs text-gray-400 mt-1">or add <code class="bg-gray-100 px-1 rounded text-xs">?dbwatch=true</code> to any URL</p>
55
53
  </div>
56
- </div>
57
- <% else %>
58
- <div class="bg-white border border-gray-300 rounded shadow-sm">
59
- <table class="compact-table sessions-table w-full">
54
+ <% else %>
55
+ <table class="compact-table w-full">
60
56
  <thead>
61
57
  <tr>
62
- <th class="text-left" style="min-width:180px; max-width:260px; width:18%">Session ID</th>
63
- <th class="text-left" style="min-width:160px; max-width:260px; width:22%">Name</th>
64
- <th class="text-center" style="width:100px">Status</th>
65
- <th class="text-center" style="width:100px">Changes</th>
66
- <th class="text-right" style="width:120px">Started</th>
67
- <th class="text-right" style="width:120px">Duration</th>
68
- <th class="text-center" style="width:80px">Actions</th>
58
+ <th class="text-left" style="width: 18%; min-width: 180px">Session ID</th>
59
+ <th class="text-left" style="width: 22%; min-width: 160px">Name</th>
60
+ <th class="text-center" style="width: 100px">Status</th>
61
+ <th class="text-center" style="width: 100px">Changes</th>
62
+ <th class="text-right" style="width: 120px">Started</th>
63
+ <th class="text-right" style="width: 120px">Duration</th>
64
+ <th class="text-center" style="width: 80px">Actions</th>
69
65
  </tr>
70
66
  </thead>
71
67
  <tbody>
72
68
  <% @sessions.each do |session| %>
73
69
  <tr class="session-row">
74
- <td style="min-width:180px; max-width:260px; width:18%">
75
- <span class="font-mono text-xs">
76
- <%= safe_value(session, :id) %>
77
- </span>
70
+ <td class="font-mono text-xs">
71
+ <%= safe_value(session, :id) %>
78
72
  </td>
79
- <td style="min-width:160px; max-width:260px; width:22%" title="<%= safe_value(session, :name) %>">
73
+ <td title="<%= safe_value(session, :name) %>">
80
74
  <%= link_to display_session_name(safe_value(session, :name)),
81
75
  session_path(safe_value(session, :id)),
82
- class: "text-navy-dark hover:text-blue-medium whitespace-normal break-words inline-block",
83
- style: "max-width:260px; overflow-x:auto; display:inline-block;" %>
76
+ class: "text-navy-dark hover:text-blue-medium" %>
84
77
  </td>
85
78
  <td class="text-center">
86
79
  <%= render 'dbwatcher/shared/badge',
@@ -106,44 +99,34 @@
106
99
  ) rescue 'N/A' %>
107
100
  <% end %>
108
101
  </td>
109
- <td class="text-center actions-cell">
110
- <div class="flex gap-1 justify-end">
111
- <%= link_to session_path(safe_value(session, :id)),
112
- class: "compact-button bg-navy-dark text-white hover:bg-blue-medium",
113
- title: "View session details" do %>
114
- <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
115
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
116
- d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
117
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
118
- d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
119
- </svg>
120
- <% end %>
121
- </div>
102
+ <td class="text-center">
103
+ <%= link_to "View", session_path(safe_value(session, :id)),
104
+ class: "compact-button bg-navy-dark text-white hover:bg-blue-medium",
105
+ title: "View session details" %>
122
106
  </td>
123
107
  </tr>
124
108
  <% end %>
125
109
  </tbody>
126
110
  </table>
127
- </div>
128
111
 
129
- <!-- No Results Message -->
130
- <div
131
- x-show="filterText && document.querySelectorAll('.session-row:not(.hidden)').length === 0"
132
- x-cloak
133
- class="mt-4 text-center py-8 bg-gray-50 border border-gray-200 rounded">
134
- <p class="text-gray-500">No sessions match your filter criteria</p>
135
- <button
136
- @click="filterText = ''"
137
- class="mt-2 text-blue-medium hover:text-blue-700 text-sm">
138
- Clear filter
139
- </button>
140
- </div>
141
- <% end %>
112
+ <!-- No Results Message -->
113
+ <div
114
+ x-show="filterText && document.querySelectorAll('.session-row:not(.hidden)').length === 0"
115
+ x-cloak
116
+ class="p-8 text-center text-gray-500 bg-gray-50">
117
+ <p class="mb-2">No sessions match your filter criteria</p>
118
+ <button
119
+ @click="filterText = ''"
120
+ class="compact-button bg-gray-500 text-white hover:bg-gray-600">
121
+ Clear filter
122
+ </button>
123
+ </div>
124
+ <% end %>
125
+ </div>
142
126
 
143
127
  <!-- Status Bar -->
144
- <div class="h-6 bg-gray-100 border-t border-gray-300 flex items-center px-4 text-xs text-gray-600 mt-4">
145
- <span x-show="!filterText"><%= @sessions.count %> sessions total •
146
- <%= @sessions.count { |s| session_active?(s) } %> active</span>
128
+ <div class="h-6 bg-gray-100 border-t border-gray-300 flex items-center px-4 text-xs text-gray-600">
129
+ <span x-show="!filterText"><%= @sessions.count %> sessions total • <%= @sessions.count { |s| session_active?(s) } %> active</span>
147
130
  <span x-show="filterText" x-text="`${document.querySelectorAll('.session-row:not(.hidden)').length} of ${<%= @sessions.count %>} sessions shown`"></span>
148
131
  <span class="ml-auto">Last updated: <%= Time.current.strftime("%H:%M:%S") %></span>
149
132
  </div>
@@ -5,6 +5,14 @@
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <%= csrf_meta_tags %>
7
7
 
8
+ <!-- Favicon -->
9
+ <%= favicon_link_tag asset_path('dbwatcher/favicon.ico') %>
10
+ <%= favicon_link_tag asset_path('dbwatcher/favicon-96x96.png'), sizes: '96x96', type: 'image/png' %>
11
+ <%= favicon_link_tag asset_path('dbwatcher/apple-touch-icon.png'), rel: 'apple-touch-icon', sizes: '180x180' %>
12
+ <%= favicon_link_tag asset_path('dbwatcher/web-app-manifest-192x192.png'), sizes: '192x192', type: 'image/png' %>
13
+ <%= favicon_link_tag asset_path('dbwatcher/web-app-manifest-512x512.png'), sizes: '512x512', type: 'image/png' %>
14
+ <link rel="manifest" href="<%= asset_path('dbwatcher/site.webmanifest') %>"
15
+
8
16
  <!-- Alpine.js and Plugins - ensure plugins load BEFORE Alpine.js core -->
9
17
  <script defer src="https://unpkg.com/@alpinejs/collapse@3.x.x/dist/cdn.min.js"></script>
10
18
  <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
@@ -119,12 +127,20 @@
119
127
  :style="{ width: sidebarCollapsed ? '48px' : sidebarWidth + 'px' }">
120
128
  <div class="flex flex-col h-full">
121
129
  <!-- Logo -->
122
- <div class="h-10 flex items-center justify-between px-3 border-b border-gray-700">
123
- <div class="flex items-center gap-2" x-show="!sidebarCollapsed">
124
- <svg class="w-5 h-5 text-gold-light" fill="none" stroke="currentColor" viewBox="0 0 24 24">
125
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
126
- </svg>
127
- <span class="text-sm font-medium text-white">DB Watcher</span>
130
+ <div class="h-12 flex items-center justify-between px-3 border-b border-gray-700 bg-gray-800">
131
+ <div class="flex items-center gap-3" x-show="!sidebarCollapsed">
132
+ <%= image_tag "dbwatcher/dbwatcher-tranparent_512x512.png",
133
+ alt: "DBWatcher Logo",
134
+ class: "w-10 h-10" %>
135
+ <div class="flex flex-col">
136
+ <span class="text-base font-bold text-white">dbwatcher</span>
137
+ </div>
138
+ </div>
139
+ <!-- Collapsed state logo -->
140
+ <div class="flex items-center justify-center w-full" x-show="sidebarCollapsed">
141
+ <%= image_tag "dbwatcher/dbwatcher-tranparent_512x512.png",
142
+ alt: "dbwatcher Logo",
143
+ class: "w-8 h-8" %>
128
144
  </div>
129
145
  <button @click="sidebarCollapsed = !sidebarCollapsed"
130
146
  class="p-1 hover:bg-gray-800 rounded text-gray-400">
@@ -3,93 +3,84 @@
3
3
  module Dbwatcher
4
4
  # Configuration class for DBWatcher
5
5
  #
6
- # This class manages all configuration options for DBWatcher, including
7
- # storage, tracking, and diagram visualization settings.
6
+ # Simplified configuration with logical groupings and sensible defaults
8
7
  class Configuration
9
- # Storage configuration
10
- attr_accessor :storage_path, :enabled, :max_sessions, :auto_clean_after_days
8
+ # Core settings - what users need most
9
+ attr_accessor :enabled, :storage_path
11
10
 
12
- # Query tracking configuration
13
- attr_accessor :track_queries, :slow_query_threshold, :max_query_logs_per_day
11
+ # Session management - how data is stored and cleaned
12
+ attr_accessor :max_sessions, :auto_clean_days
14
13
 
15
- # Routing configuration
16
- attr_accessor :mount_path
14
+ # Query tracking - performance monitoring
15
+ attr_accessor :track_queries
17
16
 
18
- # Diagram configuration
19
- attr_accessor :diagram_show_attributes, :diagram_show_methods, :diagram_show_cardinality,
20
- :diagram_max_attributes, :diagram_attribute_types, :diagram_relationship_labels,
21
- :diagram_preserve_table_case, :diagram_direction, :diagram_cardinality_format,
22
- :diagram_show_attribute_count, :diagram_show_method_count
17
+ # System info - debugging and monitoring
18
+ attr_accessor :system_info, :debug_mode
23
19
 
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
20
+ # Advanced diagram options - available but not commonly needed
21
+ attr_accessor :diagram_show_methods, :diagram_max_attributes,
22
+ :diagram_attribute_types, :diagram_relationship_labels,
23
+ :diagram_show_attributes, :diagram_show_cardinality
28
24
 
29
25
  # Initialize with default values
30
26
  def initialize
31
- # Storage configuration defaults
32
- @storage_path = default_storage_path
27
+ # Core settings
33
28
  @enabled = true
29
+ @storage_path = default_storage_path
30
+
31
+ # Session management
34
32
  @max_sessions = 50
35
- @auto_clean_after_days = 7
33
+ @auto_clean_days = 7
36
34
 
37
- # Query tracking configuration defaults
35
+ # Query tracking
38
36
  @track_queries = false
39
- @slow_query_threshold = 200 # milliseconds
40
- @max_query_logs_per_day = 1000
41
37
 
42
- # Routing configuration defaults
43
- @mount_path = "/dbwatcher"
38
+ # System info
39
+ @system_info = true
40
+ @debug_mode = false
44
41
 
45
- # Initialize diagram configuration with defaults
46
- initialize_diagram_config
42
+ # Advanced diagram options - sensible defaults
43
+ @diagram_show_methods = false
44
+ @diagram_max_attributes = 10
45
+ @diagram_attribute_types = true
46
+ @diagram_relationship_labels = true
47
+ @diagram_show_attributes = true
48
+ @diagram_show_cardinality = true
49
+ end
47
50
 
48
- # Initialize system information configuration with defaults
49
- initialize_system_info_config
51
+ # Fixed defaults for options that are still used in codebase but not configurable
52
+ def slow_query_threshold
53
+ 200 # Fixed default value
50
54
  end
51
55
 
52
- # Initialize diagram configuration with default values
53
- def initialize_diagram_config
54
- @diagram_show_attributes = true
55
- @diagram_show_methods = false # Hide methods by default
56
- @diagram_show_cardinality = true
57
- @diagram_max_attributes = 10
58
- @diagram_attribute_types = true # Changed from array to boolean
59
- @diagram_relationship_labels = true # Changed from symbol to boolean
60
- @diagram_preserve_table_case = false # Changed from true to false
61
- @diagram_direction = "LR" # Left to right by default
62
- @diagram_cardinality_format = :simple # Use simpler 1:N format
63
- @diagram_show_attribute_count = true
64
- @diagram_show_method_count = true
56
+ def diagram_direction
57
+ "LR" # Fixed default value
65
58
  end
66
59
 
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
60
+ # Fixed defaults for complex options that are still used in codebase but not configurable
61
+ def max_query_logs_per_day = 1000
62
+ def system_info_refresh_interval = 5 * 60
63
+ def system_info_cache_duration = 60 * 60
64
+
65
+ def collect_sensitive_env_vars?
66
+ false
67
+ end
68
+
69
+ def system_info_include_performance_metrics?
70
+ true
74
71
  end
75
72
 
76
73
  # Validate configuration
77
- #
78
- # @return [Boolean] true if configuration is valid
79
74
  def valid?
80
75
  validate_storage_path
81
76
  validate_max_sessions
82
- validate_auto_clean_after_days
83
- validate_slow_query_threshold
84
- validate_max_query_logs_per_day
77
+ validate_auto_clean_days
85
78
  true
86
79
  end
87
80
 
88
81
  private
89
82
 
90
83
  # Default storage path based on Rails or current directory
91
- #
92
- # @return [String] default storage path
93
84
  def default_storage_path
94
85
  if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
95
86
  Rails.root.join("tmp", "dbwatcher").to_s
@@ -116,25 +107,11 @@ module Dbwatcher
116
107
  raise ConfigurationError, "max_sessions must be a positive integer"
117
108
  end
118
109
 
119
- # Validate auto clean after days
120
- def validate_auto_clean_after_days
121
- return if auto_clean_after_days.is_a?(Integer) && auto_clean_after_days.positive?
122
-
123
- raise ConfigurationError, "auto_clean_after_days must be a positive integer"
124
- end
125
-
126
- # Validate slow query threshold
127
- def validate_slow_query_threshold
128
- return if slow_query_threshold.is_a?(Integer) && slow_query_threshold.positive?
129
-
130
- raise ConfigurationError, "slow_query_threshold must be a positive integer"
131
- end
132
-
133
- # Validate max query logs per day
134
- def validate_max_query_logs_per_day
135
- return if max_query_logs_per_day.is_a?(Integer) && max_query_logs_per_day.positive?
110
+ # Validate auto clean days
111
+ def validate_auto_clean_days
112
+ return if auto_clean_days.is_a?(Integer) && auto_clean_days.positive?
136
113
 
137
- raise ConfigurationError, "max_query_logs_per_day must be a positive integer"
114
+ raise ConfigurationError, "auto_clean_days must be a positive integer"
138
115
  end
139
116
  end
140
117