feat: replace hub error toasts with warning alerts and add disable hub setting (#8225)

* feat: replace hub error toasts with warning alerts and add disable hub setting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: guard hub script cache refresh when hub is disabled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hugocasa
2026-03-04 16:12:00 +01:00
committed by GitHub
parent e66a682c4a
commit 26f5cd6b8e
10 changed files with 89 additions and 14 deletions
+3 -2
View File
@@ -43,8 +43,8 @@ use windmill_common::{
get_database_url,
global_settings::{
APP_WORKSPACED_ROUTE_SETTING, AUTOMATE_USERNAME_CREATION_SETTING,
CRITICAL_ALERT_MUTE_UI_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING, EMAIL_DOMAIN_SETTING,
ENV_SETTINGS, HUB_ACCESSIBLE_URL_SETTING, HUB_BASE_URL_SETTING,
CRITICAL_ALERT_MUTE_UI_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING, DISABLE_HUB_SETTING,
EMAIL_DOMAIN_SETTING, ENV_SETTINGS, HUB_ACCESSIBLE_URL_SETTING, HUB_BASE_URL_SETTING,
},
instance_config::{self, ApplyMode, InstanceConfig},
server::Smtp,
@@ -519,6 +519,7 @@ pub async fn get_global_setting(
&& key != DEFAULT_TAGS_WORKSPACES_SETTING
&& key != HUB_BASE_URL_SETTING
&& key != HUB_ACCESSIBLE_URL_SETTING
&& key != DISABLE_HUB_SETTING
&& key != EMAIL_DOMAIN_SETTING
&& key != APP_WORKSPACED_ROUTE_SETTING
{
@@ -44,6 +44,7 @@ pub const HUB_API_SECRET_SETTING: &str = "hub_api_secret";
pub const AUTOMATE_USERNAME_CREATION_SETTING: &str = "automate_username_creation";
pub const HUB_BASE_URL_SETTING: &str = "hub_base_url";
pub const HUB_ACCESSIBLE_URL_SETTING: &str = "hub_accessible_url";
pub const DISABLE_HUB_SETTING: &str = "disable_hub";
pub const CRITICAL_ERROR_CHANNELS_SETTING: &str = "critical_error_channels";
pub const CRITICAL_ALERT_MUTE_UI_SETTING: &str = "critical_alert_mute_ui";
pub const CRITICAL_ALERTS_ON_DB_OVERSIZE_SETTING: &str = "critical_alerts_on_db_oversize";
@@ -230,6 +230,8 @@ pub struct GlobalSettings {
pub no_default_maven: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_tags_per_workspace: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disable_hub: Option<bool>,
// String settings
#[serde(skip_serializing_if = "Option::is_none")]
@@ -7,6 +7,8 @@
import RowIcon from '$lib/components/common/table/RowIcon.svelte'
import { loadHubApps } from '$lib/hub'
import TextInput from '$lib/components/text_input/TextInput.svelte'
import { Alert } from '$lib/components/common'
import { disableHubStore } from '$lib/stores'
interface Props {
filter?: string
@@ -30,11 +32,22 @@
const dispatch = createEventDispatcher()
let hubNotAvailable = $state(false)
onMount(async () => {
hubApps = await loadHubApps()
if ($disableHubStore) return
const result = await loadHubApps()
if (result === undefined) {
hubNotAvailable = true
} else {
hubApps = result
}
})
</script>
{#if $disableHubStore}
<!-- Hub disabled, show nothing -->
{:else}
<SearchItems
{filter}
items={prefilteredItems}
@@ -54,7 +67,11 @@
</div>
<ListFilters {syncQuery} filters={apps} bind:selectedFilter={appFilter} resourceType />
{#if hubApps}
{#if hubNotAvailable}
<Alert type="warning" title="Hub not available">
Could not connect to the Windmill Hub. If you are in a closed environment, you can disable the Hub in the <a href="/#superadmin-settings?tab=private_hub">instance settings</a>.
</Alert>
{:else if hubApps}
{#if filteredItems.length == 0}
<NoItemFound />
{:else}
@@ -93,3 +110,4 @@
<Skeleton layout={[[4], 0.5]} />
{/each}
{/if}
{/if}
@@ -7,6 +7,8 @@
import RowIcon from '$lib/components/common/table/RowIcon.svelte'
import { loadHubFlows } from '$lib/hub'
import TextInput from '$lib/components/text_input/TextInput.svelte'
import { Alert } from '$lib/components/common'
import { disableHubStore } from '$lib/stores'
interface Props {
filter?: string
@@ -30,11 +32,22 @@
const dispatch = createEventDispatcher()
let hubNotAvailable = $state(false)
onMount(async () => {
hubFlows = await loadHubFlows()
if ($disableHubStore) return
const result = await loadHubFlows()
if (result === undefined) {
hubNotAvailable = true
} else {
hubFlows = result
}
})
</script>
{#if $disableHubStore}
<!-- Hub disabled, show nothing -->
{:else}
<SearchItems
{filter}
items={prefilteredItems}
@@ -54,7 +67,11 @@
</div>
<ListFilters {syncQuery} filters={apps} bind:selectedFilter={appFilter} resourceType />
{#if hubFlows}
{#if hubNotAvailable}
<Alert type="warning" title="Hub not available">
Could not connect to the Windmill Hub. If you are in a closed environment, you can disable the Hub in the <a href="/#superadmin-settings?tab=private_hub">instance settings</a>.
</Alert>
{:else if hubFlows}
{#if filteredItems.length == 0}
<NoItemFound />
{:else}
@@ -95,3 +112,4 @@
<Skeleton layout={[[4], 0.5]} />
{/each}
{/if}
{/if}
@@ -8,6 +8,7 @@
import { IntegrationService, ScriptService, type HubScriptKind } from '$lib/gen'
import { Loader2 } from 'lucide-svelte'
import TextInput from '$lib/components/text_input/TextInput.svelte'
import { disableHubStore } from '$lib/stores'
interface Props {
kind?: HubScriptKind & string
@@ -47,6 +48,7 @@
)
async function getAllApps(filterKind: typeof kind) {
if ($disableHubStore) return
try {
hubNotAvailable = false
allApps = (
@@ -67,6 +69,7 @@
filterKind: typeof kind,
appFilter: string | undefined
) {
if ($disableHubStore) return
try {
loading = true
hubNotAvailable = false
@@ -138,6 +141,9 @@
})
</script>
{#if $disableHubStore}
<!-- Hub disabled, show nothing -->
{:else}
<div class="w-full flex items-center gap-2">
{@render children?.()}
<div class="relative w-full">
@@ -156,7 +162,9 @@
</div>
{#if hubNotAvailable}
<Alert type="error" title="Hub not available" />
<Alert type="warning" title="Hub not available">
Could not connect to the Windmill Hub. If you are in a closed environment, you can disable the Hub in the <a href="/#superadmin-settings?tab=private_hub">instance settings</a>.
</Alert>
{:else if (items.length > 0 && apps.length > 0) || !loading}
<ListFilters {syncQuery} filters={apps} bind:selectedFilter={appFilter} resourceType />
{#if items.length == 0}
@@ -204,3 +212,4 @@
<Skeleton layout={[0.5, [4]]} />
{/each}
{/if}
{/if}
@@ -24,7 +24,7 @@
[])
: undefined
} catch (err) {
sendUserToast('Failed to fetch hub scripts: ' + err, 'error')
console.error('Failed to fetch hub scripts:', err)
return undefined
}
},
@@ -44,9 +44,10 @@
import { Circle, ExternalLink } from 'lucide-svelte'
import Popover from '$lib/components/Popover.svelte'
import { usePromise } from '$lib/svelte5Utils.svelte'
import { hubBaseUrlStore, userStore } from '$lib/stores'
import { disableHubStore, hubBaseUrlStore, userStore } from '$lib/stores'
import { get } from 'svelte/store'
import Button from '$lib/components/common/button/Button.svelte'
import { Alert } from '$lib/components/common'
let hubNotAvailable = $state(false)
@@ -94,13 +95,14 @@
})
async function getAllApps(filterKind: typeof kind) {
if ($disableHubStore) return
try {
hubNotAvailable = false
allApps = (await listHubIntegrationsCached({ kind: filterKind, refreshCount })).map(
(x) => x.name
)
} catch (err) {
sendUserToast('Failed to fetch hub integrations: ' + err, 'error')
console.error('Failed to fetch hub integrations:', err)
allApps = []
hubNotAvailable = true
}
@@ -112,7 +114,9 @@
)
$effect(() => {
;[filter, kind, appFilter, refreshCount]
hubScriptsFilteredPromise.refresh()
if (!$disableHubStore) {
hubScriptsFilteredPromise.refresh()
}
})
$effect(() => {
loading = hubScriptsFilteredPromise.status === 'loading'
@@ -175,9 +179,13 @@
</script>
<svelte:window onkeydown={onKeyDown} />
{#if hubNotAvailable}
<div class="text-2xs text-red-400 font-normal text-center py-2 px-3 items-center">
Hub not available
{#if $disableHubStore}
<!-- Hub disabled, show nothing -->
{:else if hubNotAvailable}
<div class="px-3 py-2 mt-2">
<Alert type="warning" title="Hub not available" size="xs">
Could not connect to the Windmill Hub. If you are in a closed environment, you can disable the Hub in the <a href="/#superadmin-settings?tab=private_hub">instance settings</a>.
</Alert>
</div>
{:else if loading}
{#each Array(15).fill(0) as _}
@@ -331,6 +331,16 @@ export const settings: Record<string, Setting[]> = {
storage: 'setting',
ee_only: '',
hiddenIfEmpty: true
},
{
label: 'Disable Hub',
description:
'Disable the Windmill Hub integration entirely. Enable this if your instance runs in a closed environment without internet access and you do not have a private hub setup.',
key: 'disable_hub',
fieldType: 'boolean',
storage: 'setting',
ee_only: '',
requiresReloadOnChange: true
}
],
SMTP: [
+1
View File
@@ -83,6 +83,7 @@ export const superadmin = writable<string | false | undefined>(undefined)
export const devopsRole = writable<string | false | undefined>(undefined)
export const lspTokenStore = writable<string | undefined>(undefined)
export const hubBaseUrlStore = writable<string>(DEFAULT_HUB_BASE_URL)
export const disableHubStore = writable<boolean>(false)
export const userWorkspaces: Readable<Array<UserWorkspace>> = derived(
[usersWorkspaceStore, superadmin],
([store, superadmin]) => {
@@ -26,6 +26,7 @@
type UserExt,
defaultScripts,
hubBaseUrlStore,
disableHubStore,
usedTriggerKinds,
devopsRole,
whitelabelNameStore,
@@ -157,6 +158,7 @@
loadUsage()
syncTutorialsTodos()
loadHubBaseUrl()
loadDisableHub()
loadUsedTriggerKinds()
}
@@ -176,6 +178,11 @@
DEFAULT_HUB_BASE_URL
}
async function loadDisableHub() {
$disableHubStore =
((await SettingService.getGlobal({ key: 'disable_hub' })) as boolean) ?? false
}
async function loadFavorites() {
const scripts = await ScriptService.listScripts({
workspace: $workspaceStore ?? '',