mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 00:01:34 +00:00
fix: leave workspace + instance api
This commit is contained in:
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM usr WHERE workspace_id = $1 AND email = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "00be497354f5375e9ccffb998d126a853da91d607ff9e57e10d0e5481e4d3848"
|
||||
}
|
||||
@@ -731,6 +731,20 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/users/leave_instance:
|
||||
post:
|
||||
summary: leave instance
|
||||
operationId: leaveInstance
|
||||
tags:
|
||||
- user
|
||||
responses:
|
||||
"200":
|
||||
description: status
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/users/usage:
|
||||
get:
|
||||
summary: get current usage outside of premium workspaces
|
||||
@@ -827,22 +841,6 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/User"
|
||||
|
||||
/w/{workspace}/users/leave_workspace:
|
||||
post:
|
||||
summary: leave workspace
|
||||
operationId: leaveWorkspace
|
||||
tags:
|
||||
- user
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
responses:
|
||||
"200":
|
||||
description: status
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/users/accept_invite:
|
||||
post:
|
||||
summary: accept invite to workspace
|
||||
@@ -1051,6 +1049,22 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/workspaces/leave:
|
||||
post:
|
||||
summary: leave workspace
|
||||
operationId: leaveWorkspace
|
||||
tags:
|
||||
- workspace
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
responses:
|
||||
"200":
|
||||
description: status
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/users/whois/{username}:
|
||||
get:
|
||||
summary: whois
|
||||
|
||||
@@ -94,6 +94,7 @@ pub fn global_service() -> Router {
|
||||
"/tutorial_progress",
|
||||
post(update_tutorial_progress).get(get_tutorial_progress),
|
||||
)
|
||||
.route("/leave_instance", post(leave_instance))
|
||||
// .route("/list_invite_codes", get(list_invite_codes))
|
||||
// .route("/create_invite_code", post(create_invite_code))
|
||||
// .route("/signup", post(signup))
|
||||
@@ -1416,6 +1417,29 @@ async fn add_user_to_workspace<'c>(
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
async fn leave_instance(
|
||||
Extension(db): Extension<DB>,
|
||||
ApiAuthed { email, username, .. }: ApiAuthed,
|
||||
) -> Result<String> {
|
||||
let mut tx = db.begin().await?;
|
||||
sqlx::query!("DELETE FROM password WHERE email = $1", &email)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&username,
|
||||
"workspaces.leave",
|
||||
ActionKind::Delete,
|
||||
"global",
|
||||
Some(&email),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(format!("Left instance",))
|
||||
}
|
||||
async fn update_workspace_user(
|
||||
ApiAuthed { username, is_admin, .. }: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
|
||||
@@ -83,7 +83,8 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/edit_copilot_config", post(edit_copilot_config))
|
||||
.route("/get_copilot_info", get(get_copilot_info) )
|
||||
.route("/edit_error_handler", post(edit_error_handler))
|
||||
.route("/edit_large_file_storage_config", post(edit_large_file_storage_config));
|
||||
.route("/edit_large_file_storage_config", post(edit_large_file_storage_config))
|
||||
.route("/leave", post(leave_workspace));
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
{
|
||||
@@ -1306,6 +1307,31 @@ async fn archive_workspace(
|
||||
Ok(format!("Archived workspace {}", &w_id))
|
||||
}
|
||||
|
||||
async fn leave_workspace(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
ApiAuthed { email, username, .. }: ApiAuthed,
|
||||
) -> Result<String> {
|
||||
let mut tx = db.begin().await?;
|
||||
sqlx::query!("DELETE FROM usr WHERE workspace_id = $1 AND email = $2", &w_id, &email)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&username,
|
||||
"workspaces.leave",
|
||||
ActionKind::Delete,
|
||||
&w_id,
|
||||
Some(&email),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(format!("Left workspace {}", &w_id))
|
||||
}
|
||||
|
||||
async fn unarchive_workspace(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
|
||||
@@ -1847,7 +1847,6 @@ async fn spawn_dedicated_worker(
|
||||
mpsc::channel::<Arc<QueuedJob>>(MAX_BUFFERED_DEDICATED_JOBS);
|
||||
let mut killpill_rx = killpill_rx.resubscribe();
|
||||
let db = db.clone();
|
||||
let worker_dir = worker_dir.clone();
|
||||
let base_internal_url = base_internal_url.to_string();
|
||||
let worker_name = worker_name.to_string();
|
||||
let job_completed_tx = job_completed_tx.clone();
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
{/if}
|
||||
|
||||
<div class="grid grid-cols-2 pt-8 pb-1">
|
||||
<h2 class="py-0 my-0 border-b">Tokens</h2>
|
||||
<h2 class="py-0 my-0 border-b pt-3">Tokens</h2>
|
||||
<div class="flex justify-end border-b pb-1">
|
||||
<Button
|
||||
size="sm"
|
||||
|
||||
@@ -263,7 +263,7 @@
|
||||
tooltip="Manage invites on your workspace."
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/authentification#adding-users-to-a-workspace"
|
||||
>
|
||||
<div class="flex gap-8">
|
||||
<div class="flex gap-8 items-center">
|
||||
{#if auto_invite_domain != undefined}
|
||||
<Toggle
|
||||
size="xs"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import MenuLink from './MenuLink.svelte'
|
||||
import { superadmin, userStore } from '$lib/stores'
|
||||
import { superadmin, userStore, workspaceStore } from '$lib/stores'
|
||||
import { SIDEBAR_SHOW_SCHEDULES } from '$lib/consts'
|
||||
import {
|
||||
BookOpen,
|
||||
@@ -14,6 +14,7 @@
|
||||
Github,
|
||||
HelpCircle,
|
||||
Home,
|
||||
LogOut,
|
||||
Play,
|
||||
ServerCog,
|
||||
Settings,
|
||||
@@ -24,6 +25,11 @@
|
||||
import { MenuItem } from '@rgossiaux/svelte-headlessui'
|
||||
import UserMenu from './UserMenu.svelte'
|
||||
import DiscordIcon from '../icons/brands/Discord.svelte'
|
||||
import { WorkspaceService } from '$lib/gen'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { clearStores } from '$lib/storeUtils'
|
||||
import { goto } from '$app/navigation'
|
||||
import ConfirmationModal from '../common/confirmationModal/ConfirmationModal.svelte'
|
||||
|
||||
$: mainMenuLinks = [
|
||||
{ label: 'Home', href: '/', icon: Home },
|
||||
@@ -38,6 +44,13 @@
|
||||
}
|
||||
]
|
||||
|
||||
async function leaveWorkspace() {
|
||||
await WorkspaceService.leaveWorkspace({ workspace: $workspaceStore ?? '' })
|
||||
sendUserToast('You left the workspace')
|
||||
clearStores()
|
||||
goto('/user/workspaces')
|
||||
}
|
||||
|
||||
$: secondaryMenuLinks = [
|
||||
// {
|
||||
// label: 'Workspace',
|
||||
@@ -74,6 +87,19 @@
|
||||
faIcon: undefined
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(!$superadmin && !$userStore?.is_admin
|
||||
? [
|
||||
{
|
||||
label: 'Leave Workspace',
|
||||
action: () => {
|
||||
leaveWorkspaceModal = true
|
||||
},
|
||||
class: 'text-red-400',
|
||||
icon: LogOut,
|
||||
faIcon: undefined
|
||||
}
|
||||
]
|
||||
: [])
|
||||
],
|
||||
disabled: $userStore?.operator
|
||||
@@ -124,6 +150,8 @@
|
||||
]
|
||||
|
||||
export let isCollapsed: boolean = false
|
||||
|
||||
let leaveWorkspaceModal = false
|
||||
</script>
|
||||
|
||||
<nav
|
||||
@@ -146,20 +174,35 @@
|
||||
{#each menuLink.subItems as subItem (subItem.href ?? subItem.label)}
|
||||
<MenuItem>
|
||||
<div class="py-1" role="none">
|
||||
<a
|
||||
href={subItem.href}
|
||||
class="text-secondary block px-4 py-2 text-2xs hover:bg-surface-hover hover:text-primary"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
{#if subItem.icon}
|
||||
<svelte:component this={subItem.icon} size={16} />
|
||||
{/if}
|
||||
{#if subItem?.['action']}
|
||||
<button
|
||||
class="{subItem['class']} px-4 py-2 !text-2xs"
|
||||
on:click={subItem?.['action']}
|
||||
>
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
{#if subItem.icon}
|
||||
<svelte:component this={subItem.icon} size={16} />
|
||||
{/if}
|
||||
|
||||
{subItem.label}
|
||||
</div>
|
||||
</a>
|
||||
{subItem.label}
|
||||
</div>
|
||||
</button>
|
||||
{:else}
|
||||
<a
|
||||
href={subItem.href}
|
||||
class="text-secondary block px-4 py-2 text-2xs hover:bg-surface-hover hover:text-primary"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
{#if subItem.icon}
|
||||
<svelte:component this={subItem.icon} size={16} />
|
||||
{/if}
|
||||
|
||||
{subItem.label}
|
||||
</div>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/each}
|
||||
@@ -203,3 +246,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<ConfirmationModal
|
||||
open={leaveWorkspaceModal}
|
||||
title="Leave workspace"
|
||||
confirmationText="Remove"
|
||||
on:canceled={() => {
|
||||
leaveWorkspaceModal = false
|
||||
}}
|
||||
on:confirmed={() => {
|
||||
leaveWorkspace()
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-col w-full space-y-4">
|
||||
<span>Are you sure you want to leave this workspace?</span>
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { userWorkspaces, workspaceStore } from '$lib/stores'
|
||||
import { superadmin, userStore, userWorkspaces, workspaceStore } from '$lib/stores'
|
||||
import { Building, Plus, Settings } from 'lucide-svelte'
|
||||
|
||||
import Menu from '../common/menu/MenuV2.svelte'
|
||||
@@ -87,19 +87,21 @@
|
||||
All workspaces & invites
|
||||
</a>
|
||||
</div>
|
||||
<div class="py-1" role="none">
|
||||
<MenuItem>
|
||||
<a
|
||||
href="/workspace_settings"
|
||||
class="text-secondary px-4 py-2 text-xs hover:bg-surface-hover hover:text-primary flex flex-flow gap-2"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<Settings size={16} />
|
||||
Workspace Settings
|
||||
</a>
|
||||
</MenuItem>
|
||||
</div>
|
||||
{#if $userStore?.is_admin || $superadmin}
|
||||
<div class="py-1" role="none">
|
||||
<MenuItem>
|
||||
<a
|
||||
href="/workspace_settings"
|
||||
class="text-secondary px-4 py-2 text-xs hover:bg-surface-hover hover:text-primary flex flex-flow gap-2"
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<Settings size={16} />
|
||||
Workspace Settings
|
||||
</a>
|
||||
</MenuItem>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $enterpriseLicense}
|
||||
<MultiplayerMenu />
|
||||
|
||||
Reference in New Issue
Block a user