Can export family data

This commit is contained in:
Pierre HUBERT 2023-08-17 17:37:44 +02:00
parent 680c2d2624
commit 3b5efb46cd
7 changed files with 224 additions and 6 deletions

View File

@ -1,6 +1,5 @@
import { Link } from "react-router-dom";
import { RouterLink } from "../widgets/RouterLink";
import { Button } from "@mui/material";
import { RouterLink } from "../widgets/RouterLink";
export function NotFoundRoute(): React.ReactElement {
return (

View File

@ -70,7 +70,7 @@ dependencies = [
"tokio",
"tokio-util",
"tracing",
"zstd",
"zstd 0.12.3+zstd.1.5.2",
]
[[package]]
@ -262,6 +262,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aes"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "ahash"
version = "0.7.6"
@ -469,6 +480,12 @@ version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bcrypt"
version = "0.15.0"
@ -573,6 +590,27 @@ dependencies = [
"bytes",
]
[[package]]
name = "bzip2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2-sys"
version = "0.1.11+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "cc"
version = "1.0.79"
@ -674,6 +712,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "convert_case"
version = "0.4.0"
@ -1257,6 +1301,7 @@ dependencies = [
"light-openid",
"log",
"mailchecker",
"mime_guess",
"rand",
"redis",
"rust-s3",
@ -1267,6 +1312,7 @@ dependencies = [
"sha2",
"thiserror",
"uuid",
"zip",
]
[[package]]
@ -1779,6 +1825,16 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minidom"
version = "0.15.2"
@ -1991,12 +2047,35 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "944553dd59c802559559161f9816429058b869003836120e262e8caec061b7ae"
[[package]]
name = "password-hash"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "paste"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35"
[[package]]
name = "pbkdf2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
dependencies = [
"digest",
"hmac",
"password-hash",
"sha2",
]
[[package]]
name = "percent-encoding"
version = "2.3.0"
@ -2902,6 +2981,15 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.13"
@ -3204,13 +3292,52 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
[[package]]
name = "zip"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"aes",
"byteorder",
"bzip2",
"constant_time_eq",
"crc32fast",
"crossbeam-utils",
"flate2",
"hmac",
"pbkdf2",
"sha1",
"time",
"zstd 0.11.2+zstd.1.5.2",
]
[[package]]
name = "zstd"
version = "0.11.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
dependencies = [
"zstd-safe 5.0.2+zstd.1.5.2",
]
[[package]]
name = "zstd"
version = "0.12.3+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806"
dependencies = [
"zstd-safe",
"zstd-safe 6.0.5+zstd.1.5.4",
]
[[package]]
name = "zstd-safe"
version = "5.0.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
dependencies = [
"libc",
"zstd-sys",
]
[[package]]

View File

@ -32,4 +32,6 @@ rust-s3 = "0.33.0"
sha2 = "0.10.7"
image = "0.24.6"
uuid = { version = "1.4.1", features = ["v4"] }
httpdate = "1.0.2"
httpdate = "1.0.2"
zip = "0.6.6"
mime_guess = "2.0.4"

View File

@ -0,0 +1,58 @@
use crate::connections::s3_connection;
use crate::controllers::HttpResult;
use crate::extractors::family_extractor::FamilyInPath;
use crate::services::{couples_service, members_service, photos_service};
use actix_web::HttpResponse;
use std::io::{Cursor, Write};
use zip::write::FileOptions;
use zip::CompressionMethod;
const MEMBERS_FILE: &str = "members.json";
const COUPLES_FILE: &str = "couples.json";
/// Export whole family data
pub async fn export_family(f: FamilyInPath) -> HttpResult {
let files_opt = FileOptions::default().compression_method(CompressionMethod::Bzip2);
let members = members_service::get_all_of_family(f.family_id()).await?;
let couples = couples_service::get_all_of_family(f.family_id()).await?;
let buff = Vec::with_capacity(1000000);
let mut zip_file = zip::ZipWriter::new(Cursor::new(buff));
// Add main files
zip_file.start_file(MEMBERS_FILE, files_opt)?;
zip_file.write_all(serde_json::to_string(&members)?.as_bytes())?;
zip_file.start_file(COUPLES_FILE, files_opt)?;
zip_file.write_all(serde_json::to_string(&couples)?.as_bytes())?;
// Add photos
let mut photos = Vec::new();
for member in &members {
if let Some(id) = member.photo_id() {
photos.push(id);
}
}
for couple in &couples {
if let Some(id) = couple.photo_id() {
photos.push(id);
}
}
for id in photos {
let photo = photos_service::get_by_id(id).await?;
let ext = photo.mime_extension().unwrap_or("bad");
let file = s3_connection::get_file(&photo.photo_path()).await?;
zip_file.start_file(format!("photos/{}.{ext}", id.0), files_opt)?;
zip_file.write_all(&file)?;
}
let buff = zip_file.finish()?.into_inner();
Ok(HttpResponse::Ok()
.content_type("application/zip")
.body(buff))
}

View File

@ -3,9 +3,11 @@
use actix_web::body::BoxBody;
use actix_web::HttpResponse;
use std::fmt::{Debug, Display, Formatter};
use zip::result::ZipError;
pub mod auth_controller;
pub mod couples_controller;
pub mod data_controller;
pub mod families_controller;
pub mod members_controller;
pub mod photos_controller;
@ -37,4 +39,22 @@ impl From<anyhow::Error> for HttpErr {
}
}
impl From<ZipError> for HttpErr {
fn from(value: ZipError) -> Self {
HttpErr { err: value.into() }
}
}
impl From<serde_json::Error> for HttpErr {
fn from(value: serde_json::Error) -> Self {
HttpErr { err: value.into() }
}
}
impl From<std::io::Error> for HttpErr {
fn from(value: std::io::Error) -> Self {
HttpErr { err: value.into() }
}
}
pub type HttpResult = Result<HttpResponse, HttpErr>;

View File

@ -6,7 +6,7 @@ use actix_web::{web, App, HttpServer};
use geneit_backend::app_config::AppConfig;
use geneit_backend::connections::s3_connection;
use geneit_backend::controllers::{
auth_controller, couples_controller, families_controller, members_controller,
auth_controller, couples_controller, data_controller, families_controller, members_controller,
photos_controller, server_controller, users_controller,
};
@ -191,6 +191,11 @@ async fn main() -> std::io::Result<()> {
"/family/{id}/couple/{couple_id}/photo",
web::delete().to(couples_controller::remove_photo),
)
// Data controller
.route(
"/family/{id}/data/export",
web::get().to(data_controller::export_family),
)
// Photos controller
.route(
"/photo/{id}",

View File

@ -168,6 +168,13 @@ impl Photo {
pub fn thumbnail_path(&self) -> String {
format!("thumbnail/{}", self.file_id)
}
pub fn mime_extension(&self) -> Option<&str> {
mime_guess::get_mime_extensions_str(&self.mime_type)
.map(|e| e.first())
.unwrap_or_default()
.copied()
}
}
#[derive(Insertable)]