dbviewer 0.5.3 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f8d59c15a88c9b63e2765f80b554c2b2a0a33f82fdb094e1ed6f86fdf8dde20
4
- data.tar.gz: d2bbe1fd8acae50c791ab4c32a061c78abbee095001e1647e32b4af4e6918753
3
+ metadata.gz: 5f0218efe5ea6bc79f61ca87472b6b413916b82e3aedf28e6030c37f302b2ca3
4
+ data.tar.gz: 65c58c041d85f80f0ad609e54225ec9c5bf3707bc36954ea07c36b95eb69a23c
5
5
  SHA512:
6
- metadata.gz: fcdf464a23ec52c45d85645ea0dcc9f57fe59c69b8452a68de7f000606d31d3e29c8b81ca237a228debc6884b57e8fb7a40500c1f02af550b3c5515456009ae5
7
- data.tar.gz: 768b6cb0f7d67c0ecda6301c6a94471ad16a89f37c6164c8e4f976222f1e1f95720b17477e43811f4d9574c79c71e1f6e582471d0acf408b815aeba17b58c961
6
+ metadata.gz: 15b2f9f0cf3543c59bc0230ef23070b93d64c7038387e8d384aa2613ef0f28badec97957c2b3d48ab991da86f22f48e9fd71a41844d9247d4cf3d8c015f46331
7
+ data.tar.gz: 05a6f0cfdd61f901a6dd17edbcd2a93c5244f461a4bfe61b350c2ed864af7e83ed1afa6489a8d2d0e81a66b307d0aa65ea8b81c080a4dc3cca896cec4ee20598
data/README.md CHANGED
@@ -111,7 +111,7 @@ This is necessary because API-only Rails applications don't include the Flash mi
111
111
  You can configure DBViewer by using our generator to create an initializer in your application:
112
112
 
113
113
  ```bash
114
- rails generate dbviewer:initializer
114
+ rails generate dbviewer:install
115
115
  ```
116
116
 
117
117
  This will create a file at `config/initializers/dbviewer.rb` with the default configuration:
@@ -172,7 +172,7 @@ config.query_logging_mode = :file # Store queries in a log file
172
172
  config.query_log_path = "log/dbviewer.log" # Path where query log file will be stored
173
173
  ```
174
174
 
175
- The file format uses one JSON entry per line, making it easy to analyze with standard tools.
175
+ The file format uses one JSON entry per line, making it easy to analyze with standard tools. Query Log collector are disabled by default on non development environtment.
176
176
 
177
177
  ## 🔒 Security Features
178
178
 
@@ -226,7 +226,7 @@ With the addition of Basic Authentication, DBViewer can now be used in any envir
226
226
  3. Access the tool through your regular application URL:
227
227
 
228
228
  ```
229
- https://yourdomain.com/dbviewer?override_env_check=your_secure_random_key
229
+ https://yourdomain.com/dbviewer
230
230
  ```
231
231
 
232
232
  ## 📝 Security Note
@@ -334,6 +334,7 @@ gem build dbviewer.gemspec
334
334
  3. The dummy app includes sample data across multiple tables to test various DBViewer features
335
335
 
336
336
  ### Architecture Diagram
337
+
337
338
  ```mermaid
338
339
  graph TB
339
340
  subgraph "DBViewer Engine"
@@ -33,12 +33,35 @@
33
33
  <div class="card-body p-0">
34
34
  <div id="erd-container" class="w-100 h-100" style="min-height: 450px;">
35
35
  <div id="erd-loading" class="d-flex justify-content-center align-items-center h-100" style="min-height: 450px;">
36
- <div class="text-center">
37
- <div class="spinner-border text-primary mb-3" role="status">
38
- <span class="visually-hidden">Loading...</span>
36
+ <div class="text-center" style="width: 100%; max-width: 500px;">
37
+ <div class="mb-4">
38
+ <i class="bi bi-diagram-3 text-primary" style="font-size: 3rem;"></i>
39
39
  </div>
