dbviewer 0.3.1

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 (53) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +250 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/stylesheets/dbviewer/application.css +21 -0
  6. data/app/assets/stylesheets/dbviewer/dbviewer.css +0 -0
  7. data/app/assets/stylesheets/dbviewer/enhanced.css +0 -0
  8. data/app/controllers/concerns/dbviewer/database_operations.rb +354 -0
  9. data/app/controllers/concerns/dbviewer/error_handling.rb +42 -0
  10. data/app/controllers/concerns/dbviewer/pagination_concern.rb +43 -0
  11. data/app/controllers/dbviewer/application_controller.rb +21 -0
  12. data/app/controllers/dbviewer/databases_controller.rb +0 -0
  13. data/app/controllers/dbviewer/entity_relationship_diagrams_controller.rb +24 -0
  14. data/app/controllers/dbviewer/home_controller.rb +10 -0
  15. data/app/controllers/dbviewer/logs_controller.rb +39 -0
  16. data/app/controllers/dbviewer/tables_controller.rb +73 -0
  17. data/app/helpers/dbviewer/application_helper.rb +118 -0
  18. data/app/jobs/dbviewer/application_job.rb +4 -0
  19. data/app/mailers/dbviewer/application_mailer.rb +6 -0
  20. data/app/models/dbviewer/application_record.rb +5 -0
  21. data/app/services/dbviewer/file_storage.rb +0 -0
  22. data/app/services/dbviewer/in_memory_storage.rb +0 -0
  23. data/app/services/dbviewer/query_analyzer.rb +0 -0
  24. data/app/services/dbviewer/query_collection.rb +0 -0
  25. data/app/services/dbviewer/query_logger.rb +0 -0
  26. data/app/services/dbviewer/query_parser.rb +82 -0
  27. data/app/services/dbviewer/query_storage.rb +0 -0
  28. data/app/views/dbviewer/entity_relationship_diagrams/index.html.erb +564 -0
  29. data/app/views/dbviewer/home/index.html.erb +237 -0
  30. data/app/views/dbviewer/logs/index.html.erb +614 -0
  31. data/app/views/dbviewer/shared/_sidebar.html.erb +177 -0
  32. data/app/views/dbviewer/tables/_table_structure.html.erb +102 -0
  33. data/app/views/dbviewer/tables/index.html.erb +128 -0
  34. data/app/views/dbviewer/tables/query.html.erb +600 -0
  35. data/app/views/dbviewer/tables/show.html.erb +271 -0
  36. data/app/views/layouts/dbviewer/application.html.erb +728 -0
  37. data/config/routes.rb +22 -0
  38. data/lib/dbviewer/configuration.rb +79 -0
  39. data/lib/dbviewer/database_manager.rb +450 -0
  40. data/lib/dbviewer/engine.rb +20 -0
  41. data/lib/dbviewer/initializer.rb +23 -0
  42. data/lib/dbviewer/logger.rb +102 -0
  43. data/lib/dbviewer/query_analyzer.rb +109 -0
  44. data/lib/dbviewer/query_collection.rb +41 -0
  45. data/lib/dbviewer/query_parser.rb +82 -0
  46. data/lib/dbviewer/sql_validator.rb +194 -0
  47. data/lib/dbviewer/storage/base.rb +31 -0
  48. data/lib/dbviewer/storage/file_storage.rb +96 -0
  49. data/lib/dbviewer/storage/in_memory_storage.rb +59 -0
  50. data/lib/dbviewer/version.rb +3 -0
  51. data/lib/dbviewer.rb +65 -0
  52. data/lib/tasks/dbviewer_tasks.rake +4 -0
  53. metadata +126 -0
