aboutsummaryrefslogtreecommitdiff
path: root/src/get.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/get.rs')
-rw-r--r--src/get.rs255
1 files changed, 255 insertions, 0 deletions
diff --git a/src/get.rs b/src/get.rs
index a00ac48..1e364be 100644
--- a/src/get.rs
+++ b/src/get.rs
@@ -1,3 +1,4 @@
+use std::collections::hash_map::HashMap;
use std::net::SocketAddr;
use axum::extract::{ConnectInfo, Path};
@@ -9,9 +10,16 @@ use axum::Extension;
use info_utils::prelude::*;
use crate::ServerState;
+use crate::TrackingRow;
use crate::UdsConnectInfo;
use crate::UrlRow;
+enum TrackingParameter {
+ Ip,
+ Referrer,
+ UserAgent,
+}
+
pub async fn index(Extension(state): Extension<ServerState>) -> impl IntoResponse {
if let Some(redirect) = state.main_page_redirect {
return Redirect::temporary(redirect.as_str()).into_response();
@@ -212,3 +220,250 @@ pub async fn create_id(Extension(state): Extension<ServerState>) -> Html<String>
state.host
))
}
+
+pub async fn tracking(Extension(state): Extension<ServerState>) -> impl IntoResponse {
+ let url_rows: Vec<UrlRow> = sqlx::query_as("SELECT * FROM chela.urls")
+ .fetch_all(&state.db_pool)
+ .await
+ .unwrap();
+ let html = format!(
+ r#"
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>{} Tracking</title>
+ </head>
+ <style>{}</style>
+ <body>
+ {}
+ </body>
+ </html>
+ "#,
+ state.host,
+ table_css(),
+ make_table_from_urls(&url_rows)
+ );
+
+ return Html(html).into_response();
+}
+
+pub async fn tracking_id(
+ Extension(state): Extension<ServerState>,
+ Path(id): Path<String>,
+) -> impl IntoResponse {
+ let tracking_rows: Vec<TrackingRow> =
+ sqlx::query_as("SELECT * FROM chela.tracking WHERE id = $1")
+ .bind(id.clone())
+ .fetch_all(&state.db_pool)
+ .await
+ .unwrap();
+ let url: UrlRow = sqlx::query_as("SELECT * FROM chela.urls WHERE id = $1")
+ .bind(id.clone())
+ .fetch_one(&state.db_pool)
+ .await
+ .unwrap();
+
+ let html = format!(
+ r#"
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>{} Tracking {}</title>
+ </head>
+ <style>{}</style>
+ <body>
+ <h1>Tracking for <a href="{}">{}</a> from ID '{}'</h1>
+ <h2>Visited {} times</h2>
+ {}
+
+ <h2>By IP</h2>
+ {}
+ <h2>By Referrer</h2>
+ {}
+ <h2>By User Agent</h2>
+ {}
+ </body>
+ </html>
+ "#,
+ state.host,
+ id,
+ table_css(),
+ url.url,
+ url.url,
+ url.id,
+ tracking_rows.len(),
+ make_table_from_tracking(&tracking_rows),
+ make_grouped_table_from_tracking(&tracking_rows, TrackingParameter::Ip),
+ make_grouped_table_from_tracking(&tracking_rows, TrackingParameter::Referrer),
+ make_grouped_table_from_tracking(&tracking_rows, TrackingParameter::UserAgent)
+ );
+
+ return Html(html).into_response();
+}
+
+fn make_table_from_tracking(rows: &Vec<TrackingRow>) -> String {
+ let mut html = r#"<table>
+ <colgroup>
+ <col>
+ <col>
+ <col>
+ <col>
+ <col>
+ </colgroup>
+ <tr>
+ <th>Timestamp</th>
+ <th>ID</th>
+ <th>IP</th>
+ <th>Referrer</th>
+ <th>User Agent</th>
+ </tr>
+ "#
+ .to_string();
+
+ for row in rows {
+ html += &format!(
+ r#"
+ <tr>
+ <td>{}</td>
+ <td>{}</td>
+ <td>{}</td>
+ <td>{}</td>
+ <td>{}</td>
+ </tr>
+ "#,
+ row.timestamp,
+ row.id,
+ row.ip.as_ref().unwrap_or(&String::default()),
+ row.referrer.as_ref().unwrap_or(&String::default()),
+ row.user_agent.as_ref().unwrap_or(&String::default())
+ );
+ }
+
+ html += r#"
+ </table>
+ "#;
+
+ html
+}
+
+fn make_grouped_table_from_tracking(rows: &Vec<TrackingRow>, group: TrackingParameter) -> String {
+ let column_name = match group {
+ TrackingParameter::Ip => "IP",
+ TrackingParameter::Referrer => "Referrer",
+ TrackingParameter::UserAgent => "User Agent",
+ }
+ .to_string();
+
+ let mut html = format!(
+ r#"
+ <table>
+ <colgroup>
+ <col>
+ <col>
+ </colgroup>
+ <tr>
+ <th>Occurrences</th>
+ <th>{}</th>
+ </tr>
+ "#,
+ column_name
+ );
+
+ let mut aggregate: HashMap<String, u32> = HashMap::new();
+
+ for row in rows {
+ let tracker = match group {
+ TrackingParameter::Ip => {
+ let v = match &row.ip {
+ Some(val) => val,
+ None => continue,
+ };
+ v
+ }
+ TrackingParameter::Referrer => {
+ let v = match &row.referrer {
+ Some(val) => val,
+ None => continue,
+ };
+ v
+ }
+ TrackingParameter::UserAgent => {
+ let v = match &row.user_agent {
+ Some(val) => val,
+ None => continue,
+ };
+ v
+ }
+ };
+ let count = aggregate.get(tracker).unwrap_or(&0);
+ aggregate.insert(tracker.to_string(), count + 1);
+ }
+
+ for (key, val) in aggregate {
+ html += &format!(
+ r#"
+ <tr>
+ <td>{}</td>
+ <td>{}</td>
+ </tr>
+ "#,
+ val, key
+ );
+ }
+
+ html += r#"
+ </table>
+ "#;
+
+ html
+}
+
+fn make_table_from_urls(urls: &Vec<UrlRow>) -> String {
+ let mut html = r#"<table>
+ <colgroup>
+ <col>
+ <col>
+ <col>
+ <col>
+ </colgroup>
+ <tr>
+ <th>Index</th>
+ <th>ID</th>
+ <th>URL</th>
+ <th>Custom ID</th>
+ </tr>
+ "#
+ .to_string();
+
+ for url in urls {
+ html += &format!(
+ r#"
+ <tr>
+ <td>{}</td>
+ <td><a href="/tracking/{}">{}</a></td>
+ <td><a href="{}">{}</a></td>
+ <td>{}</td>
+ </tr>
+ "#,
+ url.index, url.id, url.id, url.url, url.url, url.custom_id
+ );
+ }
+ html += r#"
+ </table>
+ "#;
+
+ html
+}
+
+fn table_css() -> String {
+ r#"
+ tr:nth-child(even) {
+ background: #f2f2f2;
+ }
+
+ table, th, td {
+ border: 1px solid black;
+ }
+ "#
+ .to_string()
+}