windmill-components 1.665.1 → 1.677.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/dist/sharedUtils/assets/tokens/colorTokensConfig.d.ts +2 -0
  2. package/dist/sharedUtils/base.d.ts +1 -0
  3. package/dist/sharedUtils/cloud.d.ts +1 -0
  4. package/dist/sharedUtils/common.d.ts +111 -0
  5. package/dist/sharedUtils/components/apps/components/display/dbtable/queries/count.d.ts +5 -0
  6. package/dist/sharedUtils/components/apps/components/display/dbtable/queries/delete.d.ts +5 -0
  7. package/dist/sharedUtils/components/apps/components/display/dbtable/queries/insert.d.ts +5 -0
  8. package/dist/sharedUtils/components/apps/components/display/dbtable/queries/select.d.ts +13 -0
  9. package/dist/sharedUtils/components/apps/components/display/dbtable/queries/update.d.ts +11 -0
  10. package/dist/sharedUtils/components/apps/components/display/dbtable/utils.d.ts +95 -0
  11. package/dist/sharedUtils/components/apps/editor/appPolicy.d.ts +6 -0
  12. package/dist/sharedUtils/components/apps/editor/appUtilsCore.d.ts +7 -0
  13. package/dist/sharedUtils/components/apps/editor/appUtilsS3.d.ts +33 -0
  14. package/dist/sharedUtils/components/apps/editor/commonAppUtils.d.ts +10 -0
  15. package/dist/sharedUtils/components/apps/editor/component/components.d.ts +5371 -0
  16. package/dist/sharedUtils/components/apps/editor/component/default-codes.d.ts +3 -0
  17. package/dist/sharedUtils/components/apps/editor/component/index.d.ts +3 -0
  18. package/dist/sharedUtils/components/apps/editor/component/sets.d.ts +7 -0
  19. package/dist/sharedUtils/components/apps/editor/componentsPanel/componentDefaultProps.d.ts +3 -0
  20. package/dist/sharedUtils/components/apps/gridUtils.d.ts +14 -0
  21. package/dist/sharedUtils/components/apps/inputType.d.ts +178 -0
  22. package/dist/sharedUtils/components/apps/rx.d.ts +29 -0
  23. package/dist/sharedUtils/components/apps/sharedTypes.d.ts +21 -0
  24. package/dist/sharedUtils/components/apps/types.d.ts +274 -0
  25. package/dist/sharedUtils/components/assets/lib.d.ts +25 -0
  26. package/dist/sharedUtils/components/common/alert/model.d.ts +2 -0
  27. package/dist/sharedUtils/components/common/badge/model.d.ts +8 -0
  28. package/dist/sharedUtils/components/common/button/model.d.ts +45 -0
  29. package/dist/sharedUtils/components/common/fileInput/model.d.ts +1 -0
  30. package/dist/sharedUtils/components/common/index.d.ts +24 -0
  31. package/dist/sharedUtils/components/common/skeleton/model.d.ts +21 -0
  32. package/dist/sharedUtils/components/dbTypes.d.ts +14 -0
  33. package/dist/sharedUtils/components/diff_drawer.d.ts +26 -0
  34. package/dist/sharedUtils/components/ducklake.d.ts +1 -0
  35. package/dist/sharedUtils/components/flows/scheduleUtils.d.ts +7 -0
  36. package/dist/sharedUtils/components/icons/index.d.ts +101 -0
  37. package/dist/sharedUtils/components/random_positive_adjetive.d.ts +1 -0
  38. package/dist/sharedUtils/components/raw_apps/rawAppPolicy.d.ts +10 -0
  39. package/dist/sharedUtils/components/raw_apps/utils.d.ts +15 -0
  40. package/dist/sharedUtils/components/triggers/email/utils.d.ts +4 -0
  41. package/dist/sharedUtils/components/triggers/gcp/utils.d.ts +2 -0
  42. package/dist/sharedUtils/components/triggers/http/utils.d.ts +11 -0
  43. package/dist/sharedUtils/components/triggers/kafka/utils.d.ts +2 -0
  44. package/dist/sharedUtils/components/triggers/mqtt/utils.d.ts +2 -0
  45. package/dist/sharedUtils/components/triggers/nats/utils.d.ts +2 -0
  46. package/dist/sharedUtils/components/triggers/postgres/utils.d.ts +8 -0
  47. package/dist/sharedUtils/components/triggers/sqs/utils.d.ts +2 -0
  48. package/dist/sharedUtils/components/triggers/triggers.svelte.d.ts +32 -0
  49. package/dist/sharedUtils/components/triggers/utils.d.ts +80 -0
  50. package/dist/sharedUtils/components/triggers/websocket/utils.d.ts +2 -0
  51. package/dist/sharedUtils/components/triggers.d.ts +20 -0
  52. package/dist/sharedUtils/gen/core/ApiError.d.ts +10 -0
  53. package/dist/sharedUtils/gen/core/ApiRequestOptions.d.ts +13 -0
  54. package/dist/sharedUtils/gen/core/ApiResult.d.ts +7 -0
  55. package/dist/sharedUtils/gen/core/CancelablePromise.d.ts +26 -0
  56. package/dist/sharedUtils/gen/core/OpenAPI.d.ts +27 -0
  57. package/dist/sharedUtils/gen/core/request.d.ts +29 -0
  58. package/dist/sharedUtils/gen/index.d.ts +6 -0
  59. package/dist/sharedUtils/gen/schemas.gen.d.ts +7036 -0
  60. package/dist/sharedUtils/gen/services.gen.d.ts +6047 -0
  61. package/dist/sharedUtils/gen/types.gen.d.ts +21881 -0
  62. package/dist/sharedUtils/history.svelte.d.ts +9 -0
  63. package/dist/sharedUtils/hub.d.ts +49 -0
  64. package/dist/sharedUtils/jsr.json +6 -0
  65. package/dist/sharedUtils/lib.d.ts +5 -0
  66. package/dist/sharedUtils/lib.es.js +1588 -0
  67. package/dist/sharedUtils/package.json +12 -0
  68. package/dist/sharedUtils/schema.d.ts +3 -0
  69. package/dist/sharedUtils/stores.d.ts +97 -0
  70. package/dist/sharedUtils/svelte5Utils.svelte.d.ts +80 -0
  71. package/dist/sharedUtils/toast.d.ts +8 -0
  72. package/dist/sharedUtils/utils.d.ts +265 -0
  73. package/package/components/AddUser.svelte +67 -34
  74. package/package/components/AppConnectInner.svelte +9 -1
  75. package/package/components/ArgInfo.svelte +9 -1
  76. package/package/components/ArgInput.svelte +21 -1
  77. package/package/components/CompareWorkspaces.svelte +11 -2
  78. package/package/components/DedicatedWorkersSelector.svelte +262 -247
  79. package/package/components/DefaultTagsInner.svelte +40 -2
  80. package/package/components/DeployWorkspace.svelte +13 -0
  81. package/package/components/DiffEditor.svelte +44 -1
  82. package/package/components/EditableSchemaForm.svelte +5 -2
  83. package/package/components/EditableSchemaForm.svelte.d.ts +1 -0
  84. package/package/components/Editor.svelte +5 -1
  85. package/package/components/EditorBar.svelte +12 -3
  86. package/package/components/FilterSearchbar.svelte +26 -2
  87. package/package/components/FlowBuilder.svelte +6 -3
  88. package/package/components/FlowGraphDiffViewer.svelte +16 -17
  89. package/package/components/FlowGraphViewer.svelte +20 -6
  90. package/package/components/FlowGraphViewer.svelte.d.ts +2 -0
  91. package/package/components/FlowGraphViewerStep.svelte +14 -32
  92. package/package/components/FlowMetadata.svelte +4 -1
  93. package/package/components/FlowPreviewContent.svelte +2 -0
  94. package/package/components/FlowStatusWaitingForEvents.svelte +25 -4
  95. package/package/components/HighlightCode.svelte +3 -0
  96. package/package/components/InstanceSetting.svelte +9 -25
  97. package/package/components/InstanceSettings.svelte +16 -0
  98. package/package/components/LabelsInput.svelte +149 -0
  99. package/package/components/LabelsInput.svelte.d.ts +8 -0
  100. package/package/components/Login.svelte +6 -1
  101. package/package/components/ObjectStoreConfigSettings.svelte +273 -1
  102. package/package/components/OktaSetting.svelte +6 -5
  103. package/package/components/Password.svelte +74 -20
  104. package/package/components/Password.svelte.d.ts +1 -0
  105. package/package/components/PasswordArgInput.svelte +2 -2
  106. package/package/components/PasswordArgInput.svelte.d.ts +1 -0
  107. package/package/components/Path.svelte +8 -10
  108. package/package/components/PathNameAutocomplete.svelte +308 -0
  109. package/package/components/PathNameAutocomplete.svelte.d.ts +27 -0
  110. package/package/components/PowerShellCommonParams.svelte +84 -0
  111. package/package/components/PowerShellCommonParams.svelte.d.ts +6 -0
  112. package/package/components/Range.svelte +8 -3
  113. package/package/components/ResourceEditor.svelte +6 -2
  114. package/package/components/RunForm.svelte +71 -7
  115. package/package/components/RunForm.svelte.d.ts +2 -1
  116. package/package/components/ScriptBuilder.svelte +7 -3
  117. package/package/components/ScriptEditor.svelte +221 -187
  118. package/package/components/ScriptEditor.svelte.d.ts +1 -1
  119. package/package/components/ScriptSchema.svelte +1 -1
  120. package/package/components/StringTypeNarrowing.svelte +1 -1
  121. package/package/components/StringTypeNarrowing.svelte.d.ts +1 -1
  122. package/package/components/SummaryPathDisplay.svelte +32 -10
  123. package/package/components/SummaryPathDisplay.svelte.d.ts +2 -1
  124. package/package/components/VariableEditor.svelte +9 -2
  125. package/package/components/WorkerGroup.svelte +47 -2
  126. package/package/components/apps/editor/DeploymentHistory.svelte +112 -13
  127. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptRunnableByPath.svelte +135 -35
  128. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptRunnableByPath.svelte.d.ts +3 -1
  129. package/package/components/apps/editor/settingsPanel/mainInput/RunnableSelector.svelte +11 -35
  130. package/package/components/apps/editor/settingsPanel/mainInput/runnableSelectorUtils.d.ts +10 -0
  131. package/package/components/apps/editor/settingsPanel/mainInput/runnableSelectorUtils.js +14 -0
  132. package/package/components/apps/editor/settingsPanel/mainInput/runnableSelectorUtils.test.d.ts +1 -0
  133. package/package/components/apps/editor/settingsPanel/mainInput/runnableSelectorUtils.test.js +34 -0
  134. package/package/components/assets/AssetButtons.svelte +21 -25
  135. package/package/components/assets/AssetsUsageDrawer.svelte +7 -9
  136. package/package/components/common/fileUpload/FileUpload.svelte +6 -2
  137. package/package/components/common/languageIcons/LanguageIcon.svelte +3 -0
  138. package/package/components/common/table/AppRow.svelte +18 -0
  139. package/package/components/common/table/FlowRow.svelte +18 -0
  140. package/package/components/common/table/ScriptRow.svelte +18 -0
  141. package/package/components/copilot/chat/AIChatManager.svelte.js +3 -3
  142. package/package/components/copilot/chat/flow/openFlow.json +1 -1
  143. package/package/components/copilot/chat/flow/openFlowZod.js +3 -3
  144. package/package/components/custom_ui.d.ts +2 -0
  145. package/package/components/details/DetailPageHeader.svelte +2 -2
  146. package/package/components/details/DetailPageHeader.svelte.d.ts +2 -1
  147. package/package/components/flows/agentToolUtils.d.ts +5 -0
  148. package/package/components/flows/agentToolUtils.js +49 -0
  149. package/package/components/flows/agentToolUtils.test.d.ts +1 -0
  150. package/package/components/flows/agentToolUtils.test.js +55 -0
  151. package/package/components/flows/content/FlowInput.svelte +2 -0
  152. package/package/components/flows/content/FlowInputsQuick.svelte +1 -1
  153. package/package/components/flows/content/FlowLoop.svelte +5 -12
  154. package/package/components/flows/content/FlowModuleScript.svelte +5 -3
  155. package/package/components/flows/content/FlowPathViewer.svelte +2 -2
  156. package/package/components/flows/content/FlowPathViewer.svelte.d.ts +1 -0
  157. package/package/components/flows/content/FlowSettings.svelte +2 -0
  158. package/package/components/flows/content/FlowWhileLoop.svelte +5 -12
  159. package/package/components/flows/flowInfers.js +8 -3
  160. package/package/components/flows/flowStore.svelte.d.ts +1 -1
  161. package/package/components/flows/map/FlowModuleSchemaMap.svelte +49 -9
  162. package/package/components/flows/pickers/PickHubScriptQuick.svelte +5 -3
  163. package/package/components/flows/scheduleUtils.js +2 -1
  164. package/package/components/graph/FlowGraphV2.svelte +13 -1
  165. package/package/components/graph/WacDiagram.svelte +96 -0
  166. package/package/components/graph/WacDiagram.svelte.d.ts +7 -0
  167. package/package/components/graph/noteEditor.svelte.d.ts +1 -1
  168. package/package/components/graph/noteEditor.svelte.js +12 -1
  169. package/package/components/graph/noteUtils.svelte.d.ts +1 -1
  170. package/package/components/graph/noteUtils.svelte.js +9 -2
  171. package/package/components/graph/renderers/edges/WacEdge.svelte +41 -0
  172. package/package/components/graph/renderers/edges/WacEdge.svelte.d.ts +4 -0
  173. package/package/components/graph/renderers/nodes/WacControlNode.svelte +51 -0
  174. package/package/components/graph/renderers/nodes/WacControlNode.svelte.d.ts +9 -0
  175. package/package/components/graph/renderers/nodes/WacStepNode.svelte +35 -0
  176. package/package/components/graph/renderers/nodes/WacStepNode.svelte.d.ts +9 -0
  177. package/package/components/graph/wacDagLayout.d.ts +10 -0
  178. package/package/components/graph/wacDagLayout.js +120 -0
  179. package/package/components/graph/wacToFlow.js +1 -1
  180. package/package/components/home/ItemsList.svelte +28 -4
  181. package/package/components/icons/RIcon.svelte +32 -0
  182. package/package/components/icons/RIcon.svelte.d.ts +7 -0
  183. package/package/components/instanceSettings/DbHealth.svelte +723 -0
  184. package/package/components/instanceSettings/DbHealth.svelte.d.ts +3 -0
  185. package/package/components/instanceSettings/SecretBackendConfig.svelte +343 -304
  186. package/package/components/instanceSettings/SmtpSettings.svelte +8 -0
  187. package/package/components/instanceSettings.js +14 -5
  188. package/package/components/mcp/McpScopeSelector.svelte +82 -16
  189. package/package/components/moveRenameManager.d.ts +1 -0
  190. package/package/components/moveRenameManager.js +7 -4
  191. package/package/components/raw_apps/RawAppInlineScriptRunnable.svelte +14 -1
  192. package/package/components/raw_apps/rawAppPolicy.js +3 -2
  193. package/package/components/raw_apps/utils.test.d.ts +1 -0
  194. package/package/components/raw_apps/utils.test.js +38 -0
  195. package/package/components/resources/resourcesFilter.d.ts +15 -2
  196. package/package/components/resources/resourcesFilter.js +11 -2
  197. package/package/components/runs/JobDetailFieldConfig.js +5 -3
  198. package/package/components/runs/JobDetailHeader.svelte +5 -2
  199. package/package/components/runs/JobRunsPreview.svelte +1 -0
  200. package/package/components/runs/RunBadges.svelte +7 -4
  201. package/package/components/runs/RunRow.svelte +7 -7
  202. package/package/components/schedules/schedulesFilter.d.ts +15 -2
  203. package/package/components/schedules/schedulesFilter.js +11 -2
  204. package/package/components/schema/EditableSchemaWrapper.svelte +6 -8
  205. package/package/components/schema/PropertyEditor.svelte +22 -1
  206. package/package/components/schema/PropertyEditor.svelte.d.ts +1 -0
  207. package/package/components/schema/editable_schema_wrapper.d.ts +1 -0
  208. package/package/components/secretArgUtils.d.ts +7 -0
  209. package/package/components/secretArgUtils.js +45 -0
  210. package/package/components/settings/WorkspaceUserSettings.svelte +359 -286
  211. package/package/components/sidebar/OperatorMenu.svelte +215 -197
  212. package/package/components/triggers/CaptureWrapper.svelte +1 -1
  213. package/package/components/triggers/TriggerFilters.svelte +17 -5
  214. package/package/components/triggers/TriggerFilters.svelte.d.ts +2 -1
  215. package/package/components/triggers/kafka/KafkaCapture.svelte +6 -2
  216. package/package/components/triggers/kafka/KafkaCapture.svelte.d.ts +1 -1
  217. package/package/components/triggers/kafka/KafkaTriggerEditorInner.svelte +5 -1
  218. package/package/components/triggers/kafka/utils.js +1 -0
  219. package/package/components/triggers/mqtt/MqttEditorConfigSection.svelte.d.ts +1 -1
  220. package/package/components/triggers/schedules/ScheduleEditorInner.svelte +6 -0
  221. package/package/components/triggers/websocket/WebsocketTriggerEditorInner.svelte +87 -1
  222. package/package/components/triggers/websocket/utils.js +2 -0
  223. package/package/components/variables/variablesFilter.d.ts +15 -2
  224. package/package/components/variables/variablesFilter.js +11 -2
  225. package/package/components/worker_group.js +1 -0
  226. package/package/components/workspaceSettings/DucklakeSettings.svelte +33 -41
  227. package/package/consts.d.ts +1 -0
  228. package/package/consts.js +1 -0
  229. package/package/editorLangUtils.d.ts +1 -1
  230. package/package/editorLangUtils.js +2 -0
  231. package/package/gen/core/OpenAPI.js +1 -1
  232. package/package/gen/schemas.gen.d.ts +346 -4
  233. package/package/gen/schemas.gen.js +347 -5
  234. package/package/gen/services.gen.d.ts +163 -1
  235. package/package/gen/services.gen.js +323 -17
  236. package/package/gen/types.gen.d.ts +671 -5
  237. package/package/hubPaths.json +6 -3
  238. package/package/infer.d.ts +55 -0
  239. package/package/infer.js +131 -0
  240. package/package/infer.svelte.js +2 -0
  241. package/package/mcpEndpointTools.js +213 -22
  242. package/package/script_helpers.d.ts +3 -0
  243. package/package/script_helpers.js +26 -0
  244. package/package/scripts.d.ts +2 -1
  245. package/package/scripts.js +15 -3
  246. package/package/stores.d.ts +2 -0
  247. package/package/system_prompts/prompts.d.ts +6 -5
  248. package/package/system_prompts/prompts.js +188 -29
  249. package/package/user.js +5 -1
  250. package/package/utils.js +21 -0
  251. package/package/utils_deployable.d.ts +11 -0
  252. package/package/utils_workspace_deploy.js +36 -8
  253. package/package.json +5 -4