40
- <p>Generating Entity Relationship Diagram...</p>
41
- <small class="text-muted">This may take a moment for databases with many tables</small>
40
+ <h5 class="mb-3">Generating Entity Relationship Diagram</h5>
41
+ <p id="loading-phase" class="mb-3">Initializing...</p>
42
+
43
+ <!-- Progress bar for table loading -->
44
+ <div class="progress mb-3" style="height: 8px;">
45
+ <div id="table-progress-bar" class="progress-bar bg-primary" role="progressbar"
46
+ style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
47
+ </div>
48
+ </div>
49
+
50
+ <!-- Progress text -->
51
+ <div class="d-flex justify-content-between align-items-center mb-2">
52
+ <small class="text-muted">Table Details</small>
53
+ <small id="table-progress-text" class="text-muted">0 / 0</small>
54
+ </div>
55
+
56
+ <!-- Relationships loading indicator -->
57
+ <div id="relationships-status" class="d-flex align-items-center justify-content-center mt-3">
58
+ <div class="spinner-border spinner-border-sm text-secondary me-2" role="status">
59
+ <span class="visually-hidden">Loading...</span>
60
+ </div>
61
+ <small class="text-muted">Loading relationships...</small>
62
+ </div>
63
+
64
+ <small class="text-muted d-block mt-3">This may take a moment for databases with many tables</small>
42
65
  </div>
43
66
  </div>
44
67
  <!-- The ERD will be rendered here -->
@@ -175,11 +198,13 @@
175
198
  console.log('Relationships loaded:', data);
176
199
  relationships = data.relationships || [];
177
200
  relationshipsLoaded = true;
201
+ updateRelationshipsStatus(true);
178
202
  return relationships;
179
203
  })
180
204
  .catch(error => {
181
205
  console.error('Error fetching relationships:', error);
182
206
  relationshipsLoaded = true; // Mark as loaded even on error to prevent infinite loading
207
+ updateRelationshipsStatus(true);
183
208
  return [];
184
209
  });
185
210
  }
@@ -187,9 +212,48 @@
187
212
  // Function to update loading status
188
213
  function updateLoadingStatus(message) {
189
214
  const loadingElement = document.getElementById('erd-loading');
190
- const loadingText = loadingElement.querySelector('p');
191
- if (loadingText) {
192
- loadingText.textContent = message;
215
+ const loadingPhase = document.getElementById('loading-phase');
216
+ if (loadingPhase) {
217
+ loadingPhase.textContent = message;
218
+ }
219
+ }
220
+
221
+ // Function to update table loading progress
222
+ function updateTableProgress(loaded, total) {
223
+ const progressBar = document.getElementById('table-progress-bar');
224
+ const progressText = document.getElementById('table-progress-text');
225
+
226
+ if (progressBar && progressText) {
227
+ const percentage = total > 0 ? Math.round((loaded / total) * 100) : 0;
228
+ progressBar.style.width = percentage + '%';
229
+ progressBar.setAttribute('aria-valuenow', percentage);
230
+ progressText.textContent = `${loaded} / ${total}`;
231
+
232
+ // Update progress bar color based on completion
233
+ if (percentage === 100) {
234
+ progressBar.classList.remove('bg-primary');
235
+ progressBar.classList.add('bg-success');
236
+ }
237
+ }
238
+ }
239
+
240
+ // Function to update relationships status
241
+ function updateRelationshipsStatus(loaded) {
242
+ const relationshipsStatus = document.getElementById('relationships-status');
243
+ if (relationshipsStatus) {
244
+ if (loaded) {
245
+ relationshipsStatus.innerHTML = `
246
+ <i class="bi bi-check-circle text-success me-2"></i>
247
+ <small class="text-success">Relationships loaded</small>
248
+ `;
249
+ } else {
250
+ relationshipsStatus.innerHTML = `
251
+ <div class="spinner-border spinner-border-sm text-secondary me-2" role="status">
252
+ <span class="visually-hidden">Loading...</span>
253
+ </div>
254
+ <small class="text-muted">Loading relationships...</small>
255
+ `;
256
+ }
193
257
  }
194
258
  }
195
259
 
@@ -203,8 +267,12 @@
203
267
  let columnsLoadedCount = 0;
204
268
  const totalTables = tables.length;
205
269
 
270
+ // Initialize progress bar
271
+ updateTableProgress(0, totalTables);
272
+ updateLoadingStatus('Loading table details...');
273
+
206
274
  // Start fetching relationships immediately
207
- updateLoadingStatus('Loading database relationships...');
275
+ updateRelationshipsStatus(false);
208
276
  const relationshipsPromise = fetchRelationships();
209
277
 
210
278
  // First pass: add all tables with minimal info and start loading columns
@@ -227,8 +295,8 @@
227
295
  tableColumns[tableName] = data.columns;
228
296
  columnsLoadedCount++;
229
297
 
230
- // Update loading status
231
- updateLoadingStatus(`Loading table details... (${columnsLoadedCount}/${totalTables} tables)`);
298
+ // Update progress bar
299
+ updateTableProgress(columnsLoadedCount, totalTables);
232
300
 
233
301
  checkIfReadyToUpdate();
234
302
  }
