Refactor statistics page and enhance logging
- Updated the layout and styling of the statistics page for better responsiveness and visual appeal. - Introduced a new error page for 404 errors with user-friendly messaging and navigation options. - Enhanced logging functionality to capture detailed events related to asset uploads, deletions, and HTTP requests. - Implemented an AssetTracker to manage assets in memory, allowing for efficient tracking and retrieval. - Improved the API for uploading and retrieving assets, ensuring better error handling and response formatting. - Added auto-refresh functionality to the statistics page to keep data up-to-date.
This commit is contained in:
115
src/data_mgt.rs
115
src/data_mgt.rs
@@ -1,22 +1,27 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use chrono::{Duration, Utc};
|
||||
use futures::lock::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::DATA_STORAGE;
|
||||
use crate::logs::log_asset_event;
|
||||
use crate::logs::{LogEventType, log_event};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||
pub struct Asset {
|
||||
id: String,
|
||||
share_duration: u32,
|
||||
created_at: i64,
|
||||
expires_at: i64,
|
||||
mime: String,
|
||||
#[serde(skip)]
|
||||
content: Vec<u8>,
|
||||
#[serde(default)]
|
||||
uploader_ip: Option<String>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Asset {
|
||||
pub fn new(share_duration: u32, mime: String, content: Vec<u8>, uploader_ip: Option<String>) -> Self {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
@@ -36,8 +41,8 @@ impl Asset {
|
||||
Utc::now().timestamp_millis() > self.expires_at
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
pub fn id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
pub fn mime(&self) -> &str {
|
||||
@@ -82,29 +87,85 @@ impl Asset {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn clear_assets() -> Result<()> {
|
||||
let entries = std::fs::read_dir(DATA_STORAGE)?;
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
let data = std::fs::read(&path)?;
|
||||
let asset = serde_json::from_slice::<Asset>(&data)?;
|
||||
if asset.is_expired() {
|
||||
println!("Removing expired asset: {}", asset.id());
|
||||
log_asset_event(
|
||||
"delete_expired",
|
||||
asset.id(),
|
||||
asset.mime(),
|
||||
asset.size_bytes(),
|
||||
asset.share_duration(),
|
||||
asset.created_at(),
|
||||
asset.expires_at(),
|
||||
asset.uploader_ip().unwrap_or("-"),
|
||||
);
|
||||
std::fs::remove_file(&path)?;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct AssetTracker {
|
||||
assets: Arc<Mutex<Vec<Asset>>>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl AssetTracker {
|
||||
pub fn new() -> Self {
|
||||
AssetTracker {
|
||||
assets: Arc::new(Mutex::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn add_asset(&self, asset: Asset) {
|
||||
print!("[{}] Adding asset: {}", chrono::Local::now().to_rfc3339(), asset.id());
|
||||
self.assets.lock().await.push(asset);
|
||||
self.show_assets().await;
|
||||
}
|
||||
|
||||
pub async fn remove_expired(&self) {
|
||||
let mut assets = self.assets.lock().await;
|
||||
let removed_assets = assets.extract_if(.., |asset| asset.is_expired());
|
||||
for asset in removed_assets {
|
||||
log_event(LogEventType::AssetDeleted(&asset));
|
||||
println!("[{}] Removing asset: {}", chrono::Local::now().to_rfc3339(), asset.id());
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn active_assets(&self) -> usize {
|
||||
self.assets.lock().await.len()
|
||||
}
|
||||
|
||||
pub async fn stats_summary(&self) -> (usize, u64, usize, usize) {
|
||||
let assets = self.assets.lock().await;
|
||||
let mut active_assets = 0;
|
||||
let mut storage_bytes: u64 = 0;
|
||||
let mut image_count = 0;
|
||||
let mut text_count = 0;
|
||||
|
||||
for asset in assets.iter() {
|
||||
if asset.is_expired() {
|
||||
continue;
|
||||
}
|
||||
active_assets += 1;
|
||||
storage_bytes += asset.size_bytes() as u64;
|
||||
if asset.mime().starts_with("image/") {
|
||||
image_count += 1;
|
||||
} else if asset.mime().starts_with("text/") {
|
||||
text_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
(active_assets, storage_bytes, image_count, text_count)
|
||||
}
|
||||
|
||||
pub async fn show_assets(&self) {
|
||||
for asset in self.assets.lock().await.iter() {
|
||||
println!(
|
||||
"Asset ID: {}, Expires At: {}, MIME: {}, Size: {} bytes",
|
||||
asset.id(),
|
||||
asset.expires_at(),
|
||||
asset.mime(),
|
||||
asset.size_bytes()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_asset(&self, id: &str) -> Option<Asset> {
|
||||
let assets = self.assets.lock().await;
|
||||
for asset in assets.iter().cloned() {
|
||||
if asset.id() == id {
|
||||
return Some(asset.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn clear_assets(assets: AssetTracker) -> Result<()> {
|
||||
assets.remove_expired().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user