diff --git a/backend/windmill-api/src/job_helpers_oss.rs b/backend/windmill-api/src/job_helpers_oss.rs index 2f00462075..7a4fb7643a 100644 --- a/backend/windmill-api/src/job_helpers_oss.rs +++ b/backend/windmill-api/src/job_helpers_oss.rs @@ -144,3 +144,24 @@ pub async fn read_object_streamable( "Not implemented in Windmill's Open Source repository".to_string(), )) } + +#[cfg(not(feature = "private"))] +pub async fn delete_s3_file_internal( + authed: &ApiAuthed, + db: &DB, + UserDB: Option, + token: &str, + w_id: &str, + query: DeleteS3FileQuery, +) -> error::Result<()> { + Err(error::Error::internal_err( + "Not implemented in Windmill's Open Source repository".to_string(), + )) +} + +#[cfg(not(feature = "private"))] +#[derive(Deserialize)] +pub struct DeleteS3FileQuery { + pub file_key: String, + pub storage: Option, +} diff --git a/backend/windmill-api/src/s3_proxy.rs b/backend/windmill-api/src/s3_proxy.rs index 2894cc8ddf..7ceb41cac3 100644 --- a/backend/windmill-api/src/s3_proxy.rs +++ b/backend/windmill-api/src/s3_proxy.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use axum::{ extract::{Path, Request}, response::{IntoResponse, Response}, - routing::{get, put}, + routing::{delete, get, put}, Extension, Router, }; use object_store::PutMultipartOpts; @@ -16,7 +16,11 @@ use windmill_common::{ use crate::{ auth::AuthCache, db::DB, - job_helpers_ee::{get_workspace_s3_resource, read_object_streamable, upload_file_from_req}, + job_helpers_ee::DeleteS3FileQuery, + job_helpers_oss::{ + delete_s3_file_internal, get_workspace_s3_resource, read_object_streamable, + upload_file_from_req, + }, }; pub fn workspaced_unauthed_service() -> Router { @@ -26,6 +30,8 @@ pub fn workspaced_unauthed_service() -> Router { .route("/:storage/*key", get(get_object)) .route("/s3%3A//:storage/*key", put(put_object)) .route("/:storage/*key", put(put_object)) + .route("/s3%3A//:storage/*key", delete(delete_object)) + .route("/:storage/*key", delete(delete_object)) } async fn get_object( @@ -87,6 +93,30 @@ async fn put_object( .await } +async fn delete_object( + Extension(db): Extension, + Extension(user_db): Extension, + Path((w_id, storage_str, object_key)): Path<(String, String, String)>, + Extension(auth_cache): Extension>, + req: Request, +) -> Result<()> { + let token = get_token(&req)?; + let Some(authed) = auth_cache.get_authed(Some(w_id.clone()), token).await else { + return Err(Error::NotAuthorized("Invalid token".to_string())); + }; + let storage = Some(storage_str.clone()).filter(|s| !s.is_empty()); + + delete_s3_file_internal( + &authed, + &db, + Some(user_db), + token, + &w_id, + DeleteS3FileQuery { file_key: object_key, storage }, + ) + .await +} + fn get_token(req: &Request) -> Result<&str> { get_header(&req, "Authorization") .map_err(|e| Error::InternalErr(format!("Failed to get token: {}", e)))