@@ -236,6 +304,7 @@
236
304
  .catch(error => {
237
305
  console.error(`Error fetching columns for table ${tableName}:`, error);
238
306
  columnsLoadedCount++;
307
+ updateTableProgress(columnsLoadedCount, totalTables);
239
308
  checkIfReadyToUpdate();
240
309
  });
241
310
  });
@@ -261,9 +330,9 @@
261
330
  if (isUpdatingDiagram) return;
262
331
 
263
332
  isUpdatingDiagram = true;
264
- console.log('Updating diagram with full column and relationship data');
333
+ console.log('Rendering diagram with full column and relationship data');
265
334
 
266
- updateLoadingStatus('Generating diagram...');
335
+ updateLoadingStatus('Generating final diagram...');
267
336
 
268
337
  // Regenerate the diagram with complete data
269
338
  let updatedDefinition = 'erDiagram\n';
@@ -289,43 +358,33 @@
289
358
  updatedDefinition += ' %% No relationships found in the database schema\n';
290
359
  }
291
360
 
292
- // Create a new diagram element
293
- const updatedErdDiv = document.createElement('div');
294
- updatedErdDiv.className = 'mermaid';
295
- updatedErdDiv.innerHTML = updatedDefinition;
361
+ // Create the diagram element
362
+ const erdDiv = document.createElement('div');
363
+ erdDiv.className = 'mermaid';
364
+ erdDiv.innerHTML = updatedDefinition;
296
365
 
297
- // Get the container but don't clear it yet
298
- const container = document.getElementById('erd-container');
299
-
300
- // First, clean up any previous zoom instance
301
- if (panZoomInstance) {
302
- panZoomInstance.destroy();
303
- panZoomInstance = null;
304
- }
305
-
306
- // Create a temporary container
366
+ // Create a temporary container for rendering
307
367
  const tempContainer = document.createElement('div');
308
368
  tempContainer.style.visibility = 'hidden';
309
369
  tempContainer.style.position = 'absolute';
310
370
  tempContainer.style.width = '100%';
311
- tempContainer.appendChild(updatedErdDiv);
371
+ tempContainer.appendChild(erdDiv);
312
372
  document.body.appendChild(tempContainer);
313
373
 
