API Crate
API Crate
Section titled “API Crate”The faber-api
crate provides the HTTP API layer for Faber, built with Axum and featuring automatic OpenAPI/Swagger documentation. It handles HTTP requests, authentication, validation, and task execution coordination.
Overview
Section titled “Overview”The API crate is responsible for:
- HTTP Server: RESTful API endpoints
- Authentication: API key validation and authorization
- Request Validation: Input validation and sanitization
- Response Handling: JSON serialization and error responses
- OpenAPI Documentation: Automatic Swagger UI generation
- Middleware: Logging, CORS, rate limiting
Architecture
Section titled “Architecture”The API crate follows a modular architecture:
api/├── lib.rs # Main library entry point├── router.rs # Route definitions and server setup├── execution.rs # Task execution endpoints├── health.rs # Health check endpoints├── middleware.rs # HTTP middleware (auth, logging, etc.)├── validation.rs # Request validation logic└── error.rs # API-specific error handling
Key Components
Section titled “Key Components”Router Setup
Section titled “Router Setup”The main router is created in router.rs
:
pub fn create_router() -> Router { Router::new() .route("/health", get(health::health_check)) .route("/execute", post(execution::execute_tasks)) .route("/swagger-ui/*path", get(swagger_ui_handler)) .layer(middleware::auth_middleware()) .layer(middleware::logging_middleware()) .layer(middleware::cors_middleware())}
Task Execution Endpoint
Section titled “Task Execution Endpoint”The main execution endpoint handles task requests:
pub async fn execute_tasks( State(state): State<AppState>, auth: Auth, Json(request): Json<ExecuteRequest>,) -> Result<Json<ExecuteResponse>, ApiError> { // Validate request let tasks = validate_execute_request(request)?;
// Execute tasks let results = state.executor.execute_tasks(tasks).await?;
// Return results Ok(Json(ExecuteResponse { results }))}
Authentication Middleware
Section titled “Authentication Middleware”API key authentication is handled by middleware:
pub async fn auth_middleware( mut request: Request, next: Next,) -> Result<Response, ApiError> { let auth_header = request .headers() .get("Authorization") .and_then(|h| h.to_str().ok());
if let Some(api_key) = auth_header { if validate_api_key(api_key) { request.extensions_mut().insert(Auth { api_key }); Ok(next.run(request).await) } else { Err(ApiError::Unauthorized("Invalid API key".into())) } } else { Err(ApiError::Unauthorized("Missing API key".into())) }}
API Endpoints
Section titled “API Endpoints”Health Check
Section titled “Health Check”Endpoint: GET /health
Response:
{ "status": "healthy", "timestamp": "2024-01-01T12:00:00Z", "version": "0.1.0"}
Task Execution
Section titled “Task Execution”Endpoint: POST /execute
Request:
{ "tasks": [ { "command": "echo", "args": ["hello", "world"], "env": { "CUSTOM_VAR": "value" }, "files": { "script.py": "print('Hello')" }, "resource_limits": { "memory_limit": 536870912, "cpu_time_limit": 30000000000 } } ]}
Response:
{ "results": [ { "status": "Success", "exit_code": 0, "stdout": "hello world\n", "stderr": null, "resource_usage": { "cpu_time_ns": 1000000, "wall_time_ns": 5000000, "memory_peak_bytes": 1048576 } } ]}
Error Handling
Section titled “Error Handling”The API crate provides comprehensive error handling:
#[derive(Debug, thiserror::Error)]pub enum ApiError { #[error("Bad request: {0}")] BadRequest(String),
#[error("Unauthorized: {0}")] Unauthorized(String),
#[error("Forbidden: {0}")] Forbidden(String),
#[error("Not found: {0}")] NotFound(String),
#[error("Internal server error: {0}")] Internal(String),
#[error("Validation error: {0}")] Validation(String),}
Error Responses
Section titled “Error Responses”All errors return consistent JSON responses:
{ "error": "Error type", "details": "Detailed error message", "timestamp": "2024-01-01T12:00:00Z"}
Request Validation
Section titled “Request Validation”The API validates all incoming requests:
pub fn validate_execute_request(request: ExecuteRequest) -> Result<Vec<Task>, ApiError> { if request.tasks.is_empty() { return Err(ApiError::Validation("No tasks provided".into())); }
for task in &request.tasks { if task.command.is_empty() { return Err(ApiError::Validation("Command cannot be empty".into())); }
// Validate command is not dangerous if is_dangerous_command(&task.command) { return Err(ApiError::Validation("Dangerous command not allowed".into())); } }
Ok(request.tasks)}
Middleware
Section titled “Middleware”Authentication Middleware
Section titled “Authentication Middleware”Validates API keys for protected endpoints:
pub async fn auth_middleware( request: Request, next: Next,) -> Result<Response, ApiError> { // Extract and validate API key // Add auth context to request extensions next.run(request).await}
Logging Middleware
Section titled “Logging Middleware”Logs all HTTP requests and responses:
pub async fn logging_middleware( request: Request, next: Next,) -> Response { let start = Instant::now(); let method = request.method().clone(); let uri = request.uri().clone();
let response = next.run(request).await; let duration = start.elapsed();
info!( "{} {} {} - {}ms", method, uri, response.status(), duration.as_millis() );
response}
CORS Middleware
Section titled “CORS Middleware”Handles Cross-Origin Resource Sharing:
pub fn cors_middleware() -> CorsLayer { CorsLayer::new() .allow_origin(Any) .allow_methods([Method::GET, Method::POST]) .allow_headers([AUTHORIZATION, CONTENT_TYPE])}
OpenAPI Documentation
Section titled “OpenAPI Documentation”The API automatically generates OpenAPI documentation:
#[derive(OpenApi)]#[openapi( paths(health::health_check, execution::execute_tasks), components(schemas(ExecuteRequest, ExecuteResponse, Task, TaskResult)), tags( (name = "health", description = "Health check endpoints"), (name = "execution", description = "Task execution endpoints") ))]struct ApiDoc;
pub fn create_swagger_ui() -> Router { Router::new().nest_service( "/swagger-ui", SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", ApiDoc::openapi()), )}
Rate Limiting
Section titled “Rate Limiting”The API implements rate limiting to prevent abuse:
pub fn rate_limit_middleware() -> Router { Router::new() .layer(RateLimitLayer::new( RateLimit::new(100, Duration::from_secs(60)) ))}
Configuration
Section titled “Configuration”The API can be configured via environment variables:
pub struct ApiConfig { pub host: String, pub port: u16, pub enable_swagger: bool, pub api_key: Option<String>, pub open_mode: bool, pub rate_limit: u32, pub rate_limit_window: Duration,}
Testing
Section titled “Testing”The API crate includes comprehensive tests:
#[cfg(test)]mod tests { use super::*; use axum::http::StatusCode; use axum_test::TestServer;
#[tokio::test] async fn test_health_check() { let app = create_router(); let server = TestServer::new(app).unwrap();
let response = server.get("/health").await; assert_eq!(response.status_code(), StatusCode::OK); }
#[tokio::test] async fn test_execute_tasks() { let app = create_router(); let server = TestServer::new(app).unwrap();
let request = ExecuteRequest { tasks: vec![Task { command: "echo".to_string(), args: Some(vec!["hello".to_string()]), env: None, files: None, }], };
let response = server .post("/execute") .json(&request) .await;
assert_eq!(response.status_code(), StatusCode::OK); }}
Dependencies
Section titled “Dependencies”The API crate uses the following dependencies:
[dependencies]axum = { workspace = true }tokio = { workspace = true }tower-http = { workspace = true }utoipa = { workspace = true }utoipa-axum = { workspace = true }utoipa-swagger-ui = { workspace = true }serde = { workspace = true }serde_json = { workspace = true }tracing = { workspace = true }faber-core = { path = "../core" }faber-executor = { path = "../executor" }faber-config = { path = "../config" }
Best Practices
Section titled “Best Practices”- Validate all inputs: Always validate and sanitize user inputs
- Use proper error handling: Return appropriate HTTP status codes
- Implement rate limiting: Prevent API abuse
- Log all requests: Maintain audit trails
- Use HTTPS in production: Encrypt all communications
- Implement proper authentication: Use strong API keys
- Monitor performance: Track response times and errors
- Version your API: Plan for API versioning