dbviewer

๐Ÿ‘๏ธ DBViewer

DBViewer is a powerful Rails engine that provides a comprehensive interface to view and explore database tables, records, and schema. It's designed for development, debugging, and database analysis, offering a clean and intuitive way to interact with your application's database.

image

โœจ Features

  • Dashboard
  • Table Overview
  • Detailed Schema Information
  • Entity Relationship Diagram (ERD)
  • Data Browsing
  • SQL Queries
  • Multiple Database Connections
  • Enhanced UI Features

๐Ÿงช Demo Application

You can explore a live demo of DBViewer at https://dbviewer-demo.wailantirajoh.tech/. This demo showcases all the features of DBViewer on a sample database, allowing you to try out the tool before installing it in your own application.

๐Ÿ“ธ Screenshots

Click to see more screenshots image image

๐Ÿ“ฅ Installation

Add this line to your application's Gemfile:

gem "dbviewer", group: :development

And then execute:

bundle

๐Ÿ”ง Usage

Mount the engine in your application's config/routes.rb file:

Rails.application.routes.draw do
  # Your application routes...

  # Mount the DBViewer engine
  mount Dbviewer::Engine, at: "/dbviewer"
end

Then, visit /dbviewer in your browser to access the database viewer.

Rails API-only Applications

If you're using a Rails API-only application (created with --api flag), you'll need to enable the Flash middleware for DBViewer to work properly. Add the following to your config/application.rb:

module YourApp
  class Application < Rails::Application
    # ... existing configuration

    # Required for DBViewer flash messages
    config.middleware.use ActionDispatch::Flash
  end
end

This is necessary because API-only Rails applications don't include the Flash middleware by default, which DBViewer uses for displaying notifications.

Available Pages

  • Dashboard (/dbviewer): Comprehensive overview with database statistics and analytics
  • Tables Index (/dbviewer/tables): Shows all tables in your database with column counts and quick access
  • Table Details (/dbviewer/tables/:table_name): Shows table structure and records with pagination
  • SQL Query (/dbviewer/tables/:table_name/query): Allows running custom SQL queries
  • ERD View (/dbviewer/entity_relationship_diagrams): Interactive Entity Relationship Diagram of your database
  • SQL Query Logs (/dbviewer/logs): View and analyze logged SQL queries with performance metrics

โš™๏ธ Configuration Options

You can configure DBViewer by using our generator to create an initializer in your application:

rails generate dbviewer:install

This will create a file at config/initializers/dbviewer.rb with the default configuration:

# config/initializers/dbviewer.rb
Dbviewer.configure do |config|
  config.per_page_options = [10, 20, 50, 100, 250]  # Default pagination options
  config.default_per_page = 20                       # Default records per page
  config.max_query_length = 10000                    # Maximum SQL query length
  config.cache_expiry = 300                          # Cache expiration in seconds
  config.max_records = 10000                         # Maximum records to return in any query
  config.enable_data_export = false                  # Whether to allow data exporting
  config.query_timeout = 30                          # SQL query timeout in seconds

  # Query logging options
  config.enable_query_logging = false                # Enable or disable query logging completely (default: true)
  config.query_logging_mode = :memory                # Storage mode for SQL queries (:memory or :file)
  config.query_log_path = "log/dbviewer.log"         # Path for query log file when in :file mode
  config.max_memory_queries = 1000                   # Maximum number of queries to store in memory

  # Authentication options
  # config.admin_credentials = { username: "admin", password: "your_secure_password" } # Basic HTTP auth credentials
end

You can also create this file manually if you prefer.

The configuration is accessed through Dbviewer.configuration throughout the codebase. You can also access it via Dbviewer.config which is an alias for backward compatibility.

Multiple Database Connections

DBViewer supports working with multiple database connections in your application. This is useful for applications that connect to multiple databases or use different connection pools.

To configure multiple database connections, set them up in your initializer:

# config/initializers/dbviewer.rb
Dbviewer.configure do |config|
  # Multiple database connections configuration
  config.database_connections = {
    primary: {
      connection_class: "ActiveRecord::Base",
      name: "Primary Database"
    },
    secondary: {
      connection_class: "SecondaryDatabase",
      name: "Blog Database"
    }
  }

  # Set the default active connection
  config.current_connection = :primary
end

Each connection needs to reference an ActiveRecord class that establishes a database connection. For more details, see Multiple Database Connections.