314
- // Render in the temporary container first
315
- mermaid.init(undefined, updatedErdDiv).then(function() {
316
- console.log('Diagram fully updated with all data');
374
+ // Render the diagram in the temporary container
375
+ mermaid.init(undefined, erdDiv).then(function() {
376
+ console.log('Diagram fully rendered with all data');
317
377
 
318
- // Clear original container and move the rendered content
319
378
  try {
320
379
  // Remove from temp container without destroying
321
- tempContainer.removeChild(updatedErdDiv);
380
+ tempContainer.removeChild(erdDiv);
322
381
 
323
382
  // Hide loading indicator
324
383
  document.getElementById('erd-loading').style.display = 'none';
325
384
 
326
385
  // Clear main container and add the diagram
327
386
  container.innerHTML = '';
328
- container.appendChild(updatedErdDiv);
387
+ container.appendChild(erdDiv);
329
388
 
330
389
  // Remove temp container
331
390
  document.body.removeChild(tempContainer);
@@ -342,101 +401,16 @@
342
401
  isUpdatingDiagram = false;
343
402
  }
344
403
  }).catch(function(error) {
345
- console.error('Error rendering updated diagram:', error);
404
+ console.error('Error rendering diagram:', error);
346
405
  document.body.removeChild(tempContainer);
347
406
  isUpdatingDiagram = false;
348
- showError('Error rendering diagram', 'There was an error updating the diagram with complete data.', error.message);
407
+ showError('Error rendering diagram', 'There was an error rendering the entity relationship diagram.', error.message);
349
408
  });
350
409
  }
351
410
 
352
- // Add initial relationships placeholder (empty)
353
- mermaidDefinition += ' %% Relationships loading...\n';
354
-
355
- // Create a div for the initial diagram (shows immediately with table names only)
356
- const erdDiv = document.createElement('div');
357
- erdDiv.className = 'mermaid';
358
- erdDiv.innerHTML = mermaidDefinition;
359
-
360
411
  // Get the container reference for later use
361
412
  const container = document.getElementById('erd-container');
362
413
 
363
- // Create a temporary container for initial rendering
364
- const tempInitContainer = document.createElement('div');
365
- tempInitContainer.style.visibility = 'hidden';
366
- tempInitContainer.style.position = 'absolute';
367
- tempInitContainer.style.width = '100%';
368
- tempInitContainer.appendChild(erdDiv);
369
- document.body.appendChild(tempInitContainer);
370
-
371
- // Update loading status for initial render
372
- updateLoadingStatus('Rendering initial diagram...');
373
-
374
- // Render the initial diagram in the temporary container
375
- mermaid.init(undefined, erdDiv).then(function() {
376
- try {
377
- // Remove from temp container without destroying
378
- tempInitContainer.removeChild(erdDiv);
379
-
380
- // Hide the loading indicator temporarily (will show updated loading for data fetching)
381
- document.getElementById('erd-loading').style.display = 'none';
382
-
383
- // Add the rendered diagram to the main container
384
- container.appendChild(erdDiv);
385
-
386
- // Remove temp container
387
- document.body.removeChild(tempInitContainer);
388
-
389
- // Setup initial zoom controls
390
- setTimeout(() => {
391
- setupZoomControls();
392
- // Show a subtle loading indicator that data is still loading
393
- showDataLoadingIndicator();
394
- }, 100);
395
- } catch(err) {
396
- console.error('Error moving initial diagram to container:', err);
397
- }
398
- }).catch(function(error) {
399
- console.error('Error rendering initial diagram:', error);
400
- document.body.removeChild(tempInitContainer);
401
- showError('Error generating diagram', 'There was an error generating the initial entity relationship diagram.', error.message);
402
- });
403
-
404
- // Function to show a subtle loading indicator for data loading
405
- function showDataLoadingIndicator() {
406
- // Create a small loading badge in the top-right corner
407
- const loadingBadge = document.createElement('div');
408
- loadingBadge.id = 'data-loading-badge';
409
- loadingBadge.className = 'position-absolute top-0 end-0 m-3';
410
- loadingBadge.style.zIndex = '1000';
411
- loadingBadge.innerHTML = `
412
- <div class="badge bg-info d-flex align-items-center">
413
- <div class="spinner-border spinner-border-sm me-2" role="status" style="width: 0.8rem; height: 0.8rem;">
414
- <span class="visually-hidden">Loading...</span>
415
- </div>
416
- Loading details...
417
- </div>
418
- `;
419
-
420
- container.style.position = 'relative';
421
- container.appendChild(loadingBadge);
422
-
423
- // Remove the badge when data loading is complete
424
- const checkComplete = () => {
425
- if (columnsLoadedCount === totalTables && relationshipsLoaded) {
426
- setTimeout(() => {
427
- const badge = document.getElementById('data-loading-badge');
428
- if (badge) {
429
- badge.remove();
430
- }
431
- }, 500); // Small delay to show completion
432
- } else {
433
- setTimeout(checkComplete, 500);
434
- }
435
- };
436
-
437
- setTimeout(checkComplete, 1000);
438
- }
439
-
440
414
  // SVG Pan Zoom instance
441
415
  let panZoomInstance = null;
442
416
 
@@ -1,3 +1,3 @@
1
1
  module Dbviewer
2
- VERSION = "0.5.3"
2
+ VERSION = "0.5.5"
3
3
  end
@@ -1,6 +1,6 @@
1
1
  module Dbviewer
2
2
  module Generators
3
- class InitializerGenerator < Rails::Generators::Base
3
+ class InstallGenerator < Rails::Generators::Base
4
4
  source_root File.expand_path("../templates", __FILE__)
5
5
  desc "Creates a DBViewer initializer file at config/initializers/dbviewer.rb"
6
6
 
@@ -1,21 +1,21 @@
1
1
  Dbviewer.configure do |config|
2
- config.per_page_options = [ 10, 20, 50, 100, 250 ]
3
- config.default_per_page = 20
4
- config.max_query_length = 10000
5
- config.cache_expiry = 300
6
- config.max_records = 10000
7
- config.enable_data_export = false
8
- config.query_timeout = 30
2
+ config.per_page_options = [ 10, 20, 50, 100, 250 ] # Default pagination options
3
+ config.default_per_page = 50 # Default records per page
4
+ config.max_query_length = 10000 # Maximum SQL query length
5
+ config.cache_expiry = 300 # Cache expiration in seconds
6
+ config.max_records = 10000 # Maximum records to return in any query
7
+ config.enable_data_export = false # Whether to allow data exporting
8
+ config.query_timeout = 30 # SQL query timeout in seconds
9
9
 
10
10
  # Query logging options
11
- config.enable_query_logging = false
12
- config.query_logging_mode = :memory
13
- config.query_log_path = "log/dbviewer.log"
14
- config.max_memory_queries = 1000
11
+ config.enable_query_logging = true # Enable or disable query logging completely (default: true)
12
+ config.query_logging_mode = :file # Storage mode for SQL queries (:memory or :file)
13
+ config.query_log_path = "log/dbviewer.log" # Path for query log file when in :file mode
14
+ config.max_memory_queries = 1000 # Maximum number of queries to store in memory
15
15
 
16
- # Authentication options
17
- # config.admin_credentials = { username: "admin", password: "your_secure_password" } # Basic HTTP auth credentials
18
-
19
- # Default table ordering options
20
- config.default_order_column = "updated_at" # Primary column to order by
16
+ # Authentication options (Basic Auth)
17
+ # config.admin_credentials = {
18
+ # username: "admin",
19
+ # password: "your_secure_password"
20
+ # }
21
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbviewer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wailan Tirajoh
@@ -99,7 +99,7 @@ files:
99
99
  - lib/dbviewer/storage/file_storage.rb
100
100
  - lib/dbviewer/storage/in_memory_storage.rb
101
101
  - lib/dbviewer/version.rb
102
- - lib/generators/dbviewer/initializer_generator.rb
102
+ - lib/generators/dbviewer/install_generator.rb
103
103
  - lib/generators/dbviewer/templates/initializer.rb
104
104
  - lib/tasks/dbviewer_tasks.rake
105
105
  homepage: https://github.com/wailantirajoh/dbviewer