Merge pull request #406 from AppFlowy-IO/admin-frontend/leave-workspace
admin portal: leave workspace
This commit is contained in:
commit
aa3f0fd86e
|
|
@ -238,6 +238,26 @@ pub async fn invite_user_to_workspace(
|
|||
check_response(resp).await
|
||||
}
|
||||
|
||||
pub async fn leave_workspace(
|
||||
access_token: &str,
|
||||
workspace_id: &str,
|
||||
appflowy_cloud_base_url: &str,
|
||||
) -> Result<(), Error> {
|
||||
let http_client = reqwest::Client::new();
|
||||
let url = format!(
|
||||
"{}/api/workspace/{}/leave",
|
||||
appflowy_cloud_base_url, workspace_id
|
||||
);
|
||||
let resp = http_client
|
||||
.post(url)
|
||||
.header("Authorization", format!("Bearer {}", access_token))
|
||||
.json(&())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
check_response(resp).await
|
||||
}
|
||||
|
||||
pub async fn accept_workspace_invitation(
|
||||
access_token: &str,
|
||||
invite_id: &str,
|
||||
|
|
|
|||
|
|
@ -64,6 +64,12 @@ pub struct Invite {
|
|||
pub pending_workspace_invitations: Vec<AFWorkspaceInvitation>,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "components/shared_workspaces.html")]
|
||||
pub struct SharedWorkspaces {
|
||||
pub shared_workspaces: Vec<AFWorkspace>,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "components/admin_navigate.html")]
|
||||
pub struct AdminNavigate;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use crate::error::WebApiError;
|
||||
use crate::ext::api::{accept_workspace_invitation, invite_user_to_workspace, verify_token_cloud};
|
||||
use crate::ext::api::{
|
||||
accept_workspace_invitation, invite_user_to_workspace, leave_workspace, verify_token_cloud,
|
||||
};
|
||||
use crate::models::{
|
||||
WebApiAdminCreateUserRequest, WebApiChangePasswordRequest, WebApiCreateSSOProviderRequest,
|
||||
WebApiInviteUserRequest, WebApiPutUserRequest,
|
||||
|
|
@ -34,6 +36,7 @@ pub fn router() -> Router<AppState> {
|
|||
.route("/oauth_login/:provider", post(post_oauth_login_handler))
|
||||
.route("/invite", post(invite_handler))
|
||||
.route("/workspace/:workspace_id/invite", post(workspace_invite_handler))
|
||||
.route("/workspace/:workspace_id/leave", post(leave_workspace_handler))
|
||||
.route("/invite/:invite_id/accept", post(invite_accept_handler))
|
||||
.route("/open_app", post(open_app_handler))
|
||||
|
||||
|
|
@ -148,11 +151,26 @@ pub async fn workspace_invite_handler(
|
|||
Ok(WebApiResponse::<()>::from_str("Invitation sent".into()))
|
||||
}
|
||||
|
||||
pub async fn leave_workspace_handler(
|
||||
State(state): State<AppState>,
|
||||
session: UserSession,
|
||||
Path(workspace_id): Path<String>,
|
||||
) -> Result<WebApiResponse<()>, WebApiError<'static>> {
|
||||
leave_workspace(
|
||||
&session.token.access_token,
|
||||
&workspace_id,
|
||||
&state.appflowy_cloud_url,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(WebApiResponse::<()>::from_str("Left workspace".into()))
|
||||
}
|
||||
|
||||
pub async fn invite_accept_handler(
|
||||
State(state): State<AppState>,
|
||||
session: UserSession,
|
||||
Path(invite_id): Path<String>,
|
||||
) -> Result<WebApiResponse<()>, WebApiError<'static>> {
|
||||
) -> Result<HeaderMap, WebApiError<'static>> {
|
||||
accept_workspace_invitation(
|
||||
&session.token.access_token,
|
||||
&invite_id,
|
||||
|
|
@ -160,9 +178,7 @@ pub async fn invite_accept_handler(
|
|||
)
|
||||
.await?;
|
||||
|
||||
Ok(WebApiResponse::<()>::from_str(
|
||||
"Invitation accepted.\nPlease refresh page to see changes".into(),
|
||||
))
|
||||
Ok(htmx_trigger("workspaceInvitationAccepted"))
|
||||
}
|
||||
|
||||
pub async fn change_password_handler(
|
||||
|
|
@ -300,6 +316,12 @@ pub async fn login_refresh_handler(
|
|||
))
|
||||
.await?;
|
||||
|
||||
verify_token_cloud(
|
||||
token.access_token.as_str(),
|
||||
state.appflowy_cloud_url.as_str(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let new_session_id = uuid::Uuid::new_v4();
|
||||
let new_session = session::UserSession::new(new_session_id.to_string(), token);
|
||||
state.session_store.put_user_session(&new_session).await?;
|
||||
|
|
@ -390,6 +412,12 @@ fn htmx_redirect(url: &str) -> HeaderMap {
|
|||
h
|
||||
}
|
||||
|
||||
fn htmx_trigger(trigger: &str) -> HeaderMap {
|
||||
let mut h = HeaderMap::new();
|
||||
h.insert("HX-Trigger", trigger.parse().unwrap());
|
||||
h
|
||||
}
|
||||
|
||||
fn new_session_cookie(id: uuid::Uuid) -> Cookie<'static> {
|
||||
let mut cookie = Cookie::new("session_id", id.to_string());
|
||||
cookie.set_path("/");
|
||||
|
|
|
|||
|
|
@ -32,8 +32,9 @@ pub fn component_router() -> Router<AppState> {
|
|||
// User actions
|
||||
.route("/user/navigate", get(user_navigate_handler))
|
||||
.route("/user/user", get(user_user_handler))
|
||||
.route("/user/change_password", get(user_change_password_handler))
|
||||
.route("/user/change-password", get(user_change_password_handler))
|
||||
.route("/user/invite", get(user_invite_handler))
|
||||
.route("/user/shared-workspaces", get(shared_workspaces_handler))
|
||||
.route("/user/user-usage", get(user_usage_handler))
|
||||
.route("/user/workspace-usage", get(workspace_usage_handler))
|
||||
|
||||
|
|
@ -93,14 +94,34 @@ pub async fn admin_navigate_handler() -> Result<Html<String>, WebAppError> {
|
|||
render_template(templates::AdminNavigate)
|
||||
}
|
||||
|
||||
pub async fn shared_workspaces_handler(
|
||||
State(state): State<AppState>,
|
||||
session: UserSession,
|
||||
) -> Result<Html<String>, WebAppError> {
|
||||
let user_workspaces =
|
||||
get_user_workspaces(&session.token.access_token, &state.appflowy_cloud_url).await?;
|
||||
|
||||
let profile = get_user_profile(
|
||||
session.token.access_token.as_str(),
|
||||
state.appflowy_cloud_url.as_str(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let shared_workspaces = user_workspaces
|
||||
.into_iter()
|
||||
.filter(|workspace| workspace.owner_uid != profile.uid)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
render_template(templates::SharedWorkspaces { shared_workspaces })
|
||||
}
|
||||
|
||||
pub async fn user_invite_handler(
|
||||
State(state): State<AppState>,
|
||||
session: UserSession,
|
||||
) -> Result<Html<String>, WebAppError> {
|
||||
let user_workspaces =
|
||||
get_user_workspaces(&session.token.access_token, &state.appflowy_cloud_url).await;
|
||||
get_user_workspaces(&session.token.access_token, &state.appflowy_cloud_url).await?;
|
||||
|
||||
let user_workspaces = user_workspaces?;
|
||||
let profile = get_user_profile(
|
||||
session.token.access_token.as_str(),
|
||||
state.appflowy_cloud_url.as_str(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<h3>Password Change</h3>
|
||||
<form hx-post="/web-api/change_password" hx-target="#none">
|
||||
<form hx-post="/web-api/change-password" hx-target="#none">
|
||||
<table>
|
||||
<tr>
|
||||
<td>New Password:</td>
|
||||
|
|
|
|||
|
|
@ -24,20 +24,13 @@
|
|||
|
||||
<br />
|
||||
<h4>Workspaces shared with you</h4>
|
||||
<table class="cyan-table table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Workspace Name</th>
|
||||
<th>Owner Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for shared_workspace in shared_workspaces %}
|
||||
<tr>
|
||||
<td> {{ shared_workspace.workspace_name|escape }} </td>
|
||||
<td> {{ shared_workspace.owner_name|escape }} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<div
|
||||
hx-get="/web/components/user/shared-workspaces"
|
||||
hx-trigger="workspaceInvitationAccepted from:body"
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
{% include "shared_workspaces.html" %}
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<h4>Invite another user to your workspace</h4>
|
||||
|
|
@ -87,7 +80,11 @@
|
|||
<td> {{ pending_workspace_invitation.workspace_name|default("")|escape }} </td>
|
||||
<td> {{ pending_workspace_invitation.inviter_email|default("")|escape }} </td>
|
||||
<td>
|
||||
<form hx-post="/web-api/invite/{{ pending_workspace_invitation.invite_id|escape }}/accept" hx-target="#none">
|
||||
<form
|
||||
hx-post="/web-api/invite/{{ pending_workspace_invitation.invite_id|escape }}/accept"
|
||||
hx-target="closest tr"
|
||||
hx-swap="delete"
|
||||
>
|
||||
<button class="button cyan" type="submit">Accept</button>
|
||||
</form>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
<table class="cyan-table table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Workspace Name</th>
|
||||
<th>Owner Name</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for shared_workspace in shared_workspaces %}
|
||||
<tr>
|
||||
<td> {{ shared_workspace.workspace_name|escape }} </td>
|
||||
<td> {{ shared_workspace.owner_name|escape }} </td>
|
||||
<td>
|
||||
<button
|
||||
class="button red"
|
||||
hx-post="/web-api/workspace/{{ shared_workspace.workspace_id|escape }}/leave"
|
||||
hx-confirm="Are you sure?"
|
||||
hx-target="closest tr"
|
||||
hx-swap="delete"
|
||||
>
|
||||
Leave
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
<div
|
||||
class="sidebar-item"
|
||||
hx-target="#sidebar-content"
|
||||
hx-get="/web/components/user/change_password"
|
||||
hx-get="/web/components/user/change-password"
|
||||
>
|
||||
Change Password
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ services:
|
|||
- RUST_LOG=${RUST_LOG:-info}
|
||||
- ADMIN_FRONTEND_REDIS_URL=${ADMIN_FRONTEND_REDIS_URL:-redis://redis:6379}
|
||||
- ADMIN_FRONTEND_GOTRUE_URL=${ADMIN_FRONTEND_GOTRUE_URL:-http://gotrue:9999}
|
||||
- ADMIN_FRONTEND_APPFLOWY_CLOUD_URL=${ADMIN_FRONTEND_APPFLOWY_CLOUD_URL:-http://appflowy_cloud:8000}
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
|
|
|||
Loading…
Reference in New Issue