diff --git a/src/logs.rs b/src/logs.rs index df44bfc..11ca89a 100644 --- a/src/logs.rs +++ b/src/logs.rs @@ -33,10 +33,17 @@ impl LogEntry { ))?; Ok(log) } - pub fn get_all(conn: &Connection) -> Result, DatabaseError> { + pub fn total_count(conn: &Connection) -> Result { + Ok(conn.query_row("SELECT COUNT(*) FROM logs", (), |r| r.get(0))?) + } + pub fn get_chronological_offset( + conn: &Connection, + offset: i64, + limit: i64, + ) -> Result, DatabaseError> { Ok(conn - .prepare("SELECT id, actor, target, actiontype, payload FROM logs ORDER BY id DESC")? - .query_map((), |r| { + .prepare("SELECT id, actor, target, actiontype, payload FROM logs ORDER BY id DESC LIMIT ?1 OFFSET ?2")? + .query_map((limit, offset), |r| { let payload: String = r.get(4)?; Ok(LogEntry { id: r.get(0)?, diff --git a/src/web/pages/logs.rs b/src/web/pages/logs.rs index 72b1b37..ab93cee 100644 --- a/src/web/pages/logs.rs +++ b/src/web/pages/logs.rs @@ -1,8 +1,9 @@ use axum::{ - extract::Request, + extract::{Query, Request}, response::{IntoResponse, Redirect, Response}, }; use maud::{PreEscaped, html}; +use serde::Deserialize; use crate::{ database::{self}, @@ -12,14 +13,29 @@ use crate::{ web::{components::nav::nav, icons, pages::base}, }; -pub async fn page(req: Request) -> Result { +#[derive(Deserialize)] +pub struct PageQuery { + page: Option, +} + +pub async fn page( + Query(query): Query, + req: Request, +) -> Result { let u = match User::authenticate(req.headers())? { Some(u) => u, None => return Ok(Redirect::to(&format!("/login?r={}", req.uri().path())).into_response()), }; let mut conn = database::conn()?; let tx = conn.transaction()?; - let logs = LogEntry::get_all(&tx)?; + + let page = query.page.unwrap_or(1).max(1); + let per_page = 20; + let offset = (page - 1) * per_page; + + let logs = LogEntry::get_chronological_offset(&tx, offset, per_page)?; + let total_logs = LogEntry::total_count(&tx)?; + let total_pages = (total_logs as f64 / per_page as f64).ceil() as i64; Ok(base( "Logs | Mnemosyne", @@ -59,8 +75,26 @@ pub async fn page(req: Request) -> Result { div class="p-2 font-light" style=(s) {(log.actor.handle)} div class="p-2 font-light" style=(s) {(log.data.get_humanreadable_payload())} } - @if true { - div class="p-2 col-span-3 text-center font-light text-neutral-400" {"You've reached the end of all logs."} + } + div class="flex justify-between items-center my-4 text-neutral-400" { + @if page > 1 { + a href=(format!("/logs?page={}", (page - 1).max(1))) class="px-4 py-2 border border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-200/5 hover:bg-neutral-200/15 rounded" { + "Previous" + } + } @else { + div {} + } + + span { + "Page " (page) " of " (total_pages) + } + + @if page < total_pages { + a href=(format!("/logs?page={}", page + 1)) class="px-4 py-2 border border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-200/5 hover:bg-neutral-200/15 rounded" { + "Next" + } + } @else { + div {} } } }