@@ -0,0 +1,237 @@
1
+ <%# sidebar content %>
2
+ <% content_for :sidebar do %>
3
+ <%= render 'dbviewer/shared/sidebar' %>
4
+ <% end %>
5
+
6
+ <div class="container-fluid px-0">
7
+ <div class="row mb-3">
8
+ <div class="col">
9
+ <h1 class="h3 mb-2">Database Overview</h1>
10
+ <p class="database-connection-info">
11
+ Connected to database: <span class="badge rounded-pill database-name-badge"><%= get_database_name %></span>
12
+ </p>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="row g-3 mb-4">
17
+ <div class="col-md-3">
18
+ <div class="card h-100 text-center border-0 <%= stat_card_bg_class %>">
19
+ <div class="card-body">
20
+ <h5 class="mb-1">Tables</h5>
21
+ <h2 class="mb-0"><%= @analytics[:total_tables] %></h2>
22
+ </div>
23
+ </div>
24
+ </div>
25
+
26
+ <div class="col-md-3">
27
+ <div class="card h-100 text-center border-0 <%= stat_card_bg_class %>">
28
+ <div class="card-body">
29
+ <h5 class="mb-1">Records</h5>
30
+ <h2 class="mb-0"><%= number_with_delimiter(@analytics[:total_records]) %></h2>
31
+ </div>
32
+ </div>
33
+ </div>
34
+
35
+ <div class="col-md-3">
36
+ <div class="card h-100 text-center border-0 <%= stat_card_bg_class %>">
37
+ <div class="card-body">
38
+ <h5 class="mb-1">Columns</h5>
39
+ <h2 class="mb-0"><%= @analytics[:total_columns] %></h2>
40
+ </div>
41
+ </div>
42
+ </div>
43
+
44
+ <div class="col-md-3">
45
+ <div class="card h-100 text-center border-0 <%= stat_card_bg_class %>">
46
+ <div class="card-body">
47
+ <h5 class="mb-1">Database Size</h5>
48
+ <h2 class="mb-0"><%= number_to_human_size(@analytics[:schema_size]) %></h2>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ </div>
53
+
54
+ <div class="row g-3">
55
+ <div class="col-md-6 mb-4">
56
+ <div class="card h-100 shadow-sm">
57
+ <div class="card-header">
58
+ <h5 class="card-title mb-0">Largest Tables</h5>
59
+ </div>
60
+ <div class="card-body">
61
+ <% if @analytics[:largest_tables].any? %>
62
+ <div class="table-responsive">
63
+ <table class="table table-sm table-hover">
64
+ <thead>
65
+ <tr>
66
+ <th>Table Name</th>
67
+ <th class="text-end">Records</th>
68
+ <th class="text-end">Columns</th>
69
+ </tr>
70
+ </thead>
71
+ <tbody>
72
+ <% @analytics[:largest_tables].each do |table| %>
73
+ <tr>
74
+ <td>
75
+ <a href="<%= dbviewer.table_path(table[:name]) %>">
76
+ <%= table[:name] %>
77
+ </a>
78
+ </td>
79
+ <td class="text-end"><%= number_with_delimiter(table[:record_count]) %></td>
80
+ <td class="text-end"><%= table[:columns_count] %></td>
81
+ </tr>
82
+ <% end %>
83
+ </tbody>
84
+ </table>
85
+ </div>
86
+ <% else %>
87
+ <div class="text-center my-4 empty-data-message">
88
+ <p>No table data available</p>
89
+ </div>
90
+ <% end %>
91
+ </div>
92
+ </div>
93
+ </div>
94
+
95
+ <div class="col-md-6 mb-4">
96
+ <div class="card h-100 shadow-sm">
97
+ <div class="card-header">
98
+ <h5 class="card-title mb-0">Tables with Most Columns</h5>
99
+ </div>
100
+ <div class="card-body">
101
+ <% if @analytics[:widest_tables].any? %>
102
+ <div class="table-responsive">
103
+ <table class="table table-sm table-hover">
104
+ <thead>
105
+ <tr>
106
+ <th>Table Name</th>
107
+ <th class="text-end">Columns</th>
108
+ <th class="text-end">Records</th>
109
+ </tr>
110
+ </thead>
111
+ <tbody>
112
+ <% @analytics[:widest_tables].each do |table| %>
113
+ <tr>
114
+ <td>
115
+ <a href="<%= dbviewer.table_path(table[:name]) %>">
116
+ <%= table[:name] %>
117
+ </a>
118
+ </td>
119
+ <td class="text-end"><%= table[:columns_count] %></td>
120
+ <td class="text-end"><%= number_with_delimiter(table[:record_count]) %></td>
121
+ </tr>
122
+ <% end %>
123
+ </tbody>
124
+ </table>
125
+ </div>
126
+ <% else %>
127
+ <div class="text-center my-4 empty-data-message">
128
+ <p>No table data available</p>
129
+ </div>
130
+ <% end %>
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ <div class="col-12 mb-4">
136
+ <div class="card shadow-sm">
137
+ <div class="card-header d-flex justify-content-between align-items-center">
138
+ <h5 class="card-title mb-0">Recent SQL Queries</h5>
139
+ <a href="<%= dbviewer.logs_path %>" class="btn btn-sm btn-primary">View All Logs</a>
140
+ </div>
141
+ <div class="card-body p-0">
142
+ <% begin %>
143
+ <% require_dependency "dbviewer/logger" %>
144
+ <% queries = Dbviewer::Logger.instance.recent_queries(limit: 5) %>
145
+
146
+ <% if queries.any? %>
147
+ <div class="table-responsive">
148
+ <table class="table table-sm table-hover mb-0">
149
+
150
+ <thead>
151
+ <tr>
152
+ <th>Query</th>
153
+ <th class="text-end" style="width: 120px">Duration</th>
154
+ <th class="text-end" style="width: 180px">Time</th>
155
+ </tr>
156
+ </thead>
157
+ <tbody>
158
+ <% queries.each do |query| %>
159
+ <tr>
160
+ <td class="text-truncate" style="max-width: 500px;">
161
+ <code class="sql-query-code"><%= query[:sql] %></code>
162
+ </td>
163
+ <td class="text-end">
164
+ <span class="<%= query[:duration_ms] > 100 ? 'query-duration-slow' : 'query-duration' %>">
165
+ <%= query[:duration_ms] %> ms
166
+ </span>
167
+ </td>
168
+ <td class="text-end query-timestamp">
169
+ <small><%= query[:timestamp].strftime("%H:%M:%S") %></small>
170
+ </td>
171
+ </tr>
172
+ <% end %>
173
+ </tbody>
174
+ </table>
175
+ </div>
176
+ <% else %>
177
+ <div class="text-center my-4 empty-data-message">
178
+ <p>No queries recorded yet</p>
179
+ </div>
180
+ <% end %>
181
+ <% rescue => e %>
182
+ <div class="text-center my-4 empty-data-message">
183
+ <p>Error loading query logs: <%= e.message %></p>
184
+ </div>
185
+ <% end %>
186
+ </div>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </div>
191
+
192
+ <script>
193
+ document.addEventListener('DOMContentLoaded', function() {
194
+ const searchInput = document.getElementById('tableSearch');
195
+ const tablesList = document.getElementById('tablesList');
196
+ const tables = tablesList ? tablesList.querySelectorAll('.list-group-item') : [];
197
+
198
+ if (searchInput) {
199
+ searchInput.addEventListener('keyup', function() {
200
+ const query = this.value.trim().toLowerCase();
201
+
202
+ Array.from(tables).forEach(function(table) {
203
+ const tableName = table.textContent.trim().toLowerCase();
204
+ if (tableName.includes(query)) {
205
+ table.style.display = '';
206
+ } else {
207
+ table.style.display = 'none';
208
+ }
209
+ });
210
+ });
211
+ }
212
+
213
+ // Update code blocks for the current theme
214
+ function updateCodeBlockTheme() {
215
+ const isDarkMode = document.documentElement.getAttribute('data-bs-theme') === 'dark';
216
+ const codeBlocks = document.querySelectorAll('code.sql-query-code');
217
+
218
+ codeBlocks.forEach(codeBlock => {
219
+ if (isDarkMode) {
220
+ codeBlock.style.backgroundColor = 'rgba(255, 255, 255, 0.1)';
221
+ codeBlock.style.color = '#65cdff';
222
+ } else {
223
+ codeBlock.style.backgroundColor = 'rgba(0, 0, 0, 0.05)';
224
+ codeBlock.style.color = '';
225
+ }
226
+ });
227
+ }
228
+
229
+ // Initial theme check
230
+ updateCodeBlockTheme();
231
+
232
+ // Listen for theme changes
233
+ document.addEventListener('dbviewerThemeChanged', function(event) {
234
+ updateCodeBlockTheme();
235
+ });
236
+ });
237
+ </script>