pg_reports 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +335 -0
- data/app/controllers/pg_reports/dashboard_controller.rb +133 -0
- data/app/views/layouts/pg_reports/application.html.erb +594 -0
- data/app/views/pg_reports/dashboard/index.html.erb +435 -0
- data/app/views/pg_reports/dashboard/show.html.erb +481 -0
- data/config/routes.rb +13 -0
- data/lib/pg_reports/annotation_parser.rb +114 -0
- data/lib/pg_reports/configuration.rb +83 -0
- data/lib/pg_reports/dashboard/reports_registry.rb +89 -0
- data/lib/pg_reports/engine.rb +22 -0
- data/lib/pg_reports/error.rb +15 -0
- data/lib/pg_reports/executor.rb +51 -0
- data/lib/pg_reports/modules/connections.rb +106 -0
- data/lib/pg_reports/modules/indexes.rb +111 -0
- data/lib/pg_reports/modules/queries.rb +140 -0
- data/lib/pg_reports/modules/system.rb +148 -0
- data/lib/pg_reports/modules/tables.rb +113 -0
- data/lib/pg_reports/report.rb +228 -0
- data/lib/pg_reports/sql/connections/active_connections.sql +20 -0
- data/lib/pg_reports/sql/connections/blocking_queries.sql +35 -0
- data/lib/pg_reports/sql/connections/connection_stats.sql +13 -0
- data/lib/pg_reports/sql/connections/idle_connections.sql +19 -0
- data/lib/pg_reports/sql/connections/locks.sql +20 -0
- data/lib/pg_reports/sql/connections/long_running_queries.sql +21 -0
- data/lib/pg_reports/sql/indexes/bloated_indexes.sql +36 -0
- data/lib/pg_reports/sql/indexes/duplicate_indexes.sql +38 -0
- data/lib/pg_reports/sql/indexes/index_sizes.sql +14 -0
- data/lib/pg_reports/sql/indexes/index_usage.sql +19 -0
- data/lib/pg_reports/sql/indexes/invalid_indexes.sql +15 -0
- data/lib/pg_reports/sql/indexes/missing_indexes.sql +27 -0
- data/lib/pg_reports/sql/indexes/unused_indexes.sql +18 -0
- data/lib/pg_reports/sql/queries/all_queries.sql +20 -0
- data/lib/pg_reports/sql/queries/expensive_queries.sql +22 -0
- data/lib/pg_reports/sql/queries/heavy_queries.sql +17 -0
- data/lib/pg_reports/sql/queries/low_cache_hit_queries.sql +19 -0
- data/lib/pg_reports/sql/queries/missing_index_queries.sql +25 -0
- data/lib/pg_reports/sql/queries/slow_queries.sql +17 -0
- data/lib/pg_reports/sql/system/activity_overview.sql +29 -0
- data/lib/pg_reports/sql/system/cache_stats.sql +19 -0
- data/lib/pg_reports/sql/system/database_sizes.sql +10 -0
- data/lib/pg_reports/sql/system/extensions.sql +12 -0
- data/lib/pg_reports/sql/system/settings.sql +33 -0
- data/lib/pg_reports/sql/tables/bloated_tables.sql +23 -0
- data/lib/pg_reports/sql/tables/cache_hit_ratios.sql +24 -0
- data/lib/pg_reports/sql/tables/recently_modified.sql +20 -0
- data/lib/pg_reports/sql/tables/row_counts.sql +18 -0
- data/lib/pg_reports/sql/tables/seq_scans.sql +26 -0
- data/lib/pg_reports/sql/tables/table_sizes.sql +16 -0
- data/lib/pg_reports/sql/tables/vacuum_needed.sql +22 -0
- data/lib/pg_reports/sql_loader.rb +35 -0
- data/lib/pg_reports/telegram_sender.rb +83 -0
- data/lib/pg_reports/version.rb +5 -0
- data/lib/pg_reports.rb +114 -0
- metadata +184 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f0a634540a400676a49a8db9574fe8abbccd9faeae920e5d22323b1d54c791bd
|
|
4
|
+
data.tar.gz: ec2665323a7adbc2eae7dbcdd2a0b49ca5d7e4a96ca3f9603b1fd4304c23c98b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ff18cbb7ffe10932d42b8a277fb33ef812a7d8d4211874575ea51869d9478c868143b7e912d639c493eb4ee81252b6a0c296aaaeefd132aa381bb6e5b99aef99
|
|
7
|
+
data.tar.gz: ef94bcbc4e686d1c82f6e6664735dbdbdbdbd441285be485c43fa593c69ba47886d16dd25998776ab7ef772382ec7e97d472fa34011b8683ee0c7d88881ec69b
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-01-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release
|
|
13
|
+
- Query analysis module (slow, heavy, expensive queries)
|
|
14
|
+
- Index analysis module (unused, duplicate, invalid, missing indexes)
|
|
15
|
+
- Table analysis module (sizes, bloat, vacuum status)
|
|
16
|
+
- Connection analysis module (active connections, locks, blocking queries)
|
|
17
|
+
- System module (database sizes, settings, extensions)
|
|
18
|
+
- Web dashboard with beautiful dark theme
|
|
19
|
+
- Expandable table rows for full query text
|
|
20
|
+
- Download reports in TXT, CSV, JSON formats
|
|
21
|
+
- Telegram integration for sending reports
|
|
22
|
+
- pg_stat_statements management (enable/status check)
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Your Name
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# PgReports
|
|
2
|
+
|
|
3
|
+
[](https://www.ruby-lang.org/)
|
|
4
|
+
[](https://rubyonrails.org/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
A comprehensive PostgreSQL monitoring and analysis library for Rails applications. Get insights into query performance, index usage, table statistics, connection health, and more. Includes a beautiful web dashboard and Telegram integration for notifications.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- 📊 **Query Analysis** - Identify slow, heavy, and expensive queries using `pg_stat_statements`
|
|
14
|
+
- 📇 **Index Analysis** - Find unused, duplicate, invalid, and missing indexes
|
|
15
|
+
- 📋 **Table Statistics** - Monitor table sizes, bloat, vacuum needs, and cache hit ratios
|
|
16
|
+
- 🔌 **Connection Monitoring** - Track active connections, locks, and blocking queries
|
|
17
|
+
- 🖥️ **System Overview** - Database sizes, PostgreSQL settings, installed extensions
|
|
18
|
+
- 🌐 **Web Dashboard** - Beautiful dark-themed UI with expandable rows
|
|
19
|
+
- 📨 **Telegram Integration** - Send reports directly to Telegram
|
|
20
|
+
- 📥 **Export** - Download reports in TXT, CSV, or JSON format
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
Add to your Gemfile:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
gem "pg_reports"
|
|
28
|
+
|
|
29
|
+
# Optional: for Telegram support
|
|
30
|
+
gem "telegram-bot-ruby"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Run:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bundle install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### Mount the Dashboard
|
|
42
|
+
|
|
43
|
+
Add to your `config/routes.rb`:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
Rails.application.routes.draw do
|
|
47
|
+
# Mount in development only (recommended)
|
|
48
|
+
if Rails.env.development?
|
|
49
|
+
mount PgReports::Engine, at: "/pg_reports"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Or with authentication
|
|
53
|
+
authenticate :user, ->(u) { u.admin? } do
|
|
54
|
+
mount PgReports::Engine, at: "/pg_reports"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Visit `http://localhost:3000/pg_reports` to access the dashboard.
|
|
60
|
+
|
|
61
|
+
### Use in Console or Code
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
# Get slow queries
|
|
65
|
+
PgReports.slow_queries.display
|
|
66
|
+
|
|
67
|
+
# Get unused indexes
|
|
68
|
+
report = PgReports.unused_indexes
|
|
69
|
+
report.each { |row| puts row["index_name"] }
|
|
70
|
+
|
|
71
|
+
# Export to different formats
|
|
72
|
+
report.to_text # Plain text
|
|
73
|
+
report.to_csv # CSV
|
|
74
|
+
report.to_a # Array of hashes
|
|
75
|
+
|
|
76
|
+
# Send to Telegram
|
|
77
|
+
PgReports.expensive_queries.send_to_telegram
|
|
78
|
+
|
|
79
|
+
# Health report
|
|
80
|
+
PgReports.health_report.display
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Configuration
|
|
84
|
+
|
|
85
|
+
Create an initializer `config/initializers/pg_reports.rb`:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
PgReports.configure do |config|
|
|
89
|
+
# Telegram (optional)
|
|
90
|
+
config.telegram_bot_token = ENV["PG_REPORTS_TELEGRAM_TOKEN"]
|
|
91
|
+
config.telegram_chat_id = ENV["PG_REPORTS_TELEGRAM_CHAT_ID"]
|
|
92
|
+
|
|
93
|
+
# Query thresholds
|
|
94
|
+
config.slow_query_threshold_ms = 100 # Queries slower than this
|
|
95
|
+
config.heavy_query_threshold_calls = 1000 # Queries with more calls
|
|
96
|
+
config.expensive_query_threshold_ms = 10000 # Total time threshold
|
|
97
|
+
|
|
98
|
+
# Index thresholds
|
|
99
|
+
config.unused_index_threshold_scans = 50 # Index with fewer scans
|
|
100
|
+
|
|
101
|
+
# Table thresholds
|
|
102
|
+
config.bloat_threshold_percent = 20 # Tables with more bloat
|
|
103
|
+
config.dead_rows_threshold = 10000 # Dead rows needing vacuum
|
|
104
|
+
|
|
105
|
+
# Output settings
|
|
106
|
+
config.max_query_length = 200 # Truncate queries in text output
|
|
107
|
+
|
|
108
|
+
# Dashboard authentication (optional)
|
|
109
|
+
config.dashboard_auth = -> {
|
|
110
|
+
authenticate_or_request_with_http_basic do |user, pass|
|
|
111
|
+
user == "admin" && pass == "secret"
|
|
112
|
+
end
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Query Source Tracking
|
|
119
|
+
|
|
120
|
+
PgReports automatically parses query annotations to show **where queries originated**. Works with:
|
|
121
|
+
|
|
122
|
+
### Marginalia (recommended)
|
|
123
|
+
|
|
124
|
+
If you use [marginalia](https://github.com/basecamp/marginalia), PgReports will automatically parse and display controller/action info in the **source** column.
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
# Gemfile
|
|
128
|
+
gem 'marginalia'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Rails 7+ Query Logs
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
# config/application.rb
|
|
135
|
+
config.active_record.query_log_tags_enabled = true
|
|
136
|
+
config.active_record.query_log_tags = [:controller, :action]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Available Reports
|
|
140
|
+
|
|
141
|
+
### Queries (requires pg_stat_statements)
|
|
142
|
+
|
|
143
|
+
| Method | Description |
|
|
144
|
+
|--------|-------------|
|
|
145
|
+
| `slow_queries` | Queries with high mean execution time |
|
|
146
|
+
| `heavy_queries` | Most frequently called queries |
|
|
147
|
+
| `expensive_queries` | Queries consuming most total time |
|
|
148
|
+
| `missing_index_queries` | Queries potentially missing indexes |
|
|
149
|
+
| `low_cache_hit_queries` | Queries with poor cache utilization |
|
|
150
|
+
| `all_queries` | All query statistics |
|
|
151
|
+
| `reset_statistics!` | Reset pg_stat_statements data |
|
|
152
|
+
|
|
153
|
+
### Indexes
|
|
154
|
+
|
|
155
|
+
| Method | Description |
|
|
156
|
+
|--------|-------------|
|
|
157
|
+
| `unused_indexes` | Indexes rarely or never scanned |
|
|
158
|
+
| `duplicate_indexes` | Redundant indexes |
|
|
159
|
+
| `invalid_indexes` | Indexes that failed to build |
|
|
160
|
+
| `missing_indexes` | Tables potentially missing indexes |
|
|
161
|
+
| `index_usage` | Index scan statistics |
|
|
162
|
+
| `bloated_indexes` | Indexes with high bloat |
|
|
163
|
+
| `index_sizes` | Index disk usage |
|
|
164
|
+
|
|
165
|
+
### Tables
|
|
166
|
+
|
|
167
|
+
| Method | Description |
|
|
168
|
+
|--------|-------------|
|
|
169
|
+
| `table_sizes` | Table disk usage |
|
|
170
|
+
| `bloated_tables` | Tables with high dead tuple ratio |
|
|
171
|
+
| `vacuum_needed` | Tables needing vacuum |
|
|
172
|
+
| `row_counts` | Table row counts |
|
|
173
|
+
| `cache_hit_ratios` | Table cache statistics |
|
|
174
|
+
| `seq_scans` | Tables with high sequential scans |
|
|
175
|
+
| `recently_modified` | Tables with recent activity |
|
|
176
|
+
|
|
177
|
+
### Connections
|
|
178
|
+
|
|
179
|
+
| Method | Description |
|
|
180
|
+
|--------|-------------|
|
|
181
|
+
| `active_connections` | Current database connections |
|
|
182
|
+
| `connection_stats` | Connection statistics by state |
|
|
183
|
+
| `long_running_queries` | Queries running for extended period |
|
|
184
|
+
| `blocking_queries` | Queries blocking others |
|
|
185
|
+
| `locks` | Current database locks |
|
|
186
|
+
| `idle_connections` | Idle connections |
|
|
187
|
+
| `kill_connection(pid)` | Terminate a backend process |
|
|
188
|
+
| `cancel_query(pid)` | Cancel a running query |
|
|
189
|
+
|
|
190
|
+
### System
|
|
191
|
+
|
|
192
|
+
| Method | Description |
|
|
193
|
+
|--------|-------------|
|
|
194
|
+
| `database_sizes` | Size of all databases |
|
|
195
|
+
| `settings` | PostgreSQL configuration |
|
|
196
|
+
| `extensions` | Installed extensions |
|
|
197
|
+
| `activity_overview` | Current activity summary |
|
|
198
|
+
| `cache_stats` | Database cache statistics |
|
|
199
|
+
| `pg_stat_statements_available?` | Check if extension is ready |
|
|
200
|
+
| `enable_pg_stat_statements!` | Create the extension |
|
|
201
|
+
|
|
202
|
+
## pg_stat_statements Setup
|
|
203
|
+
|
|
204
|
+
For query analysis, you need to enable `pg_stat_statements`:
|
|
205
|
+
|
|
206
|
+
1. Edit `postgresql.conf`:
|
|
207
|
+
```
|
|
208
|
+
shared_preload_libraries = 'pg_stat_statements'
|
|
209
|
+
pg_stat_statements.track = all
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
2. Restart PostgreSQL:
|
|
213
|
+
```bash
|
|
214
|
+
sudo systemctl restart postgresql
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
3. Create extension (via dashboard or console):
|
|
218
|
+
```ruby
|
|
219
|
+
PgReports.enable_pg_stat_statements!
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Report Object
|
|
223
|
+
|
|
224
|
+
Every method returns a `PgReports::Report` object:
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
report = PgReports.slow_queries
|
|
228
|
+
|
|
229
|
+
report.title # "Slow Queries (mean time >= 100ms)"
|
|
230
|
+
report.data # Array of hashes
|
|
231
|
+
report.columns # Column names
|
|
232
|
+
report.size # Row count
|
|
233
|
+
report.empty? # Boolean
|
|
234
|
+
report.generated_at # Timestamp
|
|
235
|
+
|
|
236
|
+
# Output formats
|
|
237
|
+
report.to_text # Plain text table
|
|
238
|
+
report.to_markdown # Markdown table
|
|
239
|
+
report.to_html # HTML table
|
|
240
|
+
report.to_csv # CSV
|
|
241
|
+
report.to_a # Raw data
|
|
242
|
+
|
|
243
|
+
# Actions
|
|
244
|
+
report.display # Print to STDOUT
|
|
245
|
+
report.send_to_telegram # Send as message
|
|
246
|
+
report.send_to_telegram_as_file # Send as file attachment
|
|
247
|
+
|
|
248
|
+
# Enumerable
|
|
249
|
+
report.each { |row| puts row }
|
|
250
|
+
report.map { |row| row["query"] }
|
|
251
|
+
report.select { |row| row["calls"] > 100 }
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Web Dashboard
|
|
255
|
+
|
|
256
|
+
The dashboard provides:
|
|
257
|
+
|
|
258
|
+
- 📊 Overview of all report categories
|
|
259
|
+
- ⚡ One-click report execution
|
|
260
|
+
- 🔍 Expandable rows for full query text
|
|
261
|
+
- 📋 Copy query to clipboard
|
|
262
|
+
- 📥 Download in multiple formats
|
|
263
|
+
- 📨 Send to Telegram
|
|
264
|
+
- 🔧 pg_stat_statements management
|
|
265
|
+
|
|
266
|
+
### Authentication
|
|
267
|
+
|
|
268
|
+
```ruby
|
|
269
|
+
PgReports.configure do |config|
|
|
270
|
+
# HTTP Basic Auth
|
|
271
|
+
config.dashboard_auth = -> {
|
|
272
|
+
authenticate_or_request_with_http_basic do |user, pass|
|
|
273
|
+
user == ENV["ADMIN_USER"] && pass == ENV["ADMIN_PASS"]
|
|
274
|
+
end
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
# Or use Devise
|
|
278
|
+
config.dashboard_auth = -> {
|
|
279
|
+
redirect_to main_app.root_path unless current_user&.admin?
|
|
280
|
+
}
|
|
281
|
+
end
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Telegram Integration
|
|
285
|
+
|
|
286
|
+
1. Create a bot via [@BotFather](https://t.me/BotFather)
|
|
287
|
+
2. Get your chat ID (add [@userinfobot](https://t.me/userinfobot) to get it)
|
|
288
|
+
3. Configure:
|
|
289
|
+
|
|
290
|
+
```ruby
|
|
291
|
+
PgReports.configure do |config|
|
|
292
|
+
config.telegram_bot_token = "123456:ABC-DEF..."
|
|
293
|
+
config.telegram_chat_id = "-1001234567890"
|
|
294
|
+
end
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
4. Send reports:
|
|
298
|
+
|
|
299
|
+
```ruby
|
|
300
|
+
PgReports.slow_queries.send_to_telegram
|
|
301
|
+
PgReports.health_report.send_to_telegram_as_file
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Development
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Clone the repo
|
|
308
|
+
git clone https://github.com/yourusername/pg_reports
|
|
309
|
+
cd pg_reports
|
|
310
|
+
|
|
311
|
+
# Install dependencies
|
|
312
|
+
bundle install
|
|
313
|
+
|
|
314
|
+
# Run tests
|
|
315
|
+
bundle exec rspec
|
|
316
|
+
|
|
317
|
+
# Run linter
|
|
318
|
+
bundle exec rubocop
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Contributing
|
|
322
|
+
|
|
323
|
+
1. Fork it
|
|
324
|
+
2. Create your feature branch (`git checkout -b feature/my-feature`)
|
|
325
|
+
3. Commit your changes (`git commit -am 'Add my feature'`)
|
|
326
|
+
4. Push to the branch (`git push origin feature/my-feature`)
|
|
327
|
+
5. Create a Pull Request
|
|
328
|
+
|
|
329
|
+
## License
|
|
330
|
+
|
|
331
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
332
|
+
|
|
333
|
+
## Acknowledgments
|
|
334
|
+
|
|
335
|
+
Inspired by [rails-pg-extras](https://github.com/pawurb/rails-pg-extras) and built with ❤️ for the Rails community.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgReports
|
|
4
|
+
class DashboardController < ActionController::Base
|
|
5
|
+
layout "pg_reports/application"
|
|
6
|
+
|
|
7
|
+
before_action :authenticate_dashboard!, if: -> { PgReports.config.dashboard_auth.present? }
|
|
8
|
+
before_action :set_categories
|
|
9
|
+
|
|
10
|
+
def index
|
|
11
|
+
@pg_stat_status = PgReports.pg_stat_statements_status
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def enable_pg_stat_statements
|
|
15
|
+
result = PgReports.enable_pg_stat_statements!
|
|
16
|
+
render json: result
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def reset_statistics
|
|
20
|
+
PgReports.reset_statistics!
|
|
21
|
+
render json: {success: true, message: "Statistics have been reset successfully"}
|
|
22
|
+
rescue => e
|
|
23
|
+
render json: {success: false, error: e.message}, status: :unprocessable_entity
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def show
|
|
27
|
+
@category = params[:category].to_sym
|
|
28
|
+
@report_key = params[:report].to_sym
|
|
29
|
+
@report_info = Dashboard::ReportsRegistry.find(@category, @report_key)
|
|
30
|
+
|
|
31
|
+
if @report_info.nil?
|
|
32
|
+
redirect_to root_path, alert: "Report not found"
|
|
33
|
+
return
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
@report = execute_report(@category, @report_key)
|
|
37
|
+
rescue => e
|
|
38
|
+
@error = e.message
|
|
39
|
+
@report = nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def run
|
|
43
|
+
category = params[:category].to_sym
|
|
44
|
+
report_key = params[:report].to_sym
|
|
45
|
+
|
|
46
|
+
report = execute_report(category, report_key)
|
|
47
|
+
|
|
48
|
+
render json: {
|
|
49
|
+
success: true,
|
|
50
|
+
title: report.title,
|
|
51
|
+
columns: report.columns,
|
|
52
|
+
data: report.data.first(100),
|
|
53
|
+
total: report.size,
|
|
54
|
+
generated_at: report.generated_at.strftime("%Y-%m-%d %H:%M:%S")
|
|
55
|
+
}
|
|
56
|
+
rescue => e
|
|
57
|
+
render json: {success: false, error: e.message}, status: :unprocessable_entity
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def send_to_telegram
|
|
61
|
+
category = params[:category].to_sym
|
|
62
|
+
report_key = params[:report].to_sym
|
|
63
|
+
|
|
64
|
+
report = execute_report(category, report_key)
|
|
65
|
+
|
|
66
|
+
if report.size > 50
|
|
67
|
+
report.send_to_telegram_as_file
|
|
68
|
+
else
|
|
69
|
+
report.send_to_telegram
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
render json: {success: true, message: "Report sent to Telegram"}
|
|
73
|
+
rescue => e
|
|
74
|
+
render json: {success: false, error: e.message}, status: :unprocessable_entity
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def download
|
|
78
|
+
category = params[:category].to_sym
|
|
79
|
+
report_key = params[:report].to_sym
|
|
80
|
+
format_type = params[:format] || "txt"
|
|
81
|
+
|
|
82
|
+
report = execute_report(category, report_key)
|
|
83
|
+
filename = "#{report.title.parameterize}-#{Time.current.strftime("%Y%m%d-%H%M%S")}"
|
|
84
|
+
|
|
85
|
+
case format_type
|
|
86
|
+
when "csv"
|
|
87
|
+
send_data report.to_csv,
|
|
88
|
+
filename: "#{filename}.csv",
|
|
89
|
+
type: "text/csv; charset=utf-8",
|
|
90
|
+
disposition: "attachment"
|
|
91
|
+
when "json"
|
|
92
|
+
send_data report.to_a.to_json,
|
|
93
|
+
filename: "#{filename}.json",
|
|
94
|
+
type: "application/json; charset=utf-8",
|
|
95
|
+
disposition: "attachment"
|
|
96
|
+
else
|
|
97
|
+
send_data report.to_text,
|
|
98
|
+
filename: "#{filename}.txt",
|
|
99
|
+
type: "text/plain; charset=utf-8",
|
|
100
|
+
disposition: "attachment"
|
|
101
|
+
end
|
|
102
|
+
rescue => e
|
|
103
|
+
render json: {success: false, error: e.message}, status: :unprocessable_entity
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def authenticate_dashboard!
|
|
109
|
+
instance_exec(&PgReports.config.dashboard_auth)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def set_categories
|
|
113
|
+
@categories = Dashboard::ReportsRegistry.all
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def execute_report(category, report_key)
|
|
117
|
+
mod = case category
|
|
118
|
+
when :queries then Modules::Queries
|
|
119
|
+
when :indexes then Modules::Indexes
|
|
120
|
+
when :tables then Modules::Tables
|
|
121
|
+
when :connections then Modules::Connections
|
|
122
|
+
when :system then Modules::System
|
|
123
|
+
else raise ArgumentError, "Unknown category: #{category}"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
unless mod.respond_to?(report_key)
|
|
127
|
+
raise ArgumentError, "Unknown report: #{report_key}"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
mod.public_send(report_key)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|