Mercurial > lbo > hg > analyrics
changeset 56:649097da34ae
Surface recent sessions in UI
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Fri, 22 Jul 2022 19:31:57 -0700 |
parents | ced9006f0f91 |
children | ad0625965942 |
files | assets/index_dashboard.html.hbs assets/static/style.css src/logsdb.rs src/main.rs |
diffstat | 4 files changed, 130 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/assets/index_dashboard.html.hbs Fri Jul 22 18:41:04 2022 -0700 +++ b/assets/index_dashboard.html.hbs Fri Jul 22 19:31:57 2022 -0700 @@ -99,8 +99,8 @@ <!-- Plots -- only shown when logged in. --> <div class="plotrow row1"> <div class="plotframe fullwidth"> - <div class="plottitle">Visits and Sessions</div> - <div class="chartcontainer1"><canvas id="visitsAndSessions" height="100"></canvas></div> + <div class="plottitle">Visits and Sessions</div> + <div class="chartcontainer1"><canvas id="visitsAndSessions" height="100"></canvas></div> </div> </div> <div class="plotrow row2"> @@ -115,6 +115,20 @@ <div class="plotframe halfwidth">Top External Referers <div class="chartcontainer2"><canvas id="topRefer"></canvas></div> </div> </div> + <div class="plotrow row3"> + <div class="plotframe fullwidth">Recent Sessions + <table align="center" id="sessionstable"> + <tr> + <th>Start</th><th>Duration</th><th>Requests</th><th>Referer</th><th>Country/City</th><th>User Agent</th> + </tr> + {{ #each recentSessions }} + <tr> + <td>{{ start }}</td><td>{{ duration }}</td><td>{{ count }}</td><td>{{ refer }}</td><td>{{ origin_country }} {{ origin_city }}</td><td>{{ ua }}</td> + </tr> + {{ /each }} + </table> + </div> + </div> <script> let plots = {"visitsAndSessions": {{{ chartconfig.visitsAndSessions }}},
--- a/assets/static/style.css Fri Jul 22 18:41:04 2022 -0700 +++ b/assets/static/style.css Fri Jul 22 19:31:57 2022 -0700 @@ -27,6 +27,9 @@ #botbox { position: relative; vertical-align: top; height: 80%; } #botboxlabel { position: relative; vertical-align: middle; } +#sessionstable tr, td { border: 1px dotted gray; } +#sessionstable tr td { text-align: left; } + /* Login form */ #loginformbox { display: flex; justify-content: center; } #loginform { }
--- a/src/logsdb.rs Fri Jul 22 18:41:04 2022 -0700 +++ b/src/logsdb.rs Fri Jul 22 19:31:57 2022 -0700 @@ -6,6 +6,7 @@ use rocket::futures::{future::ready, StreamExt}; use rocket::http::hyper::uri::Uri; +use rocket::serde::Serialize; use rocket_db_pools::sqlx::{Executor, Row, Sqlite, SqlitePool}; use rocket_db_pools::{Connection, Database, Pool}; use sqlx::prelude::FromRow; @@ -26,6 +27,48 @@ pub include_bots: bool, } +#[derive(sqlx::FromRow, Serialize)] +#[serde(crate = "rocket::serde")] +pub struct RecentSessionsRow { + start: i64, + duration: i64, + count: i64, + refer: Option<String>, + origin_country: Option<String>, + origin_city: Option<String>, + ua: String, +} + +#[derive(Serialize)] +#[serde(crate = "rocket::serde")] +pub struct RecentSessionsTableRow { + start: String, + duration: String, + count: i64, + refer: Option<String>, + origin_country: Option<String>, + origin_city: Option<String>, + ua: String, +} + +impl RecentSessionsTableRow { + pub fn from_row(r: RecentSessionsRow, off: time::UtcOffset) -> RecentSessionsTableRow { + RecentSessionsTableRow { + start: time::OffsetDateTime::from_unix_timestamp(r.start) + .unwrap_or(time::OffsetDateTime::UNIX_EPOCH) + .to_offset(off) + .format(&time::format_description::well_known::Iso8601::DEFAULT) + .unwrap_or(String::new()), + duration: format!("{:.0}", time::Duration::seconds(r.duration)), + count: r.count, + refer: r.refer, + origin_country: r.origin_country, + origin_city: r.origin_city, + ua: r.ua, + } + } +} + impl<'p> LogsDBSession<'p, Sqlite> { pub async fn log_request< S1: AsRef<str>, @@ -358,4 +401,49 @@ by_origin_vec.sort_by_key(|(_, v)| -*v); Ok(by_origin_vec) } + + pub async fn query_recent_sessions( + &mut self, + ctx: &LogsQueryContext, + n: i64, + ) -> Result<Vec<RecentSessionsRow>, Error> { + let include_bots = if ctx.include_bots { 1 } else { 0 }; + let result = sqlx::query( + r#" +SELECT Sessions.start AS start, + Sessions.last-Sessions.start AS duration, + COUNT(*) AS count, + RequestLog.Refer AS refer, + Sessions.origin_country AS origin_country, + Sessions.origin_city AS origin_city, + RequestLog.ua AS ua +FROM RequestLog +JOIN Sessions ON (RequestLog.session = Sessions.id) +WHERE Sessions.is_bot <= ? +GROUP BY Sessions.id +ORDER BY Sessions.start DESC, RequestLog.atime DESC +LIMIT ?;"#, + ) + .bind(include_bots) + .bind(n) + .fetch(&mut *self.0) + .take_while(|r| { + if let Err(e) = r { + error!("Error fetching in query_last_visits: {}", e); + } + ready(r.is_ok()) + }) + .map(Result::unwrap) + .map(|r| RecentSessionsRow::from_row(&r)) + .take_while(|r| { + if let Err(e) = r { + error!("Error parsing row in query_last_visits: {}", e); + } + ready(r.is_ok()) + }) + .map(Result::unwrap) + .collect::<Vec<RecentSessionsRow>>() + .await; + Ok(result) + } }
--- a/src/main.rs Fri Jul 22 18:41:04 2022 -0700 +++ b/src/main.rs Fri Jul 22 19:31:57 2022 -0700 @@ -337,6 +337,26 @@ "undefined".to_string() } }; + let recent_sessions = match LogsDBSession(&mut conn) + .query_recent_sessions(&ctx, 15) + .await + { + Ok(rs) => Some( + rs.into_iter() + .map(|r| { + logsdb::RecentSessionsTableRow::from_row( + r, + time::UtcOffset::from_whole_seconds(tz_offset as i32) + .unwrap_or(time::UtcOffset::UTC), + ) + }) + .collect::<Vec<logsdb::RecentSessionsTableRow>>(), + ), + Err(e) => { + error!("Couldn't query recent sessions: {}", e); + None + } + }; let ymd_format = time::format_description::parse("[year]-[month]-[day]").unwrap(); let tmpl_today = begin.date().format(&ymd_format).unwrap(); @@ -358,7 +378,9 @@ topPaths: toppaths, requestsBySession: reqbyses, sessionsByCountry: sesbycountry, - topRefer: toprefer, ] + topRefer: toprefer, + ], + recentSessions: recent_sessions, ], ) }