sqlite_dashboard 1.0.0

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.
@@ -0,0 +1,5 @@
1
+ <div id="query-results">
2
+ <div class="error-message">
3
+ <i class="fas fa-exclamation-circle"></i> <strong>Error:</strong> <%= error %>
4
+ </div>
5
+ </div>
@@ -0,0 +1,37 @@
1
+ <%= turbo_stream.replace "query-results" do %>
2
+ <div id="query-results">
3
+ <% if @results[:columns].present? %>
4
+ <div class="table-wrapper">
5
+ <table class="table table-striped table-hover table-bordered">
6
+ <thead class="table-dark">
7
+ <tr>
8
+ <% @results[:columns].each do |column| %>
9
+ <th><%= column %></th>
10
+ <% end %>
11
+ </tr>
12
+ </thead>
13
+ <tbody>
14
+ <% @results[:rows].each do |row| %>
15
+ <tr>
16
+ <% row.each do |value| %>
17
+ <td><%= value %></td>
18
+ <% end %>
19
+ </tr>
20
+ <% end %>
21
+ </tbody>
22
+ </table>
23
+ </div>
24
+ <div class="mt-3">
25
+ <small class="text-muted">
26
+ <%= pluralize(@results[:rows].size, 'row') %> returned
27
+ </small>
28
+ </div>
29
+ <% elsif @results[:message].present? %>
30
+ <div class="success-message">
31
+ <i class="fas fa-check-circle"></i> <%= @results[:message] %>
32
+ </div>
33
+ <% end %>
34
+ </div>
35
+ <% end %>
36
+
37
+ <%= turbo_stream.update "query", @query %>
@@ -0,0 +1,97 @@
1
+ <div class="databases-index-page">
2
+ <div class="row g-0" style="min-height: 100vh;">
3
+ <!-- Sidebar -->
4
+ <div class="col-md-2 sidebar index-sidebar">
5
+ <div class="p-3">
6
+ <div class="sidebar-brand">
7
+ <i class="fas fa-database"></i>
8
+ <div>
9
+ <h6 class="mb-0">SQLite Dashboard</h6>
10
+ <small class="text-muted">Database Manager</small>
11
+ </div>
12
+ </div>
13
+
14
+ <div class="sidebar-stats mt-4">
15
+ <div class="stat-item">
16
+ <i class="fas fa-server"></i>
17
+ <div>
18
+ <div class="stat-value"><%= @databases.count %></div>
19
+ <div class="stat-label">Database<%= @databases.count != 1 ? 's' : '' %></div>
20
+ </div>
21
+ </div>
22
+ </div>
23
+
24
+ <% if @databases.any? %>
25
+ <div class="mt-4">
26
+ <h6 class="text-muted mb-2" style="font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em;">Quick Access</h6>
27
+ <ul class="database-list">
28
+ <% @databases.each do |database| %>
29
+ <li>
30
+ <%= link_to database_path(database[:id]), class: "database-link" do %>
31
+ <i class="fas fa-database"></i>
32
+ <span><%= database[:name] %></span>
33
+ <i class="fas fa-chevron-right ms-auto"></i>
34
+ <% end %>
35
+ </li>
36
+ <% end %>
37
+ </ul>
38
+ </div>
39
+ <% end %>
40
+ </div>
41
+ </div>
42
+
43
+ <!-- Main content -->
44
+ <div class="col-md-10" style="background-color: var(--main-bg); min-height: 100vh;">
45
+ <div class="index-main-content">
46
+ <div class="content-header">
47
+ <h1 class="page-title">
48
+ <i class="fas fa-server"></i>
49
+ Your Databases
50
+ </h1>
51
+ <p class="page-subtitle">Select a database to explore and manage</p>
52
+ </div>
53
+
54
+ <% if @databases.any? %>
55
+ <div class="databases-grid">
56
+ <% @databases.each do |database| %>
57
+ <div class="database-card-modern">
58
+ <div class="database-card-icon">
59
+ <i class="fas fa-database"></i>
60
+ </div>
61
+ <div class="database-card-content">
62
+ <h3 class="database-card-title"><%= database[:name] %></h3>
63
+ <p class="database-card-path">
64
+ <i class="fas fa-folder-open"></i>
65
+ <%= database[:path] %>
66
+ </p>
67
+ </div>
68
+ <div class="database-card-actions">
69
+ <%= link_to database_path(database[:id]), class: "btn btn-primary btn-card-action" do %>
70
+ <i class="fas fa-arrow-right"></i>
71
+ Open Dashboard
72
+ <% end %>
73
+ </div>
74
+ </div>
75
+ <% end %>
76
+ </div>
77
+ <% else %>
78
+ <div class="empty-state-modern">
79
+ <div class="empty-state-icon">
80
+ <i class="fas fa-database"></i>
81
+ </div>
82
+ <h3>No Databases Configured</h3>
83
+ <p>Configure databases in your application's initializer to get started</p>
84
+ <div class="empty-state-code">
85
+ <pre><code>SqliteDashboard.configure do |config|
86
+ config.db_files = [
87
+ { name: "Development", path: Rails.root.join("db/development.sqlite3").to_s },
88
+ { name: "Production", path: Rails.root.join("db/production.sqlite3").to_s }
89
+ ]
90
+ end</code></pre>
91
+ </div>
92
+ </div>
93
+ <% end %>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
@@ -0,0 +1,60 @@
1
+ <div class="row g-0" style="height: 100vh;">
2
+ <!-- Sidebar with tables -->
3
+ <div class="col-md-2 sidebar">
4
+ <div class="p-3">
5
+ <h6 class="mb-3">
6
+ <i class="fas fa-database"></i> <%= @database[:name] %>
7
+ </h6>
8
+ <%= link_to "← Back to Databases", databases_path, class: "btn btn-sm btn-outline-secondary mb-3" %>
9
+
10
+ <h6 class="mb-2">Tables</h6>
11
+ <ul class="table-sidebar" data-controller="table-selector">
12
+ <% @tables.each do |table| %>
13
+ <li data-action="click->table-selector#selectTable"
14
+ data-table-name="<%= table %>"
15
+ data-table-selector-target="tableItem">
16
+ <i class="fas fa-table"></i> <%= table %>
17
+ </li>
18
+ <% end %>
19
+ </ul>
20
+ </div>
21
+ </div>
22
+
23
+ <!-- Main content area -->
24
+ <div class="col-md-10 main-content">
25
+ <!-- Query Console -->
26
+ <div class="query-console">
27
+ <%= form_with url: execute_query_database_path(@database[:id]),
28
+ method: :post,
29
+ data: {
30
+ controller: "query-executor",
31
+ action: "submit->query-executor#execute"
32
+ } do |form| %>
33
+ <div class="mb-3">
34
+ <label for="query" class="form-label fw-bold">SQL Query</label>
35
+ <textarea name="query"
36
+ id="query"
37
+ class="form-control query-textarea"
38
+ data-query-executor-target="queryInput"
39
+ placeholder="Enter your SQL query here..."><%= params[:query] || "SELECT * FROM sqlite_master WHERE type='table';" %></textarea>
40
+ </div>
41
+ <div class="d-flex gap-2">
42
+ <button type="submit" class="btn btn-primary">
43
+ <i class="fas fa-play"></i> Execute Query
44
+ </button>
45
+ <button type="button" class="btn btn-outline-secondary" data-action="click->query-executor#clear">
46
+ <i class="fas fa-eraser"></i> Clear
47
+ </button>
48
+ </div>
49
+ <% end %>
50
+ </div>
51
+
52
+ <!-- Results Container -->
53
+ <div class="results-container" id="query-results">
54
+ <div class="text-muted text-center py-5">
55
+ <i class="fas fa-database fa-3x mb-3"></i>
56
+ <p>Execute a query to see results</p>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
@@ -0,0 +1,2 @@
1
+ pin "sqlite_dashboard/application"
2
+ pin_all_from File.expand_path("../app/javascript/controllers", __dir__), under: "controllers", to: "sqlite_dashboard/controllers"
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+ SqliteDashboard::Engine.routes.draw do
2
+ root to: "databases#index"
3
+
4
+ resources :databases, only: [:index, :show] do
5
+ member do
6
+ post :execute_query
7
+ post :export_csv
8
+ post :export_json
9
+ get :tables
10
+ get :table_schema
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ require 'rails/generators'
2
+
3
+ module SqliteDashboard
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ desc "Install SQLite Dashboard in your Rails application"
7
+
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ def create_initializer
11
+ template "initializer.rb", "config/initializers/sqlite_dashboard.rb"
12
+ end
13
+
14
+ def add_route
15
+ route_content = <<-RUBY
16
+
17
+ # SQLite Dashboard Engine
18
+ mount SqliteDashboard::Engine => "/sqlite_dashboard"
19
+ RUBY
20
+
21
+ route route_content.strip
22
+ end
23
+
24
+ def display_readme
25
+ readme "README" if behavior == :invoke
26
+ end
27
+
28
+ private
29
+
30
+ def app_name
31
+ Rails.application.class.module_parent_name.underscore
32
+ end
33
+
34
+ def database_paths
35
+ paths = []
36
+
37
+ # Common Rails database locations
38
+ if File.exist?(Rails.root.join("storage", "development.sqlite3"))
39
+ paths << 'Rails.root.join("storage", "development.sqlite3").to_s'
40
+ end
41
+
42
+ if File.exist?(Rails.root.join("db", "development.sqlite3"))
43
+ paths << 'Rails.root.join("db", "development.sqlite3").to_s'
44
+ end
45
+
46
+ if File.exist?(Rails.root.join("storage", "test.sqlite3"))
47
+ paths << 'Rails.root.join("storage", "test.sqlite3").to_s'
48
+ end
49
+
50
+ if File.exist?(Rails.root.join("db", "test.sqlite3"))
51
+ paths << 'Rails.root.join("db", "test.sqlite3").to_s'
52
+ end
53
+
54
+ paths.empty? ? ['"/path/to/your/database.sqlite3"'] : paths
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,51 @@
1
+ ===============================================================================
2
+
3
+ SQLite Dashboard has been installed successfully!
4
+
5
+ Next Steps:
6
+ -----------
7
+
8
+ 1. Review the configuration in:
9
+ config/initializers/sqlite_dashboard.rb
10
+
11
+ 2. Update the database paths to match your SQLite files
12
+
13
+ 3. Start your Rails server:
14
+ rails server
15
+
16
+ 4. Visit the dashboard at:
17
+ http://localhost:3000/sqlite_dashboard
18
+
19
+ Security Warning:
20
+ -----------------
21
+ SQLite Dashboard provides direct SQL access to your databases.
22
+
23
+ For production or shared environments, add authentication:
24
+
25
+ In config/routes.rb, replace the mount line with:
26
+
27
+ authenticate :user, ->(user) { user.admin? } do
28
+ mount SqliteDashboard::Engine => "/sqlite_dashboard"
29
+ end
30
+
31
+ Or use HTTP Basic Auth by adding to the initializer:
32
+
33
+ SqliteDashboard::Engine.middleware.use Rack::Auth::Basic do |user, pass|
34
+ user == ENV['DASHBOARD_USER'] && pass == ENV['DASHBOARD_PASS']
35
+ end
36
+
37
+ Features:
38
+ ---------
39
+ ✨ SQL syntax highlighting with autocomplete
40
+ 📊 Paginated query results
41
+ 🔍 Quick table browsing
42
+ ⌨️ Keyboard shortcuts (Ctrl/Cmd + Enter to execute)
43
+ 📱 Responsive design
44
+
45
+ Documentation:
46
+ --------------
47
+ https://github.com/sqlite-dashboard/sqlite_dashboard
48
+
49
+ Enjoy exploring your SQLite databases! 🎉
50
+
51
+ ===============================================================================
@@ -0,0 +1,28 @@
1
+ # SQLite Dashboard Configuration
2
+ #
3
+ # Configure which SQLite databases you want to access through the dashboard.
4
+ # Only configure databases you trust, as this provides direct SQL access.
5
+
6
+ SqliteDashboard.configure do |config|
7
+ # Array of database configurations
8
+ # Each database should have a name and path
9
+ config.db_files = [
10
+ <% database_paths.each_with_index do |path, index| %>
11
+ {
12
+ name: "<%= index == 0 ? 'Development' : index == 1 ? 'Test' : "Database #{index + 1}" %>",
13
+ path: <%= path %>
14
+ }<%= index < database_paths.length - 1 ? ',' : '' %>
15
+ <% end %>
16
+ ]
17
+
18
+ # You can also add databases dynamically:
19
+ # config.add_database("Custom DB", "/path/to/custom.sqlite3")
20
+
21
+ # Security Note:
22
+ # For production use, wrap the mount in authentication:
23
+ #
24
+ # In config/routes.rb:
25
+ # authenticate :user, ->(user) { user.admin? } do
26
+ # mount SqliteDashboard::Engine => "/sqlite_dashboard"
27
+ # end
28
+ end
@@ -0,0 +1,89 @@
1
+ module SqliteDashboard
2
+ class Configuration
3
+ attr_accessor :db_files, :allow_dml
4
+
5
+ def initialize
6
+ @db_files = []
7
+ @allow_dml = false
8
+ end
9
+
10
+ def add_database(name, path)
11
+ @db_files << { name: name, path: path }
12
+ end
13
+
14
+ def databases
15
+ # If no databases are configured, try to read from config/database.yml
16
+ databases_list = @db_files.empty? ? load_from_database_yml : @db_files
17
+
18
+ databases_list.map.with_index do |db, index|
19
+ { id: index, name: db[:name] || File.basename(db[:path], ".*"), path: db[:path] }
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def load_from_database_yml
26
+ return [] unless defined?(Rails)
27
+
28
+ database_yml_path = Rails.root.join("config", "database.yml")
29
+ return [] unless File.exist?(database_yml_path)
30
+
31
+ begin
32
+ require 'yaml'
33
+ require 'erb'
34
+
35
+ yaml_content = ERB.new(File.read(database_yml_path)).result
36
+ config = YAML.safe_load(yaml_content, aliases: true)
37
+
38
+ current_env = Rails.env.to_s
39
+ env_config = config[current_env]
40
+
41
+ return [] unless env_config
42
+
43
+ databases = []
44
+
45
+ # Handle single database configuration
46
+ if env_config['adapter'] == 'sqlite3' && env_config['database']
47
+ database_path = resolve_database_path(env_config['database'])
48
+ databases << {
49
+ name: "#{current_env.titleize} DB",
50
+ path: database_path
51
+ } if database_path && File.exist?(database_path)
52
+ end
53
+
54
+ # Handle multiple databases configuration (Rails 6+)
55
+ if env_config.key?('primary') || env_config.any? { |k, v| v.is_a?(Hash) && v['adapter'] }
56
+ env_config.each do |key, db_config|
57
+ next unless db_config.is_a?(Hash)
58
+ next unless db_config['adapter'] == 'sqlite3'
59
+ next unless db_config['database']
60
+
61
+ database_path = resolve_database_path(db_config['database'])
62
+ if database_path && File.exist?(database_path)
63
+ db_name = key == 'primary' ? "#{current_env.titleize} DB" : "#{current_env.titleize} - #{key.titleize}"
64
+ databases << {
65
+ name: db_name,
66
+ path: database_path
67
+ }
68
+ end
69
+ end
70
+ end
71
+
72
+ databases
73
+ rescue => e
74
+ Rails.logger.warn("SqliteDashboard: Failed to load databases from database.yml: #{e.message}") if defined?(Rails.logger)
75
+ []
76
+ end
77
+ end
78
+
79
+ def resolve_database_path(database_path)
80
+ return nil if database_path.blank?
81
+
82
+ # Handle absolute paths
83
+ return database_path if Pathname.new(database_path).absolute?
84
+
85
+ # Handle relative paths
86
+ Rails.root.join(database_path).to_s if defined?(Rails)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,16 @@
1
+ module SqliteDashboard
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace SqliteDashboard
4
+
5
+ config.autoload_paths << File.expand_path("../../app/models", __FILE__)
6
+ config.autoload_paths << File.expand_path("../../app/services", __FILE__)
7
+
8
+ initializer "sqlite_dashboard.assets.precompile" do |app|
9
+ app.config.assets.precompile += %w( sqlite_dashboard/application.js sqlite_dashboard/application.css )
10
+ end
11
+
12
+ initializer "sqlite_dashboard.importmap", before: "importmap" do |app|
13
+ app.config.importmap.paths << root.join("config/importmap.rb") if app.config.respond_to?(:importmap)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module SqliteDashboard
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,17 @@
1
+ require "sqlite_dashboard/version"
2
+ require "sqlite_dashboard/engine"
3
+ require "sqlite_dashboard/configuration"
4
+
5
+ module SqliteDashboard
6
+ class << self
7
+ attr_writer :configuration
8
+
9
+ def configuration
10
+ @configuration ||= Configuration.new
11
+ end
12
+
13
+ def configure
14
+ yield(configuration)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,55 @@
1
+ require_relative "lib/sqlite_dashboard/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "sqlite_dashboard"
5
+ spec.version = SqliteDashboard::VERSION
6
+ spec.authors = ["SQLite Dashboard Contributors"]
7
+ spec.email = ["sqlite-dashboard@example.com"]
8
+ spec.homepage = "https://github.com/giovapanasiti/sqlite_dashboard"
9
+ spec.summary = "Beautiful SQLite database browser and query interface for Rails"
10
+ spec.description = <<~DESC
11
+ A feature-rich, mountable Rails engine that provides a beautiful web interface
12
+ for browsing and querying SQLite databases. Includes SQL syntax highlighting,
13
+ pagination, table browsing, and a responsive Bootstrap UI.
14
+ DESC
15
+ spec.license = "MIT"
16
+
17
+ spec.metadata = {
18
+ "homepage_uri" => spec.homepage,
19
+ "source_code_uri" => "https://github.com/giovapanasiti/sqlite_dashboard",
20
+ "changelog_uri" => "https://github.com/giovapanasiti/sqlite_dashboard/blob/main/CHANGELOG.md",
21
+ "bug_tracker_uri" => "https://github.com/giovapanasiti/sqlite_dashboard/issues",
22
+ "documentation_uri" => "https://github.com/giovapanasiti/sqlite_dashboard#readme",
23
+ "wiki_uri" => "https://github.com/giovapanasiti/sqlite_dashboard/wiki",
24
+ "funding_uri" => "https://github.com/sponsors/sqlite-dashboard",
25
+ "rubygems_mfa_required" => "true"
26
+ }
27
+
28
+ spec.required_ruby_version = ">= 3.0.0"
29
+
30
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
31
+ Dir[
32
+ "{app,config,db,lib}/**/*",
33
+ "LICENSE",
34
+ "Rakefile",
35
+ "README.md",
36
+ "CHANGELOG.md",
37
+ "*.gemspec"
38
+ ].reject { |f| File.directory?(f) }
39
+ end
40
+
41
+ spec.require_paths = ["lib"]
42
+
43
+ # Runtime dependencies
44
+ spec.add_dependency "rails", ">= 7.0.0"
45
+ spec.add_dependency "sqlite3", ">= 1.4"
46
+
47
+ # Development dependencies
48
+ spec.add_development_dependency "bundler", ">= 2.0"
49
+ spec.add_development_dependency "rake", "~> 13.0"
50
+ spec.add_development_dependency "minitest", "~> 5.0"
51
+ spec.add_development_dependency "rubocop", "~> 1.21"
52
+ spec.add_development_dependency "rubocop-rails", "~> 2.0"
53
+ spec.add_development_dependency "rubocop-minitest", "~> 0.15"
54
+ spec.add_development_dependency "yard", "~> 0.9"
55
+ end