From 6fdcda8781438644f80f8df1022a25014b2304a7 Mon Sep 17 00:00:00 2001 From: gennyble Date: Fri, 4 Apr 2025 04:42:24 -0500 Subject: meow --- Cargo.toml | 2 +- README.md | 4 +- corgi_stats/Cargo.toml | 8 --- corgi_stats/src/main.rs | 132 ---------------------------------------------- corgi_stats/src/style.css | 37 ------------- stats/Cargo.toml | 8 +++ stats/src/main.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++++ stats/src/style.css | 37 +++++++++++++ 8 files changed, 180 insertions(+), 180 deletions(-) delete mode 100644 corgi_stats/Cargo.toml delete mode 100644 corgi_stats/src/main.rs delete mode 100644 corgi_stats/src/style.css create mode 100644 stats/Cargo.toml create mode 100644 stats/src/main.rs create mode 100644 stats/src/style.css diff --git a/Cargo.toml b/Cargo.toml index 25c0cd5..9568d9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["corgi", "parrot", "corgi_stats"] +members = ["corgi", "parrot", "stats"] resolver = "3" # use this profile like this: diff --git a/README.md b/README.md index 32c8f3d..ffa270b 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ Script See [corgi.conf](corgi.conf) for the configuration I use with my cgit instance. -Scripts are tried in order, looking for one that matches. If none match, -the first script is ran. +Scripts are tried in order, looking for one that has a matching regex. If a +script does not have a regex, it is selected. If none match, 404 is returned. Sets the following environmental variables for the CGI script, many following [RFC 3875][rfc]: - **`GATEWAY_INTERFACE`** to the fixed value `CGI/1.1` diff --git a/corgi_stats/Cargo.toml b/corgi_stats/Cargo.toml deleted file mode 100644 index a012b94..0000000 --- a/corgi_stats/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "corgi-stats" -version = "0.1.0" -edition = "2024" - -[dependencies] -rusqlite = { version = "0.34.0", features = ["bundled", "time"] } -time = "0.3.40" diff --git a/corgi_stats/src/main.rs b/corgi_stats/src/main.rs deleted file mode 100644 index 2e0c372..0000000 --- a/corgi_stats/src/main.rs +++ /dev/null @@ -1,132 +0,0 @@ -use rusqlite::{Connection, params}; -use std::time::Instant; -use time::{Duration, OffsetDateTime}; - -// Thank you, cat, for optimizing my query -const TOP_TEN_ALL_TIME: &str = "\ - SELECT reqs.cnt, agents.agent - FROM agents - JOIN ( - SELECT count(id) as cnt, agent_id - FROM requests - GROUP BY agent_id - ) reqs - ON reqs.agent_id=agents.id - ORDER BY reqs.cnt DESC LIMIT 10; -"; - -const STYLE: &'static str = include_str!("style.css"); - -fn main() { - let db_path = std::env::var("CORGI_STATS_DB").ok(); - let db = if let Some(path) = db_path { - if let Ok(db) = Connection::open(path) { - db - } else { - error_and_die(500, "failed to open database"); - } - } else { - error_and_die(500, "database key not set"); - }; - - let now = OffsetDateTime::now_utc(); - let fifteen_ago = now - Duration::minutes(15); - - let query = "SELECT count(requests.id) AS request_count, agents.agent FROM requests \ - INNER JOIN agents ON requests.agent_id = agents.id \ - WHERE requests.timestamp > ?1 \ - GROUP BY requests.agent_id;"; - - let mut prepared = db.prepare(query).unwrap(); - let mut agents: Vec<(usize, String)> = prepared - .query_map(params![fifteen_ago], |row| Ok((row.get(0)?, row.get(1)?))) - .unwrap() - .map(|r| r.unwrap()) - .collect(); - - agents.sort_by(|a, b| a.0.cmp(&b.0).reverse()); - - let mut prepared = db.prepare(TOP_TEN_ALL_TIME).unwrap(); - let highest_five: Vec<(usize, String)> = prepared - .query_map(params![], |row| Ok((row.get(0)?, row.get(1)?))) - .unwrap() - .map(|r| r.unwrap()) - .collect(); - let sum_highest_five = highest_five.iter().fold(0, |acc, (count, _)| acc + count); - - println!("Content-Type: text/html\n"); - println!(""); - #[rustfmt::skip] - println!("\n\ - corgi stats\n\ - \n\ - "); - - println!(""); - println!("

