use super::system::AppState;
use crate::error::DbError;
use crate::server::authorization::{AuthorizationService, PermissionAction};
use crate::sync::{LogEntry, Operation};
use axum::{
extract::{Extension, Path, State},
http::StatusCode,
response::Json,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]
pub struct CreateDatabaseRequest {
pub name: String,
}
#[derive(Debug, Serialize)]
pub struct CreateDatabaseResponse {
pub name: String,
pub status: String,
}
#[derive(Debug, Serialize)]
pub struct ListDatabasesResponse {
pub databases: Vec<String>,
}
pub async fn create_database(
State(state): State<AppState>,
Extension(claims): Extension<crate::server::auth::Claims>,
Json(req): Json<CreateDatabaseRequest>,
) -> Result<Json<CreateDatabaseResponse>, DbError> {
if req.name.is_empty() {
return Err(DbError::BadRequest(
"Database name cannot be empty".to_string(),
));
}
AuthorizationService::check_permission(&claims, &state, PermissionAction::Admin, None).await?;
state.storage.create_database(req.name.clone())?;
if let Some(ref log) = state.replication_log {
let entry = LogEntry {
sequence: 0,
node_id: "".to_string(),
database: req.name.clone(),
collection: "".to_string(),
operation: Operation::CreateDatabase,
key: "".to_string(),
data: None,
timestamp: chrono::Utc::now().timestamp_millis() as u64,
origin_sequence: None,
};
let _ = log.append(entry);
}
if let Ok(db) = state.storage.get_database(&req.name) {
for system_coll in ["_scripts", "_slow_queries"] {
if db.create_collection(system_coll.to_string(), None).is_ok() {
if let Some(ref log) = state.replication_log {
let metadata = serde_json::json!({
"type": "document",
"shardConfig": None::<serde_json::Value>
});
let entry = LogEntry {
sequence: 0,
node_id: "".to_string(),
database: req.name.clone(),
collection: system_coll.to_string(),
operation: Operation::CreateCollection,
key: "".to_string(),
data: serde_json::to_vec(&metadata).ok(),
timestamp: chrono::Utc::now().timestamp_millis() as u64,
origin_sequence: None,
};
let _ = log.append(entry);
}
}
}
}
Ok(Json(CreateDatabaseResponse {
name: req.name,
status: "created".to_string(),
}))
}
pub async fn list_databases(
State(state): State<AppState>,
Extension(claims): Extension<crate::server::auth::Claims>,
) -> Json<ListDatabasesResponse> {
let permissions = AuthorizationService::get_effective_permissions(&claims, &state)
.await
.unwrap_or_default();
let scoped = claims.scoped_databases.as_deref();
let databases = state
.storage
.list_databases()
.into_iter()
.filter(|db| {
AuthorizationService::check_permission_raw(
&permissions,
PermissionAction::Read,
Some(db),
scoped,
)
.is_ok()
})
.collect();
Json(ListDatabasesResponse { databases })
}
pub async fn delete_database(
State(state): State<AppState>,
Extension(claims): Extension<crate::server::auth::Claims>,
Path(name): Path<String>,
) -> Result<StatusCode, DbError> {
AuthorizationService::check_permission(&claims, &state, PermissionAction::Admin, Some(&name))
.await?;
state.storage.delete_database(&name)?;
if let Some(ref log) = state.replication_log {
let entry = LogEntry {
sequence: 0,
node_id: "".to_string(),
database: name.clone(),
collection: "".to_string(),
operation: Operation::DeleteDatabase,
key: "".to_string(),
data: None,
timestamp: chrono::Utc::now().timestamp_millis() as u64,
origin_sequence: None,
};
let _ = log.append(entry);
}
Ok(StatusCode::NO_CONTENT)
}