Add Rust storage engine foundation
This commit is contained in:
623
myfsio-engine/crates/myfsio-server/src/handlers/config.rs
Normal file
623
myfsio-engine/crates/myfsio-server/src/handlers/config.rs
Normal file
@@ -0,0 +1,623 @@
|
||||
use axum::body::Body;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{IntoResponse, Response};
|
||||
|
||||
use myfsio_common::error::{S3Error, S3ErrorCode};
|
||||
use myfsio_storage::traits::StorageEngine;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
fn xml_response(status: StatusCode, xml: String) -> Response {
|
||||
(status, [("content-type", "application/xml")], xml).into_response()
|
||||
}
|
||||
|
||||
fn storage_err(err: myfsio_storage::error::StorageError) -> Response {
|
||||
let s3err = S3Error::from(err);
|
||||
let status = StatusCode::from_u16(s3err.http_status()).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
(status, [("content-type", "application/xml")], s3err.to_xml()).into_response()
|
||||
}
|
||||
|
||||
pub async fn get_versioning(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.is_versioning_enabled(bucket).await {
|
||||
Ok(enabled) => {
|
||||
let status_str = if enabled { "Enabled" } else { "Suspended" };
|
||||
let xml = format!(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<VersioningConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">\
|
||||
<Status>{}</Status>\
|
||||
</VersioningConfiguration>",
|
||||
status_str
|
||||
);
|
||||
xml_response(StatusCode::OK, xml)
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_versioning(state: &AppState, bucket: &str, body: Body) -> Response {
|
||||
let body_bytes = match http_body_util::BodyExt::collect(body).await {
|
||||
Ok(collected) => collected.to_bytes(),
|
||||
Err(_) => {
|
||||
return xml_response(
|
||||
StatusCode::BAD_REQUEST,
|
||||
S3Error::from_code(S3ErrorCode::MalformedXML).to_xml(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let xml_str = String::from_utf8_lossy(&body_bytes);
|
||||
let enabled = xml_str.contains("<Status>Enabled</Status>");
|
||||
|
||||
match state.storage.set_versioning(bucket, enabled).await {
|
||||
Ok(()) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_tagging(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(config) => {
|
||||
let mut xml = String::from(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<Tagging xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><TagSet>"
|
||||
);
|
||||
for tag in &config.tags {
|
||||
xml.push_str(&format!(
|
||||
"<Tag><Key>{}</Key><Value>{}</Value></Tag>",
|
||||
tag.key, tag.value
|
||||
));
|
||||
}
|
||||
xml.push_str("</TagSet></Tagging>");
|
||||
xml_response(StatusCode::OK, xml)
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_tagging(state: &AppState, bucket: &str, body: Body) -> Response {
|
||||
let body_bytes = match http_body_util::BodyExt::collect(body).await {
|
||||
Ok(collected) => collected.to_bytes(),
|
||||
Err(_) => {
|
||||
return xml_response(
|
||||
StatusCode::BAD_REQUEST,
|
||||
S3Error::from_code(S3ErrorCode::MalformedXML).to_xml(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let xml_str = String::from_utf8_lossy(&body_bytes);
|
||||
let tags = parse_tagging_xml(&xml_str);
|
||||
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.tags = tags;
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_tagging(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.tags.clear();
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::NO_CONTENT.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_cors(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(config) => {
|
||||
if let Some(cors) = &config.cors {
|
||||
xml_response(StatusCode::OK, cors.to_string())
|
||||
} else {
|
||||
xml_response(
|
||||
StatusCode::NOT_FOUND,
|
||||
S3Error::new(S3ErrorCode::NoSuchKey, "The CORS configuration does not exist").to_xml(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_cors(state: &AppState, bucket: &str, body: Body) -> Response {
|
||||
let body_bytes = match http_body_util::BodyExt::collect(body).await {
|
||||
Ok(collected) => collected.to_bytes(),
|
||||
Err(_) => return StatusCode::BAD_REQUEST.into_response(),
|
||||
};
|
||||
|
||||
let body_str = String::from_utf8_lossy(&body_bytes);
|
||||
let value = serde_json::Value::String(body_str.to_string());
|
||||
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.cors = Some(value);
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_cors(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.cors = None;
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::NO_CONTENT.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_location(state: &AppState, _bucket: &str) -> Response {
|
||||
let xml = format!(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<LocationConstraint xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">{}</LocationConstraint>",
|
||||
state.config.region
|
||||
);
|
||||
xml_response(StatusCode::OK, xml)
|
||||
}
|
||||
|
||||
pub async fn get_encryption(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(config) => {
|
||||
if let Some(enc) = &config.encryption {
|
||||
xml_response(StatusCode::OK, enc.to_string())
|
||||
} else {
|
||||
xml_response(
|
||||
StatusCode::NOT_FOUND,
|
||||
S3Error::new(
|
||||
S3ErrorCode::InvalidRequest,
|
||||
"The server side encryption configuration was not found",
|
||||
).to_xml(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_encryption(state: &AppState, bucket: &str, body: Body) -> Response {
|
||||
let body_bytes = match http_body_util::BodyExt::collect(body).await {
|
||||
Ok(collected) => collected.to_bytes(),
|
||||
Err(_) => return StatusCode::BAD_REQUEST.into_response(),
|
||||
};
|
||||
let value = serde_json::Value::String(String::from_utf8_lossy(&body_bytes).to_string());
|
||||
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.encryption = Some(value);
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_encryption(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.encryption = None;
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::NO_CONTENT.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_lifecycle(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(config) => {
|
||||
if let Some(lc) = &config.lifecycle {
|
||||
xml_response(StatusCode::OK, lc.to_string())
|
||||
} else {
|
||||
xml_response(
|
||||
StatusCode::NOT_FOUND,
|
||||
S3Error::new(S3ErrorCode::NoSuchKey, "The lifecycle configuration does not exist").to_xml(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_lifecycle(state: &AppState, bucket: &str, body: Body) -> Response {
|
||||
let body_bytes = match http_body_util::BodyExt::collect(body).await {
|
||||
Ok(collected) => collected.to_bytes(),
|
||||
Err(_) => return StatusCode::BAD_REQUEST.into_response(),
|
||||
};
|
||||
let value = serde_json::Value::String(String::from_utf8_lossy(&body_bytes).to_string());
|
||||
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.lifecycle = Some(value);
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_lifecycle(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.lifecycle = None;
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::NO_CONTENT.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_acl(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(config) => {
|
||||
if let Some(acl) = &config.acl {
|
||||
xml_response(StatusCode::OK, acl.to_string())
|
||||
} else {
|
||||
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<AccessControlPolicy xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">\
|
||||
<Owner><ID>myfsio</ID><DisplayName>myfsio</DisplayName></Owner>\
|
||||
<AccessControlList>\
|
||||
<Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\">\
|
||||
<ID>myfsio</ID><DisplayName>myfsio</DisplayName></Grantee>\
|
||||
<Permission>FULL_CONTROL</Permission></Grant>\
|
||||
</AccessControlList></AccessControlPolicy>";
|
||||
xml_response(StatusCode::OK, xml.to_string())
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_acl(state: &AppState, bucket: &str, body: Body) -> Response {
|
||||
let body_bytes = match http_body_util::BodyExt::collect(body).await {
|
||||
Ok(collected) => collected.to_bytes(),
|
||||
Err(_) => return StatusCode::BAD_REQUEST.into_response(),
|
||||
};
|
||||
let value = serde_json::Value::String(String::from_utf8_lossy(&body_bytes).to_string());
|
||||
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.acl = Some(value);
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_website(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(config) => {
|
||||
if let Some(ws) = &config.website {
|
||||
xml_response(StatusCode::OK, ws.to_string())
|
||||
} else {
|
||||
xml_response(
|
||||
StatusCode::NOT_FOUND,
|
||||
S3Error::new(S3ErrorCode::NoSuchKey, "The website configuration does not exist").to_xml(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_website(state: &AppState, bucket: &str, body: Body) -> Response {
|
||||
let body_bytes = match http_body_util::BodyExt::collect(body).await {
|
||||
Ok(collected) => collected.to_bytes(),
|
||||
Err(_) => return StatusCode::BAD_REQUEST.into_response(),
|
||||
};
|
||||
let value = serde_json::Value::String(String::from_utf8_lossy(&body_bytes).to_string());
|
||||
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.website = Some(value);
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_website(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(mut config) => {
|
||||
config.website = None;
|
||||
match state.storage.set_bucket_config(bucket, &config).await {
|
||||
Ok(()) => StatusCode::NO_CONTENT.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_object_lock(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(config) => {
|
||||
if let Some(ol) = &config.object_lock {
|
||||
xml_response(StatusCode::OK, ol.to_string())
|
||||
} else {
|
||||
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<ObjectLockConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">\
|
||||
<ObjectLockEnabled>Disabled</ObjectLockEnabled>\
|
||||
</ObjectLockConfiguration>";
|
||||
xml_response(StatusCode::OK, xml.to_string())
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_notification(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(config) => {
|
||||
if let Some(n) = &config.notification {
|
||||
xml_response(StatusCode::OK, n.to_string())
|
||||
} else {
|
||||
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<NotificationConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">\
|
||||
</NotificationConfiguration>";
|
||||
xml_response(StatusCode::OK, xml.to_string())
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_logging(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.get_bucket_config(bucket).await {
|
||||
Ok(config) => {
|
||||
if let Some(l) = &config.logging {
|
||||
xml_response(StatusCode::OK, l.to_string())
|
||||
} else {
|
||||
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<BucketLoggingStatus xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">\
|
||||
</BucketLoggingStatus>";
|
||||
xml_response(StatusCode::OK, xml.to_string())
|
||||
}
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_object_versions(state: &AppState, bucket: &str) -> Response {
|
||||
match state.storage.list_buckets().await {
|
||||
Ok(buckets) => {
|
||||
if !buckets.iter().any(|b| b.name == bucket) {
|
||||
return storage_err(myfsio_storage::error::StorageError::BucketNotFound(
|
||||
bucket.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => return storage_err(e),
|
||||
}
|
||||
|
||||
let params = myfsio_common::types::ListParams {
|
||||
max_keys: 1000,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let objects = match state.storage.list_objects(bucket, ¶ms).await {
|
||||
Ok(result) => result.objects,
|
||||
Err(e) => return storage_err(e),
|
||||
};
|
||||
|
||||
let mut xml = String::from(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<ListVersionsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"
|
||||
);
|
||||
xml.push_str(&format!("<Name>{}</Name>", bucket));
|
||||
|
||||
for obj in &objects {
|
||||
xml.push_str("<Version>");
|
||||
xml.push_str(&format!("<Key>{}</Key>", obj.key));
|
||||
xml.push_str("<VersionId>null</VersionId>");
|
||||
xml.push_str("<IsLatest>true</IsLatest>");
|
||||
xml.push_str(&format!(
|
||||
"<LastModified>{}</LastModified>",
|
||||
obj.last_modified.to_rfc3339()
|
||||
));
|
||||
if let Some(ref etag) = obj.etag {
|
||||
xml.push_str(&format!("<ETag>\"{}\"</ETag>", etag));
|
||||
}
|
||||
xml.push_str(&format!("<Size>{}</Size>", obj.size));
|
||||
xml.push_str("<StorageClass>STANDARD</StorageClass>");
|
||||
xml.push_str("</Version>");
|
||||
}
|
||||
|
||||
xml.push_str("</ListVersionsResult>");
|
||||
xml_response(StatusCode::OK, xml)
|
||||
}
|
||||
|
||||
pub async fn get_object_tagging(state: &AppState, bucket: &str, key: &str) -> Response {
|
||||
match state.storage.get_object_tags(bucket, key).await {
|
||||
Ok(tags) => {
|
||||
let mut xml = String::from(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<Tagging xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><TagSet>"
|
||||
);
|
||||
for tag in &tags {
|
||||
xml.push_str(&format!(
|
||||
"<Tag><Key>{}</Key><Value>{}</Value></Tag>",
|
||||
tag.key, tag.value
|
||||
));
|
||||
}
|
||||
xml.push_str("</TagSet></Tagging>");
|
||||
xml_response(StatusCode::OK, xml)
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_object_tagging(state: &AppState, bucket: &str, key: &str, body: Body) -> Response {
|
||||
let body_bytes = match http_body_util::BodyExt::collect(body).await {
|
||||
Ok(collected) => collected.to_bytes(),
|
||||
Err(_) => {
|
||||
return xml_response(
|
||||
StatusCode::BAD_REQUEST,
|
||||
S3Error::from_code(S3ErrorCode::MalformedXML).to_xml(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let xml_str = String::from_utf8_lossy(&body_bytes);
|
||||
let tags = parse_tagging_xml(&xml_str);
|
||||
|
||||
match state.storage.set_object_tags(bucket, key, &tags).await {
|
||||
Ok(()) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_object_tagging(state: &AppState, bucket: &str, key: &str) -> Response {
|
||||
match state.storage.delete_object_tags(bucket, key).await {
|
||||
Ok(()) => StatusCode::NO_CONTENT.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_object_acl(state: &AppState, bucket: &str, key: &str) -> Response {
|
||||
match state.storage.head_object(bucket, key).await {
|
||||
Ok(_) => {
|
||||
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<AccessControlPolicy xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">\
|
||||
<Owner><ID>myfsio</ID><DisplayName>myfsio</DisplayName></Owner>\
|
||||
<AccessControlList>\
|
||||
<Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\">\
|
||||
<ID>myfsio</ID><DisplayName>myfsio</DisplayName></Grantee>\
|
||||
<Permission>FULL_CONTROL</Permission></Grant>\
|
||||
</AccessControlList></AccessControlPolicy>";
|
||||
xml_response(StatusCode::OK, xml.to_string())
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_object_acl(state: &AppState, bucket: &str, key: &str, _body: Body) -> Response {
|
||||
match state.storage.head_object(bucket, key).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_object_retention(state: &AppState, bucket: &str, key: &str) -> Response {
|
||||
match state.storage.head_object(bucket, key).await {
|
||||
Ok(_) => {
|
||||
xml_response(
|
||||
StatusCode::NOT_FOUND,
|
||||
S3Error::new(
|
||||
S3ErrorCode::InvalidRequest,
|
||||
"No retention policy configured",
|
||||
).to_xml(),
|
||||
)
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_object_retention(state: &AppState, bucket: &str, key: &str, _body: Body) -> Response {
|
||||
match state.storage.head_object(bucket, key).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_object_legal_hold(state: &AppState, bucket: &str, key: &str) -> Response {
|
||||
match state.storage.head_object(bucket, key).await {
|
||||
Ok(_) => {
|
||||
let xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
|
||||
<LegalHold xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">\
|
||||
<Status>OFF</Status></LegalHold>";
|
||||
xml_response(StatusCode::OK, xml.to_string())
|
||||
}
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn put_object_legal_hold(state: &AppState, bucket: &str, key: &str, _body: Body) -> Response {
|
||||
match state.storage.head_object(bucket, key).await {
|
||||
Ok(_) => StatusCode::OK.into_response(),
|
||||
Err(e) => storage_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_tagging_xml(xml: &str) -> Vec<myfsio_common::types::Tag> {
|
||||
let mut tags = Vec::new();
|
||||
let mut in_tag = false;
|
||||
let mut current_key = String::new();
|
||||
let mut current_value = String::new();
|
||||
let mut current_element = String::new();
|
||||
|
||||
let mut reader = quick_xml::Reader::from_str(xml);
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match reader.read_event_into(&mut buf) {
|
||||
Ok(quick_xml::events::Event::Start(ref e)) => {
|
||||
let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
|
||||
current_element = name.clone();
|
||||
if name == "Tag" {
|
||||
in_tag = true;
|
||||
current_key.clear();
|
||||
current_value.clear();
|
||||
}
|
||||
}
|
||||
Ok(quick_xml::events::Event::Text(ref e)) => {
|
||||
if in_tag {
|
||||
let text = e.unescape().unwrap_or_default().to_string();
|
||||
match current_element.as_str() {
|
||||
"Key" => current_key = text,
|
||||
"Value" => current_value = text,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(quick_xml::events::Event::End(ref e)) => {
|
||||
let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
|
||||
if name == "Tag" && in_tag {
|
||||
if !current_key.is_empty() {
|
||||
tags.push(myfsio_common::types::Tag {
|
||||
key: current_key.clone(),
|
||||
value: current_value.clone(),
|
||||
});
|
||||
}
|
||||
in_tag = false;
|
||||
}
|
||||
}
|
||||
Ok(quick_xml::events::Event::Eof) => break,
|
||||
Err(_) => break,
|
||||
_ => {}
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
tags
|
||||
}
|
||||
Reference in New Issue
Block a user