docs: Add rustdoc for server, diff, and gzip components
Co-authored-by: aider (openai/andrew/openrouter/sonoma-sky-alpha) <aider@aider.chat>
This commit is contained in:
@@ -17,138 +17,229 @@ use tokio::sync::Mutex;
|
||||
use utoipa::ToSchema;
|
||||
use crate::services::item_service::ItemService;
|
||||
|
||||
/// Server configuration structure
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServerConfig {
|
||||
/// Server bind address
|
||||
pub address: String,
|
||||
/// Optional server port
|
||||
pub port: Option<u16>,
|
||||
/// Optional authentication password
|
||||
pub password: Option<String>,
|
||||
/// Optional hashed authentication password
|
||||
pub password_hash: Option<String>,
|
||||
}
|
||||
|
||||
/// Application state shared across all routes
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
/// Database connection wrapped in Arc<Mutex>
|
||||
pub db: Arc<Mutex<rusqlite::Connection>>,
|
||||
/// Data directory path
|
||||
pub data_dir: PathBuf,
|
||||
/// Item service instance
|
||||
pub item_service: Arc<ItemService>,
|
||||
/// Command line argument parser
|
||||
pub cmd: Arc<Mutex<clap::Command>>,
|
||||
/// Application settings
|
||||
pub settings: Arc<crate::config::Settings>,
|
||||
}
|
||||
|
||||
/// Standard API response wrapper containing success status, data payload, and error information
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
#[schema(description = "Standard API response wrapper containing success status, data payload, and error information")]
|
||||
pub struct ApiResponse<T> {
|
||||
/// Success indicator
|
||||
pub success: bool,
|
||||
/// Optional data payload
|
||||
pub data: Option<T>,
|
||||
/// Optional error message
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
// Specific response types for OpenAPI documentation
|
||||
/// Response type for list of item information
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct ItemInfoListResponse {
|
||||
/// Success indicator
|
||||
pub success: bool,
|
||||
/// Optional list of item information
|
||||
pub data: Option<Vec<ItemInfo>>,
|
||||
/// Optional error message
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Response type for single item information
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct ItemInfoResponse {
|
||||
/// Success indicator
|
||||
pub success: bool,
|
||||
/// Optional item information
|
||||
pub data: Option<ItemInfo>,
|
||||
/// Optional error message
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Response type for item content information
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct ItemContentInfoResponse {
|
||||
/// Success indicator
|
||||
pub success: bool,
|
||||
/// Optional item content information
|
||||
pub data: Option<ItemContentInfo>,
|
||||
/// Optional error message
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Response type for metadata
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct MetadataResponse {
|
||||
/// Success indicator
|
||||
pub success: bool,
|
||||
/// Optional metadata hashmap
|
||||
pub data: Option<HashMap<String, String>>,
|
||||
/// Optional error message
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Response type for status information
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
pub struct StatusInfoResponse {
|
||||
/// Success indicator
|
||||
pub success: bool,
|
||||
/// Optional status information
|
||||
pub data: Option<crate::common::status::StatusInfo>,
|
||||
/// Optional error message
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Complete information about a stored item including metadata and tags
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[schema(description = "Complete information about a stored item including metadata and tags")]
|
||||
pub struct ItemInfo {
|
||||
/// Item ID
|
||||
#[schema(example = 42)]
|
||||
pub id: i64,
|
||||
/// Timestamp
|
||||
#[schema(example = "2023-12-01T15:30:45Z")]
|
||||
pub ts: String,
|
||||
/// Size in bytes
|
||||
#[schema(example = 1024)]
|
||||
pub size: Option<i64>,
|
||||
/// Compression type
|
||||
#[schema(example = "gzip")]
|
||||
pub compression: String,
|
||||
/// List of tags
|
||||
#[schema(example = json!(["important", "work", "document"]))]
|
||||
pub tags: Vec<String>,
|
||||
/// Metadata hashmap
|
||||
#[schema(example = json!({"mime_type": "text/plain", "mime_encoding": "utf-8", "line_count": "42"}))]
|
||||
pub metadata: HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// Item information including content and metadata, with binary detection
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
#[schema(description = "Item information including content and metadata, with binary detection")]
|
||||
pub struct ItemContentInfo {
|
||||
/// Metadata hashmap
|
||||
#[serde(flatten)]
|
||||
#[schema(example = json!({"mime_type": "text/plain", "mime_encoding": "utf-8", "line_count": "42"}))]
|
||||
pub metadata: HashMap<String, String>,
|
||||
/// Optional content as string
|
||||
#[schema(example = "Hello, world!\nThis is the content of the file.")]
|
||||
pub content: Option<String>,
|
||||
/// Binary content indicator
|
||||
#[schema(example = false)]
|
||||
pub binary: bool,
|
||||
}
|
||||
|
||||
/// Query parameters for tags
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct TagsQuery {
|
||||
/// Optional comma-separated tags
|
||||
pub tags: Option<String>,
|
||||
}
|
||||
|
||||
/// Query parameters for listing items
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ListItemsQuery {
|
||||
/// Optional comma-separated tags for filtering
|
||||
pub tags: Option<String>,
|
||||
/// Optional sort order
|
||||
pub order: Option<String>,
|
||||
/// Optional pagination start index
|
||||
pub start: Option<u32>,
|
||||
/// Optional number of items to return
|
||||
pub count: Option<u32>,
|
||||
}
|
||||
|
||||
/// Query parameters for item retrieval
|
||||
#[derive(Debug, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ItemQuery {
|
||||
/// Allow binary content (default: true)
|
||||
#[serde(default = "default_allow_binary")]
|
||||
pub allow_binary: bool,
|
||||
/// Byte offset (default: 0)
|
||||
#[serde(default)]
|
||||
pub offset: u64,
|
||||
/// Byte length (default: 0, meaning all)
|
||||
#[serde(default)]
|
||||
pub length: u64,
|
||||
/// Stream response (default: false)
|
||||
#[serde(default = "default_stream")]
|
||||
pub stream: bool,
|
||||
/// Return as metadata JSON (default: false)
|
||||
#[serde(default = "default_as_meta")]
|
||||
pub as_meta: bool,
|
||||
}
|
||||
|
||||
/// Query parameters for item content retrieval
|
||||
#[derive(Debug, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ItemContentQuery {
|
||||
/// Optional comma-separated tags for filtering
|
||||
pub tags: Option<String>,
|
||||
/// Allow binary content (default: true)
|
||||
#[serde(default = "default_allow_binary")]
|
||||
pub allow_binary: bool,
|
||||
/// Byte offset (default: 0)
|
||||
#[serde(default)]
|
||||
pub offset: u64,
|
||||
/// Byte length (default: 0, meaning all)
|
||||
#[serde(default)]
|
||||
pub length: u64,
|
||||
/// Stream response (default: false)
|
||||
#[serde(default = "default_stream")]
|
||||
pub stream: bool,
|
||||
/// Return as metadata JSON (default: false)
|
||||
#[serde(default = "default_as_meta")]
|
||||
pub as_meta: bool,
|
||||
}
|
||||
|
||||
/// Default function for allow_binary parameter
|
||||
fn default_allow_binary() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Default function for stream parameter
|
||||
fn default_stream() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Default function for as_meta parameter
|
||||
fn default_as_meta() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Validates bearer authentication token
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `auth_str` - The authorization string from the header
|
||||
/// * `expected_password` - The expected plain text password
|
||||
/// * `expected_hash` - Optional expected password hash
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `bool` - True if authentication succeeds, false otherwise
|
||||
fn check_bearer_auth(auth_str: &str, expected_password: &str, expected_hash: &Option<String>) -> bool {
|
||||
if !auth_str.starts_with("Bearer ") {
|
||||
return false;
|
||||
@@ -165,18 +256,17 @@ fn check_bearer_auth(auth_str: &str, expected_password: &str, expected_hash: &Op
|
||||
provided_password == expected_password
|
||||
}
|
||||
|
||||
fn default_allow_binary() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn default_stream() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn default_as_meta() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Validates basic authentication credentials
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `auth_str` - The authorization string from the header
|
||||
/// * `expected_password` - The expected plain text password
|
||||
/// * `expected_hash` - Optional expected password hash
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `bool` - True if authentication succeeds, false otherwise
|
||||
fn check_basic_auth(auth_str: &str, expected_password: &str, expected_hash: &Option<String>) -> bool {
|
||||
if !auth_str.starts_with("Basic ") {
|
||||
return false;
|
||||
@@ -202,6 +292,17 @@ fn check_basic_auth(auth_str: &str, expected_password: &str, expected_hash: &Opt
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks authorization header for valid credentials
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `headers` - HTTP headers from the request
|
||||
/// * `password` - Optional expected password
|
||||
/// * `password_hash` - Optional expected password hash
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `bool` - True if authorization is valid, false otherwise
|
||||
pub fn check_auth(headers: &HeaderMap, password: &Option<String>, password_hash: &Option<String>) -> bool {
|
||||
// If neither password nor hash is set, no authentication required
|
||||
if password.is_none() && password_hash.is_none() {
|
||||
@@ -217,6 +318,17 @@ pub fn check_auth(headers: &HeaderMap, password: &Option<String>, password_hash:
|
||||
false
|
||||
}
|
||||
|
||||
/// Middleware for logging requests and responses
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `ConnectInfo(addr)` - Connection information including client address
|
||||
/// * `request` - The incoming HTTP request
|
||||
/// * `next` - The next middleware in the chain
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The response with logging applied
|
||||
pub async fn logging_middleware(
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
request: Request,
|
||||
@@ -249,10 +361,20 @@ pub async fn logging_middleware(
|
||||
response
|
||||
}
|
||||
|
||||
/// Creates authentication middleware for the application
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `password` - Optional plain text password for authentication
|
||||
/// * `password_hash` - Optional hashed password for authentication
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An authentication middleware function that can be used with axum
|
||||
pub fn create_auth_middleware(
|
||||
password: Option<String>,
|
||||
password_hash: Option<String>,
|
||||
) -> impl Fn(ConnectInfo<SocketAddr>, Request, Next) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Response, StatusCode>> + Send>> + Clone {
|
||||
) -> impl Fn(ConnectInfo<SocketAddr>, Request, Next) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Response, StatusCode>> + Send>> + Clone + Send {
|
||||
move |ConnectInfo(addr): ConnectInfo<SocketAddr>, request: Request, next: Next| {
|
||||
let password = password.clone();
|
||||
let password_hash = password_hash.clone();
|
||||
|
||||
Reference in New Issue
Block a user