logs filtering functionality
This commit is contained in:
76
src/logs.rs
76
src/logs.rs
@@ -1,9 +1,9 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{PgConnection, Row};
|
||||
use strum::IntoStaticStr;
|
||||
use strum::{EnumDiscriminants, IntoStaticStr, VariantNames};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{database::DatabaseError, users::User};
|
||||
use crate::{database::DatabaseError, users::User, web::icons};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LogEntry {
|
||||
@@ -46,15 +46,26 @@ impl LogEntry {
|
||||
|
||||
pub async fn get_chronological_offset(
|
||||
conn: &mut PgConnection,
|
||||
action_type: Option<LogActionDiscriminant>,
|
||||
offset: i64,
|
||||
limit: i64,
|
||||
) -> Result<Vec<LogEntry>, DatabaseError> {
|
||||
let rows =
|
||||
sqlx::query("SELECT id, actor, payload FROM logs ORDER BY id DESC LIMIT $1 OFFSET $2")
|
||||
.bind(limit)
|
||||
.bind(offset)
|
||||
.fetch_all(&mut *conn)
|
||||
.await?;
|
||||
let mut qstr = String::from("SELECT id, actor, payload FROM logs ");
|
||||
if action_type.is_some() {
|
||||
qstr += "WHERE actiontype = $3 "
|
||||
}
|
||||
qstr += "ORDER BY id DESC LIMIT $1 OFFSET $2";
|
||||
|
||||
let q = sqlx::query(&qstr).bind(limit).bind(offset);
|
||||
|
||||
let rows = match action_type {
|
||||
Some(at) => {
|
||||
let atstr: &'static str = at.into();
|
||||
q.bind(atstr).fetch_all(&mut *conn).await?
|
||||
}
|
||||
None => q.fetch_all(&mut *conn).await?,
|
||||
};
|
||||
|
||||
let mut entries = Vec::new();
|
||||
for row in rows {
|
||||
let payload: serde_json::Value = row.try_get("payload")?;
|
||||
@@ -69,10 +80,9 @@ impl LogEntry {
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(Debug, thiserror::Error)]
|
||||
// pub enum LogError {}
|
||||
|
||||
#[derive(Debug, IntoStaticStr, Serialize, Deserialize)]
|
||||
#[derive(Debug, IntoStaticStr, Serialize, Deserialize, VariantNames, EnumDiscriminants)]
|
||||
#[strum_discriminants(derive(IntoStaticStr, Serialize, Deserialize))]
|
||||
#[strum_discriminants(name(LogActionDiscriminant))]
|
||||
pub enum LogAction {
|
||||
Initialize,
|
||||
RegenInfradmin,
|
||||
@@ -191,3 +201,45 @@ impl LogAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LogActionDiscriminant {
|
||||
pub fn human_readable(&self) -> &'static str {
|
||||
use LogActionDiscriminant as LAD;
|
||||
match self {
|
||||
LAD::Initialize => "Mnemosyne Initialization",
|
||||
LAD::RegenInfradmin => "Infradmin Regeneration",
|
||||
LAD::CreateUser => "User Creation",
|
||||
LAD::ManuallyChangeUsersPassword => "Another User's Password Change",
|
||||
LAD::CreateTag => "Tag Creation",
|
||||
LAD::RenameTag => "Tag Rename",
|
||||
LAD::DeleteTag => "Tag Deletion",
|
||||
LAD::CreatePerson => "Person Creation",
|
||||
LAD::ChangeUserHandle => "User Handle Change",
|
||||
LAD::AddPersonName => "Person Name Addition",
|
||||
LAD::DeletePersonName => "Person Name Deletion",
|
||||
LAD::SetPersonPrimaryName => "Person Primary Name Set",
|
||||
LAD::CreateQuote => "Quote Creation",
|
||||
LAD::ManuallyRevokeSession => "Manual Session Revocation",
|
||||
}
|
||||
}
|
||||
pub fn icon(&self) -> &'static str {
|
||||
use LogActionDiscriminant as LAD;
|
||||
match self {
|
||||
LAD::Initialize => icons::LINE_DOT_RIGHT_HORIZONTAL,
|
||||
// LAD::RegenInfradmin =>
|
||||
// LAD::CreateUser =>
|
||||
// LAD::ManuallyChangeUsersPassword =>
|
||||
// LAD::CreateTag =>
|
||||
// LAD::RenameTag =>
|
||||
// LAD::DeleteTag =>
|
||||
// LAD::CreatePerson =>
|
||||
// LAD::ChangeUserHandle =>
|
||||
// LAD::AddPersonName =>
|
||||
// LAD::DeletePersonName =>
|
||||
// LAD::SetPersonPrimaryName =>
|
||||
// LAD::CreateQuote =>
|
||||
// LAD::ManuallyRevokeSession =>
|
||||
_ => icons::GIT_COMMIT_VERTICAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
src/web/icons/git-commit-vertical.svg
Normal file
1
src/web/icons/git-commit-vertical.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-git-commit-vertical-icon lucide-git-commit-vertical"><path d="M12 3v6"/><circle cx="12" cy="12" r="3"/><path d="M12 15v6"/></svg>
|
||||
|
After Width: | Height: | Size: 332 B |
1
src/web/icons/line-dot-right-horizontal.svg
Normal file
1
src/web/icons/line-dot-right-horizontal.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-line-dot-right-horizontal-icon lucide-line-dot-right-horizontal"><path d="M 3 12 L 15 12"/><circle cx="18" cy="12" r="3"/></svg>
|
||||
|
After Width: | Height: | Size: 331 B |
@@ -8,8 +8,10 @@ pub const CLOCK: &str = include_str!("clock.svg");
|
||||
pub const CONTACT: &str = include_str!("contact.svg");
|
||||
pub const EYE: &str = include_str!("eye.svg");
|
||||
pub const FILE_IMAGE: &str = include_str!("file-image.svg");
|
||||
pub const GIT_COMMIT_VERTICAL: &str = include_str!("git-commit-vertical.svg");
|
||||
pub const INFO: &str = include_str!("info.svg");
|
||||
pub const LAYOUT_DASHBOARD: &str = include_str!("layout-dashboard.svg");
|
||||
pub const LINE_DOT_RIGHT_HORIZONTAL: &str = include_str!("line-dot-right-horizontal.svg");
|
||||
pub const LOG_OUT: &str = include_str!("log-out.svg");
|
||||
pub const MAP_PIN: &str = include_str!("map-pin.svg");
|
||||
pub const PEN: &str = include_str!("pen.svg");
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use axum::{ Router, http::header, routing::get};
|
||||
use axum::{Router, http::header, routing::get};
|
||||
|
||||
use crate::MnemoState;
|
||||
|
||||
mod components;
|
||||
mod icons;
|
||||
pub mod icons;
|
||||
mod pages;
|
||||
|
||||
pub const CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/styles.css"));
|
||||
|
||||
@@ -8,19 +8,20 @@ use serde::Deserialize;
|
||||
use crate::{
|
||||
MnemoState,
|
||||
error::CompositeError,
|
||||
logs::LogEntry,
|
||||
logs::{LogActionDiscriminant, LogEntry},
|
||||
users::{User, auth::UserAuthenticate},
|
||||
web::{components::nav::nav, icons, pages::base},
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PageQuery {
|
||||
pub struct LogsPageQuery {
|
||||
page: Option<i64>,
|
||||
action: Option<LogActionDiscriminant>,
|
||||
}
|
||||
|
||||
pub async fn page(
|
||||
State(state): State<MnemoState>,
|
||||
Query(query): Query<PageQuery>,
|
||||
Query(query): Query<LogsPageQuery>,
|
||||
req: Request,
|
||||
) -> Result<Response, CompositeError> {
|
||||
let mut tx = state.pool.acquire().await?;
|
||||
@@ -33,7 +34,7 @@ pub async fn page(
|
||||
let per_page = 20;
|
||||
let offset = (page - 1) * per_page;
|
||||
|
||||
let logs = LogEntry::get_chronological_offset(&mut *tx, offset, per_page).await?;
|
||||
let logs = LogEntry::get_chronological_offset(&mut *tx, query.action, 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;
|
||||
|
||||
@@ -50,6 +51,7 @@ pub async fn page(
|
||||
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" {
|
||||
|
||||
Reference in New Issue
Block a user