sqlite_dashboard 1.0.0 → 1.0.2

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.
@@ -21,13 +21,12 @@
21
21
  </div>
22
22
 
23
23
  <!-- Main content area -->
24
- <div class="col-md-10 main-content">
24
+ <div class="col-md-10 main-content" data-controller="query-executor" data-database-name="<%= @database[:name] %>">
25
25
  <!-- Query Console -->
26
26
  <div class="query-console">
27
27
  <%= form_with url: execute_query_database_path(@database[:id]),
28
28
  method: :post,
29
29
  data: {
30
- controller: "query-executor",
31
30
  action: "submit->query-executor#execute"
32
31
  } do |form| %>
33
32
  <div class="mb-3">
@@ -45,6 +44,12 @@
45
44
  <button type="button" class="btn btn-outline-secondary" data-action="click->query-executor#clear">
46
45
  <i class="fas fa-eraser"></i> Clear
47
46
  </button>
47
+ <button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#saveQueryModal">
48
+ <i class="fas fa-save"></i> Save Query
49
+ </button>
50
+ <%= link_to worksheet_path, class: "btn btn-outline-info" do %>
51
+ <i class="fas fa-file-code"></i> SQL Worksheet
52
+ <% end %>
48
53
  </div>
49
54
  <% end %>
50
55
  </div>
@@ -56,5 +61,34 @@
56
61
  <p>Execute a query to see results</p>
57
62
  </div>
58
63
  </div>
64
+
65
+ <!-- Save Query Modal -->
66
+ <div class="modal fade" id="saveQueryModal" tabindex="-1" aria-labelledby="saveQueryModalLabel" aria-hidden="true">
67
+ <div class="modal-dialog">
68
+ <div class="modal-content">
69
+ <div class="modal-header">
70
+ <h5 class="modal-title" id="saveQueryModalLabel">Save Query</h5>
71
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
72
+ </div>
73
+ <div class="modal-body">
74
+ <div class="mb-3">
75
+ <label for="query-name" class="form-label">Query Name *</label>
76
+ <input type="text" class="form-control" id="query-name" data-query-executor-target="queryName" required>
77
+ </div>
78
+ <div class="mb-3">
79
+ <label for="query-description" class="form-label">Description *</label>
80
+ <textarea class="form-control" id="query-description" rows="2" data-query-executor-target="queryDescription" required></textarea>
81
+ </div>
82
+ <div id="save-error" class="alert alert-danger d-none" data-query-executor-target="saveError"></div>
83
+ </div>
84
+ <div class="modal-footer">
85
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
86
+ <button type="button" class="btn btn-primary" data-action="click->query-executor#saveQuery">
87
+ <i class="fas fa-save"></i> Save
88
+ </button>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </div>
59
93
  </div>
60
94
  </div>