@@ -3,28 +3,23 @@ import Password from '../Password.svelte';
3
3
  import { SettingService } from '../../gen';
4
4
  import { sendUserToast } from '../../toast';
5
5
  import TextInput from '../text_input/TextInput.svelte';
6
- import { Database, Lock, Server, ArrowLeft, ArrowRight } from 'lucide-svelte';
6
+ import { Database, Lock, Server, ArrowLeft, ArrowRight, Cloud } from 'lucide-svelte';
7
7
  import { enterpriseLicense } from '../../stores';
8
8
  import ToggleButtonGroup from '../common/toggleButton-v2/ToggleButtonGroup.svelte';
9
9
  import ToggleButton from '../common/toggleButton-v2/ToggleButton.svelte';
10
10
  import ConfirmationModal from '../common/confirmationModal/ConfirmationModal.svelte';
11
11
  import EEOnly from '../EEOnly.svelte';
12
12
  let { values, disabled = false } = $props();
13
- // Initialize default values if not set
14
13
  $effect(() => {
15
14
  if (!$values['secret_backend']) {
16
15
  $values['secret_backend'] = { type: 'Database' };
17
16
  }
18
17
  });
19
18
  let selectedType = $derived($values['secret_backend']?.type ?? 'Database');
20
- // Derive auth method from current config
21
- // We check jwt_role === null because setAuthMethod explicitly sets jwt_role to null for token mode
22
- // and sets token to null for jwt mode. This allows empty token values while still tracking the selection.
23
19
  let authMethod = $derived.by(() => {
24
20
  const config = $values['secret_backend'];
25
21
  if (!config || config.type !== 'HashiCorpVault')
26
22
  return 'jwt';
27
- // If jwt_role is explicitly null, we're in token mode; otherwise jwt mode
28
23
  return config.jwt_role === null ? 'token' : 'jwt';
29
24
  });
