Add HTTP REST API gateway for Synor Database L2: - Gateway server with Axum HTTP framework - API key authentication with permissions and rate limiting - Full REST endpoints for all database models: - Key-Value: GET/PUT/DELETE /kv/:key, POST /kv/batch - Documents: CRUD operations, MongoDB-style queries - Vectors: embedding insert, similarity search - Time-series: metrics recording and queries - Usage metering for billing integration - CORS and request timeout configuration All 51 tests passing. Phase 10 now complete (100%).
130 lines
3.4 KiB
Rust
130 lines
3.4 KiB
Rust
//! Database Gateway - HTTP/REST API for Synor Database.
|
|
//!
|
|
//! Provides external access to database operations via REST API.
|
|
//!
|
|
//! # Endpoints
|
|
//!
|
|
//! ## Key-Value Operations
|
|
//! - `GET /kv/:key` - Get value
|
|
//! - `PUT /kv/:key` - Set value
|
|
//! - `DELETE /kv/:key` - Delete key
|
|
//! - `POST /kv/batch` - Batch operations
|
|
//!
|
|
//! ## Document Operations
|
|
//! - `POST /db/:database/collections` - Create collection
|
|
//! - `GET /db/:database/:collection` - Query documents
|
|
//! - `POST /db/:database/:collection` - Insert document
|
|
//! - `PUT /db/:database/:collection/:id` - Update document
|
|
//! - `DELETE /db/:database/:collection/:id` - Delete document
|
|
//!
|
|
//! ## Vector Operations
|
|
//! - `POST /db/:database/vectors` - Insert embeddings
|
|
//! - `POST /db/:database/vectors/search` - Similarity search
|
|
//!
|
|
//! ## Time-Series Operations
|
|
//! - `POST /db/:database/metrics/:name` - Record metric
|
|
//! - `GET /db/:database/metrics/:name` - Query metrics
|
|
|
|
pub mod auth;
|
|
pub mod handlers;
|
|
pub mod router;
|
|
pub mod server;
|
|
|
|
pub use auth::{ApiKey, AuthConfig, AuthMiddleware};
|
|
pub use router::create_router;
|
|
pub use server::{GatewayConfig, GatewayServer};
|
|
|
|
use crate::error::DatabaseError;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// API response wrapper.
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct ApiResponse<T> {
|
|
/// Whether the request succeeded.
|
|
pub success: bool,
|
|
/// Response data (if successful).
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub data: Option<T>,
|
|
/// Error message (if failed).
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub error: Option<String>,
|
|
/// Request ID for tracing.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub request_id: Option<String>,
|
|
}
|
|
|
|
impl<T> ApiResponse<T> {
|
|
/// Creates a successful response.
|
|
pub fn ok(data: T) -> Self {
|
|
Self {
|
|
success: true,
|
|
data: Some(data),
|
|
error: None,
|
|
request_id: None,
|
|
}
|
|
}
|
|
|
|
/// Creates an error response.
|
|
pub fn error(message: impl Into<String>) -> Self {
|
|
Self {
|
|
success: false,
|
|
data: None,
|
|
error: Some(message.into()),
|
|
request_id: None,
|
|
}
|
|
}
|
|
|
|
/// Sets the request ID.
|
|
pub fn with_request_id(mut self, id: impl Into<String>) -> Self {
|
|
self.request_id = Some(id.into());
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<T> From<Result<T, DatabaseError>> for ApiResponse<T> {
|
|
fn from(result: Result<T, DatabaseError>) -> Self {
|
|
match result {
|
|
Ok(data) => ApiResponse::ok(data),
|
|
Err(e) => ApiResponse::error(e.to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Pagination parameters.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Pagination {
|
|
/// Number of items to skip.
|
|
#[serde(default)]
|
|
pub skip: usize,
|
|
/// Maximum items to return.
|
|
#[serde(default = "default_limit")]
|
|
pub limit: usize,
|
|
}
|
|
|
|
fn default_limit() -> usize {
|
|
100
|
|
}
|
|
|
|
impl Default for Pagination {
|
|
fn default() -> Self {
|
|
Self {
|
|
skip: 0,
|
|
limit: default_limit(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Usage metrics for billing.
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
pub struct UsageMetrics {
|
|
/// Read operations count.
|
|
pub reads: u64,
|
|
/// Write operations count.
|
|
pub writes: u64,
|
|
/// Vector search operations.
|
|
pub vector_searches: u64,
|
|
/// Storage used (bytes).
|
|
pub storage_bytes: u64,
|
|
/// Data transferred (bytes).
|
|
pub bandwidth_bytes: u64,
|
|
}
|