diff --git a/frontend/src/lib/components/Auth0Setting.svelte b/frontend/src/lib/components/Auth0Setting.svelte
index 8ad744f286..beb9ec03f9 100644
--- a/frontend/src/lib/components/Auth0Setting.svelte
+++ b/frontend/src/lib/components/Auth0Setting.svelte
@@ -7,6 +7,7 @@
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
import TextInput from './text_input/TextInput.svelte'
+ import SettingCard from './instanceSettings/SettingCard.svelte'
interface Props {
value: any
@@ -58,7 +59,7 @@
/>
{#if enabled}
-
+
+
{/if}
diff --git a/frontend/src/lib/components/AuthSettings.svelte b/frontend/src/lib/components/AuthSettings.svelte
index 8d9d90ff2b..c157808017 100644
--- a/frontend/src/lib/components/AuthSettings.svelte
+++ b/frontend/src/lib/components/AuthSettings.svelte
@@ -23,6 +23,7 @@
import Tooltip from './Tooltip.svelte'
import { tick } from 'svelte'
import { Popover } from './meltComponents'
+ import SettingsPageHeader from './settings/SettingsPageHeader.svelte'
interface Props {
snowflakeAccountIdentifier?: string
@@ -30,6 +31,8 @@
requirePreexistingUserForOauth?: boolean
baseUrl?: string
scim?: import('svelte').Snippet
+ tab?: 'sso' | 'oauth' | 'scim'
+ hideTabs?: boolean
}
let {
@@ -37,7 +40,9 @@
oauths = $bindable(),
requirePreexistingUserForOauth = $bindable(),
baseUrl,
- scim
+ scim,
+ tab = $bindable('sso'),
+ hideTabs = false
}: Props = $props()
$effect(() => {
@@ -87,8 +92,6 @@
let ssoClientName = $state('')
let ssoNameInput = $state()
- let tab: 'sso' | 'oauth' | 'scim' = $state('sso')
-
function createOAuthClient(name: string) {
if (oauths && name) {
// Create a new object to ensure the new item is added at the end
@@ -212,34 +215,30 @@
}
-
-
-
-
-
-
-
+{#if !hideTabs}
+
+
+
+
+
+
+
+{/if}
{#if oauths}
{#if tab === 'sso'}
+
{#if !$enterpriseLicense || $enterpriseLicense.endsWith('_pro')}
-
+
Without EE, the number of SSO users is limited to 10. SCIM/SAML is available on EE
{/if}
-
-
-
When at least one of the below options is set, users will be able to login to Windmill
- via their third-party account.
- To test SSO, the recommended workflow is to to save the settings and try to login
- in an incognito window.
- Learn more
-
-
-
-
+
+ Custom Name
-
+ Client Id
-
+ Client Secret
{:else if tab === 'oauth'}
-
-
Connect third-party services like Slack, Teams or Google to let users authenticate
- directly from Windmill and automatically obtain access tokens. Once configured, users can
- create resources of the corresponding type (e.g. a 'github' resource) and authenticate via
- OAuth without manually handling credentials.
- Learn more
-
+
@@ -544,6 +534,11 @@
{/if}
{:else if tab == 'scim'}
+
{@render scim?.()}
{/if}
{/if}
diff --git a/frontend/src/lib/components/AutheliaSetting.svelte b/frontend/src/lib/components/AutheliaSetting.svelte
index ece8cb7d58..bd32b632a9 100644
--- a/frontend/src/lib/components/AutheliaSetting.svelte
+++ b/frontend/src/lib/components/AutheliaSetting.svelte
@@ -2,6 +2,7 @@
import IconedResourceType from './IconedResourceType.svelte'
import TextInput from './text_input/TextInput.svelte'
import Toggle from './Toggle.svelte'
+ import SettingCard from './instanceSettings/SettingCard.svelte'
export let value: any
@@ -46,7 +47,7 @@
/>
{#if enabled}
-
- Pick a script or flow meant to be triggered when the `/windmill` command is invoked.
+
+ {/if}
+
+
documentation.
-
+
diff --git a/frontend/src/lib/components/CustomSso.svelte b/frontend/src/lib/components/CustomSso.svelte
index 8e19a7e7e4..e11a8d98d8 100644
--- a/frontend/src/lib/components/CustomSso.svelte
+++ b/frontend/src/lib/components/CustomSso.svelte
@@ -27,7 +27,7 @@
}
-
+Auth URL
-
+Token URL
-
+Userinfo URL
-
+Scopes
-
+Extra Query Args for Authorize Request Not needed in most cases. Examples of uses: google apis require the 2 extra args
@@ -67,14 +67,14 @@
-
+Extra Query Args for Token request Not needed in most cases
-
+Payload Auth is passed in query most commonly. LinkedIn is an example of OAuth using
diff --git a/frontend/src/lib/components/DeployToSetting.svelte b/frontend/src/lib/components/DeployToSetting.svelte
index e4e56bd6b1..c66f868c31 100644
--- a/frontend/src/lib/components/DeployToSetting.svelte
+++ b/frontend/src/lib/components/DeployToSetting.svelte
@@ -11,6 +11,8 @@
import { validateDeployPathFilters } from '$lib/validators/workspaceSettings'
import Alert from './common/alert/Alert.svelte'
import SettingsFooter from './workspaceSettings/SettingsFooter.svelte'
+ import SettingCard from './instanceSettings/SettingCard.svelte'
+ import Select from './select/Select.svelte'
let deployableWorkspaces = $derived(
$usersWorkspaceStore?.workspaces.map((w) => w.id).filter((w) => w != $workspaceStore)
@@ -151,122 +153,126 @@
}
-
Workspace to link to
-
-
-
-
Deployable items
-
- You can filter which items can be deployed to the production workspace. By default everything is
- deployable.
-
Filter on path
+ Only scripts, flows and apps with their path matching one of those filters will be
+ allowed to be deployed in the deploy UI. The filters allow '*'' and '**' characters,
+ with '*'' matching any character allowed in paths until the next slash (/) and '**'
+ matching anything including slashes.
+
+ {#each deployUiSettings.include_path ?? [] as _, idx}
+
+
+
+
+
+ {#if pathValidationErrors[idx]}
+
{pathValidationErrors[idx]}
+ {/if}
+
+ {/each}
+ {/if}
+
+
+
+
+
+
Filter on path
- Only scripts, flows and apps with their path matching one of those filters will be allowed
- to be deployed in the deploy UI. The filters allow '*'' and '**' characters, with '*''
- matching any character allowed in paths until the next slash (/) and '**' matching
- anything including slashes.
+ >Filter on type
+ You can filter which types of item can be deployed to the production workspace. By default
+ everything is deployable.
- {#each deployUiSettings.include_path ?? [] as _, idx}
-
-
-
-
-
- {#if pathValidationErrors[idx]}
-
{pathValidationErrors[idx]}
- {/if}
-
- {/each}
- {/if}
-
-
-
-
-
-
-
Filter on type
- You can filter which types of item can be deployed to the production workspace. By default
- everything is deployable.
-
{:else if setting.fieldType == 'critical_error_channels'}
- {:else if setting.fieldType == 'indexer_rates'}
-
- {#if $values[setting.key]}
-
-
-
- Index writer memory budget (MB)
-
- The allocated memory arena for the indexer. A bigger value means less writing
- to disk and potentially higher indexing throughput
-
-
- {
- if (e.target instanceof HTMLInputElement) {
- if (e.target.valueAsNumber) {
- $values[setting.key].writer_memory_budget =
- e.target.valueAsNumber * (1024 * 1024)
- }
- }
- }
- }}
- value={$values[setting.key].writer_memory_budget / (1024 * 1024)}
- />
-
-
- This buttons will clear the whole index, and the service will start reindexing
- from scratch. Full text search might be down during this time.
-
-
-
-
-
- {
- clearJobsIndexModalOpen = false
- }}
- on:confirmed={async () => {
- const r = await IndexSearchService.clearIndex({
- idxName: 'JobIndex'
- })
- sendUserToast(r)
- clearJobsIndexModalOpen = false
- }}
- >
- Are you sure you want to clear the jobs index? The service will start reindexing
- from scratch. Full text search might be down during this time.
-
- {
- clearServiceLogsIndexModalOpen = false
- }}
- on:confirmed={async () => {
- const r = await IndexSearchService.clearIndex({
- idxName: 'ServiceLogIndex'
- })
- sendUserToast(r)
- clearServiceLogsIndexModalOpen = false
- }}
- >
- Are you sure you want to clear the service logs index? The service will start
- reindexing from scratch. Full text search might be down during this time.
-
-
-
-
-
-
- Commit max batch size
- The max amount of documents (here jobs) per commit. To optimize indexing
- throughput, it is best to keep this as high as possible. However, especially
- when reindexing the whole instance, it can be useful to have a limit on how
- many jobs can be written without being committed. A commit will make the jobs
- available for search, constitute a "checkpoint" state in the indexing and will
- be logged.
-
-
-
-
-
-
- Refresh index period (s)
- The index will query new jobs periodically and write them on the index. This
- setting sets that period.
-
-
-
-
-
- Max indexed job log size (KB)
- Job logs are included when indexing, but to avoid the index size growing
- artificially, the logs will be truncated after a size has been reached.
-
-
- {
- if (e.target instanceof HTMLInputElement) {
- if (e.target.valueAsNumber) {
- $values[setting.key].max_indexed_job_log_size =
- e.target.valueAsNumber * 1024
- }
- }
- }
- }}
- value={$values[setting.key].max_indexed_job_log_size / 1024}
- />
-
-
-
-
-
- Commit max batch size
- The max amount of documents per commit. In this case 1 document is one log
- file representing all logs during 1 minute for a specific host. To optimize
- indexing throughput, it is best to keep this as high as possible. However,
- especially when reindexing the whole instance, it can be useful to have a
- limit on how many logs can be written without being committed. A commit will
- make the logs available for search, appear as a log line, and be a
- "checkpoint" of the indexing progress.
-
-
-
-
-
-
-
- Refresh index period (s)
- The index will query new service logs peridically and write them on the index.
- This setting sets that period.
-
-
-
-
-
- {/if}
-
{:else if setting.fieldType == 'otel'}
-
+
{#if $values[setting.key]}
{:else if setting.fieldType == 'object_store_config'}
-
{:else if setting.fieldType == 'critical_alerts_on_db_oversize'}
{#if $values[setting.key]}
@@ -893,7 +637,6 @@
GB
{/if}
-
{/if}
{:else if setting.fieldType == 'number'}
{:else if setting.fieldType == 'password'}
@@ -922,25 +666,19 @@
clearable
/>
- {:else if setting.fieldType == 'select'}
- TODO
{:else if setting.fieldType == 'smtp_connect'}
{:else if setting.fieldType == 'secret_backend'}
{/if}
{#if hasError}
-
+
{setting.error ?? ''}
{/if}
{:else}
{/if}
- {/snippet}
-
-
+ On Enterprise Edition, you must send data to check that usage is in line with the terms of
+ the subscription. You can either enable telemetry or regularly send usage data by clicking
+ the button below. For air-gapped instances, you can download the telemetry data and send
+ it manually.
+
{#if bucket_config.type === 'S3'}
Bucket
-
+ (bucket_config as S3Config).bucket,
+ (v) => (bucket_config = { ...(bucket_config as S3Config), bucket: v })
+ }
+ />
@@ -168,7 +207,12 @@
If left empty, will be derived automatically from $AWS_REGION
-
+ (bucket_config as S3Config).region,
+ (v) => (bucket_config = { ...(bucket_config as S3Config), region: v })
+ }
+ />
Access key ID
@@ -176,17 +220,24 @@
>If left empty, will be derived automatically from $AWS_ACCESS_KEY_ID, pod or ec2
profile
-
+ (bucket_config as S3Config).access_key,
+ (v) => (bucket_config = { ...(bucket_config as S3Config), access_key: v })
+ }
+ />
Secret keyIf left empty, will be derived automatically from $AWS_SECRET_KEY, pod or ec2 profile
- (bucket_config as S3Config).secret_key,
+ (v) => (bucket_config = { ...(bucket_config as S3Config), secret_key: v })
+ }
/>
@@ -194,42 +245,79 @@
Only needed for non AWS S3 providers like R2 or MinIo
-
+ (bucket_config as S3Config).endpoint,
+ (v) => (bucket_config = { ...(bucket_config as S3Config), endpoint: v })
+ }
+ />
+ Automatically create a username for new users based on their email, shared
+ across workspaces. Learn more
+
+ {
+ automateUsernameModalOpen = true
+ }}
>
+ Enable (recommended)
+
+ {
+ automateUsernameModalOpen = false
+ enableAutomateUsernameCreationSetting()
+ }}
+ on:canceled={() => (automateUsernameModalOpen = false)}
+ title="Automatic username creation"
+ confirmationText="Enable"
+ >
+ Once activated, it will not be possible to disable this feature. In case
+ existing users have different usernames in different workspaces, you will have
+ to manually confirm the username for each user.
+
- {
- automateUsernameModalOpen = true
- }}
- >
- Enable (recommended)
-
- {
- automateUsernameModalOpen = false
- enableAutomateUsernameCreationSetting()
- }}
- on:canceled={() => (automateUsernameModalOpen = false)}
- title="Automatic username creation"
- confirmationText="Enable"
- >
- Once activated, it will not be possible to disable this feature. In case existing
- users have different usernames in different workspaces, you will have to manually
- confirm the username for each user.
-
-
{displayTitle}
diff --git a/frontend/src/lib/components/git_sync/GitSyncSection.svelte b/frontend/src/lib/components/git_sync/GitSyncSection.svelte
index 7db74affd9..893e782b19 100644
--- a/frontend/src/lib/components/git_sync/GitSyncSection.svelte
+++ b/frontend/src/lib/components/git_sync/GitSyncSection.svelte
@@ -56,7 +56,20 @@
title="Git Sync"
description="Connect the Windmill workspace to a Git repository to automatically commit and push scripts, flows, and apps to the repository on each deploy."
link="https://www.windmill.dev/docs/advanced/git_sync"
- />
+ >
+ {#snippet actions()}
+ {#if $enterpriseLicense && gitSyncContext.repositories != undefined}
+
+ See sync jobs
+
+ {/if}
+ {/snippet}
+
Only new changes matching the filters will trigger a git sync. You still need to initialize the
repo to the desired state first.
@@ -70,20 +83,8 @@
{/if}
{#if $enterpriseLicense && gitSyncContext.repositories != undefined}
-
-
- See sync jobs
-
-
-
-
-
+
boolean
+ validate?: (value: any) => Record
error?: string
defaultValue?: () => any
codeAreaLang?: string
@@ -73,6 +76,31 @@ export interface Setting {
export type SettingStorage = 'setting'
+const positiveNumber = z.number().positive('Must be a positive number')
+
+const indexerSettingsSchema = z
+ .object({
+ writer_memory_budget: positiveNumber.optional(),
+ commit_job_max_batch_size: positiveNumber.optional(),
+ refresh_index_period: positiveNumber.optional(),
+ max_indexed_job_log_size: positiveNumber.optional(),
+ commit_log_max_batch_size: positiveNumber.optional(),
+ refresh_log_index_period: positiveNumber.optional()
+ })
+ .passthrough()
+
+function validateIndexerSettings(v: any): Record {
+ if (!v) return {}
+ const result = indexerSettingsSchema.safeParse(v)
+ if (result.success) return {}
+ const errors: Record = {}
+ for (const issue of result.error.issues) {
+ const field = issue.path[0]?.toString()
+ if (field) errors[field] = issue.message
+ }
+ return errors
+}
+
export const scimSamlSetting: Setting[] = [
{
label: 'SCIM token',
@@ -119,7 +147,11 @@ export const settings: Record = {
key: 'email_domain',
fieldType: 'text',
storage: 'setting',
- placeholder: 'mail.windmill.com'
+ placeholder: 'mail.windmill.com',
+ error:
+ 'Email domain must be a valid domain (e.g. mail.windmill.com) without protocol or trailing slash',
+ isValid: (value: string | undefined) =>
+ !value || /^(?!-)([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/.test(value)
},
{
label: 'Request size limit in MB',
@@ -130,32 +162,6 @@ export const settings: Record = {
placeholder: '50',
storage: 'setting'
},
- {
- label: 'Default timeout',
- key: 'job_default_timeout',
- description:
- 'Default timeout for individual jobs. Learn more',
- fieldType: 'seconds',
- storage: 'setting',
- cloudonly: false
- },
- {
- label: 'Keep job directories for debug',
- key: 'keep_job_dir',
- fieldType: 'boolean',
- description: 'Keep Job directories after execution at /tmp/windmill/WORKER/JOB_ID',
- storage: 'setting'
- },
- {
- label: 'Max timeout for sync endpoints',
- description:
- 'Maximum amount of time (measured in seconds) that a sync endpoint is allowed to run before it is forcibly stopped or timed out.',
- key: 'timeout_wait_result',
- cloudonly: true,
- fieldType: 'seconds',
- placeholder: '60',
- storage: 'setting'
- },
{
label: 'License key',
description:
@@ -172,7 +178,46 @@ export const settings: Record = {
key: 'dev_instance',
fieldType: 'boolean',
storage: 'setting',
- ee_only: ''
+ ee_only: '',
+ hideInQuickSetup: true
+ },
+ {
+ label: 'App workspace prefix',
+ description:
+ 'When enabled apps will be accessible at /a/{workspace_id}/{custom_path} instead of /a/{custom_path} allowing you to define same custom path for apps in different workspace without conflict',
+ key: 'app_workspaced_route',
+ fieldType: 'boolean',
+ storage: 'setting',
+ ee_only: '',
+ hideInQuickSetup: true
+ }
+ ],
+ Jobs: [
+ {
+ label: 'Default timeout',
+ key: 'job_default_timeout',
+ description:
+ 'Default timeout for individual jobs. Learn more',
+ fieldType: 'seconds',
+ storage: 'setting',
+ cloudonly: false
+ },
+ {
+ label: 'Max timeout for sync endpoints',
+ description:
+ 'Maximum amount of time (measured in seconds) that a sync endpoint is allowed to run before it is forcibly stopped or timed out.',
+ key: 'timeout_wait_result',
+ cloudonly: true,
+ fieldType: 'seconds',
+ placeholder: '60',
+ storage: 'setting'
+ },
+ {
+ label: 'Keep job directories for debug',
+ key: 'keep_job_dir',
+ fieldType: 'boolean',
+ description: 'Keep Job directories after execution at /tmp/windmill/WORKER/JOB_ID',
+ storage: 'setting'
},
{
label: 'Retention period in secs',
@@ -184,6 +229,21 @@ export const settings: Record = {
storage: 'setting',
ee_only: 'You can only adjust this setting to above 30 days in the EE version',
cloudonly: false
+ }
+ ],
+ 'Object Storage': [
+ {
+ label: 'Instance object storage',
+ description:
+ ' S3/Azure bucket to store large logs and global cache for Python and Go. Learn more',
+ key: 'object_store_cache_config',
+ fieldType: 'object_store_config',
+ storage: 'setting',
+ ee_only: '',
+ isValid: (v) => {
+ if (!v || v.type !== 'Gcs') return true
+ return v.serviceAccountKey !== undefined
+ }
},
{
label: 'Delete logs from s3 periodically',
@@ -193,28 +253,9 @@ export const settings: Record = {
fieldType: 'boolean',
storage: 'setting',
ee_only: ''
- },
-
- {
- label: 'Instance object storage',
- description:
- ' S3/Azure bucket to store large logs and global cache for Python and Go. Learn more',
- key: 'object_store_cache_config',
- fieldType: 'object_store_config',
- storage: 'setting',
- ee_only: ''
- },
-
- {
- label: 'Azure OpenAI base path',
- description:
- 'All workspaces using an OpenAI resource for Windmill AI will run on the specified deployed model. Format: https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}. Learn more',
- key: 'openai_azure_base_path',
- fieldType: 'text',
- storage: 'setting',
- ee_only: '',
- hiddenIfEmpty: true
- },
+ }
+ ],
+ 'Private Hub': [
{
label: 'Private Hub base url',
description:
@@ -245,7 +286,6 @@ export const settings: Record = {
key: 'hub_accessible_url',
fieldType: 'text',
hiddenIfNull: true,
-
storage: 'setting',
ee_only: '',
requiresReloadOnChange: true
@@ -260,13 +300,14 @@ export const settings: Record = {
ee_only: ''
},
{
- label: 'App workspace prefix',
+ label: 'Azure OpenAI base path',
description:
- 'When enabled apps will be accessible at /a/{workspace_id}/{custom_path} instead of /a/{custom_path} allowing you to define same custom path for apps in different workspace without conflict',
- key: 'app_workspaced_route',
- fieldType: 'boolean',
+ 'All workspaces using an OpenAI resource for Windmill AI will run on the specified deployed model. Format: https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}. Learn more',
+ key: 'openai_azure_base_path',
+ fieldType: 'text',
storage: 'setting',
- ee_only: ''
+ ee_only: '',
+ hiddenIfEmpty: true
}
],
SMTP: [
@@ -336,7 +377,7 @@ export const settings: Record = {
key: 'uv_index_strategy',
fieldType: 'select',
placeholder: 'unsafe-best-match',
- defaultValue: () => "unsafe-best-match",
+ defaultValue: () => 'unsafe-best-match',
select_items: [
{
label: 'first-index',
@@ -515,7 +556,7 @@ export const settings: Record = {
key: 'indexer_settings',
fieldType: 'indexer_rates',
storage: 'setting',
- ee_only: 'Full text search across jobs and service logs is an EE feature'
+ validate: validateIndexerSettings
}
],
@@ -530,9 +571,9 @@ export const settings: Record = {
],
'Secret Storage': [
{
- label: 'Secret Storage Backend',
+ label: 'Backend type',
description:
- 'Configure where secrets (secret variables) are stored. By default, secrets are encrypted and stored in the database. Enterprise Edition supports HashiCorp Vault as an external secret store.',
+ 'By default, secrets are encrypted and stored in the database. Enterprise Edition supports HashiCorp Vault as an external secret store.',
key: 'secret_backend',
fieldType: 'secret_backend',
storage: 'setting',
@@ -542,3 +583,173 @@ export const settings: Record = {
}
export const settingsKeys = Object.keys(settings)
+
+// --- Sidebar navigation for instance settings ---
+export const instanceSettingsNavigationGroups = [
+ {
+ items: [
+ {
+ id: 'users',
+ label: 'Users',
+ aiId: 'instance-settings-users',
+ aiDescription: 'Instance users settings'
+ },
+ {
+ id: 'general',
+ label: 'General',
+ aiId: 'instance-settings-general',
+ aiDescription: 'Instance general settings'
+ }
+ ]
+ },
+ {
+ title: 'Authentication',
+ items: [
+ {
+ id: 'sso',
+ label: 'SSO',
+ aiId: 'instance-settings-sso',
+ aiDescription: 'Instance SSO settings'
+ },
+ {
+ id: 'oauth',
+ label: 'OAuth',
+ aiId: 'instance-settings-oauth',
+ aiDescription: 'Instance OAuth settings'
+ },
+ {
+ id: 'scim_saml',
+ label: 'SCIM/SAML',
+ aiId: 'instance-settings-scim-saml',
+ aiDescription: 'Instance SCIM/SAML settings',
+ isEE: true
+ }
+ ]
+ },
+ {
+ title: 'Infrastructure',
+ items: [
+ {
+ id: 'smtp',
+ label: 'SMTP',
+ aiId: 'instance-settings-smtp',
+ aiDescription: 'Instance SMTP settings'
+ },
+ {
+ id: 'registries',
+ label: 'Registries',
+ aiId: 'instance-settings-registries',
+ aiDescription: 'Instance registries settings'
+ },
+ {
+ id: 'object_storage',
+ label: 'Object Storage',
+ aiId: 'instance-settings-object-storage',
+ aiDescription: 'Instance object storage settings',
+ isEE: true
+ }
+ ]
+ },
+ {
+ title: 'Monitoring',
+ items: [
+ {
+ id: 'alerts',
+ label: 'Alerts',
+ aiId: 'instance-settings-alerts',
+ aiDescription: 'Instance alerts settings',
+ isEE: true
+ },
+ {
+ id: 'otel_prom',
+ label: 'OTEL/Prometheus',
+ aiId: 'instance-settings-otel-prom',
+ aiDescription: 'Instance OTEL/Prometheus settings',
+ isEE: true
+ },
+ {
+ id: 'indexer',
+ label: 'Indexer',
+ aiId: 'instance-settings-indexer',
+ aiDescription: 'Instance indexer settings',
+ isEE: true
+ }
+ ]
+ },
+ {
+ title: 'Advanced',
+ items: [
+ {
+ id: 'jobs',
+ label: 'Jobs',
+ aiId: 'instance-settings-jobs',
+ aiDescription: 'Instance jobs settings'
+ },
+ {
+ id: 'private_hub',
+ label: 'Private Hub',
+ aiId: 'instance-settings-private-hub',
+ aiDescription: 'Instance private hub settings',
+ isEE: true
+ },
+ {
+ id: 'telemetry',
+ label: 'Telemetry',
+ aiId: 'instance-settings-telemetry',
+ aiDescription: 'Instance telemetry settings'
+ },
+ {
+ id: 'secret_storage',
+ label: 'Secret Storage',
+ aiId: 'instance-settings-secret-storage',
+ aiDescription: 'Instance secret storage settings'
+ }
+ ]
+ }
+]
+
+export const tabToCategoryMap: Record = {
+ general: 'Core',
+ sso: 'Auth/OAuth/SAML',
+ oauth: 'Auth/OAuth/SAML',
+ scim_saml: 'Auth/OAuth/SAML',
+ smtp: 'SMTP',
+ registries: 'Registries',
+ alerts: 'Alerts',
+ otel_prom: 'OTEL/Prom',
+ indexer: 'Indexer',
+ telemetry: 'Telemetry',
+ secret_storage: 'Secret Storage',
+ object_storage: 'Object Storage',
+ jobs: 'Jobs',
+ private_hub: 'Private Hub'
+}
+
+export const tabToAuthSubTab: Record = {
+ sso: 'sso',
+ oauth: 'oauth',
+ scim_saml: 'scim'
+}
+
+// Navigation groups for the initial setup flow (no Users tab)
+export const setupNavigationGroups = instanceSettingsNavigationGroups
+ .map((group) => ({
+ ...group,
+ items: group.items.filter((item) => item.id !== 'users')
+ }))
+ .filter((group) => group.items.length > 0)
+
+export const categoryToTabMap: Record = {
+ Core: 'general',
+ SMTP: 'smtp',
+ 'Auth/OAuth/SAML': 'sso',
+ Registries: 'registries',
+ Alerts: 'alerts',
+ 'OTEL/Prom': 'otel_prom',
+ Indexer: 'indexer',
+ Telemetry: 'telemetry',
+ 'Secret Storage': 'secret_storage',
+ 'Object Storage': 'object_storage',
+ Jobs: 'jobs',
+ 'Private Hub': 'private_hub'
+}
diff --git a/frontend/src/lib/components/instanceSettings/IndexerJobIndexSettings.svelte b/frontend/src/lib/components/instanceSettings/IndexerJobIndexSettings.svelte
new file mode 100644
index 0000000000..b0bf302dc0
--- /dev/null
+++ b/frontend/src/lib/components/instanceSettings/IndexerJobIndexSettings.svelte
@@ -0,0 +1,103 @@
+
+
+
+
+
+ Commit max batch size
+ The max amount of documents (here jobs) per commit. To optimize indexing throughput, it is
+ best to keep this as high as possible. However, especially when reindexing the whole
+ instance, it can be useful to have a limit on how many jobs can be written without being
+ committed. A commit will make the jobs available for search, constitute a "checkpoint" state
+ in the indexing and will be logged.
+
+
+ {
+ if (v == null) {
+ const { commit_job_max_batch_size: _, ...rest } = $values['indexer_settings']
+ $values['indexer_settings'] = rest
+ } else {
+ $values['indexer_settings'] = {
+ ...$values['indexer_settings'],
+ commit_job_max_batch_size: v
+ }
+ }
+ }}
+ />
+
+
+
+
+ Refresh index period (s)
+ The index will query new jobs periodically and write them on the index. This setting sets
+ that period.
+
+ {
+ if (v == null) {
+ const { refresh_index_period: _, ...rest } = $values['indexer_settings']
+ $values['indexer_settings'] = rest
+ } else {
+ $values['indexer_settings'] = {
+ ...$values['indexer_settings'],
+ refresh_index_period: v
+ }
+ }
+ }}
+ />
+
+
+
+
+ Max indexed job log size (KB)
+ Job logs are included when indexing, but to avoid the index size growing artificially, the
+ logs will be truncated after a size has been reached.
+
+
+ {
+ if (v == null) {
+ const { max_indexed_job_log_size: _, ...rest } = $values['indexer_settings']
+ $values['indexer_settings'] = rest
+ } else {
+ $values['indexer_settings'] = {
+ ...$values['indexer_settings'],
+ max_indexed_job_log_size: v * 1024
+ }
+ }
+ }}
+ />
+
+
+ Commit max batch size
+ The max amount of documents per commit. In this case 1 document is one log file representing
+ all logs during 1 minute for a specific host. To optimize indexing throughput, it is best to
+ keep this as high as possible. However, especially when reindexing the whole instance, it
+ can be useful to have a limit on how many logs can be written without being committed. A
+ commit will make the logs available for search, appear as a log line, and be a "checkpoint"
+ of the indexing progress.
+
+
+ {
+ if (v == null) {
+ const { commit_log_max_batch_size: _, ...rest } = $values['indexer_settings']
+ $values['indexer_settings'] = rest
+ } else {
+ $values['indexer_settings'] = {
+ ...$values['indexer_settings'],
+ commit_log_max_batch_size: v
+ }
+ }
+ }}
+ />
+
+
+
+
+
+ Refresh index period (s)
+ The index will query new service logs peridically and write them on the index. This setting
+ sets that period.
+
+
+ {
+ if (v == null) {
+ const { refresh_log_index_period: _, ...rest } = $values['indexer_settings']
+ $values['indexer_settings'] = rest
+ } else {
+ $values['indexer_settings'] = {
+ ...$values['indexer_settings'],
+ refresh_log_index_period: v
+ }
+ }
+ }}
+ />
+
+
+
+ Index writer memory budget (MB)
+
+ The allocated memory arena for the indexer. A bigger value means less writing to disk and
+ potentially higher indexing throughput
+
+
+ {
+ if (v == null) {
+ const { writer_memory_budget: _, ...rest } = $values['indexer_settings']
+ $values['indexer_settings'] = rest
+ } else {
+ $values['indexer_settings'] = {
+ ...$values['indexer_settings'],
+ writer_memory_budget: v * (1024 * 1024)
+ }
+ }
+ }}
+ />
+
+
+
+ This buttons will clear the whole index, and the service will start reindexing from scratch.
+ Full text search might be down during this time.
+
+ {
+ clearJobsIndexModalOpen = true
+ }}
+ >
+ Clear jobs index
+
+ {
+ clearServiceLogsIndexModalOpen = true
+ }}
+ >
+ Clear service logs index
+
+
+
+ {
+ clearJobsIndexModalOpen = false
+ }}
+ on:confirmed={async () => {
+ const r = await IndexSearchService.clearIndex({
+ idxName: 'JobIndex'
+ })
+ sendUserToast(r)
+ clearJobsIndexModalOpen = false
+ }}
+ >
+ Are you sure you want to clear the jobs index? The service will start reindexing from scratch.
+ Full text search might be down during this time.
+
+ {
+ clearServiceLogsIndexModalOpen = false
+ }}
+ on:confirmed={async () => {
+ const r = await IndexSearchService.clearIndex({
+ idxName: 'ServiceLogIndex'
+ })
+ sendUserToast(r)
+ clearServiceLogsIndexModalOpen = false
+ }}
+ >
+ Are you sure you want to clear the service logs index? The service will start reindexing from
+ scratch. Full text search might be down during this time.
+
+
diff --git a/frontend/src/lib/components/instanceSettings/SecretBackendConfig.svelte b/frontend/src/lib/components/instanceSettings/SecretBackendConfig.svelte
index c66876ad65..9a1430dac7 100644
--- a/frontend/src/lib/components/instanceSettings/SecretBackendConfig.svelte
+++ b/frontend/src/lib/components/instanceSettings/SecretBackendConfig.svelte
@@ -70,7 +70,12 @@
}
function setAuthMethod(method: string | undefined) {
- if (!method || !$values['secret_backend'] || $values['secret_backend'].type !== 'HashiCorpVault') return
+ if (
+ !method ||
+ !$values['secret_backend'] ||
+ $values['secret_backend'].type !== 'HashiCorpVault'
+ )
+ return
if (method === 'token') {
// Clear JWT role when switching to token auth
@@ -194,12 +199,8 @@
Replace windmill-secrets with your role name if different.
diff --git a/frontend/src/lib/components/instanceSettings/SettingCard.svelte b/frontend/src/lib/components/instanceSettings/SettingCard.svelte
new file mode 100644
index 0000000000..b0ecc3b557
--- /dev/null
+++ b/frontend/src/lib/components/instanceSettings/SettingCard.svelte
@@ -0,0 +1,71 @@
+
+
+
- This URL will be POSTed to with a JSON body depending on the type of event. The
- type is indicated by the type field. The other fields are dependent on the type.
-
-
-
-
- {#if webhookValidationError}
-
{webhookValidationError}
- {/if}
-
-
-
+
+
+ {#if webhookValidationError}
+
{webhookValidationError}
+ {/if}
+
{:else if tab == 'error_handler'}
-
+
-
+
{#if !$enterpriseLicense}
-
+
Workspace critical alerts is a Windmill Enterprise Edition feature that sends
notifications to workspace admins when critical events occur.
@@ -1851,24 +1845,22 @@ export async function main(
before turning this feature on.
{/if}
-
+
-
+
-
-
- Limit the number of public (anonymous) app executions per minute per server. Set
- to 0 or leave empty to disable. This is a per-server limit, not a global limit.
-
-
-
- executions per minute per server
-
-
+
+
+ executions per minute per server
+
-
-
- Workspace encryption key
-
-