30
25
  let testingConnection = $state(false);
@@ -32,15 +27,22 @@ let migratingToVault = $state(false);
32
27
  let migratingToDatabase = $state(false);
33
28
  let migrateToVaultModalOpen = $state(false);
34
29
  let migrateToDatabaseModalOpen = $state(false);
35
- // Check if Vault option should be disabled (non-EE)
30
+ let testingAzureKvConnection = $state(false);
31
+ let migratingToAzureKv = $state(false);
32
+ let migratingFromAzureKv = $state(false);
33
+ let migrateToAzureKvModalOpen = $state(false);
34
+ let migrateFromAzureKvModalOpen = $state(false);
35
+ let testingAwsSmConnection = $state(false);
36
+ let migratingToAwsSm = $state(false);
37
+ let migratingFromAwsSm = $state(false);
38
+ let migrateToAwsSmModalOpen = $state(false);
39
+ let migrateFromAwsSmModalOpen = $state(false);
36
40
  let vaultDisabled = $derived(!$enterpriseLicense);
37
41
  function setBackendType(type) {
38
42
  if (!type)
39
43
  return;
40
- // Prevent selecting Vault in non-EE
41
- if (type === 'HashiCorpVault' && vaultDisabled) {
44
+ if ((type === 'HashiCorpVault' || type === 'AzureKeyVault' || type === 'AwsSecretsManager') && vaultDisabled)
42
45
  return;
43
- }
44
46
  if (type === 'Database') {
45
47
  $values['secret_backend'] = { type: 'Database' };
46
48
  }
@@ -54,27 +56,35 @@ function setBackendType(type) {
54
56
  token: $values['secret_backend']?.token ?? null
55
57
  };
56
58
  }
59
+ else if (type === 'AzureKeyVault') {
60
+ $values['secret_backend'] = {
61
+ type: 'AzureKeyVault',
62
+ vault_url: $values['secret_backend']?.vault_url ?? '',
63
+ tenant_id: $values['secret_backend']?.tenant_id ?? '',
64
+ client_id: $values['secret_backend']?.client_id ?? '',
65
+ client_secret: $values['secret_backend']?.client_secret ?? null,
66
+ token: $values['secret_backend']?.token ?? null
67
+ };
68
+ }
69
+ else if (type === 'AwsSecretsManager') {
70
+ $values['secret_backend'] = {
71
+ type: 'AwsSecretsManager',
72
+ region: $values['secret_backend']?.region ?? 'us-east-1',
73
+ access_key_id: $values['secret_backend']?.access_key_id ?? null,
74
+ secret_access_key: $values['secret_backend']?.secret_access_key ?? null,
75
+ endpoint_url: $values['secret_backend']?.endpoint_url ?? null,
76
+ prefix: $values['secret_backend']?.prefix ?? 'windmill/'
77
+ };
78
+ }
57
79
  }
58
80
  function setAuthMethod(method) {
59
- if (!method ||
60
- !$values['secret_backend'] ||
61
- $values['secret_backend'].type !== 'HashiCorpVault')
81
+ if (!method || !$values['secret_backend'] || $values['secret_backend'].type !== 'HashiCorpVault')
62
82
  return;
63
83
  if (method === 'token') {
64
- // Clear JWT role when switching to token auth
65
- $values['secret_backend'] = {
66
- ...$values['secret_backend'],
67
- jwt_role: null,
68
- token: $values['secret_backend'].token ?? ''
69
- };
84
+ $values['secret_backend'] = { ...$values['secret_backend'], jwt_role: null, token: $values['secret_backend'].token ?? '' };
70
85
  }
71
86
  else if (method === 'jwt') {
72
- // Clear token when switching to JWT auth
73
- $values['secret_backend'] = {
74
- ...$values['secret_backend'],
75
- token: null,
76
- jwt_role: $values['secret_backend'].jwt_role ?? 'windmill-secrets'
77
- };
87
+ $values['secret_backend'] = { ...$values['secret_backend'], token: null, jwt_role: $values['secret_backend'].jwt_role ?? 'windmill-secrets' };
78
88
  }
79
89
  }
