Can export data as ZIP file
This commit is contained in:
@ -53,3 +53,21 @@ pub const ACCOUNT_TYPES: [AccountTypeDesc; 3] = [
|
||||
icon: include_str!("../assets/saving.svg"),
|
||||
},
|
||||
];
|
||||
|
||||
/// ZIP export paths
|
||||
pub mod zip_export {
|
||||
/// Accounts file path inside archive
|
||||
pub const ACCOUNTS_FILE: &str = "accounts.json";
|
||||
|
||||
/// Movements file path inside archive
|
||||
pub const MOVEMENTS_FILE: &str = "movements.json";
|
||||
|
||||
/// Files list file path inside archive
|
||||
pub const FILES_FILE: &str = "files.json";
|
||||
|
||||
/// Files directory inside archive
|
||||
pub const FILES_DIR: &str = "files/";
|
||||
|
||||
/// Inbox file path inside archive
|
||||
pub const INBOX_FILE: &str = "inbox.json";
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::app_config::AppConfig;
|
||||
use crate::constants;
|
||||
use crate::controllers::HttpResult;
|
||||
use crate::converters::finances_manager_converter::{
|
||||
FinancesManagerAccount, FinancesManagerFile, FinancesManagerMovement,
|
||||
@ -7,9 +9,23 @@ use crate::extractors::file_extractor::FileExtractor;
|
||||
use crate::models::accounts::AccountType;
|
||||
use crate::services::accounts_service::UpdateAccountQuery;
|
||||
use crate::services::movements_service::UpdateMovementQuery;
|
||||
use crate::services::{accounts_service, movements_service};
|
||||
use crate::services::{accounts_service, files_service, movements_service};
|
||||
use crate::utils::time_utils::{format_date, time};
|
||||
use actix_web::HttpResponse;
|
||||
use actix_files::NamedFile;
|
||||
use actix_web::{HttpRequest, HttpResponse};
|
||||
use serde::Serialize;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use zip::ZipWriter;
|
||||
use zip::write::SimpleFileOptions;
|
||||
|
||||
/// Generate export filename
|
||||
fn export_filename(ext: &str) -> String {
|
||||
format!(
|
||||
"export_{}.{ext}",
|
||||
format_date(time() as i64).unwrap().replace('/', "-")
|
||||
)
|
||||
}
|
||||
|
||||
/// Import data from a [FinancesManager](https://gitlab.com/pierre42100/cpp-financesmanager) file
|
||||
pub async fn finances_manager_import(auth: AuthExtractor, file: FileExtractor) -> HttpResult {
|
||||
@ -69,10 +85,71 @@ pub async fn finances_manager_export(auth: AuthExtractor) -> HttpResult {
|
||||
Ok(HttpResponse::Ok()
|
||||
.insert_header((
|
||||
"Content-Disposition",
|
||||
format!(
|
||||
"attachment; filename=export_{}.finance",
|
||||
format_date(time() as i64)?.replace('/', "-")
|
||||
),
|
||||
format!("attachment; filename={}", export_filename("finance")),
|
||||
))
|
||||
.body(out.encode()))
|
||||
}
|
||||
|
||||
/// Add JSON file to ZIP
|
||||
fn zip_json<E: Serialize>(
|
||||
zip: &mut ZipWriter<File>,
|
||||
path: &str,
|
||||
content: &E,
|
||||
) -> anyhow::Result<()> {
|
||||
let file_encoded = serde_json::to_string(&content)?;
|
||||
|
||||
let options = SimpleFileOptions::default()
|
||||
.compression_method(zip::CompressionMethod::Deflated)
|
||||
.unix_permissions(0o750);
|
||||
|
||||
zip.start_file(path, options)?;
|
||||
zip.write_all(file_encoded.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Export all user data as ZIP
|
||||
pub async fn zip_export(req: HttpRequest, auth: AuthExtractor) -> HttpResult {
|
||||
let dest_dir = tempfile::tempdir_in(&AppConfig::get().temp_dir)?;
|
||||
let zip_path = dest_dir.path().join("export.zip");
|
||||
|
||||
let file = File::create(&zip_path)?;
|
||||
let mut zip = ZipWriter::new(file);
|
||||
|
||||
// Process all JSON documents
|
||||
zip_json(
|
||||
&mut zip,
|
||||
constants::zip_export::ACCOUNTS_FILE,
|
||||
&accounts_service::get_list_user(auth.user_id()).await?,
|
||||
)?;
|
||||
zip_json(
|
||||
&mut zip,
|
||||
constants::zip_export::MOVEMENTS_FILE,
|
||||
&movements_service::get_all_movements_user(auth.user_id()).await?,
|
||||
)?;
|
||||
let files_list = files_service::get_all_files_user(auth.user_id()).await?;
|
||||
zip_json(&mut zip, constants::zip_export::FILES_FILE, &files_list)?;
|
||||
// TODO : inbox
|
||||
|
||||
// Process all files
|
||||
for file in files_list {
|
||||
let buff = files_service::get_file_content(&file).await?;
|
||||
|
||||
let options = SimpleFileOptions::default()
|
||||
.compression_method(zip::CompressionMethod::Deflated)
|
||||
.unix_permissions(0o750);
|
||||
|
||||
zip.start_file(
|
||||
format!("{}{}", constants::zip_export::FILES_DIR, file.sha512),
|
||||
options,
|
||||
)?;
|
||||
zip.write_all(&buff)?;
|
||||
}
|
||||
|
||||
// Finalize ZIP and return response
|
||||
zip.finish()?;
|
||||
let file = File::open(zip_path)?;
|
||||
|
||||
let file = NamedFile::from_file(file, export_filename("zip"))?;
|
||||
|
||||
Ok(file.into_response(&req))
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{HttpResponse, ResponseError};
|
||||
use std::error::Error;
|
||||
use zip::result::ZipError;
|
||||
|
||||
pub mod accounts_controller;
|
||||
pub mod auth_controller;
|
||||
@ -30,6 +31,10 @@ pub enum HttpFailure {
|
||||
InternalError(#[from] anyhow::Error),
|
||||
#[error("a serde_json error occurred: {0}")]
|
||||
SerdeJsonError(#[from] serde_json::error::Error),
|
||||
#[error("a zip manipulation error occurred: {0}")]
|
||||
ZipError(#[from] ZipError),
|
||||
#[error("a standard I/O manipulation error occurred: {0}")]
|
||||
StdIoError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
impl ResponseError for HttpFailure {
|
||||
|
@ -170,6 +170,10 @@ async fn main() -> std::io::Result<()> {
|
||||
"/api/backup/finances_manager/export",
|
||||
web::get().to(backup_controller::finances_manager_export),
|
||||
)
|
||||
.route(
|
||||
"/api/backup/zip/export",
|
||||
web::get().to(backup_controller::zip_export),
|
||||
)
|
||||
// Static assets
|
||||
.route("/", web::get().to(static_controller::root_index))
|
||||
.route(
|
||||
|
Reference in New Issue
Block a user