synor/crates/synor-database/src/gateway/mod.rs
Gulshan Yadav ab4c967a97 feat(database): complete Phase 10 Database Gateway
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%).
2026-01-10 18:11:13 +05:30

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,
}