80
90
  function getVaultSettings() {
@@ -87,14 +97,11 @@ function getVaultSettings() {
87
97
  };
88
98
  }
89
99
  async function testVaultConnection() {
90
- if (!$values['secret_backend'] || $values['secret_backend'].type !== 'HashiCorpVault') {
100
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'HashiCorpVault')
91
101
  return;
92
- }
93
102
  testingConnection = true;
94
103
  try {
95
- await SettingService.testSecretBackend({
96
- requestBody: getVaultSettings()
97
- });
104
+ await SettingService.testSecretBackend({ requestBody: getVaultSettings() });
98
105
  sendUserToast('Successfully connected to HashiCorp Vault');
99
106
  }
100
107
  catch (error) {
@@ -105,24 +112,18 @@ async function testVaultConnection() {
105
112
  }
106
113
  }
107
114
  async function migrateSecretsToVault() {
108
- if (!$values['secret_backend'] || $values['secret_backend'].type !== 'HashiCorpVault') {
115
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'HashiCorpVault')
109
116
  return;
110
- }
111
117
  migratingToVault = true;
112
118
  try {
113
- const report = await SettingService.migrateSecretsToVault({
114
- requestBody: getVaultSettings()
115
- });
116
- if (report.failed_count > 0) {
117
- sendUserToast(`Migration completed with errors: ${report.migrated_count}/${report.total_secrets} secrets migrated, ${report.failed_count} failed`, true);
118
- console.error('Migration failures:', report.failures);
119
- }
120
- else {
121
- sendUserToast(`Successfully migrated ${report.migrated_count}/${report.total_secrets} secrets to Vault`);
122
- }
119
+ const report = await SettingService.migrateSecretsToVault({ requestBody: getVaultSettings() });
120
+ if (report.failed_count > 0)
121
+ sendUserToast(`Migration: ${report.migrated_count}/${report.total_secrets} migrated, ${report.failed_count} failed`, true);
122
+ else
123
+ sendUserToast(`Migrated ${report.migrated_count}/${report.total_secrets} secrets to Vault`);
123
124
  }
124
125
  catch (error) {
125
- sendUserToast('Failed to migrate secrets to Vault: ' + error.message, true);
126
+ sendUserToast('Failed: ' + error.message, true);
126
127
  }
127
128
  finally {
128
129
  migratingToVault = false;
@@ -130,24 +131,18 @@ async function migrateSecretsToVault() {
130
131
  }
131
132
  }
132
133
  async function migrateSecretsToDatabase() {
133
- if (!$values['secret_backend'] || $values['secret_backend'].type !== 'HashiCorpVault') {
134
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'HashiCorpVault')
134
135
  return;
135
- }
136
136
  migratingToDatabase = true;
137
137
  try {
138
- const report = await SettingService.migrateSecretsToDatabase({
139
- requestBody: getVaultSettings()
140
- });
141
- if (report.failed_count > 0) {
142
- sendUserToast(`Migration completed with errors: ${report.migrated_count}/${report.total_secrets} secrets migrated, ${report.failed_count} failed`, true);
143
- console.error('Migration failures:', report.failures);
144
- }
145
- else {
146
- sendUserToast(`Successfully migrated ${report.migrated_count}/${report.total_secrets} secrets to database`);
147
- }
138
+ const report = await SettingService.migrateSecretsToDatabase({ requestBody: getVaultSettings() });
139
+ if (report.failed_count > 0)
140
+ sendUserToast(`Migration: ${report.migrated_count}/${report.total_secrets} migrated, ${report.failed_count} failed`, true);
141
+ else
142
+ sendUserToast(`Migrated ${report.migrated_count}/${report.total_secrets} secrets to database`);
148
143
  }
149
144
  catch (error) {
150
- sendUserToast('Failed to migrate secrets to database: ' + error.message, true);
145
+ sendUserToast('Failed: ' + error.message, true);
151
146
  }
152
147
  finally {
153
148
  migratingToDatabase = false;
@@ -155,45 +150,167 @@ async function migrateSecretsToDatabase() {
155
150
  }
156
151
  }
157
152
  function isVaultConfigValid() {
158
- if (!$values['secret_backend'] || $values['secret_backend'].type !== 'HashiCorpVault') {
153
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'HashiCorpVault')
159
154
  return false;
160
- }
161
155
  const hasAddress = $values['secret_backend'].address?.trim() !== '';
162
156
  const hasMountPath = $values['secret_backend'].mount_path?.trim() !== '';
163
157
  const hasToken = $values['secret_backend'].token?.trim();
164
158
  const hasJwtRole = $values['secret_backend'].jwt_role?.trim();
165
- // Must have address and mount path, plus either token OR jwt_role (not both)
166
159
  return hasAddress && hasMountPath && (hasToken || hasJwtRole);
167
160
  }
168
- // Get the base URL for JWKS endpoint instructions (from instance settings)
161
+ function getAzureKvSettings() {
162
+ return {
163
+ vault_url: $values['secret_backend'].vault_url,
164
+ tenant_id: $values['secret_backend'].tenant_id,
165
+ client_id: $values['secret_backend'].client_id,
166
+ client_secret: $values['secret_backend'].client_secret || undefined,
167
+ token: $values['secret_backend'].token || undefined
168
+ };
169
+ }
170
+ async function testAzureKvConnection() {
171
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'AzureKeyVault')
172
+ return;
173
+ testingAzureKvConnection = true;
174
+ try {
175
+ await SettingService.testAzureKvBackend({ requestBody: getAzureKvSettings() });
176
+ sendUserToast('Successfully connected to Azure Key Vault');
177
+ }
178
+ catch (error) {
179
+ sendUserToast('Failed: ' + error.message, true);
180
+ }
181
+ finally {
182
+ testingAzureKvConnection = false;
183
+ }
184
+ }
185
+ async function migrateSecretsToAzureKv() {
186
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'AzureKeyVault')
187
+ return;
188
+ migratingToAzureKv = true;
189
+ try {
190
+ const report = await SettingService.migrateSecretsToAzureKv({ requestBody: getAzureKvSettings() });
191
+ if (report.failed_count > 0)
192
+ sendUserToast(`Migration: ${report.migrated_count}/${report.total_secrets} migrated, ${report.failed_count} failed`, true);
193
+ else
194
+ sendUserToast(`Migrated ${report.migrated_count}/${report.total_secrets} secrets to Azure Key Vault`);
195
+ }
196
+ catch (error) {
197
+ sendUserToast('Failed: ' + error.message, true);
198
+ }
199
+ finally {
200
+ migratingToAzureKv = false;
201
+ migrateToAzureKvModalOpen = false;
202
+ }
203
+ }
204
+ async function migrateSecretsFromAzureKv() {
205
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'AzureKeyVault')
206
+ return;
207
+ migratingFromAzureKv = true;
208
+ try {
209
+ const report = await SettingService.migrateSecretsFromAzureKv({ requestBody: getAzureKvSettings() });
210
+ if (report.failed_count > 0)
211
+ sendUserToast(`Migration: ${report.migrated_count}/${report.total_secrets} migrated, ${report.failed_count} failed`, true);
212
+ else
213
+ sendUserToast(`Migrated ${report.migrated_count}/${report.total_secrets} secrets to database`);
214
+ }
215
+ catch (error) {
216
+ sendUserToast('Failed: ' + error.message, true);
217
+ }
218
+ finally {
219
+ migratingFromAzureKv = false;
220
+ migrateFromAzureKvModalOpen = false;
221
+ }
222
+ }
223
+ function isAzureKvConfigValid() {
224
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'AzureKeyVault')
225
+ return false;
226
+ return ($values['secret_backend'].vault_url?.trim() !== '' &&
227
+ $values['secret_backend'].tenant_id?.trim() !== '' &&
228
+ $values['secret_backend'].client_id?.trim() !== '' &&
229
+ (!!$values['secret_backend'].client_secret?.trim() || !!$values['secret_backend'].token?.trim()));
230
+ }
231
+ function getAwsSmSettings() {
232
+ return {
233
+ region: $values['secret_backend'].region,
234
+ access_key_id: $values['secret_backend'].access_key_id || undefined,
235
+ secret_access_key: $values['secret_backend'].secret_access_key || undefined,
236
+ endpoint_url: $values['secret_backend'].endpoint_url || undefined,
237
+ prefix: $values['secret_backend'].prefix || undefined
238
+ };
239
+ }
240
+ async function testAwsSmConnection() {
241
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'AwsSecretsManager')
242
+ return;
243
+ testingAwsSmConnection = true;
244
+ try {
245
+ await SettingService.testAwsSmBackend({ requestBody: getAwsSmSettings() });
246
+ sendUserToast('Successfully connected to AWS Secrets Manager');
247
+ }
248
+ catch (error) {
249
+ sendUserToast('Failed: ' + error.message, true);
250
+ }
251
+ finally {
252
+ testingAwsSmConnection = false;
253
+ }
254
+ }
255
+ async function migrateSecretsToAwsSm() {
256
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'AwsSecretsManager')
257
+ return;
258
+ migratingToAwsSm = true;
259
+ try {
260
+ const report = await SettingService.migrateSecretsToAwsSm({ requestBody: getAwsSmSettings() });
261
+ if (report.failed_count > 0)
262
+ sendUserToast(`Migration: ${report.migrated_count}/${report.total_secrets} migrated, ${report.failed_count} failed`, true);
263
+ else
264
+ sendUserToast(`Migrated ${report.migrated_count}/${report.total_secrets} secrets to AWS Secrets Manager`);
265
+ }
266
+ catch (error) {
267
+ sendUserToast('Failed: ' + error.message, true);
268
+ }
269
+ finally {
270
+ migratingToAwsSm = false;
271
+ migrateToAwsSmModalOpen = false;
272
+ }
273
+ }
274
+ async function migrateSecretsFromAwsSm() {
275
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'AwsSecretsManager')
276
+ return;
277
+ migratingFromAwsSm = true;
278
+ try {
279
+ const report = await SettingService.migrateSecretsFromAwsSm({ requestBody: getAwsSmSettings() });
280
+ if (report.failed_count > 0)
281
+ sendUserToast(`Migration: ${report.migrated_count}/${report.total_secrets} migrated, ${report.failed_count} failed`, true);
282
+ else
283
+ sendUserToast(`Migrated ${report.migrated_count}/${report.total_secrets} secrets to database`);
284
+ }
285
+ catch (error) {
286
+ sendUserToast('Failed: ' + error.message, true);
287
+ }
288
+ finally {
289
+ migratingFromAwsSm = false;
290
+ migrateFromAwsSmModalOpen = false;
291
+ }
292
+ }
293
+ function isAwsSmConfigValid() {
294
+ if (!$values['secret_backend'] || $values['secret_backend'].type !== 'AwsSecretsManager')
295
+ return false;
296
+ return $values['secret_backend'].region?.trim() !== '';
297
+ }
169
298
  let baseUrl = $derived($values['base_url'] ?? 'https://your-windmill-instance.com');
