use tonic::{Request, Response, Status};
use actix_web::web::Data;
use actix_multipart::form::tempfile::TempFile;
use actix_multipart::form::MultipartForm;
use std::io::Write;
use ant_core::data::Wallet;
use crate::service::tarchive_service::TarchiveService;
use crate::controller::StoreType;
use crate::error::tarchive_error::TarchiveError;
pub mod tarchive_proto {
tonic::include_proto!("tarchive");
}
use tarchive_proto::tarchive_service_server::TarchiveService as TarchiveServiceTrait;
pub use tarchive_proto::tarchive_service_server::TarchiveServiceServer;
use tarchive_proto::{CreateTarchiveRequest, UpdateTarchiveRequest, TruncateTarchiveRequest, TarchiveResponse, File as ProtoFile, GetTarchiveRequest, GetTarchiveResponse, Item, ListTarchiveRequest, ListTarchiveResponse, PushTarchiveRequest};
use crate::service::archive_service::{PublicArchiveForm, Upload};
use crate::service::public_data_service::PublicDataService;
pub struct TarchiveHandler {
tarchive_service: Data<TarchiveService>,
public_data_service: Data<PublicDataService>,
evm_wallet: Data<Wallet>,
}
impl TarchiveHandler {
pub fn new(tarchive_service: Data<TarchiveService>, public_data_service: Data<PublicDataService>, evm_wallet: Data<Wallet>) -> Self {
Self { tarchive_service, public_data_service, evm_wallet }
}
fn map_to_multipart_form(&self, files: Vec<ProtoFile>) -> Result<MultipartForm<PublicArchiveForm>, Status> {
let mut temp_files = Vec::new();
for file in files {
let mut temp_file = tempfile::NamedTempFile::new().map_err(|e|
Status::internal(format!("Failed to create temp file: {}", e))
)?;
temp_file.write_all(&file.content).map_err(|e|
Status::internal(format!("Failed to write to temp file: {}", e))
)?;
temp_files.push(TempFile {
file: temp_file,
file_name: Some(file.name),
content_type: None,
size: file.content.len(),
});
}
Ok(MultipartForm(PublicArchiveForm { files: temp_files }))
}
}
impl From<Upload> for TarchiveResponse {
fn from(upload: Upload) -> Self {
TarchiveResponse {
address: upload.address,
}
}
}
impl From<TarchiveError> for Status {
fn from(error: TarchiveError) -> Self {
Status::internal(error.to_string())
}
}
#[tonic::async_trait]
impl TarchiveServiceTrait for TarchiveHandler {
async fn create_tarchive(
&self,
request: Request<CreateTarchiveRequest>,
) -> Result<Response<TarchiveResponse>, Status> {
let req = request.into_inner();
let tarchive_form = self.map_to_multipart_form(req.files)?;
let result = self.tarchive_service.create_tarchive(
req.path,
tarchive_form,
self.evm_wallet.get_ref().clone(),
StoreType::from(req.store_type.unwrap_or_default())
).await?;
Ok(Response::new(TarchiveResponse::from(result)))
}
async fn update_tarchive(
&self,
request: Request<UpdateTarchiveRequest>,
) -> Result<Response<TarchiveResponse>, Status> {
let req = request.into_inner();
let tarchive_form = self.map_to_multipart_form(req.files)?;
let result = self.tarchive_service.update_tarchive(
req.address,
req.path,
tarchive_form,
self.evm_wallet.get_ref().clone(),
StoreType::from(req.store_type.unwrap_or_default())
).await?;
Ok(Response::new(TarchiveResponse::from(result)))
}
async fn truncate_tarchive(
&self,
request: Request<TruncateTarchiveRequest>,
) -> Result<Response<TarchiveResponse>, Status> {
let req = request.into_inner();
let result = self.tarchive_service.truncate_tarchive(
req.address,
req.path,
self.evm_wallet.get_ref().clone(),
StoreType::from(req.store_type.unwrap_or_default())
).await?;
Ok(Response::new(TarchiveResponse::from(result)))
}
async fn get_tarchive(
&self,
request: Request<GetTarchiveRequest>,
) -> Result<Response<GetTarchiveResponse>, Status> {
let req = request.into_inner();
let result = self.tarchive_service.get_tarchive_binary(req.address, req.path).await?;
let items: Vec<Item> = result.items.into_iter().map(|pd| Item {
name: pd.display,
modified: pd.modified,
size: pd.size,
r#type: format!("{:?}", pd.path_type),
}).collect();
Ok(Response::new(GetTarchiveResponse {
address: Some(result.address),
items,
content: Some(result.content.into()),
}))
}
async fn list_tarchive(
&self,
request: Request<ListTarchiveRequest>,
) -> Result<Response<ListTarchiveResponse>, Status> {
let req = request.into_inner();
let result = self.tarchive_service.get_tarchive(req.address, req.path).await?;
let items: Vec<Item> = result.items.into_iter().map(|pd| Item {
name: pd.display,
modified: pd.modified,
size: pd.size,
r#type: format!("{:?}", pd.path_type),
}).collect();
Ok(Response::new(ListTarchiveResponse {
address: result.address,
items,
}))
}
async fn push_tarchive(
&self,
request: Request<PushTarchiveRequest>,
) -> Result<Response<TarchiveResponse>, Status> {
let req = request.into_inner();
let result = self.public_data_service.push_public_data(
req.address,
StoreType::from(req.store_type.unwrap_or_else(|| "network".to_string()))
).await.map_err(|e| Status::internal(e.to_string()))?;
Ok(Response::new(TarchiveResponse {
address: result.address,
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_mapping() {
let upload = Upload {
address: Some("0x1234".to_string()),
};
let response = TarchiveResponse::from(upload);
assert_eq!(response.address, Some("0x1234".to_string()));
}
}