Skip to content

Commit

Permalink
Web UI
Browse files Browse the repository at this point in the history
  • Loading branch information
pawurb committed Nov 21, 2024
1 parent 94d8993 commit 9ab1ab4
Show file tree
Hide file tree
Showing 55 changed files with 894 additions and 222 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ jobs:
env:
DATABASE_URL: postgres://postgres:secret@localhost:5432/pg-extras-rs-test
run: |
cargo run --bin pgextras || cargo run --bin pgextras cache-hit
cargo run --bin pgextras --features web || cargo run --bin pgextras --features web cache-hit
- name: Run tests for PG 12
env:
PG_VERSION: 12
Expand Down
27 changes: 21 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,39 @@ clap = { version = "4.5.20", features = ["derive"] }
lazy_static = "1.5.0"
prettytable-rs = "0.10.0"
semver = "1.0.23"
sqlx = { version = "0.8", features = [
"runtime-tokio-rustls",
"postgres",
"macros",
"bigdecimal",
] }
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres", "macros", "bigdecimal"] }

tokio = { version = "1.40", features = ["full"] }
unicode-width = "0.2.0"
textwrap = { version = "0.16.1", features = ["terminal_size"] }

# web
axum = { version = "0.7", optional = true }
askama = { version = "0.12.1", features = ["with-axum"], optional = true }
askama_axum = { version = "0.4.0", optional = true }
tower-http = { version = "0.6.2", features = ["fs", "trace"], optional = true }
eyre = "0.6.12"
tracing-subscriber = "0.3.18"
tracing = "0.1.40"
serde = "1.0.215"
serde_json = "1.0.133"
reqwest = "0.12.9"


[profile.release]
lto = true

[[bin]]
name = "pgextras"
path = "bin/main.rs"
required-features = ["web"]

[[example]]
name = "db_settings"
path = "examples/db_settings.rs"

[features]
web = ["dep:axum", "dep:askama", "dep:askama_axum", "dep:tower-http"]

[default]
features = ["web"]
Empty file added assets/styles.css
Empty file.
1 change: 1 addition & 0 deletions assets/tailwind.min.css

Large diffs are not rendered by default.

119 changes: 77 additions & 42 deletions bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ use pg_extras::{
all_locks, bloat, blocking, buffercache_stats, buffercache_usage, cache_hit, calls,
connections, db_settings, diagnose, duplicate_indexes, extensions, index_cache_hit,
index_scans, index_size, index_usage, indexes, locks, long_running_queries, mandelbrot,
null_indexes, outliers, records_rank, render_table, seq_scans, ssl_used, table_cache_hit,
table_index_scans, table_indexes_size, table_size, tables, total_index_size, total_table_size,
unused_indexes, vacuum_stats, AllLocks, Bloat, Blocking, BuffercacheStats, BuffercacheUsage,
CacheHit, Calls, Connections, DbSettings, DuplicateIndexes, Extensions, IndexCacheHit,
IndexScans, IndexSize, IndexUsage, Indexes, Locks, LongRunningQueries, Mandelbrot, NullIndexes,
Outliers, PgExtrasError, Query, RecordsRank, SeqScans, SslUsed, TableCacheHit, TableIndexScans,
TableIndexesSize, TableSize, Tables, TotalIndexSize, TotalTableSize, UnusedIndexes,
VacuumStats,
null_indexes, outliers, pg_pool, records_rank, render_table, seq_scans, ssl_used,
table_cache_hit, table_index_scans, table_indexes_size, table_size, tables, total_index_size,
total_table_size, unused_indexes, vacuum_stats, AllLocks, Bloat, Blocking, BuffercacheStats,
BuffercacheUsage, CacheHit, Calls, Connections, DbSettings, DuplicateIndexes, Extensions,
IndexCacheHit, IndexScans, IndexSize, IndexUsage, Indexes, Locks, LongRunningQueries,
Mandelbrot, NullIndexes, Outliers, PgExtrasError, Query, RecordsRank, SeqScans, SslUsed,
TableCacheHit, TableIndexScans, TableIndexesSize, TableSize, Tables, TotalIndexSize,
TotalTableSize, UnusedIndexes, VacuumStats,
};

#[derive(Parser, Debug)]
Expand All @@ -27,6 +27,9 @@ pub struct PgExtrasArgs {
pub cmd: PgSubcommand,
}

#[cfg(feature = "web")]
use sqlx::PgPool;

