feat: document storage size

This commit is contained in:
Zack Fu Zi Xiang 2024-03-14 17:32:30 +08:00
parent d7cd101c44
commit 9093363b4f
No known key found for this signature in database
6 changed files with 69 additions and 14 deletions

View File

@ -1,8 +1,8 @@
use database_entity::dto::AFWorkspace; use database_entity::dto::AFWorkspace;
use super::entities::{ use super::entities::{
JsonResponse, UserUsageLimit, WorkspaceBlobUsage, WorkspaceMember, WorkspaceUsage, JsonResponse, UserUsageLimit, WorkspaceBlobUsage, WorkspaceDocUsage, WorkspaceMember,
WorkspaceUsageLimit, WorkspaceUsageLimit, WorkspaceUsageLimits,
}; };
pub async fn get_user_workspace_count(auth_header: &str, appflowy_cloud_base_url: &str) -> u32 { pub async fn get_user_workspace_count(auth_header: &str, appflowy_cloud_base_url: &str) -> u32 {
@ -68,24 +68,35 @@ pub async fn get_user_workspace_usages(
auth_header: &str, auth_header: &str,
appflowy_cloud_base_url: &str, appflowy_cloud_base_url: &str,
appflowy_cloud_gateway_base_url: &str, appflowy_cloud_gateway_base_url: &str,
) -> Vec<WorkspaceUsage> { ) -> Vec<WorkspaceUsageLimits> {
let user_workspaces = get_user_workspaces(auth_header, appflowy_cloud_base_url).await; let user_workspaces = get_user_workspaces(auth_header, appflowy_cloud_base_url).await;
let mut workspace_usages: Vec<WorkspaceUsage> = Vec::with_capacity(user_workspaces.len()); let mut workspace_usages: Vec<WorkspaceUsageLimits> = Vec::with_capacity(user_workspaces.len());
for user_workspace in user_workspaces { for user_workspace in user_workspaces {
let workspace_id = user_workspace.workspace_id.to_string(); let workspace_id = user_workspace.workspace_id.to_string();
let members = let members =
get_user_workspace_members(&workspace_id, auth_header, appflowy_cloud_base_url).await; get_user_workspace_members(&workspace_id, auth_header, appflowy_cloud_base_url).await;
let workspace_limits = let workspace_limits =
get_user_workspace_limits(&workspace_id, auth_header, appflowy_cloud_gateway_base_url).await; get_user_workspace_limits(&workspace_id, auth_header, appflowy_cloud_gateway_base_url).await;
let blob_usage = let total_blob_size =
get_user_workspace_blob_usage(&workspace_id, auth_header, appflowy_cloud_base_url) get_user_workspace_blob_usage(&workspace_id, auth_header, appflowy_cloud_base_url)
.await .await
.map(|u| u.consumed_capacity) .map(|u| human_bytes::human_bytes(u.consumed_capacity as f64))
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
tracing::error!("Error getting user workspace blob usage: {:?}", err); tracing::error!("Error getting user workspace blob usage: {:?}", err);
0 "0".to_owned()
}); });
let total_doc_size = {
get_user_workspace_doc_usage(&workspace_id, auth_header, appflowy_cloud_base_url)
.await
.map(|u| human_bytes::human_bytes(u.total_document_size as f64))
.unwrap_or_else(|err| {
tracing::error!("Error getting user workspace doc usage: {:?}", err);
"0".to_owned()
})
};
let (member_limit, total_blob_limit) = match workspace_limits { let (member_limit, total_blob_limit) = match workspace_limits {
Some(limit) => ( Some(limit) => (
limit.member_count.to_string(), limit.member_count.to_string(),
@ -94,12 +105,12 @@ pub async fn get_user_workspace_usages(
None => ("N/A".to_string(), "N/A".to_string()), None => ("N/A".to_string(), "N/A".to_string()),
}; };
workspace_usages.push(WorkspaceUsage { workspace_usages.push(WorkspaceUsageLimits {
name: user_workspace.workspace_name, name: user_workspace.workspace_name,
member_count: members.len(), member_count: members.len(),
member_limit, member_limit,
total_doc_size: "WIP".to_string(), total_doc_size,
total_blob_size: human_bytes::human_bytes(blob_usage as f64), total_blob_size,
total_blob_limit, total_blob_limit,
}); });
} }
@ -189,3 +200,23 @@ async fn get_user_workspace_blob_usage(
let res = resp.json::<JsonResponse<WorkspaceBlobUsage>>().await?; let res = resp.json::<JsonResponse<WorkspaceBlobUsage>>().await?;
Ok(res.data) Ok(res.data)
} }
async fn get_user_workspace_doc_usage(
workspace_id: &str,
auth_header: &str,
appflowy_cloud_base_url: &str,
) -> Result<WorkspaceDocUsage, reqwest::Error> {
let http_client = reqwest::Client::new();
let url = format!(
"{}/api/workspace/{}/usage",
appflowy_cloud_base_url, workspace_id
);
let resp = http_client
.get(url)
.header("Authorization", format!("Bearer {}", auth_header))
.send()
.await?;
let res = resp.json::<JsonResponse<WorkspaceDocUsage>>().await?;
Ok(res.data)
}

View File

@ -12,7 +12,7 @@ pub struct UserUsageLimit {
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct WorkspaceUsage { pub struct WorkspaceUsageLimits {
pub name: String, pub name: String,
pub member_count: usize, pub member_count: usize,
pub member_limit: String, pub member_limit: String,
@ -39,3 +39,8 @@ pub struct WorkspaceUsageLimit {
pub struct WorkspaceBlobUsage { pub struct WorkspaceBlobUsage {
pub consumed_capacity: u64, pub consumed_capacity: u64,
} }
#[derive(Deserialize)]
pub struct WorkspaceDocUsage {
pub total_document_size: i64,
}

View File

@ -1,7 +1,7 @@
use askama::Template; use askama::Template;
use gotrue_entity::{dto::User, sso::SSOProvider}; use gotrue_entity::{dto::User, sso::SSOProvider};
use crate::ext::entities::WorkspaceUsage; use crate::ext::entities::WorkspaceUsageLimits;
#[derive(Template)] #[derive(Template)]
#[template(path = "components/user_usage.html")] #[template(path = "components/user_usage.html")]
@ -14,7 +14,7 @@ pub struct UserUsage {
#[derive(Template)] #[derive(Template)]
#[template(path = "components/workspace_usage.html")] #[template(path = "components/workspace_usage.html")]
pub struct WorkspaceUsageList { pub struct WorkspaceUsageList {
pub workspace_usages: Vec<WorkspaceUsage>, pub workspace_usages: Vec<WorkspaceUsageLimits>,
} }
#[derive(Template)] #[derive(Template)]

View File

@ -2,7 +2,7 @@ use crate::error::WebAppError;
use crate::ext::api::{ use crate::ext::api::{
get_user_workspace_count, get_user_workspace_limit, get_user_workspace_usages, get_user_workspace_count, get_user_workspace_limit, get_user_workspace_usages,
}; };
use crate::ext::entities::WorkspaceUsage; use crate::ext::entities::WorkspaceUsageLimits;
use crate::session::UserSession; use crate::session::UserSession;
use askama::Template; use askama::Template;
use axum::extract::{Path, State}; use axum::extract::{Path, State};

View File

@ -258,6 +258,11 @@ pub enum QueryCollabResult {
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct BatchQueryCollabResult(pub HashMap<String, QueryCollabResult>); pub struct BatchQueryCollabResult(pub HashMap<String, QueryCollabResult>);
#[derive(Serialize, Deserialize)]
pub struct WorkspaceUsage {
pub total_document_size: i64,
}
#[derive(Debug, Clone, Validate, Serialize, Deserialize)] #[derive(Debug, Clone, Validate, Serialize, Deserialize)]
pub struct InsertCollabMemberParams { pub struct InsertCollabMemberParams {
pub uid: i64, pub uid: i64,

View File

@ -101,6 +101,9 @@ pub fn workspace_scope() -> Scope {
.app_data(PayloadConfig::new(10 * 1024 * 1024)) .app_data(PayloadConfig::new(10 * 1024 * 1024))
.route(web::post().to(create_collab_list_handler)), .route(web::post().to(create_collab_list_handler)),
) )
.service(
web::resource("/{workspace_id}/usage").route(web::get().to(get_workspace_usage_handler)),
)
.service( .service(
web::resource("/{workspace_id}/{object_id}/snapshot") web::resource("/{workspace_id}/{object_id}/snapshot")
.route(web::get().to(get_collab_snapshot_handler)) .route(web::get().to(get_collab_snapshot_handler))
@ -841,6 +844,17 @@ async fn post_realtime_message_stream_handler(
Err(AppError::Internal(anyhow!("Failed to send message to websocket server")).into()) Err(AppError::Internal(anyhow!("Failed to send message to websocket server")).into())
} }
async fn get_workspace_usage_handler(
user_uuid: UserUuid,
workspace_id: web::Path<Uuid>,
state: Data<AppState>,
) -> Result<Json<AppResponse<WorkspaceUsage>>> {
let res = WorkspaceUsage {
total_document_size: 19978, // TODO
};
Ok(Json(AppResponse::Ok().with_data(res)))
}
#[inline] #[inline]
async fn parser_realtime_msg( async fn parser_realtime_msg(
payload: Bytes, payload: Bytes,