Corgi Stats :)

"); - - #[rustfmt::skip] - println!("\n\ - \n\ - \t\n\ - \t\t\n\ - \t\n\ - \t\n\ - \t\t\n\ - \t\t\n\ - \t\t\n\ - \t\n\ - \n"); - - for (count, agent) in &agents { - #[rustfmt::skip] - println!("\n\ - \t\n\ - \t\n\ - \t\n\ - ", - *count as f32 / 15.0); - } - - println!("\n
Requests for the last 15 minutes
# Req.Req/minAgent
{count}{:.1}{agent}
"); - - #[rustfmt::skip] - println!("\n\ - \n\ - \t\n\ - \t\t\n\ - \t\n\ - \t\n\ - \t\t\n\ - \t\t\n\ - \t\t\n\ - \t\n\ - \n"); - - // Finish what we started - println!("\n"); - - for (count, agent) in highest_five { - #[rustfmt::skip] - println!("\n\ - \t\n\ - \t\n\ - \t\n\ - ", - (count as f32 / sum_highest_five as f32) * 100.0); - } -} - -fn error_and_die>(status: u16, msg: S) -> ! { - println!("Status: {status}"); - println!("Content-Type: text/html\n"); - println!(""); - println!("\t{status}"); - println!("\t"); - println!("\t\t

{status}

"); - println!("\t\t
"); - println!("\t\t

{}

", msg.into()); - println!("\t\n"); - - std::process::exit(0); -} diff --git a/corgi_stats/src/style.css b/corgi_stats/src/style.css deleted file mode 100644 index 5b3995d..0000000 --- a/corgi_stats/src/style.css +++ /dev/null @@ -1,37 +0,0 @@ -h1 { - font-family: sans-serif; -} - -table { - border-collapse: collapse; - border: 2px solid gray; - margin: 1rem 0px; -} - -tr { - background-color: white; -} - -tbody>tr:nth-of-type(odd) { - background-color: cornsilk; -} - -th, -td { - border: 1px solid darkslateblue; - padding: 2px 3px; -} - -thead th { - background-color: darksalmon; -} - -th { - text-align: left; - padding: 4px 6px; - white-space: nowrap; -} - -th.ttitle { - text-align: center; -} \ No newline at end of file diff --git a/stats/Cargo.toml b/stats/Cargo.toml new file mode 100644 index 0000000..a012b94 --- /dev/null +++ b/stats/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "corgi-stats" +version = "0.1.0" +edition = "2024" + +[dependencies] +rusqlite = { version = "0.34.0", features = ["bundled", "time"] } +time = "0.3.40" diff --git a/stats/src/main.rs b/stats/src/main.rs new file mode 100644 index 0000000..2e0c372 --- /dev/null +++ b/stats/src/main.rs @@ -0,0 +1,132 @@ +use rusqlite::{Connection, params}; +use std::time::Instant; +use time::{Duration, OffsetDateTime}; + +// Thank you, cat, for optimizing my query +const TOP_TEN_ALL_TIME: &str = "\ + SELECT reqs.cnt, agents.agent + FROM agents + JOIN ( + SELECT count(id) as cnt, agent_id + FROM requests + GROUP BY agent_id + ) reqs + ON reqs.agent_id=agents.id + ORDER BY reqs.cnt DESC LIMIT 10; +"; + +const STYLE: &'static str = include_str!("style.css"); + +fn main() { + let db_path = std::env::var("CORGI_STATS_DB").ok(); + let db = if let Some(path) = db_path { + if let Ok(db) = Connection::open(path) { + db + } else { + error_and_die(500, "failed to open database"); + } + } else { + error_and_die(500, "database key not set"); + }; + + let now = OffsetDateTime::now_utc(); + let fifteen_ago = now - Duration::minutes(15); + + let query = "SELECT count(requests.id) AS request_count, agents.agent FROM requests \ + INNER JOIN agents ON requests.agent_id = agents.id \ + WHERE requests.timestamp > ?1 \ + GROUP BY requests.agent_id;"; + + let mut prepared = db.prepare(query).unwrap(); + let mut agents: Vec<(usize, String)> = prepared + .query_map(params![fifteen_ago], |row| Ok((row.get(0)?, row.get(1)?))) + .unwrap() + .map(|r| r.unwrap()) + .collect(); + + agents.sort_by(|a, b| a.0.cmp(&b.0).reverse()); + + let mut prepared = db.prepare(TOP_TEN_ALL_TIME).unwrap(); + let highest_five: Vec<(usize, String)> = prepared + .query_map(params![], |row| Ok((row.get(0)?, row.get(1)?))) + .unwrap() + .map(|r| r.unwrap()) + .collect(); + let sum_highest_five = highest_five.iter().fold(0, |acc, (count, _)| acc + count); + + println!("Content-Type: text/html\n"); + println!(""); + #[rustfmt::skip] + println!("\n\ + corgi stats\n\ + \n\ + "); + + println!(""); + println!("

