105 lines
4.5 KiB
Rust
105 lines
4.5 KiB
Rust
use axum::{
|
|
extract::{Query, Request, State},
|
|
response::{IntoResponse, Redirect, Response},
|
|
};
|
|
use maud::{PreEscaped, html};
|
|
use serde::Deserialize;
|
|
|
|
use crate::{
|
|
MnemoState,
|
|
error::CompositeError,
|
|
logs::LogEntry,
|
|
users::{User, auth::UserAuthenticate},
|
|
web::{components::nav::nav, icons, pages::base},
|
|
};
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct PageQuery {
|
|
page: Option<i64>,
|
|
}
|
|
|
|
pub async fn page(
|
|
State(state): State<MnemoState>,
|
|
Query(query): Query<PageQuery>,
|
|
req: Request,
|
|
) -> Result<Response, CompositeError> {
|
|
let mut tx = state.pool.acquire().await?;
|
|
let u = match User::authenticate(&mut *tx, req.headers()).await? {
|
|
Some(u) => u,
|
|
None => return Ok(Redirect::to(&format!("/login?r={}", req.uri().path())).into_response()),
|
|
};
|
|
|
|
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(&mut *tx, offset, per_page).await?;
|
|
let total_logs = LogEntry::total_count(&mut *tx).await?;
|
|
let total_pages = (total_logs as f64 / per_page as f64).ceil() as i64;
|
|
|
|
Ok(base(
|
|
"Logs | Mnemosyne",
|
|
html!(
|
|
(nav(Some(&u), req.uri().path()))
|
|
|
|
@if true {//let Ok(true) = u.has_permission(&mut *tx, Permission::BrowseServerLogs) {
|
|
div class="max-w-4xl mx-auto px-2" {
|
|
div class="my-4" {
|
|
p class="flex items-center gap-2" {
|
|
span class="text-neutral-500" {(PreEscaped(icons::CLIPBOARD_CLOCK))}
|
|
span class="text-2xl font-semibold font-lora" {"Logs"}
|
|
}
|
|
}
|
|
div class="w-full border border-neutral-200/25 rounded grid grid-cols-[auto_auto_1fr]" {
|
|
@for (txt, ico) in [("Timestamp", icons::CLOCK), ("Actor", icons::USER), ("Action", icons::PEN)] {
|
|
div class="p-2 flex gap-1 font-semibold border-b border-neutral-200/25" {
|
|
span class="text-neutral-500 scale-[.8]" {(PreEscaped(ico))}
|
|
(txt)
|
|
}
|
|
}
|
|
@for (idx, log) in logs.iter().enumerate() {
|
|
@let s = if idx % 2 == 0 {"background-color: #e5e5e50b"} else {""};
|
|
div class="p-2 font-light" style=(s) {
|
|
(log.id.get_timestamp()
|
|
.map(|ts| {
|
|
let (secs, nanos) = ts.to_unix();
|
|
chrono::DateTime::from_timestamp(secs as i64, nanos)
|
|
.map(|dt| dt.format("%Y-%m-%d %H:%M:%S").to_string())
|
|
.unwrap_or_else(|| "invalid date".to_string())
|
|
})
|
|
.unwrap_or_else(|| "no timestamp".to_string()))
|
|
}
|
|
div class="p-2 font-light" style=(s) {(log.actor.handle)}
|
|
div class="p-2 font-light" style=(s) {(log.data.get_humanreadable_payload())}
|
|
}
|
|
}
|
|
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 {}
|
|
}
|
|
}
|
|
}
|
|
} @else {
|
|
p class="text-center p-2" {"You must have permission to view logs."}
|
|
}
|
|
),
|
|
)
|
|
.into_response())
|
|
}
|