@@ -0,0 +1,111 @@
1
+ <div class="row g-0" style="height: 100vh;" data-controller="saved-queries">
2
+ <!-- Sidebar with saved queries -->
3
+ <div class="col-md-2 sidebar">
4
+ <div class="p-3">
5
+ <h6 class="mb-3">
6
+ <i class="fas fa-file-code"></i> SQL Worksheet
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">Saved Queries</h6>
11
+ <div id="saved-queries-list" data-saved-queries-target="list">
12
+ <div class="text-muted small text-center py-3">
13
+ <i class="fas fa-spinner fa-spin"></i> Loading...
14
+ </div>
15
+ </div>
16
+
17
+ <button type="button" class="btn btn-sm btn-primary w-100 mt-3" data-action="click->saved-queries#refresh">
18
+ <i class="fas fa-sync-alt"></i> Refresh
19
+ </button>
20
+ </div>
21
+ </div>
22
+
23
+ <!-- Main content area -->
24
+ <div class="col-md-10 main-content">
25
+ <!-- Database Selector -->
26
+ <div class="mb-3 px-4 pt-2">
27
+ <label for="database-selector" class="form-label fw-bold">Select Database</label>
28
+ <select id="database-selector" class="form-select" data-saved-queries-target="databaseSelector">
29
+ <option value="">-- Select a database --</option>
30
+ <% @databases.each do |db| %>
31
+ <option value="<%= db[:id] %>"><%= db[:name] %></option>
32
+ <% end %>
33
+ </select>
34
+ </div>
35
+
36
+ <!-- Query Console -->
37
+ <div class="query-console">
38
+ <div class="mb-3">
39
+ <label for="worksheet-query" class="form-label fw-bold">SQL Query</label>
40
+ <textarea id="worksheet-query"
41
+ class="form-control query-textarea"
42
+ data-saved-queries-target="queryInput"
43
+ placeholder="Enter your SQL query here..."></textarea>
44
+ </div>
45
+ <div class="d-flex gap-2 mb-2">
46
+ <button type="button" class="btn btn-primary" data-action="click->saved-queries#execute">
47
+ <i class="fas fa-play"></i> Execute Query
48
+ </button>
49
+ <button type="button" class="btn btn-outline-secondary" data-action="click->saved-queries#clear">
50
+ <i class="fas fa-eraser"></i> Clear
51
+ </button>
52
+ <button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#saveQueryModal">
53
+ <i class="fas fa-save"></i> Save Query
54
+ </button>
55
+ <button type="button" class="btn btn-outline-info" data-action="click->saved-queries#exportCSV">
56
+ <i class="fas fa-file-csv"></i> Export CSV
57
+ </button>
58
+ <button type="button" class="btn btn-outline-info" data-action="click->saved-queries#exportJSON">
59
+ <i class="fas fa-file-code"></i> Export JSON
60
+ </button>
61
+ </div>
62
+ </div>
63
+
64
+ <!-- Results Container -->
65
+ <div class="results-container" id="worksheet-results">
66
+ <div class="text-muted text-center py-5">
67
+ <i class="fas fa-database fa-3x mb-3"></i>
68
+ <p>Select a database and execute a query to see results</p>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </div>
73
+
74
+ <!-- Save Query Modal -->
75
+ <div class="modal fade" id="saveQueryModal" tabindex="-1" aria-labelledby="saveQueryModalLabel" aria-hidden="true">
76
+ <div class="modal-dialog">
77
+ <div class="modal-content">
78
+ <div class="modal-header">
79
+ <h5 class="modal-title" id="saveQueryModalLabel">Save Query</h5>
80
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
81
+ </div>
82
+ <div class="modal-body">
83
+ <div class="mb-3">
84
+ <label for="query-name" class="form-label">Query Name *</label>
85
+ <input type="text" class="form-control" id="query-name" data-saved-queries-target="queryName" required>
86
+ </div>
87
+ <div class="mb-3">
88
+ <label for="query-description" class="form-label">Description *</label>
89
+ <textarea class="form-control" id="query-description" rows="2" data-saved-queries-target="queryDescription" required></textarea>
90
+ </div>
91
+ <div id="save-error" class="alert alert-danger d-none" data-saved-queries-target="saveError"></div>
92
+ </div>
93
+ <div class="modal-footer">
94
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
95
+ <button type="button" class="btn btn-primary" data-action="click->saved-queries#saveQuery">
96
+ <i class="fas fa-save"></i> Save
97
+ </button>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </div>
102
+
103
+ <script>
104
+ // Initialize the saved queries controller when the page loads
105
+ document.addEventListener('turbo:load', () => {
106
+ const element = document.querySelector('[data-controller="saved-queries"]');
107
+ if (!element) {
108
+ document.body.setAttribute('data-controller', 'saved-queries');
109
+ }
110
+ });
111
+ </script>
data/config/routes.rb CHANGED
@@ -1,6 +1,15 @@
1
1
  SqliteDashboard::Engine.routes.draw do
2
2
  root to: "databases#index"
3
3
 
4
+ # Saved queries
5
+ get 'saved_queries', to: 'databases#saved_queries'
6
+ post 'saved_queries', to: 'databases#create_saved_query'
7
+ get 'saved_queries/:id', to: 'databases#show_saved_query', as: :saved_query
8
+ delete 'saved_queries/:id', to: 'databases#destroy_saved_query'
9
+
10
+ # SQL Worksheet
11
+ get 'worksheet', to: 'databases#worksheet', as: :worksheet
12
+
4
13
  resources :databases, only: [:index, :show] do
5
14
  member do
6
15
  post :execute_query
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateSqliteDashboardSavedQueries < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :dashboard_saved_queries do |t|
6
+ t.string :name, null: false
7
+ t.text :query, null: false
8
+ t.string :database_name
9
+ t.text :description
10
+
11
+ t.timestamps
12
+ end
13
+
14
+ add_index :dashboard_saved_queries, :name
15
+ add_index :dashboard_saved_queries, :created_at
16
+ end
17
+ end
@@ -21,6 +21,10 @@ module SqliteDashboard
21
21
  route route_content.strip
22
22
  end
23
23
 
24
+ def copy_migrations
25
+ rake "sqlite_dashboard:install:migrations"
26
+ end
27
+
24
28
  def display_readme
25
29
  readme "README" if behavior == :invoke
26
30
  end
@@ -1,6 +1,8 @@
1
1
  ===============================================================================
2
2
 