Corgi Stats :)

"); + + #[rustfmt::skip] + println!("
Top 10 User Agents All Time
# Req.Req/minAgent
{count}{:.1}{agent}
\n\ + \n\ + \t\n\ + \t\t\n\ + \t\n\ + \t\n\ + \t\t\n\ + \t\t\n\ + \t\t\n\ + \t\n\ + \n"); + + for (count, agent) in &agents { + #[rustfmt::skip] + println!("\n\ + \t\n\ + \t\n\ + \t\n\ + ", + *count as f32 / 15.0); + } + + println!("\n
Requests for the last 15 minutes
# Req.Req/minAgent
{count}{:.1}{agent}
"); + + #[rustfmt::skip] + println!("\n\ + \n\ + \t\n\ + \t\t\n\ + \t\n\ + \t\n\ + \t\t\n\ + \t\t\n\ + \t\t\n\ + \t\n\ + \n"); + + // Finish what we started + println!("\n"); + + for (count, agent) in highest_five { + #[rustfmt::skip] + println!("\n\ + \t\n\ + \t\n\ + \t\n\ + ", + (count as f32 / sum_highest_five as f32) * 100.0); + } +} + +fn error_and_die>(status: u16, msg: S) -> ! { + println!("Status: {status}"); + println!("Content-Type: text/html\n"); + println!(""); + println!("\t{status}"); + println!("\t"); + println!("\t\t

{status}

"); + println!("\t\t
"); + println!("\t\t

{}

", msg.into()); + println!("\t\n"); + + std::process::exit(0); +} diff --git a/stats/src/style.css b/stats/src/style.css new file mode 100644 index 0000000..5b3995d --- /dev/null +++ b/stats/src/style.css @@ -0,0 +1,37 @@ +h1 { + font-family: sans-serif; +} + +table { + border-collapse: collapse; + border: 2px solid gray; + margin: 1rem 0px; +} + +tr { + background-color: white; +} + +tbody>tr:nth-of-type(odd) { + background-color: cornsilk; +} + +th, +td { + border: 1px solid darkslateblue; + padding: 2px 3px; +} + +thead th { + background-color: darksalmon; +} + +th { + text-align: left; + padding: 4px 6px; + white-space: nowrap; +} + +th.ttitle { + text-align: center; +} \ No newline at end of file -- cgit 1.4.1-3-g733a5
Top 10 User Agents All Time
# Req.Req/minAgent
{count}{:.1}{agent}