๐Ÿชต Query Logging

DBViewer includes a powerful SQL query logging system that captures and analyzes database queries. You can access this log through the /dbviewer/logs endpoint. The logging system offers two storage backends:

Disabling Query Logging

You can completely disable query logging if you don't need this feature:

config.enable_query_logging = false       # Disable query logging completely

In-Memory Storage (Default)

By default, queries are stored in memory. This provides fast access but queries are lost when the application restarts:

config.query_logging_mode = :memory       # Store queries in memory (default)
config.max_memory_queries = 1000          # Maximum number of queries stored

File-Based Storage

For persistent logging across application restarts, you can use file-based storage:

config.query_logging_mode = :file         # Store queries in a log file
config.query_log_path = "log/dbviewer.log" # Path where query log file will be stored

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.

๐Ÿ”’ Security Features

DBViewer includes several security features to protect your database:

  • Read-only Mode: Only SELECT queries are allowed; all data modification operations are blocked
  • SQL Validation: Prevents potentially harmful operations with comprehensive validation
  • Query Limits: Automatic LIMIT clause added to prevent excessive data retrieval
  • Pattern Detection: Detection of SQL injection patterns and suspicious constructs
  • Error Handling: Informative error messages without exposing sensitive information
  • HTTP Basic Authentication: Protect access with username and password authentication

Basic Authentication

You can enable HTTP Basic Authentication to secure access to DBViewer:

Dbviewer.configure do |config|
  config.admin_credentials = {
    username: "your_username",
    password: "your_secure_password"
  }
end

When credentials are provided, all DBViewer routes will be protected by HTTP Basic Authentication. Without valid credentials, users will be prompted for a username and password before they can access any DBViewer page.

๐ŸŒฑ Production Access

With the addition of Basic Authentication, DBViewer can now be used in any environment including production. We recommend the following for production deployments:

  1. Always enable HTTP Basic Authentication with strong credentials:
   Dbviewer.configure do |config|
     config.admin_credentials = {
       username: "unique_username",
       password: SecureRandom.hex(16)  # Generate a strong random password
     }
   end
  1. Mount the engine in your routes file:
   # In any environment, with Basic Auth protection
   mount Dbviewer::Engine, at: "/dbviewer"
  1. Access the tool through your regular application URL:
   https://yourdomain.com/dbviewer

๐Ÿ“ Security Note

โš ๏ธ Warning: This engine provides direct access to your database contents, which contains sensitive information. Always protect it with HTTP Basic Authentication by configuring strong credentials as shown above.

When used in production, ensure:

  • You use long, randomly generated passwords (e.g., with SecureRandom.hex(16))
  • You access DBViewer over HTTPS connections only
  • Access is limited to trusted administrators only

๐Ÿ”„ Updating DBViewer

To keep DBViewer up to date with the latest features, security patches, and bug fixes, follow these steps:

Using Bundler

The simplest way to update is using Bundler:

  • Update your Gemfile with the desired version:
  # For the latest version
  gem "dbviewer", group: :development

  # Or specify a version
  gem "dbviewer", "~> 0.3.2", group: :development
  • Run bundle update:
  bundle update dbviewer
  • Restart your Rails server to apply the changes:
  rails server

๐Ÿ› ๏ธ Development Setup

To set up the development environment for contributing to DBViewer:

Quick Setup

Run the setup script to automatically configure your development environment:

bin/setup

This script will:

  • Install bundler and gem dependencies
  • Set up the test dummy Rails application
  • Create and seed the development database
  • Prepare the test environment
  • Clean up old logs and temporary files

Manual Setup

If you prefer to set up manually:

# Install dependencies
bundle install

# Set up the dummy app database
cd test/dummy
bin/rails db:prepare
bin/rails db:migrate
bin/rails db:seed
cd ../..

# Prepare test environment
cd test/dummy && bin/rails db:test:prepare && cd ../..

Development Commands

# Start the development server
cd test/dummy && bin/rails server

# Run tests
bundle exec rspec

# Run code quality checks
bin/rubocop

# Open an interactive console
bin/console

# Build the gem
gem build dbviewer.gemspec

Testing Your Changes

  1. Start the dummy Rails application: cd test/dummy && bin/rails server
  2. Visit http://localhost:3000/dbviewer to test your changes
  3. The dummy app includes sample data across multiple tables to test various DBViewer features

