fix(frontend): workspace color race condition for superadmins (#7229)

This commit is contained in:
hugocasa
2025-11-26 11:02:21 +00:00
committed by GitHub
parent c0a92f83b0
commit 8922be11a6
4 changed files with 131 additions and 12 deletions
@@ -0,0 +1,58 @@
{
"db_name": "PostgreSQL",
"query": "SELECT\n workspace.id AS \"id!\",\n workspace.name AS \"name!\",\n workspace.owner AS \"owner!\",\n workspace.deleted AS \"deleted!\",\n workspace.premium AS \"premium!\",\n workspace_settings.color AS \"color\",\n workspace.parent_workspace_id AS \"parent_workspace_id\"\n FROM workspace\n LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id\n WHERE workspace.id = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id!",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "name!",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "owner!",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "deleted!",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "premium!",
"type_info": "Bool"
},
{
"ordinal": 5,
"name": "color",
"type_info": "Varchar"
},
{
"ordinal": 6,
"name": "parent_workspace_id",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false,
false,
false,
true,
true
]
},
"hash": "00c1dd0cfaf15aafdcfcabc1f123cebdf8d777f48e148bcb171fa15e8bf6f098"
}
+16
View File
@@ -688,6 +688,22 @@ paths:
schema:
$ref: "#/components/schemas/UserWorkspaceList"
/w/{workspace}/workspaces/get_as_superadmin:
get:
summary: get workspace as super admin (require to be super admin)
operationId: getWorkspaceAsSuperAdmin
tags:
- workspace
parameters:
- $ref: "#/components/parameters/WorkspaceId"
responses:
"200":
description: workspace
content:
application/json:
schema:
$ref: "#/components/schemas/Workspace"
/workspaces/list_as_superadmin:
get:
summary: list all workspaces as super admin (require to be super admin)
+38 -3
View File
@@ -50,7 +50,7 @@ use windmill_common::{
oauth2::WORKSPACE_SLACK_BOT_TOKEN_PATH,
utils::{paginate, rd_string, require_admin, Pagination},
};
use windmill_git_sync::{handle_fork_branch_creation, handle_deployment_metadata, DeployedObject};
use windmill_git_sync::{handle_deployment_metadata, handle_fork_branch_creation, DeployedObject};
use windmill_worker::scoped_dependency_map::{DependencyMap, ScopedDependencyMap};
#[cfg(feature = "enterprise")]
@@ -73,6 +73,7 @@ lazy_static::lazy_static! {
pub fn workspaced_service() -> Router {
let router = Router::new()
.route("/get_as_superadmin", get(get_workspace_as_superadmin))
.route("/list_pending_invites", get(list_pending_invites))
.route("/update", post(edit_workspace))
.route("/archive", post(archive_workspace))
@@ -163,7 +164,10 @@ pub fn workspaced_service() -> Router {
post(acknowledge_all_critical_alerts),
)
.route("/critical_alerts/mute", post(mute_critical_alerts))
.route("/create_workspace_fork_branch", post(create_workspace_fork_branch))
.route(
"/create_workspace_fork_branch",
post(create_workspace_fork_branch),
)
.route("/operator_settings", post(update_operator_settings));
#[cfg(all(feature = "stripe", feature = "enterprise"))]
@@ -706,7 +710,9 @@ async fn get_slack_oauth_config(
.await?;
// Mask the secret if it exists
let masked_secret = settings.slack_oauth_client_secret.map(|_| "***".to_string());
let masked_secret = settings
.slack_oauth_client_secret
.map(|_| "***".to_string());
Ok(Json(GetSlackOAuthConfigResponse {
slack_oauth_client_id: settings.slack_oauth_client_id,
@@ -2227,6 +2233,35 @@ async fn get_used_triggers(
Ok(Json(websocket_used))
}
async fn get_workspace_as_superadmin(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Path(w_id): Path<String>,
) -> JsonResult<Workspace> {
require_super_admin(&db, &authed.email).await?;
let workspace = sqlx::query_as!(
Workspace,
"SELECT
workspace.id AS \"id!\",
workspace.name AS \"name!\",
workspace.owner AS \"owner!\",
workspace.deleted AS \"deleted!\",
workspace.premium AS \"premium!\",
workspace_settings.color AS \"color\",
workspace.parent_workspace_id AS \"parent_workspace_id\"
FROM workspace
LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id
WHERE workspace.id = $1",
w_id
)
.fetch_optional(&db)
.await?;
let workspace = not_found_if_none(workspace, "workspace", w_id)?;
Ok(Json(workspace))
}
async fn list_workspaces_as_super_admin(
authed: ApiAuthed,
Extension(db): Extension<DB>,
+19 -9
View File
@@ -3,9 +3,11 @@ import { derived, type Readable, writable } from 'svelte/store'
import type { IntrospectionQuery } from 'graphql'
import {
CancelablePromise,
type OperatorSettings,
type TokenResponse,
type UserWorkspaceList,
type Workspace,
type WorkspaceDefaultScripts,
WorkspaceService
} from './gen'
@@ -168,32 +170,40 @@ export const instanceSettingsSelectedTab = writable('Core')
export const isCriticalAlertsUIOpen = writable(false)
let getWorkspacePromise: CancelablePromise<Workspace> | null = null
export const workspaceColor: Readable<string | null | undefined> = derived(
[workspaceStore, usersWorkspaceStore, superadmin],
([workspaceStore, usersWorkspaceStore, superadmin], set: (value: string | undefined) => void) => {
if (!workspaceStore) {
set(undefined)
if (!workspaceStore || !usersWorkspaceStore) {
return
}
// First try to get the color from usersWorkspaceStore
const color = usersWorkspaceStore?.workspaces.find((w) => w.id === workspaceStore)?.color
const workspace = usersWorkspaceStore.workspaces.find((w) => w.id === workspaceStore)
if (color) {
set(color)
if (workspace) {
set(workspace.color)
return
}
// If not found and user is superadmin, try to get it from superadmin list
// If workspace not found and user is superadmin, get it as superadmin
if (!superadmin) {
set(undefined)
return
}
WorkspaceService.listWorkspacesAsSuperAdmin().then((workspaces) => {
const superadminColor = workspaces.find((w) => w.id === workspaceStore)?.color
set(superadminColor)
getWorkspacePromise?.cancel()
getWorkspacePromise = WorkspaceService.getWorkspaceAsSuperAdmin({
workspace: workspaceStore
})
getWorkspacePromise
.then((workspace) => set(workspace.color))
.catch((error) => {
console.error('error getting workspace as superadmin', error)
set(undefined)
})
}
)