#[derive(Subcommand, Debug)]
pub enum PgSubcommand {
#[command(about = "Diagnose common database problems")]
Expand Down Expand Up @@ -97,6 +100,9 @@ pub enum PgSubcommand {
UnusedIndexes(EmptyArgs),
#[command(about = &VacuumStats::description())]
VacuumStats(EmptyArgs),
#[cfg(feature = "web")]
#[command(about = "Start web server")]
Web(EmptyArgs),
}

#[derive(Parser, Debug)]
Expand All @@ -117,110 +123,139 @@ type PG = PgSubcommand;
async fn execute() -> Result<(), PgExtrasError> {
let args = PgExtrasArgs::parse();

let pool = pg_pool().await?;
match args.cmd {
PG::AllLocks(_args) => {
render_table(all_locks().await?);
render_table(all_locks(&pool).await?);
}
PG::Bloat(_args) => {
render_table(bloat().await?);
render_table(bloat(&pool).await?);
}
PG::Blocking(_args) => {
render_table(blocking(None).await?);
render_table(blocking(None, &pool).await?);
}
PG::BuffercacheStats(_args) => {
render_table(buffercache_stats().await?);
render_table(buffercache_stats(&pool).await?);
}
PG::BuffercacheUsage(_args) => {
render_table(buffercache_usage().await?);
render_table(buffercache_usage(&pool).await?);
}
PG::CacheHit(_args) => {
render_table(cache_hit(None).await?);
render_table(cache_hit(None, &pool).await?);
}
PG::Calls(_args) => {
render_table(calls(None).await?);
render_table(calls(None, &pool).await?);
}
PG::Connections(_args) => {
render_table(connections().await?);
render_table(connections(&pool).await?);
}
PG::DbSettings(_args) => {
render_table(db_settings().await?);
render_table(db_settings(&pool).await?);
}
PG::Diagnose(_args) => {
render_diagnose_report(diagnose().await?);
render_diagnose_report(diagnose(&pool).await?);
}
PG::DuplicateIndexes(_args) => {
render_table(duplicate_indexes().await?);
render_table(duplicate_indexes(&pool).await?);
}
PG::Extensions(_args) => {
render_table(extensions().await?);
render_table(extensions(&pool).await?);
}
PG::IndexCacheHit(_args) => {
render_table(index_cache_hit(None).await?);
render_table(index_cache_hit(None, &pool).await?);
}
PG::IndexScans(_args) => {
render_table(index_scans(None).await?);
render_table(index_scans(None, &pool).await?);
}
PG::IndexSize(_args) => {
render_table(index_size().await?);
render_table(index_size(&pool).await?);
}
PG::IndexUsage(_args) => {
render_table(index_usage(None).await?);
render_table(index_usage(None, &pool).await?);
}
PG::Indexes(_args) => {
render_table(indexes().await?);
render_table(indexes(&pool).await?);
}
PG::Locks(_args) => {
render_table(locks().await?);
render_table(locks(&pool).await?);
}
PG::LongRunningQueries(_args) => {
render_table(long_running_queries().await?);
render_table(long_running_queries(&pool).await?);
}
PG::NullIndexes(_args) => {
render_table(null_indexes(None).await?);
render_table(null_indexes(None, &pool).await?);
}
PG::Outliers(_args) => {
render_table(outliers().await?);
render_table(outliers(&pool).await?);
}
PG::Mandelbrot(_args) => {
render_table(mandelbrot().await?);
render_table(mandelbrot(&pool).await?);
}
PG::RecordsRank(_args) => {
render_table(records_rank(None).await?);
render_table(records_rank(None, &pool).await?);
}
PG::SeqScans(_args) => {
render_table(seq_scans(None).await?);
render_table(seq_scans(None, &pool).await?);
}
PG::SslUsed(_args) => {
render_table(ssl_used().await?);
render_table(ssl_used(&pool).await?);
}
PG::TableCacheHit(_args) => {
render_table(table_cache_hit().await?);
render_table(table_cache_hit(&pool).await?);
}
PG::TableIndexScans(_args) => {
render_table(table_index_scans(None).await?);
render_table(table_index_scans(None, &pool).await?);
}
PG::TableIndexesSize(_args) => {
render_table(table_indexes_size(None).await?);
render_table(table_indexes_size(None, &pool).await?);
}
PG::TableSize(_args) => {
render_table(table_size().await?);
render_table(table_size(&pool).await?);
}
PG::Tables(_args) => {
render_table(tables(None).await?);
render_table(tables(None, &pool).await?);
}
PG::TotalIndexSize(_args) => {
render_table(total_index_size().await?);
render_table(total_index_size(&pool).await?);
}
PG::TotalTableSize(_args) => {
render_table(total_table_size().await?);
render_table(total_table_size(&pool).await?);
}
PG::UnusedIndexes(_args) => {
render_table(unused_indexes(None).await?);
render_table(unused_indexes(None, &pool).await?);
}
PG::VacuumStats(_args) => {
render_table(vacuum_stats().await?);
render_table(vacuum_stats(&pool).await?);
}
#[cfg(feature = "web")]
PG::Web(_args) => {
start_web_server(pool).await?;
}
}

Ok(())
}

#[cfg(feature = "web")]
async fn start_web_server(pool: PgPool) -> Result<(), PgExtrasError> {
let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_string());

if tokio::net::TcpListener::bind(format!("0.0.0.0:{port}"))
.await
.is_err()
{
PgExtrasError::Other(format!("Port {} is already in use", port));
}

let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}"))
.await
.unwrap();
let app = pg_extras::web::routes::app(pool).await;

println!(
"Available on http://{}/pg_extras",
listener.local_addr().unwrap()
);
axum::serve(listener, app).await.unwrap();
Ok(())
}
7 changes: 4 additions & 3 deletions examples/db_settings.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use pg_extras::{db_settings, render_table, PgExtrasError};
use pg_extras::{db_settings, pg_pool, render_table, PgExtrasError};

#[tokio::main]

async fn main() -> Result<(), PgExtrasError> {
std::env::set_var(
"PG_EXTRAS_DATABASE_URL",
"postgres://postgres:secret@localhost:5432/pg-extras-rs-test",
);

let settings = db_settings().await?;
let pool = pg_pool().await?;

let settings = db_settings(&pool).await?;
render_table(settings);

Ok(())
Expand Down
Loading

0 comments on commit 9ab1ab4

Please sign in to comment.