Something went wrong. Try again.
My personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657//! `GET /api/export`: the whole vault as a zip archive, built by the daemon//! so clients never open the vault themselves.
use axum::{ body::Body, extract::State, http::header, response::{IntoResponse, Response},};use fil_core::Kasten;use tokio_stream::StreamExt as _;use tokio_util::io::ReaderStream;use tracing::instrument;
use super::{FilState, error::ApiError};
/// Header carrying how many files the archive holds, database included.pub const FILES_HEADER: &str = "x-fil-export-files";
/// Builds the archive in a temp dir under the read lock (so files and/// database are a consistent snapshot), then streams it back. The temp dir/// lives as long as the response body.#[instrument(skip(state))]pub async fn export(State(state): State<FilState>) -> Result<Response, ApiError> { let dir = tempfile::Builder::new().prefix("fil-export-").tempdir()?; let name = Kasten::default_export_name();
let kt = state.kh.read().await; let summary = kt.export(dir.path().join(&name)).await?; drop(kt);
let file = tokio::fs::File::open(&summary.path).await?; let len = file.metadata().await?.len(); let body = ReaderStream::new(file).map(move |chunk| { // keep the archive on disk until it has been sent let _ = &dir; chunk });
Ok(( [ (header::CONTENT_TYPE, "application/zip".to_owned()), (header::CONTENT_LENGTH, len.to_string()), ( header::CONTENT_DISPOSITION, format!("attachment; filename=\"{name}\""), ), ( header::HeaderName::from_static(FILES_HEADER), summary.files.to_string(), ), ], Body::from_stream(body), ) .into_response())}