brewlog/src/application/routes/backup.rs
Jon Seager 50a372015c
feat(backup): expose backup/restore via authenticated API endpoints
- Add GET /api/v1/backup and POST /api/v1/backup/restore endpoints
  behind AuthenticatedUser
- Add BackupService to AppState and BackupClient for HTTP access
- Update CLI backup/restore to use API instead of direct DB access
- Remove --database-url flag from backup and restore commands
- Increase body limit to 50MB for restore endpoint
- Add API and CLI tests for auth, export, restore, and round-trip
- Update README to document auth requirement and API endpoints
2026-02-04 19:38:49 +00:00

39 lines
1.3 KiB
Rust

use axum::Json;
use axum::extract::State;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use crate::application::auth::AuthenticatedUser;
use crate::application::errors::{ApiError, AppError};
use crate::application::server::AppState;
use crate::infrastructure::backup::BackupData;
/// GET /api/v1/backup — export all data as JSON (requires authentication)
pub(crate) async fn export_backup(
State(state): State<AppState>,
_auth_user: AuthenticatedUser,
) -> Result<Json<BackupData>, ApiError> {
let data = state
.backup_service
.export()
.await
.map_err(|e| AppError::unexpected(e.to_string()))?;
Ok(Json(data))
}
/// POST /api/v1/backup/restore — restore from JSON backup (requires authentication)
pub(crate) async fn restore_backup(
State(state): State<AppState>,
_auth_user: AuthenticatedUser,
Json(payload): Json<BackupData>,
) -> Result<Response, ApiError> {
state.backup_service.restore(payload).await.map_err(|e| {
let msg = e.to_string();
if msg.contains("not empty") {
ApiError::from(AppError::Conflict(msg))
} else {
ApiError::from(AppError::unexpected(msg))
}
})?;
Ok(StatusCode::NO_CONTENT.into_response())
}