104 lines
3.3 KiB
Rust
104 lines
3.3 KiB
Rust
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
|
|
use axum::response::{IntoResponse, Response};
|
|
use axum::{Extension, Json};
|
|
use serde::Serialize;
|
|
|
|
use crate::auth::session::SessionState;
|
|
use crate::config::AppConfig;
|
|
use crate::db;
|
|
use crate::error::AppError;
|
|
use crate::imap::client::{ImapClient, ImapCredentials};
|
|
|
|
#[derive(Serialize)]
|
|
struct QuotaResponse {
|
|
usage_bytes: Option<u64>,
|
|
|
|
limit_bytes: Option<u64>,
|
|
}
|
|
|
|
pub async fn get_quota(
|
|
Extension(session): Extension<SessionState>,
|
|
Extension(config): Extension<Arc<AppConfig>>,
|
|
Extension(imap_client): Extension<Arc<dyn ImapClient>>,
|
|
) -> Result<Response, AppError> {
|
|
let creds = ImapCredentials {
|
|
host: session.imap_host.clone(),
|
|
port: session.imap_port,
|
|
tls: session.imap_tls,
|
|
tls_verify: session.imap_tls_verify,
|
|
email: session.email.clone(),
|
|
password: session.password.clone(),
|
|
};
|
|
|
|
let quota = imap_client.get_quota(&creds).await.unwrap_or(None);
|
|
|
|
let resp = match quota {
|
|
Some(q) => QuotaResponse {
|
|
usage_bytes: Some(q.usage_bytes),
|
|
limit_bytes: if q.limit_bytes > 0 {
|
|
Some(q.limit_bytes)
|
|
} else {
|
|
None
|
|
},
|
|
},
|
|
None => {
|
|
let database = db::pool::UserDb::new(&config.data_dir, &session.user_hash);
|
|
if let Some(bytes) = database
|
|
.run(db::messages::cached_storage_bytes)
|
|
.await
|
|
.map_err(AppError::InternalError)?
|
|
{
|
|
return Ok(Json(QuotaResponse {
|
|
usage_bytes: Some(bytes),
|
|
limit_bytes: None,
|
|
})
|
|
.into_response());
|
|
}
|
|
|
|
let data_dir = config.data_dir.clone();
|
|
let user_hash = session.user_hash.clone();
|
|
let folders: Vec<String> = tokio::task::spawn_blocking(move || {
|
|
let conn = db::pool::open_user_db(&data_dir, &user_hash)?;
|
|
Ok::<_, String>(
|
|
db::folders::get_all_folders(&conn)
|
|
.unwrap_or_default()
|
|
.into_iter()
|
|
.map(|f| f.name)
|
|
.collect(),
|
|
)
|
|
})
|
|
.await
|
|
.map_err(|e| AppError::InternalError(format!("Task error: {e}")))?
|
|
.map_err(|e| AppError::InternalError(format!("Database error: {e}")))?;
|
|
|
|
let total = tokio::time::timeout(
|
|
Duration::from_secs(90),
|
|
crate::imap::pool::background(async {
|
|
let mut sum: u64 = 0;
|
|
for folder in &folders {
|
|
sum += imap_client.fetch_folder_size(&creds, folder).await?;
|
|
}
|
|
Ok::<_, crate::imap::error::ImapError>(sum)
|
|
}),
|
|
)
|
|
.await;
|
|
let total = match total {
|
|
Ok(Ok(total)) => Some(total),
|
|
other => {
|
|
tracing::warn!(error = ?other, "Quota folder-size fallback failed");
|
|
None
|
|
}
|
|
};
|
|
|
|
QuotaResponse {
|
|
usage_bytes: total,
|
|
limit_bytes: None,
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok(Json(resp).into_response())
|
|
}
|