feat: wip user usage indicator
This commit is contained in:
parent
e4934fbf2a
commit
af32cd62d0
|
|
@ -321,6 +321,7 @@ dependencies = [
|
|||
"askama",
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"database-entity",
|
||||
"dotenvy",
|
||||
"gotrue",
|
||||
"gotrue-entity",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ edition = "2021"
|
|||
# local dependencies
|
||||
gotrue = { path = "../libs/gotrue" }
|
||||
gotrue-entity = { path = "../libs/gotrue-entity" }
|
||||
database-entity = { path = "../libs/database-entity" }
|
||||
|
||||
anyhow = "1.0.79"
|
||||
axum = {version = "0.7", features = ["json"]}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,32 @@ body {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.table {
|
||||
border-collapse: collapse; /* Collapse borders so there's no double borders */
|
||||
}
|
||||
|
||||
.table th, .table td {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.yellow-table th {
|
||||
border: 1px solid var(--af-dark-yellow);
|
||||
background-color: var(--af-yellow);
|
||||
}
|
||||
|
||||
.yellow-table td {
|
||||
border: 1px solid var(--af-dark-yellow);
|
||||
}
|
||||
|
||||
.red-table th {
|
||||
border: 1px solid var(--af-dark-red);
|
||||
background-color: var(--af-red);
|
||||
}
|
||||
|
||||
.red-table td {
|
||||
border: 1px solid var(--af-dark-red);
|
||||
}
|
||||
|
||||
.purple:hover {
|
||||
background-color: var(--af-dark-purple);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ use tracing::warn;
|
|||
pub struct Config {
|
||||
pub redis_url: String,
|
||||
pub gotrue_url: String,
|
||||
pub appflowy_cloud_url: String,
|
||||
pub appflowy_cloud_gateway_url: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
|
@ -11,6 +13,15 @@ impl Config {
|
|||
Config {
|
||||
redis_url: get_or_default("ADMIN_FRONTEND_REDIS_URL", "redis://localhost:6379"),
|
||||
gotrue_url: get_or_default("ADMIN_FRONTEND_GOTRUE_URL", "http://localhost:9999"),
|
||||
appflowy_cloud_url: get_or_default(
|
||||
"ADMIN_FRONTEND_APPFLOWY_CLOUD_URL",
|
||||
"http://localhost:8000",
|
||||
),
|
||||
|
||||
appflowy_cloud_gateway_url: get_or_default(
|
||||
"ADMIN_FRONTEND_APPFLOWY_CLOUD_GATEWAY_URL",
|
||||
"http://localhost:8100",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
use database_entity::dto::AFWorkspace;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub async fn get_user_workspace_count(
|
||||
auth_header: &str,
|
||||
appflowy_cloud_base_url: &str,
|
||||
) -> Result<u32, reqwest::Error> {
|
||||
let http_client = reqwest::Client::new();
|
||||
let resp = http_client
|
||||
.get(format!("{}/api/workspace", appflowy_cloud_base_url))
|
||||
.header("Authorization", format!("Bearer {}", auth_header))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let res = resp.json::<JsonResponse<Vec<AFWorkspace>>>().await?;
|
||||
Ok(res.data.len() as u32)
|
||||
}
|
||||
|
||||
pub async fn get_user_workspace_limit(
|
||||
auth_header: &str,
|
||||
appflowy_cloud_base_url: &str,
|
||||
) -> Result<u32, reqwest::Error> {
|
||||
let http_client = reqwest::Client::new();
|
||||
let resp = http_client
|
||||
.get(format!("{}/api/user/limit", appflowy_cloud_base_url))
|
||||
.header("Authorization", format!("Bearer {}", auth_header))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let res = resp.json::<JsonResponse<UserUsageLimit>>().await?;
|
||||
let b = res.data;
|
||||
let c = b.workspace_count.unwrap_or({
|
||||
tracing::warn!("workspace_count is None, return 0");
|
||||
0
|
||||
});
|
||||
Ok(c as u32)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct JsonResponse<T> {
|
||||
pub code: u16,
|
||||
pub data: T,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct UserUsageLimit {
|
||||
pub workspace_count: Option<i64>,
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
mod config;
|
||||
mod error;
|
||||
mod ext;
|
||||
mod models;
|
||||
mod response;
|
||||
mod session;
|
||||
|
|
@ -50,6 +51,8 @@ async fn main() {
|
|||
let session_store = session::SessionStorage::new(redis_client);
|
||||
|
||||
let state = AppState {
|
||||
appflowy_cloud_url: config.appflowy_cloud_url,
|
||||
appflowy_cloud_gateway_url: config.appflowy_cloud_gateway_url,
|
||||
gotrue_client,
|
||||
session_store,
|
||||
};
|
||||
|
|
@ -85,6 +88,8 @@ async fn main() {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub appflowy_cloud_url: String,
|
||||
pub appflowy_cloud_gateway_url: String,
|
||||
pub gotrue_client: gotrue::api::Client,
|
||||
pub session_store: session::SessionStorage,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,23 @@
|
|||
use askama::Template;
|
||||
use gotrue_entity::{dto::User, sso::SSOProvider};
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "components/user_usage.html")]
|
||||
pub struct UserUsage {
|
||||
pub workspace_count: u32,
|
||||
pub workspace_limit: u32,
|
||||
}
|
||||
|
||||
// #[derive(Template)]
|
||||
// #[template(path = "components/workspace_usage.html")]
|
||||
// pub struct WorkspaceUsage {
|
||||
// pub name: String,
|
||||
// pub member_count: u32,
|
||||
// pub member_limit: u32,
|
||||
// pub total_object_size: u64,
|
||||
// pub total_object_limit: u64,
|
||||
// }
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "components/admin_sso_detail.html")]
|
||||
pub struct SsoDetail {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::error::WebAppError;
|
||||
use crate::ext::{get_user_workspace_count, get_user_workspace_limit};
|
||||
use crate::session::UserSession;
|
||||
use askama::Template;
|
||||
use axum::extract::{Path, State};
|
||||
|
|
@ -29,6 +30,8 @@ pub fn component_router() -> Router<AppState> {
|
|||
.route("/user/user", get(user_user_handler))
|
||||
.route("/user/change_password", get(user_change_password_handler))
|
||||
.route("/user/invite", get(user_invite_handler))
|
||||
.route("/user/user-usage", get(user_usage_handler))
|
||||
.route("/user/workspace-usage", get(workspace_usage_handler))
|
||||
|
||||
// Admin actions
|
||||
.route("/admin/navigate", get(admin_navigate_handler))
|
||||
|
|
@ -87,9 +90,45 @@ pub async fn admin_navigate_handler() -> Result<Html<String>, WebAppError> {
|
|||
}
|
||||
|
||||
pub async fn user_invite_handler() -> Result<Html<String>, WebAppError> {
|
||||
println!("----- user_invite_handler");
|
||||
|
||||
render_template(templates::Invite)
|
||||
}
|
||||
|
||||
pub async fn user_usage_handler(
|
||||
State(state): State<AppState>,
|
||||
session: UserSession,
|
||||
) -> Result<Html<String>, WebAppError> {
|
||||
println!("----- user_usage_handler");
|
||||
|
||||
let workspace_count =
|
||||
get_user_workspace_count(&session.token.access_token, &state.appflowy_cloud_url)
|
||||
.await
|
||||
.unwrap_or_else(|err| {
|
||||
tracing::error!("Error getting user workspace count: {:?}", err);
|
||||
0
|
||||
});
|
||||
|
||||
let workspace_limit = get_user_workspace_limit(
|
||||
&session.token.access_token,
|
||||
&state.appflowy_cloud_gateway_url,
|
||||
)
|
||||
.await;
|
||||
println!("workspace count: {:?}", workspace_count);
|
||||
|
||||
render_template(templates::Invite)
|
||||
|
||||
// todo!();
|
||||
// render_template(templates::UserUsage {
|
||||
// workspace_count: 500,
|
||||
// workspace_limit: 1000,
|
||||
// })
|
||||
}
|
||||
|
||||
pub async fn workspace_usage_handler(session: UserSession) -> Result<Html<String>, WebAppError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn admin_users_create_handler() -> Result<Html<String>, WebAppError> {
|
||||
render_template(templates::CreateUser)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,4 +20,18 @@
|
|||
>
|
||||
Invite
|
||||
</div>
|
||||
<div
|
||||
class="sidebar-item"
|
||||
hx-target="#sidebar-content"
|
||||
hx-get="/web/components/user/user-usage"
|
||||
>
|
||||
User Usage
|
||||
</div>
|
||||
<div
|
||||
class="sidebar-item"
|
||||
hx-target="#sidebar-content"
|
||||
hx-get="/web/components/user/workspace-usage"
|
||||
>
|
||||
Workspace Usage
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
<div>
|
||||
<table class="yellow-table table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Current</th>
|
||||
<th>Limit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>
|
||||
Workspaces
|
||||
</td>
|
||||
<td>
|
||||
{{ workspace_count|escape }}
|
||||
</td>
|
||||
<td>
|
||||
{{ workspace_limit|escape }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<div>
|
||||
<table class="red-table table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Workspace Name</th>
|
||||
<th>Members</th>
|
||||
<th>Members Limit</th>
|
||||
<th>Storage Space</th>
|
||||
<th>Storage Space Limit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td> {{ name|escape }} </td>
|
||||
<td> {{ member_count|escape }} </td>
|
||||
<td> {{ member_limit|escape }} </td>
|
||||
<td> {{ total_blob_size|escape }} </td>
|
||||
<td> {{ total_blob_size_limit|escape }} </td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
Loading…
Reference in New Issue