refactor upload endpoint

This commit is contained in:
neri 2022-11-22 21:32:04 +01:00
parent b9849153f0
commit ca56e501a2
1 changed files with 53 additions and 48 deletions

View File

@ -36,86 +36,82 @@ pub async fn upload(
expiry_watch_sender: web::Data<Sender<()>>, expiry_watch_sender: web::Data<Sender<()>>,
config: web::Data<Config>, config: web::Data<Config>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let (file_id, file_name) = create_unique_file(&config).await.map_err(|file_err| { let (file_id, file_path) = create_unique_file(&config).await.map_err(|file_err| {
log::error!("could not create file {:?}", file_err); log::error!("could not create file {:?}", file_err);
error::ErrorInternalServerError("could not create file") error::ErrorInternalServerError("could not create file")
})?; })?;
let UploadConfig { let upload_config = multipart::parse_multipart(payload, &file_path, &config).await?;
original_name, let file_name = upload_config.original_name.clone().unwrap_or_else(|| {
content_type,
valid_till,
delete_on_download,
} = multipart::parse_multipart(payload, &file_name, &config).await?;
let file_name = original_name.clone().unwrap_or_else(|| {
format!( format!(
"{file_id}.{}", "{file_id}.{}",
mime_relations::get_extension(&content_type).unwrap_or("txt") mime_relations::get_extension(&upload_config.content_type).unwrap_or("txt")
) )
}); });
let db_insert = sqlx::query(
"INSERT INTO Files (file_id, file_name, content_type, valid_till, delete_on_download) \ insert_file_metadata(&file_id, file_name, &upload_config, db).await?;
VALUES ($1, $2, $3, $4, $5)",
)
.bind(&file_id)
.bind(&file_name)
.bind(&content_type.to_string())
.bind(valid_till)
.bind(delete_on_download)
.execute(db.as_ref())
.await;
if let Err(db_err) = db_insert {
log::error!("could not insert into datebase {:?}", db_err);
fs::remove_file(file_name).await.map_err(|file_err| {
log::error!("could not remove file {:?}", file_err);
error::ErrorInternalServerError(
"could not insert file into database; could not remove file",
)
})?;
return Err(error::ErrorInternalServerError(
"could not insert file into database",
));
}
log::info!( log::info!(
"{} create new file {} (valid_till: {}, content_type: {}, delete_on_download: {})", "{} create new file {} (valid_till: {}, content_type: {}, delete_on_download: {})",
req.connection_info().realip_remote_addr().unwrap_or("-"), req.connection_info().realip_remote_addr().unwrap_or("-"),
file_id, file_id,
valid_till, upload_config.content_type,
content_type, upload_config.delete_on_download,
delete_on_download upload_config.valid_till
); );
expiry_watch_sender.send(()).await.unwrap(); expiry_watch_sender.send(()).await.unwrap();
let redirect = if let Some(original_name) = original_name.as_ref() { let redirect = get_redirect_url(&file_id, upload_config.original_name.as_deref());
let encoded_name = urlencoding::encode(original_name); let url = get_file_url(&req, &file_id, upload_config.original_name.as_deref());
format!("/upload/{file_id}/{encoded_name}")
} else {
format!("/upload/{file_id}")
};
let url = get_file_url(&req, &file_id, original_name.as_deref());
Ok(HttpResponse::SeeOther() Ok(HttpResponse::SeeOther()
.insert_header((LOCATION, redirect)) .insert_header((LOCATION, redirect))
.body(format!("{url}\n"))) .body(format!("{url}\n")))
} }
async fn insert_file_metadata(
file_id: &String,
file_name: String,
upload_config: &UploadConfig,
db: web::Data<sqlx::Pool<sqlx::Postgres>>,
) -> Result<(), Error> {
let db_insert = sqlx::query(
"INSERT INTO Files (file_id, file_name, content_type, valid_till, delete_on_download) \
VALUES ($1, $2, $3, $4, $5)",
)
.bind(file_id)
.bind(&file_name)
.bind(&upload_config.content_type.to_string())
.bind(upload_config.valid_till)
.bind(upload_config.delete_on_download)
.execute(db.as_ref())
.await;
if let Err(db_err) = db_insert {
log::error!("could not insert into datebase {:?}", db_err);
if let Err(file_err) = fs::remove_file(file_name).await {
log::error!("could not remove file {:?}", file_err);
}
return Err(error::ErrorInternalServerError(
"could not insert file into database",
));
}
Ok(())
}
async fn create_unique_file( async fn create_unique_file(
config: &web::Data<Config>, config: &web::Data<Config>,
) -> Result<(String, PathBuf), std::io::Error> { ) -> Result<(String, PathBuf), std::io::Error> {
loop { loop {
let file_id = gen_file_id(); let file_id = gen_file_id();
let mut file_name = config.files_dir.clone(); let mut file_path = config.files_dir.clone();
file_name.push(&file_id); file_path.push(&file_id);
match OpenOptions::new() match OpenOptions::new()
.write(true) .write(true)
.create_new(true) .create_new(true)
.open(&file_name) .open(&file_path)
.await .await
{ {
Ok(_) => return Ok((file_id, file_name)), Ok(_) => return Ok((file_id, file_path)),
Err(error) if error.kind() == ErrorKind::AlreadyExists => continue, Err(error) if error.kind() == ErrorKind::AlreadyExists => continue,
Err(error) => return Err(error), Err(error) => return Err(error),
} }
@ -140,6 +136,15 @@ fn get_file_url(req: &HttpRequest, id: &str, name: Option<&str>) -> String {
} }
} }
fn get_redirect_url(id: &str, name: Option<&str>) -> String {
if let Some(name) = name {
let encoded_name = urlencoding::encode(name);
format!("/upload/{id}/{encoded_name}")
} else {
format!("/upload/{id}")
}
}
pub async fn uploaded( pub async fn uploaded(
req: HttpRequest, req: HttpRequest,
path: web::Path<(String, Option<String>)>, path: web::Path<(String, Option<String>)>,