Files
Stalwart/crates/jmap/src/blob/upload.rs
2023-06-06 16:53:29 +00:00

101 lines
3.3 KiB
Rust

/*
* Copyright (c) 2023 Stalwart Labs Ltd.
*
* This file is part of the Stalwart JMAP Server.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* in the LICENSE file at the top-level directory of this distribution.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* You can be released from the requirements of the AGPLv3 license by
* purchasing a commercial license. Please contact licensing@stalw.art
* for more details.
*/
use std::sync::Arc;
use jmap_proto::{
error::{method::MethodError, request::RequestError},
types::{blob::BlobId, id::Id},
};
use store::BlobKind;
use crate::{auth::AccessToken, JMAP};
use super::UploadResponse;
impl JMAP {
pub async fn blob_upload(
&self,
account_id: Id,
content_type: &str,
data: &[u8],
access_token: Arc<AccessToken>,
) -> Result<UploadResponse, RequestError> {
// Limit concurrent uploads
let _in_flight = self.is_upload_allowed(&access_token)?;
#[cfg(feature = "test_mode")]
{
// Used for concurrent upload tests
if data == b"sleep" {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
}
let blob_id = BlobId::temporary(account_id.document_id());
match self.store.put_blob(&blob_id.kind, data).await {
Ok(_) => Ok(UploadResponse {
account_id,
blob_id,
c_type: content_type.to_string(),
size: data.len(),
}),
Err(err) => {
tracing::error!(event = "error",
context = "blob_store",
account_id = account_id.document_id(),
blob_id = ?blob_id,
size = data.len(),
error = ?err,
"Failed to upload blob");
Err(RequestError::internal_server_error())
}
}
}
pub async fn put_blob(&self, kind: &BlobKind, data: &[u8]) -> Result<(), MethodError> {
self.store.put_blob(kind, data).await.map_err(|err| {
tracing::error!(
event = "error",
context = "blob_put",
kind = ?kind,
error = ?err,
"Failed to store blob.");
MethodError::ServerPartialFail
})
}
pub async fn delete_blob(&self, kind: &BlobKind) -> Result<bool, MethodError> {
self.store.delete_blob(kind).await.map_err(|err| {
tracing::error!(
event = "error",
context = "delete_blob",
kind = ?kind,
error = ?err,
"Failed to delete blob.");
MethodError::ServerPartialFail
})
}
}