changeset 25:390a448dc8c7

Support timezones
author Lewin Bormann <lbo@spheniscida.de>
date Wed, 13 Jul 2022 21:49:41 -0700
parents 758a4f6c160f
children b1850e6f4d9a
files Cargo.toml config_schema_sqlite.sql src/main.rs
diffstat 3 files changed, 39 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/Cargo.toml	Tue Jul 12 12:21:09 2022 -0700
+++ b/Cargo.toml	Wed Jul 13 21:49:41 2022 -0700
@@ -10,8 +10,9 @@
 either = "1.7.0"
 env_logger = "0.9.0"
 log = "0.4.17"
-rocket = { version = "0.5.0-rc.2", features = ["secrets", "json"] }
-rocket_db_pools = { version = "0.1.0-rc.2" }
+rocket = { version = "0.5.0-rc.2", features = ["secrets", "json",] }
+sqlx = { version = "0.5", features = ["macros"] }
+rocket_db_pools = { version = "0.1.0-rc.2", features = [ "sqlx_macros"] }
 rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["handlebars"] }
 sha256 = "1.0.3"
 time = "0.3.11"
--- a/config_schema_sqlite.sql	Tue Jul 12 12:21:09 2022 -0700
+++ b/config_schema_sqlite.sql	Wed Jul 13 21:49:41 2022 -0700
@@ -2,10 +2,11 @@
 DROP TABLE IF EXISTS users;
 CREATE TABLE users (
     id INTEGER PRIMARY KEY,
-    username TEXT NOT NULL,
+    username TEXT NOT NULL UNIQUE,
     name TEXT,
     salt TEXT,
-    password_hash TEXT
+    password_hash TEXT,
+    tz_offset INTEGER,
 );
 
 DROP TABLE IF EXISTS domainpermissions;
--- a/src/main.rs	Tue Jul 12 12:21:09 2022 -0700
+++ b/src/main.rs	Wed Jul 13 21:49:41 2022 -0700
@@ -12,8 +12,9 @@
 use rocket::serde::json::{json, Value};
 use rocket::serde::{self, Serialize};
 
-use rocket_db_pools::sqlx::{self, Executor, Row, Sqlite, SqlitePool};
+use rocket_db_pools::sqlx::{Executor, Row, Sqlite, SqlitePool};
 use rocket_db_pools::{Connection, Database, Pool};
+use sqlx::prelude::FromRow;
 
 use rocket_dyn_templates::{context, Template};
 
@@ -40,6 +41,12 @@
 #[database("sqlite_main")]
 struct ConfigDB(PoolType);
 
+#[derive(Default, Clone, Debug, FromRow)]
+struct UserRecord {
+    name: String,
+    tz_offset: i64,
+}
+
 struct ConfigDBSession<'p, DB: sqlx::Database>(&'p mut sqlx::pool::PoolConnection<DB>);
 
 impl<'p> ConfigDBSession<'p, Sqlite> {
@@ -67,6 +74,14 @@
         let result = self.0.fetch_all(q).await?;
         Ok(result.len() == 1)
     }
+
+    async fn get_user_details(&mut self, user: &str) -> Result<UserRecord, Error> {
+        let entry = sqlx::query("SELECT name, tz_offset FROM users WHERE username = ? LIMIT 1")
+            .bind(user)
+            .fetch_one(&mut *self.0)
+            .await?;
+        Ok(UserRecord::from_row(&entry)?)
+    }
 }
 
 #[derive(Database)]
@@ -151,21 +166,24 @@
         &mut self,
         from: OffsetDateTime,
         to: OffsetDateTime,
+        tz_offset: Option<i64>,
         domainpattern: Option<S>,
     ) -> Result<(Vec<String>, Vec<u32>, Vec<u32>), Error> {
         let domain = domainpattern.as_ref().map(AsRef::as_ref).unwrap_or("%");
         let mut results = sqlx::query(
             r#"
-SELECT DATE(atime, 'unixepoch') AS rqdate, COUNT(requestlog.id) AS rqcount, sesscount
+SELECT DATE(atime + ?, 'unixepoch') AS rqdate, COUNT(requestlog.id) AS rqcount, sesscount
 FROM requestlog
 JOIN (
-    SELECT DATE(start, 'unixepoch') AS sessdate, COUNT(*) AS sesscount
+    SELECT DATE(start + ?, 'unixepoch') AS sessdate, COUNT(*) AS sesscount
     FROM sessions WHERE sessions.domain LIKE ? GROUP BY sessdate)
 AS sc ON (rqdate = sessdate)
 WHERE atime > ? AND atime < ? AND requestlog.domain LIKE ?
 GROUP BY rqdate
 ORDER BY rqdate ASC;"#,
         )
+        .bind(tz_offset.unwrap_or(0))
+        .bind(tz_offset.unwrap_or(0))
         .bind(domain)
         .bind(from.unix_timestamp())
         .bind(to.unix_timestamp())
@@ -344,6 +362,7 @@
 #[rocket::get("/?<domain>", rank = 1)]
 async fn route_index_loggedin(
     mut conn: Connection<LogsDB>,
+    mut config_conn: Connection<ConfigDB>,
     lig: LoggedInGuard,
     flash: Option<FlashMessage<'_>>,
     domain: Option<&str>,
@@ -357,11 +376,21 @@
 
     let mut charts = HashMap::<String, String>::new();
 
+    let tz_offset = match ConfigDBSession(&mut config_conn)
+        .get_user_details(&lig.0)
+        .await
+    {
+        Ok(ur) => ur.tz_offset,
+        Err(e) => {
+            error!("Couldn't obtain user details: {}", e);
+            0
+        }
+    };
     // TODO: Make configurable
     let begin = OffsetDateTime::now_utc() - Duration::days(30);
     let end = OffsetDateTime::now_utc();
     match LogsDBSession(&mut conn)
-        .query_visits_sessions_counts(begin, end, domain)
+        .query_visits_sessions_counts(begin, end, Some(tz_offset), domain)
         .await
     {
         Ok((dates, visits, sessions)) => {
@@ -498,22 +527,6 @@
     )
 }
 
-#[rocket::get("/static/<path>")]
-async fn route_static(
-    cc: &rocket::State<CustomConfig>,
-    path: &str,
-) -> Either<NamedFile, (Status, String)> {
-    match NamedFile::open(Path::new(&cc.asset_path).join(path)).await {
-        Ok(f) => Either::Left(f),
-        Err(e) => {
-            warn!("Static file not found: {}", path);
-            Either::Right((
-                Status::NotFound,
-                format!("Error loading file at {}: {}", path, e),
-            ))
-        }
-    }
-}
 #[derive(rocket::serde::Deserialize)]
 #[serde(crate = "rocket::serde")]
 struct CustomConfig {