feat: implement content retrieval for REST API endpoints

Co-authored-by: aider (openai/andrew/openrouter/anthropic/claude-sonnet-4) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-08-10 21:46:27 -03:00
parent 08f3c3e9e5
commit 35ae5776c0

View File

@@ -1,4 +1,4 @@
use anyhow::Result; use anyhow::{Result, anyhow};
use axum::{ use axum::{
extract::{ConnectInfo, Path, Query, State}, extract::{ConnectInfo, Path, Query, State},
http::{HeaderMap, StatusCode}, http::{HeaderMap, StatusCode},
@@ -11,6 +11,7 @@ use log::{debug, info, warn};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::Read;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
@@ -20,6 +21,7 @@ use tower_http::cors::CorsLayer;
use tower::ServiceBuilder; use tower::ServiceBuilder;
use tower_http::trace::TraceLayer; use tower_http::trace::TraceLayer;
use crate::compression_engine::{CompressionType, get_compression_engine};
use crate::db; use crate::db;
use crate::Args; use crate::Args;
@@ -441,15 +443,26 @@ async fn handle_get_content_latest(
})? })?
}; };
if let Some(_item) = item { if let Some(item) = item {
// Get the actual content - this would need to be implemented match get_item_content(&item, &state.data_dir).await {
// based on how content is stored and retrieved in your system Ok(content) => {
let response = ApiResponse {
success: true,
data: Some(content),
error: None,
};
Ok(Json(response))
}
Err(e) => {
warn!("Failed to get content for item {}: {}", item.id.unwrap_or(0), e);
let response = ApiResponse::<String> { let response = ApiResponse::<String> {
success: false, success: false,
data: None, data: None,
error: Some("Content retrieval not yet implemented".to_string()), error: Some(format!("Failed to retrieve content: {}", e)),
}; };
Ok(Json(response)) Ok(Json(response))
}
}
} else { } else {
Err(StatusCode::NOT_FOUND) Err(StatusCode::NOT_FOUND)
} }
@@ -468,20 +481,37 @@ async fn handle_get_content(
} }
if let Ok(id) = item_id.parse::<i64>() { if let Ok(id) = item_id.parse::<i64>() {
// Validate that item ID is positive to prevent path traversal issues
if id <= 0 {
warn!("Invalid item ID {} from {}", id, addr);
return Err(StatusCode::BAD_REQUEST);
}
let mut conn = state.db.lock().await; let mut conn = state.db.lock().await;
if let Some(_item) = db::get_item(&mut *conn, id).map_err(|e| { if let Some(item) = db::get_item(&mut *conn, id).map_err(|e| {
warn!("Failed to get item {} for content: {}", id, e); warn!("Failed to get item {} for content: {}", id, e);
StatusCode::INTERNAL_SERVER_ERROR StatusCode::INTERNAL_SERVER_ERROR
})? { })? {
// Get the actual content - this would need to be implemented match get_item_content(&item, &state.data_dir).await {
// based on how content is stored and retrieved in your system Ok(content) => {
let response = ApiResponse {
success: true,
data: Some(content),
error: None,
};
Ok(Json(response))
}
Err(e) => {
warn!("Failed to get content for item {}: {}", id, e);
let response = ApiResponse::<String> { let response = ApiResponse::<String> {
success: false, success: false,
data: None, data: None,
error: Some("Content retrieval not yet implemented".to_string()), error: Some(format!("Failed to retrieve content: {}", e)),
}; };
Ok(Json(response)) Ok(Json(response))
}
}
} else { } else {
Err(StatusCode::NOT_FOUND) Err(StatusCode::NOT_FOUND)
} }
@@ -703,6 +733,28 @@ async fn handle_openapi() -> Json<serde_json::Value> {
Json(openapi_spec) Json(openapi_spec)
} }
async fn get_item_content(item: &db::Item, data_dir: &PathBuf) -> Result<String> {
let item_id = item.id.ok_or_else(|| anyhow!("Item missing ID"))?;
// Validate that item ID is positive to prevent path traversal issues
if item_id <= 0 {
return Err(anyhow!("Invalid item ID: {}", item_id));
}
let mut item_path = data_dir.clone();
item_path.push(item_id.to_string());
let compression_type = CompressionType::from_str(&item.compression)?;
let compression_engine = get_compression_engine(compression_type)?;
// Read the content using the compression engine
let mut reader = compression_engine.open(item_path)?;
let mut content = String::new();
reader.read_to_string(&mut content)?;
Ok(content)
}
async fn handle_swagger_ui() -> Html<&'static str> { async fn handle_swagger_ui() -> Html<&'static str> {
let html = r#"<!DOCTYPE html> let html = r#"<!DOCTYPE html>
<html> <html>