3
- SQLite Dashboard has been installed successfully!
3
+ SQLite Dashboard has been installed successfully!
4
+
5
+ ===============================================================================
4
6
 
5
7
  Next Steps:
6
8
  -----------
@@ -8,7 +10,7 @@ Next Steps:
8
10
  1. Review the configuration in:
9
11
  config/initializers/sqlite_dashboard.rb
10
12
 
11
- 2. Update the database paths to match your SQLite files
13
+ 2. (Optional) Update database paths or use auto-detection from database.yml
12
14
 
13
15
  3. Start your Rails server:
14
16
  rails server
@@ -16,35 +18,64 @@ Next Steps:
16
18
  4. Visit the dashboard at:
17
19
  http://localhost:3000/sqlite_dashboard
18
20
 
19
- Security Warning:
20
- -----------------
21
- SQLite Dashboard provides direct SQL access to your databases.
21
+ Features:
22
+ ---------
23
+ SQL syntax highlighting with CodeMirror
24
+ 📊 Paginated query results
25
+ 🔍 Quick table browsing with sidebar navigation
26
+ 💾 Export to CSV and JSON with custom options
27
+ ⌨️ Keyboard shortcuts (Ctrl/Cmd + Enter to execute)
28
+ 🎨 Modern, responsive UI with dark sidebar theme
29
+ 🔒 Read-only mode by default (configurable)
30
+ 🗄️ Auto-detects databases from config/database.yml
31
+ ⚡ Client-side pagination for fast browsing
32
+
33
+ Security Configuration:
34
+ -----------------------
35
+ By default, SQLite Dashboard runs in READ-ONLY mode.
36
+
37
+ To enable write operations, set in the initializer:
38
+ config.allow_dml = true
22
39
 
23
- For production or shared environments, add authentication:
40
+ Note: DROP and ALTER are always forbidden for safety.
24
41
 
25
- In config/routes.rb, replace the mount line with:
42
+ Production Security:
43
+ --------------------
44
+ IMPORTANT: Add authentication before deploying to production!
45
+
46
+ Option 1 - Devise/Clearance authentication:
47
+ In config/routes.rb, replace the mount line with:
26
48
 
27
49
  authenticate :user, ->(user) { user.admin? } do
28
50
  mount SqliteDashboard::Engine => "/sqlite_dashboard"
29
51
  end
30
52
 
31
- Or use HTTP Basic Auth by adding to the initializer:
53
+ Option 2 - HTTP Basic Auth:
54
+ Add to config/application.rb or the initializer:
32
55
 
33
56
  SqliteDashboard::Engine.middleware.use Rack::Auth::Basic do |user, pass|
34
- user == ENV['DASHBOARD_USER'] && pass == ENV['DASHBOARD_PASS']
57
+ ActiveSupport::SecurityUtils.secure_compare(user, ENV['DASHBOARD_USER']) &
58
+ ActiveSupport::SecurityUtils.secure_compare(pass, ENV['DASHBOARD_PASS'])
35
59
  end
36
60
 
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
61
+ Export Features:
62
+ ----------------
63
+ 📄 CSV Export: Choose separator (comma, semicolon, tab, pipe)
64
+ - Optional headers row
65
+ - Exports all query results (not just paginated view)
66
+
67
+ 🔧 JSON Export: Two format options
68
+ - Array of objects: [{"col1": "val1"}, ...]
69
+ - Object with columns & rows: {"columns": [...], "rows": [...]}
70
+ - Pretty print option for readable formatting
44
71
 
45
72
  Documentation:
46
73
  --------------
47
- https://github.com/sqlite-dashboard/sqlite_dashboard
74
+ https://github.com/giovapanasiti/sqlite_dashboard
75
+
76
+ Support:
77
+ --------
78
+ Report issues: https://github.com/giovapanasiti/sqlite_dashboard/issues
48
79
 
49
80
  Enjoy exploring your SQLite databases! 🎉
50
81
 
@@ -4,6 +4,10 @@
4
4
  # Only configure databases you trust, as this provides direct SQL access.
5
5
 
6
6
  SqliteDashboard.configure do |config|
7
+ # =============================================================================
8
+ # Database Configuration
9
+ # =============================================================================
10
+
7
11
  # Array of database configurations
8
12
  # Each database should have a name and path