Architecture Diagram

graph TB
    subgraph "DBViewer Engine"
        Engine[Engine<br/>Rails::Engine]
        Config[Configuration<br/>Settings & Defaults]
    end

    subgraph "Controllers Layer"
        HomeController[HomeController<br/>Dashboard & Overview]
        TablesController[TablesController<br/>Table Operations]
        LogsController[LogsController<br/>Query Logs]
        ERDController[ERDController<br/>Entity Relationships]
        APIController[API Controllers<br/>JSON Endpoints]
        ConnectionsController[ConnectionsController<br/>Database Connections]
    end

    subgraph "Controller Concerns"
        DatabaseOperations[DatabaseOperations<br/>Shared Database Logic]
    end

    subgraph "Database Namespace"
        Manager[Manager<br/>Database Operations]
        CacheManager[CacheManager<br/>Caching Layer]
        MetadataManager[MetadataManager<br/>Schema Information]
        DynamicModelFactory[DynamicModelFactory<br/>ActiveRecord Models]
    end

    subgraph "Query Namespace"
        QueryExecutor[Executor<br/>SQL Execution]
        QueryLogger[Logger<br/>Query Logging]
        QueryAnalyzer[Analyzer<br/>Performance Analysis]
        QueryParser[Parser<br/>SQL Parsing]
        NotificationSubscriber[NotificationSubscriber<br/>Query Notifications]
    end

    subgraph "Datatable Namespace"
        QueryOperations[QueryOperations<br/>Table Queries & Filtering]
        QueryParams[QueryParams<br/>Parameter Handling]
        ColumnFiltering[apply_single_column_filter<br/>Individual Filter Logic]
    end

    subgraph "Storage Namespace"
        StorageBase[Base<br/>Storage Interface]
        InMemoryStorage[InMemoryStorage<br/>Memory Storage]
        FileStorage[FileStorage<br/>File Storage]
    end

    subgraph "Validation Namespace"
        ValidatorSql[Validator::Sql<br/>Query Validation]
    end

    %% Configuration Dependencies (Decoupled)
    Config -.->|"Dependency Injection"| Manager
    Manager -->|"cache_expiry"| CacheManager
    Manager -->|"config object"| QueryExecutor

    %% Engine Initialization
    Engine --> HomeController
    Engine --> TablesController
    Engine --> LogsController
    Engine --> ERDController
    Engine --> ConnectionsController
    Engine -.->|"setup()"| QueryLogger
    Engine -.->|"subscribe"| NotificationSubscriber

    %% Controller Dependencies
    TablesController --> DatabaseOperations
    HomeController --> DatabaseOperations
    ConnectionsController --> DatabaseOperations
    APIController --> DatabaseOperations
    LogsController -.->|"Get List"| QueryLogger

    DatabaseOperations --> Manager
    DatabaseOperations --> QueryOperations

    %% Manager Dependencies
    Manager --> CacheManager
    Manager --> MetadataManager
    Manager --> DynamicModelFactory
    Manager --> QueryOperations

    %% Cache Dependencies
    CacheManager --> DynamicModelFactory
    CacheManager --> MetadataManager

    %% QueryOperations Dependencies (Refactored)
    QueryOperations --> DynamicModelFactory
    QueryOperations --> MetadataManager
    QueryOperations --> QueryAnalyzer
    QueryOperations --> ColumnFiltering

    %% Storage Dependencies
    QueryLogger --> StorageBase
    StorageBase --> InMemoryStorage
    StorageBase --> FileStorage

    %% Configuration Flow
    Config -.->|"logging settings"| QueryLogger
    Config -.->|"database connections"| Manager

    %% Validation
    ValidatorSql --> QueryExecutor

    %% Styling
    class CacheManager,QueryLogger decoupled
    class HomeController,TablesController,LogsController,ERDController,APIController,ConnectionsController controller
    class DatabaseOperations concern
    class Manager,MetadataManager,DynamicModelFactory database
    class QueryExecutor,QueryAnalyzer,QueryParser,NotificationSubscriber query
    class StorageBase,InMemoryStorage,FileStorage storage
    class ColumnFiltering filtering
    class ValidatorSql validation

๐ŸคŒ๐Ÿป Contributing

Bug reports and pull requests are welcome.

๐Ÿ“„ License

The gem is available as open source under the terms of the MIT License.