feat: add export diary functionality and update API routes

This commit is contained in:
2026-05-09 20:59:30 +02:00
parent fe55d694f9
commit 6323322d97
2 changed files with 43 additions and 7 deletions

View File

@@ -846,24 +846,24 @@ pub mod api {
use uuid::Uuid; use uuid::Uuid;
use application::{ use application::{
commands::{DeleteReviewCommand, LoginCommand, RegisterCommand, SyncPosterCommand}, commands::{DeleteReviewCommand, ExportCommand, LoginCommand, RegisterCommand, SyncPosterCommand},
queries::GetReviewHistoryQuery, queries::GetReviewHistoryQuery,
use_cases::{ use_cases::{
delete_review, get_diary, get_review_history, log_review, login as login_uc, delete_review, export_diary as export_diary_uc, get_diary, get_review_history,
register as register_uc, sync_poster, log_review, login as login_uc, register as register_uc, sync_poster,
}, },
}; };
use domain::{ use domain::{
errors::DomainError, errors::DomainError,
models::{DiaryEntry, Movie, Review}, models::{DiaryEntry, ExportFormat, Movie, Review},
services::review_history::Trend, services::review_history::Trend,
value_objects::MovieId, value_objects::MovieId,
}; };
use crate::{ use crate::{
dtos::{ dtos::{
DiaryEntryDto, DiaryQueryParams, DiaryResponse, LogReviewData, LogReviewRequest, DiaryEntryDto, DiaryQueryParams, DiaryResponse, ExportQueryParams, LogReviewData,
LoginRequest, LoginResponse, MovieDto, RegisterRequest, ReviewDto, LogReviewRequest, LoginRequest, LoginResponse, MovieDto, RegisterRequest, ReviewDto,
ReviewHistoryResponse, ReviewHistoryResponse,
}, },
errors::ApiError, errors::ApiError,
@@ -1029,4 +1029,39 @@ pub mod api {
review: review_to_dto(entry.review()), review: review_to_dto(entry.review()),
} }
} }
pub async fn export_diary(
State(state): State<AppState>,
user: AuthenticatedUser,
Query(params): Query<ExportQueryParams>,
) -> impl IntoResponse {
let format = match params.format.as_str() {
"csv" => ExportFormat::Csv,
"json" => ExportFormat::Json,
_ => return StatusCode::BAD_REQUEST.into_response(),
};
let (content_type, filename) = match &format {
ExportFormat::Csv => ("text/csv; charset=utf-8", "diary.csv"),
ExportFormat::Json => ("application/json", "diary.json"),
};
let cmd = ExportCommand { user_id: user.0.value(), format };
match export_diary_uc::execute(&state.app_ctx, cmd).await {
Ok(bytes) => (
StatusCode::OK,
[
(axum::http::header::CONTENT_TYPE, content_type.to_string()),
(
axum::http::header::CONTENT_DISPOSITION,
format!("attachment; filename=\"{}\"", filename),
),
],
bytes,
)
.into_response(),
Err(e) => {
tracing::error!("export error: {:?}", e);
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
}
}
} }

View File

@@ -157,7 +157,7 @@ fn api_routes(rate_limit: u64) -> Router<AppState> {
}); });
Router::new().nest( Router::new().nest(
"/api", "/api/v1",
Router::new() Router::new()
.route("/diary", routing::get(handlers::api::get_diary)) .route("/diary", routing::get(handlers::api::get_diary))
.route( .route(
@@ -175,6 +175,7 @@ fn api_routes(rate_limit: u64) -> Router<AppState> {
) )
.route("/auth/login", routing::post(handlers::api::login)) .route("/auth/login", routing::post(handlers::api::login))
.route("/auth/register", routing::post(handlers::api::register)) .route("/auth/register", routing::post(handlers::api::register))
.route("/diary/export", routing::get(handlers::api::export_diary))
.route_layer(auth_rate_limit), .route_layer(auth_rate_limit),
) )
} }