9
13
  config.db_files = [
@@ -18,11 +22,35 @@ SqliteDashboard.configure do |config|
18
22
  # You can also add databases dynamically:
19
23
  # config.add_database("Custom DB", "/path/to/custom.sqlite3")
20
24
 
21
- # Security Note:
25
+ # If no databases are configured, SQLite Dashboard will automatically
26
+ # detect and use SQLite databases from config/database.yml for the current environment
27
+
28
+ # =============================================================================
29
+ # Security Configuration
30
+ # =============================================================================
31
+
32
+ # Allow DML operations (INSERT, UPDATE, DELETE, CREATE, TRUNCATE)
33
+ # Default: false (read-only mode)
34
+ # Note: DROP and ALTER are always forbidden for safety
35
+ config.allow_dml = false
36
+
37
+ # Set to true to allow INSERT, UPDATE, DELETE, CREATE, TRUNCATE operations
38
+ # config.allow_dml = true
39
+
40
+ # =============================================================================
41
+ # Production Security Note
42
+ # =============================================================================
43
+ #
22
44
  # For production use, wrap the mount in authentication:
23
45
  #
24
46
  # In config/routes.rb:
25
47
  # authenticate :user, ->(user) { user.admin? } do
26
48
  # mount SqliteDashboard::Engine => "/sqlite_dashboard"
27
49
  # end
50
+ #
51
+ # Or use HTTP Basic Auth (add to config/application.rb or an initializer):
52
+ # SqliteDashboard::Engine.middleware.use Rack::Auth::Basic do |user, pass|
53
+ # ActiveSupport::SecurityUtils.secure_compare(user, ENV['DASHBOARD_USER']) &
54
+ # ActiveSupport::SecurityUtils.secure_compare(pass, ENV['DASHBOARD_PASS'])
55
+ # end
28
56
  end
@@ -1,3 +1,3 @@
1
1
  module SqliteDashboard
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.2"
3
3
  end
@@ -43,6 +43,7 @@ Gem::Specification.new do |spec|
43
43
  # Runtime dependencies
44
44
  spec.add_dependency "rails", ">= 7.0.0"
45
45
  spec.add_dependency "sqlite3", ">= 1.4"
46
+ spec.add_dependency "csv", ">= 3.0"
46
47
 
47
48
  # Development dependencies
48
49
  spec.add_development_dependency "bundler", ">= 2.0"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqlite_dashboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - SQLite Dashboard Contributors
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-10-02 00:00:00.000000000 Z
10
+ date: 2025-10-28 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -38,6 +37,20 @@ dependencies:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
39
  version: '1.4'
40
+ - !ruby/object:Gem::Dependency
41
+ name: csv
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
41
54
  - !ruby/object:Gem::Dependency
42
55
  name: bundler
43
56
  requirement: !ruby/object:Gem::Requirement
@@ -152,15 +165,20 @@ files:
152
165
  - app/controllers/sqlite_dashboard/application_controller.rb
153
166
  - app/controllers/sqlite_dashboard/databases_controller.rb
154
167
  - app/javascript/controllers/query_executor_controller.js
168
+ - app/javascript/controllers/saved_queries_controller.js
155
169
  - app/javascript/controllers/table_selector_controller.js
156
170
  - app/javascript/sqlite_dashboard/application.js
171
+ - app/models/sqlite_dashboard/application_record.rb
172
+ - app/models/sqlite_dashboard/saved_query.rb
157
173
  - app/views/layouts/sqlite_dashboard/application.html.erb
158
174
  - app/views/sqlite_dashboard/databases/_error.html.erb
159
175
  - app/views/sqlite_dashboard/databases/execute_query.turbo_stream.erb
160
176
  - app/views/sqlite_dashboard/databases/index.html.erb
161
177
  - app/views/sqlite_dashboard/databases/show.html.erb
178
+ - app/views/sqlite_dashboard/databases/worksheet.html.erb
162
179
  - config/importmap.rb
163
180
  - config/routes.rb
181
+ - db/migrate/20250101000001_create_sqlite_dashboard_saved_queries.rb
164
182
  - lib/generators/sqlite_dashboard/install_generator.rb
165
183
  - lib/generators/sqlite_dashboard/templates/README
166
184
  - lib/generators/sqlite_dashboard/templates/initializer.rb
@@ -181,7 +199,6 @@ metadata:
181
199
  wiki_uri: https://github.com/giovapanasiti/sqlite_dashboard/wiki
182
200
  funding_uri: https://github.com/sponsors/sqlite-dashboard
183
201
  rubygems_mfa_required: 'true'
184
- post_install_message:
185
202
  rdoc_options: []
186
203
  require_paths:
187
204
  - lib
@@ -196,8 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
196
213
  - !ruby/object:Gem::Version
197
214
  version: '0'
198
215
  requirements: []
199
- rubygems_version: 3.5.9
200
- signing_key:
216
+ rubygems_version: 3.6.2
201
217
  specification_version: 4
202
218
  summary: Beautiful SQLite database browser and query interface for Rails
203
219
  test_files: []