diff --git a/vingo/Cargo.lock b/vingo/Cargo.lock index 47de09f..1d58318 100644 --- a/vingo/Cargo.lock +++ b/vingo/Cargo.lock @@ -2951,10 +2951,11 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vingo" -version = "1.1.0" +version = "1.2.0" dependencies = [ "axum", "chrono", + "dotenvy", "migration", "rand", "reqwest", diff --git a/vingo/Cargo.toml b/vingo/Cargo.toml index 585906e..78b883e 100644 --- a/vingo/Cargo.toml +++ b/vingo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vingo" -version = "1.1.0" +version = "1.2.0" edition = "2021" [workspace] @@ -46,3 +46,4 @@ tracing = "=0.1" tracing-subscriber = "=0.3" migration = { path = "migration" } +dotenvy = "0.15.7" diff --git a/vingo/migration/src/m20240903_194156_create_scan.rs b/vingo/migration/src/m20240903_194156_create_scan.rs index fd49f10..05411e2 100644 --- a/vingo/migration/src/m20240903_194156_create_scan.rs +++ b/vingo/migration/src/m20240903_194156_create_scan.rs @@ -8,7 +8,8 @@ pub struct Migration; enum Scan { Table, Id, - Time, + #[allow(clippy::enum_variant_names)] //TODO: rename ScanTime -> Time + ScanTime, CardSerial, } @@ -22,7 +23,9 @@ impl MigrationTrait for Migration { .table(Scan::Table) .col(pk_auto(Scan::Id)) .col(text(Scan::CardSerial)) - .col(timestamp_with_time_zone(Scan::Time).default(Expr::current_timestamp())) + .col( + timestamp_with_time_zone(Scan::ScanTime).default(Expr::current_timestamp()), + ) .foreign_key( ForeignKey::create() .from(Scan::Table, Scan::CardSerial) diff --git a/vingo/src/main.rs b/vingo/src/main.rs index d8cacdb..93a3591 100644 --- a/vingo/src/main.rs +++ b/vingo/src/main.rs @@ -45,6 +45,8 @@ struct RegisterState { #[tokio::main] async fn main() { + let _ = dotenvy::dotenv(); + tracing_subscriber::fmt() .with_max_level(tracing::Level::DEBUG) .init(); @@ -103,6 +105,8 @@ fn open_routes() -> Router { .route("/auth/callback", get(auth::callback)) .route("/scans", post(scans::add)) .route("/version", get(info::version)) + .route("/recent_scans", get(scans::recent)) + .route("/seasons", get(seasons::get_until_now)) } fn authenticated_routes() -> Router { @@ -118,7 +122,6 @@ fn authenticated_routes() -> Router { ) .route("/scans", get(scans::get_for_current_user)) .route("/leaderboard", get(leaderboard::get)) - .route("/seasons", get(seasons::get_until_now)) .route("/settings", get(settings::get).patch(settings::update)) .route_layer(from_fn(middleware::is_logged_in)) } diff --git a/vingo/src/routes/scans.rs b/vingo/src/routes/scans.rs index e211179..d9e9ba9 100644 --- a/vingo/src/routes/scans.rs +++ b/vingo/src/routes/scans.rs @@ -6,12 +6,16 @@ use crate::{ }; use axum::{extract::State, Json}; -use chrono::Local; +use chrono::{Duration, Local}; +use migration::Expr; use reqwest::StatusCode; use sea_orm::{ - ActiveModelTrait, ColumnTrait, EntityTrait, JoinType::InnerJoin, QueryFilter, QuerySelect, - RelationTrait, Set, + entity::prelude::DateTimeWithTimeZone, + ActiveModelTrait, ColumnTrait, EntityTrait, FromQueryResult, IdenStatic, + JoinType::{self, InnerJoin}, + QueryFilter, QueryOrder, QuerySelect, RelationTrait, Set, }; +use serde::Serialize; use tower_sessions::Session; use super::util::{ @@ -102,3 +106,37 @@ pub async fn add(state: State, body: String) -> ResponseResult Ok(user.name) } + +#[derive(Debug, FromQueryResult, Serialize)] +pub struct RecentScanItem { + id: i32, + scan_time: DateTimeWithTimeZone, +} + +pub async fn recent(state: State) -> ResponseResult>> { + let fourteen_days_ago = (Local::now() - Duration::days(14)).naive_local(); + let scans = Scan::find() + .select_only() + .expr(Expr::cust(format!( + "DISTINCT ON ({}, DATE({})) scan.{}, scan.{}", //NOTE: this is a pain + card::Column::UserId.as_str(), + scan::Column::ScanTime.as_str(), + scan::Column::Id.as_str(), + scan::Column::ScanTime.as_str(), + ))) + .join(JoinType::InnerJoin, scan::Relation::Card.def()) + .filter(scan::Column::ScanTime.gt(fourteen_days_ago)) + .order_by_asc(Expr::cust(format!( + "DATE({})", + scan::Column::ScanTime.as_str() + ))) + .into_model::() + .all(&state.db) + .await + .or_log(( + StatusCode::INTERNAL_SERVER_ERROR, + "could not get all recent scans", + ))?; + + Ok(Json(scans)) +}