Major improvement, security handling, file handling +fixes
This commit is contained in:
@@ -10,7 +10,7 @@ use axum::{
|
||||
use cgcx_config::Config;
|
||||
use cgcx_core::{ContentId, CgcxError};
|
||||
use cgcx_crypto::{unwrap_content_key, DecryptStream, MasterKey};
|
||||
use cgcx_db::{Database, ContentRepo, ContentFileRepo};
|
||||
use cgcx_db::{Database, ContentRepo, ContentFileRepo, UserRepo};
|
||||
use cgcx_storage::Storage;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::net::IpAddr;
|
||||
@@ -44,6 +44,12 @@ struct HealthResponse {
|
||||
status: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AuthorInfo {
|
||||
username: Option<String>,
|
||||
user_id: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ContentMetadata {
|
||||
cxid: String,
|
||||
@@ -53,6 +59,8 @@ struct ContentMetadata {
|
||||
current_views: u64,
|
||||
allow_download: bool,
|
||||
created_at: String,
|
||||
total_size: u64,
|
||||
author: Option<AuthorInfo>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -504,6 +512,20 @@ async fn get_metadata(
|
||||
|
||||
let file_repo = ContentFileRepo::new(state.db.conn());
|
||||
let files = file_repo.list_by_content(&content_id).await?;
|
||||
let total_size = files.iter().map(|f| f.size_bytes).sum();
|
||||
|
||||
let user_repo = UserRepo::new(state.db.conn());
|
||||
let author = if content.show_author {
|
||||
match user_repo.get(content.user_id).await? {
|
||||
Some(user) => Some(AuthorInfo {
|
||||
username: user.telegram_username,
|
||||
user_id: user.id,
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let body = serde_json::to_vec(&ContentMetadata {
|
||||
cxid: content.id.to_string(),
|
||||
@@ -519,6 +541,8 @@ async fn get_metadata(
|
||||
current_views: content.view_count,
|
||||
allow_download: content.allow_download,
|
||||
created_at: content.created_at.to_rfc3339(),
|
||||
total_size,
|
||||
author,
|
||||
}).map_err(|_| CgcxError::BadRequest("json serialization".into()))?;
|
||||
let mut response = Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
@@ -712,21 +736,37 @@ async fn serve_file(
|
||||
let new_views = repo.increment_views(&content_id).await?;
|
||||
if let Some(max) = content.max_views {
|
||||
if new_views >= max {
|
||||
if !state.config.content.keep_content {
|
||||
for f in &files {
|
||||
if let Err(e) = tokio::fs::remove_file(&f.stored_path).await {
|
||||
tracing::warn!("failed to remove file {:?}: {}", f.stored_path, e);
|
||||
let db = state.db.clone();
|
||||
let storage = state.storage.clone();
|
||||
let content_id = content_id.clone();
|
||||
let files = files.clone();
|
||||
let keep_content = state.config.content.keep_content;
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_secs(30)).await;
|
||||
if !keep_content {
|
||||
let file_repo = ContentFileRepo::new(db.conn());
|
||||
for f in &files {
|
||||
if f.ref_count > 0 {
|
||||
if let Err(e) = file_repo.decrement_ref_count(&f.content_id, f.file_index).await {
|
||||
tracing::warn!("failed to decrement ref_count: {}", e);
|
||||
}
|
||||
} else {
|
||||
if let Err(e) = file_repo.decrement_ref_count_for_path(&f.stored_path).await {
|
||||
tracing::warn!("failed to decrement owner ref_count: {}", e);
|
||||
}
|
||||
}
|
||||
let remaining = file_repo.count_by_path_excluding_content(&f.stored_path, &f.content_id).await.unwrap_or(1);
|
||||
if remaining == 0 {
|
||||
if let Err(e) = tokio::fs::remove_file(&f.stored_path).await {
|
||||
tracing::warn!("failed to remove file {:?}: {}", f.stored_path, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = storage.delete_content_files(&content_id, "application/octet-stream").await;
|
||||
}
|
||||
let _ = state.storage.delete_content_files(&content_id, "application/octet-stream").await;
|
||||
}
|
||||
repo.set_status(&content_id, cgcx_core::ContentStatus::Deleted).await?;
|
||||
let mut response = Response::builder()
|
||||
.status(StatusCode::GONE)
|
||||
.body(Body::empty())
|
||||
.map_err(|e| CgcxError::Storage(format!("response build failed: {}", e)))?;
|
||||
add_auth_cookie(&mut response, &auth_source, &cxid, &state.cookie_secret)?;
|
||||
return Ok(response);
|
||||
let repo = ContentRepo::new(db.conn());
|
||||
let _ = repo.set_status(&content_id, cgcx_core::ContentStatus::Deleted).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user