170
299
  </script>
171
300
 
172
301
  <div class="space-y-6">
173
- <!-- Backend Type Selector -->
174
302
  <div class="flex flex-col gap-2 mt-1">
175
303
  <ToggleButtonGroup selected={selectedType} onSelected={(v) => setBackendType(v)}>
176
304
  {#snippet children({ item: toggleButton })}
177
- <ToggleButton
178
- value="Database"
179
- label="Database"
180
- tooltip="Store secrets encrypted in the database (default)"
181
- item={toggleButton}
182
- />
183
- <ToggleButton
184
- value="HashiCorpVault"
185
- label="HashiCorp Vault (Beta)"
186
- tooltip={vaultDisabled
187
- ? 'HashiCorp Vault integration requires Enterprise Edition'
188
- : 'Store secrets in HashiCorp Vault (Beta feature)'}
189
- item={toggleButton}
190
- disabled={vaultDisabled}
191
- />
305
+ <ToggleButton value="Database" label="Database" tooltip="Store secrets encrypted in the database (default)" item={toggleButton} />
306
+ <ToggleButton value="HashiCorpVault" label="HashiCorp Vault (Beta)" tooltip={vaultDisabled ? 'Requires Enterprise Edition' : 'Store secrets in HashiCorp Vault'} item={toggleButton} disabled={vaultDisabled} />
307
+ <ToggleButton value="AzureKeyVault" label="Azure Key Vault" tooltip={vaultDisabled ? 'Requires Enterprise Edition' : 'Store secrets in Azure Key Vault'} item={toggleButton} disabled={vaultDisabled} />
308
+ <ToggleButton value="AwsSecretsManager" label="AWS Secrets Manager (Beta)" tooltip={vaultDisabled ? 'Requires Enterprise Edition' : 'Store secrets in AWS Secrets Manager'} item={toggleButton} disabled={vaultDisabled} />
192
309
  {/snippet}
193
310
  </ToggleButtonGroup>
194
311
  {#if vaultDisabled}
195
312
  <div class="flex items-center gap-1">
196
- <EEOnly>HashiCorp Vault integration requires Enterprise Edition</EEOnly>
313
+ <EEOnly>External secret store integrations require Enterprise Edition</EEOnly>
197
314
  </div>
198
315
  {/if}
199
316
  </div>
@@ -203,125 +320,54 @@ let baseUrl = $derived($values['base_url'] ?? 'https://your-windmill-instance.co
203
320
  <Database class="text-primary" size={20} />
204
321
  <div>
205
322
  <p class="text-sm font-medium text-emphasis">Database Storage (Default)</p>
206
- <p class="text-xs text-secondary">
207
- Secrets are encrypted using workspace-specific keys and stored in the PostgreSQL database.
208
- </p>
323
+ <p class="text-xs text-secondary">Secrets are encrypted using workspace-specific keys and stored in the PostgreSQL database.</p>
209
324
  </div>
210
325
  </div>
211
326
  {:else if selectedType === 'HashiCorpVault'}
212
- <!-- Vault Configuration -->
213
327
  <div class="space-y-4 p-4 border rounded-lg">
214
328
  <div class="flex items-center gap-2 mb-4">
215
329
  <Lock class="text-primary" size={20} />
216
330
  <div>
217
- <p class="text-sm font-medium text-emphasis">
218
- HashiCorp Vault Configuration
219
- <span
220
- class="ml-2 px-1.5 py-0.5 text-2xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 rounded"
221
- >Beta</span
222
- >
223
- </p>
224
- <p class="text-xs text-secondary">
225
- Store secrets in an external HashiCorp Vault instance.
226
- </p>
331
+ <p class="text-sm font-medium text-emphasis">HashiCorp Vault Configuration <span class="ml-2 px-1.5 py-0.5 text-2xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 rounded">Beta</span></p>
332
+ <p class="text-xs text-secondary">Store secrets in an external HashiCorp Vault instance.</p>
227
333
  </div>
228
334
  </div>
229
-
230
335
  <div class="grid grid-cols-1 gap-4">
231
336
  <div class="flex flex-col gap-1">
232
- <label for="vault_address" class="block text-xs font-semibold text-emphasis"
233
- >Vault Address</label
234
- >
235
- <TextInput
236
- inputProps={{
237
- type: 'text',
238
- id: 'vault_address',
239
- placeholder: 'https://vault.company.com:8200',
240
- disabled: disabled
241
- }}
242
- bind:value={$values['secret_backend'].address}
243
- />
337
+ <label for="vault_address" class="block text-xs font-semibold text-emphasis">Vault Address</label>
338
+ <TextInput inputProps={{ type: 'text', id: 'vault_address', placeholder: 'https://vault.company.com:8200', disabled }} bind:value={$values['secret_backend'].address} />
244
339
  </div>
245
-
246
340
  <div class="flex flex-col gap-1">
247
- <label for="vault_mount_path" class="block text-xs font-semibold text-emphasis"
248
- >KV Mount Path</label
249
- >
341
+ <label for="vault_mount_path" class="block text-xs font-semibold text-emphasis">KV Mount Path</label>
250
342
  <span class="text-2xs text-secondary">The KV v2 secrets engine mount path in Vault</span>
251
- <TextInput
252
- inputProps={{
253
- type: 'text',
254
- id: 'vault_mount_path',
255
- placeholder: 'windmill',
256
- disabled: disabled
257
- }}
258
- bind:value={$values['secret_backend'].mount_path}
259
- />
343
+ <TextInput inputProps={{ type: 'text', id: 'vault_mount_path', placeholder: 'windmill', disabled }} bind:value={$values['secret_backend'].mount_path} />
260
344
  </div>
261
-
262
- <!-- Authentication Method Toggle -->
263
345
  <div class="flex flex-col gap-2">
264
346
  <span class="block text-xs font-semibold text-emphasis">Authentication Method</span>
265
347
  <ToggleButtonGroup selected={authMethod} onSelected={(v) => setAuthMethod(v)}>
266
348
  {#snippet children({ item: toggleButton })}
267
- <ToggleButton
268
- value="jwt"
269
- label="JWT Auth"
270
- tooltip="Authenticate using Windmill-signed JWTs (recommended for production)"
271
- item={toggleButton}
272
- {disabled}
273
- />
274
- <ToggleButton
275
- value="token"
276
- label="Static Token"
277
- tooltip="Use a static Vault token (for testing/development)"
278
- item={toggleButton}
279
- {disabled}
280
- />
349
+ <ToggleButton value="jwt" label="JWT Auth" tooltip="Authenticate using Windmill-signed JWTs" item={toggleButton} {disabled} />
350
+ <ToggleButton value="token" label="Static Token" tooltip="Use a static Vault token" item={toggleButton} {disabled} />
281
351
  {/snippet}
282
352
  </ToggleButtonGroup>
283
353
  </div>
284
-
285
354
  {#if authMethod === 'token'}
286
355
  <div class="flex flex-col gap-1 p-3 bg-surface-secondary rounded-lg">
287
- <label for="vault_token" class="block text-xs font-semibold text-emphasis"
288
- >Vault Token</label
289
- >
290
- <span class="text-2xs text-secondary"
291
- >Static token for authentication. Recommended only for testing/development.</span
292
- >
356
+ <label for="vault_token" class="block text-xs font-semibold text-emphasis">Vault Token</label>
357
+ <span class="text-2xs text-secondary">Static token. Recommended only for testing/development.</span>
293
358
  <Password bind:password={$values['secret_backend'].token} small {disabled} />
294
359
  </div>
295
360
  {:else}
296
361
  <div class="flex flex-col gap-2 p-3 bg-surface-secondary rounded-lg">
297
- <label for="vault_jwt_role" class="block text-xs font-semibold text-emphasis"
298
- >JWT Auth Role</label
299
- >
300
- <span class="text-2xs text-secondary"
301
- >The JWT authentication role configured in Vault.</span
302
- >
303
- <TextInput
304
- inputProps={{
305
- type: 'text',
306
- id: 'vault_jwt_role',
307
- placeholder: 'windmill-secrets',
308
- disabled: disabled
309
- }}
310
- bind:value={$values['secret_backend'].jwt_role}
311
- />
312
-
313
- <!-- Vault JWT Setup Instructions -->
362
+ <label for="vault_jwt_role" class="block text-xs font-semibold text-emphasis">JWT Auth Role</label>
363
+ <span class="text-2xs text-secondary">The JWT authentication role configured in Vault.</span>
364
+ <TextInput inputProps={{ type: 'text', id: 'vault_jwt_role', placeholder: 'windmill-secrets', disabled }} bind:value={$values['secret_backend'].jwt_role} />
314
365
  <details class="mt-2">
315
- <summary class="text-xs font-medium text-secondary cursor-pointer hover:text-primary"
316
- >Vault JWT Setup Instructions</summary
317
- >
366
+ <summary class="text-xs font-medium text-secondary cursor-pointer hover:text-primary">Vault JWT Setup Instructions</summary>
318
367
  <div class="mt-2 p-2 bg-surface rounded text-2xs text-secondary space-y-2">
319
368
  <p>Configure Vault to accept JWTs from Windmill:</p>
320
- <div
321
- class="bg-gray-100 dark:bg-gray-800 p-2 rounded font-mono text-2xs overflow-x-auto"
322
- >
323
- <pre
324
- ># Enable JWT auth method
369
+ <div class="bg-gray-100 dark:bg-gray-800 p-2 rounded font-mono text-2xs overflow-x-auto">
370
+ <pre># Enable JWT auth method
325
371
  vault auth enable jwt
326
372
 
327
373
  # Configure JWT auth with Windmill's JWKS endpoint
@@ -345,102 +391,139 @@ vault write auth/jwt/role/windmill-secrets \
345
391
  bound_audiences="{baseUrl}" \
346
392
  user_claim="email" \
347
393
  policies="windmill-secrets" \
348
- ttl="1h"</pre
349
- >
394
+ ttl="1h"</pre>
350
395
  </div>
351
- <p class="text-yellow-600 dark:text-yellow-400">
352
- Replace <code>windmill-secrets</code> with your role name if different.
353
- </p>
354
396
  </div>
355
397
  </details>
356
398
  </div>
357
399
  {/if}
358
-
359
400
  <div class="flex flex-col gap-1">
360
- <label for="vault_namespace" class="block text-xs font-semibold text-emphasis"
361
- >Namespace (optional)</label
362
- >
363
- <span class="text-2xs text-secondary"
364
- >Vault Enterprise namespace (leave empty if not using namespaces)</span
365
- >
366
- <TextInput
367
- inputProps={{
368
- type: 'text',
369
- id: 'vault_namespace',
370
- placeholder: 'admin/my-namespace',
371
- disabled: disabled
372
- }}
373
- bind:value={$values['secret_backend'].namespace}
374
- />
401
+ <label for="vault_namespace" class="block text-xs font-semibold text-emphasis">Namespace (optional)</label>
402
+ <span class="text-2xs text-secondary">Vault Enterprise namespace</span>
403
+ <TextInput inputProps={{ type: 'text', id: 'vault_namespace', placeholder: 'admin/my-namespace', disabled }} bind:value={$values['secret_backend'].namespace} />
375
404
  </div>
376
405
  </div>
377
-
378
- <!-- Action Buttons -->
379
406
  <div class="flex flex-col gap-4 pt-4 border-t">
380
- <div class="flex gap-2">
381
- <Button
382
- unifiedSize="md"
383
- variant="accent"
384
- onclick={testVaultConnection}
385
- disabled={disabled || !isVaultConfigValid() || testingConnection}
386
- loading={testingConnection}
387
- startIcon={{ icon: Server }}
388
- >
389
- Test Connection
390
- </Button>
391
- </div>
392
-
393
- <!-- Migration Section -->
407
+ <Button unifiedSize="md" variant="accent" onclick={testVaultConnection} disabled={disabled || !isVaultConfigValid() || testingConnection} loading={testingConnection} startIcon={{ icon: Server }}>Test Connection</Button>
394
408
  <div class="flex flex-col gap-4 pt-4 border-t">
395
409
  <span class="block text-xs font-semibold text-emphasis">Secret Migration</span>
396
- <span class="text-2xs text-secondary">
397
- Migrate secrets between the database and HashiCorp Vault. Original values are NOT
398
- deleted to allow for rollback.
399
- </span>
400
-
410
+ <span class="text-2xs text-secondary">Original values are NOT deleted to allow for rollback.</span>
401
411
  <div class="flex gap-4">
402
- <!-- Database to Vault -->
403
412
  <div class="flex-1 p-3 border rounded-lg">
404
- <div class="flex items-center gap-2 mb-2">
405
- <Database size={16} />
406
- <ArrowRight size={16} />
407
- <Lock size={16} />
408
- </div>
413
+ <div class="flex items-center gap-2 mb-2"><Database size={16} /><ArrowRight size={16} /><Lock size={16} /></div>
409
414
  <p class="text-xs font-medium mb-2">Database → Vault</p>
410
- <p class="text-2xs text-secondary mb-3">
411
- Decrypt secrets from database and store in Vault
412
- </p>
413
- <Button
414
- unifiedSize="sm"
415
- variant="default"
416
- onclick={() => (migrateToVaultModalOpen = true)}
417
- disabled={disabled || !isVaultConfigValid() || migratingToVault}
418
- startIcon={{ icon: ArrowRight }}
419
- >
420
- Migrate to Vault
421
- </Button>
415
+ <Button unifiedSize="sm" variant="default" onclick={() => (migrateToVaultModalOpen = true)} disabled={disabled || !isVaultConfigValid() || migratingToVault} startIcon={{ icon: ArrowRight }}>Migrate to Vault</Button>
422
416
  </div>
423
-
424
- <!-- Vault to Database -->
425
417
  <div class="flex-1 p-3 border rounded-lg">
426
- <div class="flex items-center gap-2 mb-2">
427
- <Lock size={16} />
428
- <ArrowLeft size={16} />
429
- <Database size={16} />
430
- </div>
418
+ <div class="flex items-center gap-2 mb-2"><Lock size={16} /><ArrowLeft size={16} /><Database size={16} /></div>
431
419
  <p class="text-xs font-medium mb-2">Vault → Database</p>
432
- <p class="text-2xs text-secondary mb-3">
433
- Read secrets from Vault and encrypt in database
434
- </p>
435
- <Button
436
- unifiedSize="sm"
437
- variant="default"
438
- onclick={() => (migrateToDatabaseModalOpen = true)}
439
- disabled={disabled || !isVaultConfigValid() || migratingToDatabase}
440
- startIcon={{ icon: ArrowLeft }}
441
- >
442
- Migrate to Database
443
- </Button>
420
+ <Button unifiedSize="sm" variant="default" onclick={() => (migrateToDatabaseModalOpen = true)} disabled={disabled || !isVaultConfigValid() || migratingToDatabase} startIcon={{ icon: ArrowLeft }}>Migrate to Database</Button>
421
+ </div>
422
+ </div>
423
+ </div>
424
+ </div>
425
+ </div>
426
+ {:else if selectedType === 'AzureKeyVault'}
427
+ <div class="space-y-4 p-4 border rounded-lg">
428
+ <div class="flex items-center gap-2 mb-4">
429
+ <Cloud class="text-primary" size={20} />
430
+ <div>
431
+ <p class="text-sm font-medium text-emphasis">Azure Key Vault Configuration</p>
432
+ <p class="text-xs text-secondary">Store secrets in an Azure Key Vault instance.</p>
433
+ </div>
434
+ </div>
435
+ <div class="grid grid-cols-1 gap-4">
436
+ <div class="flex flex-col gap-1">
437
+ <label for="azure_vault_url" class="block text-xs font-semibold text-emphasis">Vault URL</label>
438
+ <TextInput inputProps={{ type: 'text', id: 'azure_vault_url', placeholder: 'https://my-vault.vault.azure.net', disabled }} bind:value={$values['secret_backend'].vault_url} />
439
+ </div>
440
+ <div class="flex flex-col gap-1">
441
+ <label for="azure_tenant_id" class="block text-xs font-semibold text-emphasis">Tenant ID</label>
442
+ <TextInput inputProps={{ type: 'text', id: 'azure_tenant_id', placeholder: '00000000-0000-0000-0000-000000000000', disabled }} bind:value={$values['secret_backend'].tenant_id} />
443
+ </div>
444
+ <div class="flex flex-col gap-1">
445
+ <label for="azure_client_id" class="block text-xs font-semibold text-emphasis">Client ID</label>
446
+ <TextInput inputProps={{ type: 'text', id: 'azure_client_id', placeholder: '00000000-0000-0000-0000-000000000000', disabled }} bind:value={$values['secret_backend'].client_id} />
447
+ </div>
448
+ <div class="flex flex-col gap-1 p-3 bg-surface-secondary rounded-lg">
449
+ <label for="azure_client_secret" class="block text-xs font-semibold text-emphasis">Client Secret</label>
450
+ <Password bind:password={$values['secret_backend'].client_secret} small {disabled} />
451
+ </div>
452
+ <div class="flex flex-col gap-1 p-3 bg-surface-secondary rounded-lg">
453
+ <label for="azure_token" class="block text-xs font-semibold text-emphasis">Token (optional)</label>
454
+ <span class="text-2xs text-secondary">Static Bearer token for testing. If provided, OAuth2 is skipped.</span>
455
+ <Password bind:password={$values['secret_backend'].token} small {disabled} />
456
+ </div>
457
+ </div>
458
+ <div class="flex flex-col gap-4 pt-4 border-t">
459
+ <Button unifiedSize="md" variant="accent" onclick={testAzureKvConnection} disabled={disabled || !isAzureKvConfigValid() || testingAzureKvConnection} loading={testingAzureKvConnection} startIcon={{ icon: Server }}>Test Connection</Button>
460
+ <div class="flex flex-col gap-4 pt-4 border-t">
461
+ <span class="block text-xs font-semibold text-emphasis">Secret Migration</span>
462
+ <span class="text-2xs text-secondary">Original values are NOT deleted to allow for rollback.</span>
463
+ <div class="flex gap-4">
464
+ <div class="flex-1 p-3 border rounded-lg">
465
+ <div class="flex items-center gap-2 mb-2"><Database size={16} /><ArrowRight size={16} /><Cloud size={16} /></div>
466
+ <p class="text-xs font-medium mb-2">Database → Azure Key Vault</p>
467
+ <Button unifiedSize="sm" variant="default" onclick={() => (migrateToAzureKvModalOpen = true)} disabled={disabled || !isAzureKvConfigValid() || migratingToAzureKv} startIcon={{ icon: ArrowRight }}>Migrate to Azure KV</Button>
468
+ </div>
469
+ <div class="flex-1 p-3 border rounded-lg">
470
+ <div class="flex items-center gap-2 mb-2"><Cloud size={16} /><ArrowLeft size={16} /><Database size={16} /></div>
471
+ <p class="text-xs font-medium mb-2">Azure Key Vault → Database</p>
472
+ <Button unifiedSize="sm" variant="default" onclick={() => (migrateFromAzureKvModalOpen = true)} disabled={disabled || !isAzureKvConfigValid() || migratingFromAzureKv} startIcon={{ icon: ArrowLeft }}>Migrate to Database</Button>
473
+ </div>
474
+ </div>
475
+ </div>
476
+ </div>
477
+ </div>
478
+ {:else if selectedType === 'AwsSecretsManager'}
479
+ <div class="space-y-4 p-4 border rounded-lg">
480
+ <div class="flex items-center gap-2 mb-4">
481
+ <Cloud class="text-primary" size={20} />
482
+ <div>
483
+ <p class="text-sm font-medium text-emphasis">AWS Secrets Manager Configuration <span class="ml-2 px-1.5 py-0.5 text-2xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 rounded">Beta</span></p>
484
+ <p class="text-xs text-secondary">Store secrets in AWS Secrets Manager.</p>
485
+ </div>
486
+ </div>
487
+ <div class="grid grid-cols-1 gap-4">
488
+ <div class="flex flex-col gap-1">
489
+ <label for="aws_sm_region" class="block text-xs font-semibold text-emphasis">Region</label>
490
+ <TextInput inputProps={{ type: 'text', id: 'aws_sm_region', placeholder: 'us-east-1', disabled }} bind:value={$values['secret_backend'].region} />
491
+ </div>
492
+ <div class="flex flex-col gap-1">
493
+ <label for="aws_sm_access_key_id" class="block text-xs font-semibold text-emphasis">Access Key ID (optional)</label>
494
+ <span class="text-2xs text-secondary">If not provided, the default AWS credential chain is used (env vars, instance profile, EKS pod identity)</span>
495
+ <TextInput inputProps={{ type: 'text', id: 'aws_sm_access_key_id', placeholder: 'AKIA...', disabled }} bind:value={$values['secret_backend'].access_key_id} />
496
+ </div>
497
+ <div class="flex flex-col gap-1 p-3 bg-surface-secondary rounded-lg">
498
+ <label for="aws_sm_secret_access_key" class="block text-xs font-semibold text-emphasis">Secret Access Key (optional)</label>
499
+ <Password bind:password={$values['secret_backend'].secret_access_key} small {disabled} />
500
+ </div>
501
+ <div class="flex flex-col gap-1">
502
+ <label for="aws_sm_prefix" class="block text-xs font-semibold text-emphasis">Secret Name Prefix (optional)</label>
503
+ <span class="text-2xs text-secondary">Prefix for secret names in AWS Secrets Manager (default: windmill/)</span>
504
+ <TextInput inputProps={{ type: 'text', id: 'aws_sm_prefix', placeholder: 'windmill/', disabled }} bind:value={$values['secret_backend'].prefix} />
505
+ </div>
506
+ <div class="flex flex-col gap-1">
507
+ <label for="aws_sm_endpoint_url" class="block text-xs font-semibold text-emphasis">Endpoint URL (optional)</label>
508
+ <span class="text-2xs text-secondary">Custom endpoint for LocalStack or other compatible services</span>
509
+ <TextInput inputProps={{ type: 'text', id: 'aws_sm_endpoint_url', placeholder: 'http://localhost:4566', disabled }} bind:value={$values['secret_backend'].endpoint_url} />
510
+ </div>
511
+ </div>
512
+ <div class="flex flex-col gap-4 pt-4 border-t">
513
+ <Button unifiedSize="md" variant="accent" onclick={testAwsSmConnection} disabled={disabled || !isAwsSmConfigValid() || testingAwsSmConnection} loading={testingAwsSmConnection} startIcon={{ icon: Server }}>Test Connection</Button>
514
+ <div class="flex flex-col gap-4 pt-4 border-t">
515
+ <span class="block text-xs font-semibold text-emphasis">Secret Migration</span>
516
+ <span class="text-2xs text-secondary">Original values are NOT deleted to allow for rollback.</span>
517
+ <div class="flex gap-4">
518
+ <div class="flex-1 p-3 border rounded-lg">
519
+ <div class="flex items-center gap-2 mb-2"><Database size={16} /><ArrowRight size={16} /><Cloud size={16} /></div>
520
+ <p class="text-xs font-medium mb-2">Database → AWS Secrets Manager</p>
521
+ <Button unifiedSize="sm" variant="default" onclick={() => (migrateToAwsSmModalOpen = true)} disabled={disabled || !isAwsSmConfigValid() || migratingToAwsSm} startIcon={{ icon: ArrowRight }}>Migrate to AWS SM</Button>
522
+ </div>
523
+ <div class="flex-1 p-3 border rounded-lg">
524
+ <div class="flex items-center gap-2 mb-2"><Cloud size={16} /><ArrowLeft size={16} /><Database size={16} /></div>
525
+ <p class="text-xs font-medium mb-2">AWS Secrets Manager → Database</p>
526
+ <Button unifiedSize="sm" variant="default" onclick={() => (migrateFromAwsSmModalOpen = true)} disabled={disabled || !isAwsSmConfigValid() || migratingFromAwsSm} startIcon={{ icon: ArrowLeft }}>Migrate to Database</Button>
444
527
  </div>
445
528
  </div>
446
529
  </div>
@@ -449,65 +532,21 @@ vault write auth/jwt/role/windmill-secrets \
449
532
  {/if}
450
533
  </div>
451
534
 
452
- <!-- Migrate to Vault Modal -->
453
- <ConfirmationModal
454
- title="Migrate Secrets to Vault"
455
- confirmationText="Migrate"
456
- open={migrateToVaultModalOpen}
457
- loading={migratingToVault}
458
- type="reload"
459
- onCanceled={() => {
460
- migrateToVaultModalOpen = false
461
- }}
462
- onConfirmed={migrateSecretsToVault}
463
- >
464
- {#snippet children()}
465
- <div class="flex flex-col gap-2">
466
- <p>
467
- This will migrate all existing secrets from the database to HashiCorp Vault. The process
468
- will:
469
- </p>
470
- <ol class="list-decimal list-inside text-sm space-y-1">
471
- <li>Read all encrypted secrets from the database</li>
472
- <li>Decrypt them using the workspace encryption keys</li>
473
- <li>Store them in HashiCorp Vault under the configured mount path</li>
474
- </ol>
475
- <p class="text-yellow-600 dark:text-yellow-400 text-sm mt-2">
476
- Note: Database values are NOT deleted automatically. You can manually clear them after
477
- verifying the migration was successful.
478
- </p>
479
- <p>Are you sure you want to proceed?</p>
480
- </div>
481
- {/snippet}
535
+ <ConfirmationModal title="Migrate to AWS Secrets Manager" confirmationText="Migrate" open={migrateToAwsSmModalOpen} loading={migratingToAwsSm} type="reload" onCanceled={() => { migrateToAwsSmModalOpen = false }} onConfirmed={migrateSecretsToAwsSm}>
536
+ {#snippet children()}<div class="flex flex-col gap-2"><p>This will copy all secrets from the database to AWS Secrets Manager.</p><p class="text-yellow-600 dark:text-yellow-400 text-sm">Database values are NOT deleted automatically.</p></div>{/snippet}
482
537
  </ConfirmationModal>
483
-
484
- <!-- Migrate to Database Modal -->
485
- <ConfirmationModal
486
- title="Migrate Secrets to Database"
487
- confirmationText="Migrate"
488
- open={migrateToDatabaseModalOpen}
489
- loading={migratingToDatabase}
490
- type="reload"
491
- onCanceled={() => {
492
- migrateToDatabaseModalOpen = false
493
- }}
494
- onConfirmed={migrateSecretsToDatabase}
495
- >
496
- {#snippet children()}
497
- <div class="flex flex-col gap-2">
498
- <p>
499
- This will migrate all secrets from HashiCorp Vault back to the database. The process will:
500
- </p>
501
- <ol class="list-decimal list-inside text-sm space-y-1">
502
- <li>List all secrets in Vault for each workspace</li>
503
- <li>Read each secret value from Vault</li>
504
- <li>Encrypt and store them in the database</li>
505
- </ol>
506
- <p class="text-yellow-600 dark:text-yellow-400 text-sm mt-2">
507
- Note: Vault values are NOT deleted automatically. Only secrets that already exist in the
508
- database will be updated.
509
- </p>
510
- <p>Are you sure you want to proceed?</p>
511
- </div>
512
- {/snippet}
538
+ <ConfirmationModal title="Migrate to Database" confirmationText="Migrate" open={migrateFromAwsSmModalOpen} loading={migratingFromAwsSm} type="reload" onCanceled={() => { migrateFromAwsSmModalOpen = false }} onConfirmed={migrateSecretsFromAwsSm}>
539
+ {#snippet children()}<div class="flex flex-col gap-2"><p>This will copy all secrets from AWS Secrets Manager back to the database.</p><p class="text-yellow-600 dark:text-yellow-400 text-sm">AWS Secrets Manager values are NOT deleted automatically.</p></div>{/snippet}
540
+ </ConfirmationModal>
541
+ <ConfirmationModal title="Migrate to Azure Key Vault" confirmationText="Migrate" open={migrateToAzureKvModalOpen} loading={migratingToAzureKv} type="reload" onCanceled={() => { migrateToAzureKvModalOpen = false }} onConfirmed={migrateSecretsToAzureKv}>
542
+ {#snippet children()}<div class="flex flex-col gap-2"><p>This will copy all secrets to Azure Key Vault.</p><p class="text-yellow-600 dark:text-yellow-400 text-sm">Database values are NOT deleted automatically.</p></div>{/snippet}
543
+ </ConfirmationModal>
544
+ <ConfirmationModal title="Migrate to Database" confirmationText="Migrate" open={migrateFromAzureKvModalOpen} loading={migratingFromAzureKv} type="reload" onCanceled={() => { migrateFromAzureKvModalOpen = false }} onConfirmed={migrateSecretsFromAzureKv}>
545
+ {#snippet children()}<div class="flex flex-col gap-2"><p>This will copy all secrets from Azure Key Vault back to the database.</p></div>{/snippet}
546
+ </ConfirmationModal>
547
+ <ConfirmationModal title="Migrate to Vault" confirmationText="Migrate" open={migrateToVaultModalOpen} loading={migratingToVault} type="reload" onCanceled={() => { migrateToVaultModalOpen = false }} onConfirmed={migrateSecretsToVault}>
548
+ {#snippet children()}<div class="flex flex-col gap-2"><p>This will copy all secrets to HashiCorp Vault.</p><p class="text-yellow-600 dark:text-yellow-400 text-sm">Database values are NOT deleted automatically.</p></div>{/snippet}
549
+ </ConfirmationModal>
550
+ <ConfirmationModal title="Migrate to Database" confirmationText="Migrate" open={migrateToDatabaseModalOpen} loading={migratingToDatabase} type="reload" onCanceled={() => { migrateToDatabaseModalOpen = false }} onConfirmed={migrateSecretsToDatabase}>
551
+ {#snippet children()}<div class="flex flex-col gap-2"><p>This will copy all secrets from Vault back to the database.</p></div>{/snippet}
513
552
  </ConfirmationModal>