windmill-components 1.698.0 → 1.700.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.
- package/package/components/Auth0Setting.svelte +7 -5
- package/package/components/AuthSettings.svelte +11 -6
- package/package/components/AutheliaSetting.svelte +6 -4
- package/package/components/AuthentikSetting.svelte +7 -2
- package/package/components/DisplayResult.svelte +41 -23
- package/package/components/FlowStatusViewerInner.svelte +23 -9
- package/package/components/HistoricInputs.svelte +2 -1
- package/package/components/InstanceSetting.svelte +47 -5
- package/package/components/KanidmSetting.svelte +6 -4
- package/package/components/KeycloakSetting.svelte +6 -4
- package/package/components/LogViewer.svelte +69 -29
- package/package/components/NextcloudSetting.svelte +6 -4
- package/package/components/OAuthSetting.svelte +6 -4
- package/package/components/OktaSetting.svelte +7 -2
- package/package/components/ParqetCsvTableRenderer.svelte +21 -8
- package/package/components/Path.svelte +10 -0
- package/package/components/PocketIdSetting.svelte +6 -4
- package/package/components/S3FilePickerInner.svelte +22 -8
- package/package/components/ScriptEditor.svelte +34 -4
- package/package/components/ShareModal.svelte.d.ts +1 -1
- package/package/components/VariableForm.svelte +1 -1
- package/package/components/ZitadelSetting.svelte +6 -4
- package/package/components/apps/components/helpers/RunnableComponent.svelte.d.ts +1 -0
- package/package/components/common/fileDownload/FileDownload.svelte +26 -17
- package/package/components/flows/idUtils.js +4 -1
- package/package/components/flows/stepsInputArgs.svelte.js +6 -1
- package/package/components/instanceSettings/SecretBackendConfig.svelte +36 -11
- package/package/components/propertyPicker/ObjectViewer.svelte +23 -9
- package/package/components/runs/runsFilter.d.ts +1 -1
- package/package/components/runs/useJobsLoader.svelte.d.ts +1 -0
- package/package/components/runs/useJobsLoader.svelte.js +3 -1
- package/package/components/scriptEditor/LogPanel.svelte +4 -1
- package/package/components/scriptEditor/LogPanel.svelte.d.ts +1 -0
- package/package/components/settings/WorkspaceOperatorSettings.svelte +1 -1
- package/package/components/sidebar/SidebarContent.svelte +40 -2
- package/package/gen/core/OpenAPI.js +1 -1
- package/package/gen/schemas.gen.d.ts +33 -4
- package/package/gen/schemas.gen.js +33 -4
- package/package/gen/services.gen.d.ts +20 -1
- package/package/gen/services.gen.js +40 -0
- package/package/gen/types.gen.d.ts +182 -3
- package/package/system_prompts/prompts.d.ts +2 -2
- package/package/system_prompts/prompts.js +2 -2
- package/package/utils/downloadFile.d.ts +11 -0
- package/package/utils/downloadFile.js +48 -0
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { twMerge } from 'tailwind-merge';
|
|
|
6
6
|
import DarkModeObserver from './DarkModeObserver.svelte';
|
|
7
7
|
import { HelpersService } from '../gen';
|
|
8
8
|
import { base } from '../base';
|
|
9
|
+
import { downloadViaClient, shouldDownloadViaClient } from '../utils/downloadFile';
|
|
9
10
|
import { enterpriseLicense, workspaceStore } from '../stores';
|
|
10
11
|
import { Download } from 'lucide-svelte';
|
|
11
12
|
import { Loader2 } from 'lucide-svelte';
|
|
@@ -171,14 +172,26 @@ run(() => {
|
|
|
171
172
|
</div>
|
|
172
173
|
{/if}
|
|
173
174
|
{#if !disable_download && !s3resource.endsWith('.csv')}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
175
|
+
{@const csvApiPath = `/w/${workspaceId}/job_helpers/download_s3_parquet_file_as_csv?file_key=${encodeURIComponent(s3resource)}${storage ? `&storage=${storage}` : ''}`}
|
|
176
|
+
{@const csvName = (s3resource.split('/').pop() ?? 'download') + '.csv'}
|
|
177
|
+
{#if shouldDownloadViaClient()}
|
|
178
|
+
<button
|
|
179
|
+
class="text-secondary w-full text-right underline text-2xs whitespace-nowrap"
|
|
180
|
+
onclick={() => downloadViaClient(csvApiPath, csvName)}
|
|
181
|
+
><div class="flex flex-row-reverse gap-2 items-center"
|
|
182
|
+
><Download size={12} /> CSV</div
|
|
183
|
+
></button
|
|
184
|
+
>
|
|
185
|
+
{:else}
|
|
186
|
+
<a
|
|
187
|
+
target="_blank"
|
|
188
|
+
href="{base}/api{csvApiPath}"
|
|
189
|
+
class="text-secondary w-full text-right underline text-2xs whitespace-nowrap"
|
|
190
|
+
><div class="flex flex-row-reverse gap-2 items-center"
|
|
191
|
+
><Download size={12} /> CSV</div
|
|
192
|
+
></a
|
|
193
|
+
>
|
|
194
|
+
{/if}
|
|
182
195
|
{/if}
|
|
183
196
|
|
|
184
197
|
{#if nbRows != undefined}
|
|
@@ -282,6 +282,16 @@ async function initPath() {
|
|
|
282
282
|
function setDirty() {
|
|
283
283
|
!dirty && (dirty = true);
|
|
284
284
|
}
|
|
285
|
+
$effect(() => {
|
|
286
|
+
if (path !== undefined &&
|
|
287
|
+
path !== '' &&
|
|
288
|
+
initialPath &&
|
|
289
|
+
!initialPath.startsWith('tmp/') &&
|
|
290
|
+
path !== initialPath &&
|
|
291
|
+
!dirty) {
|
|
292
|
+
dirty = true;
|
|
293
|
+
}
|
|
294
|
+
});
|
|
285
295
|
const openSearchWithPrefilledText = getContext('openSearchWithPrefilledText');
|
|
286
296
|
$effect.pre(() => {
|
|
287
297
|
;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">import { untrack } from 'svelte';
|
|
2
2
|
import IconedResourceType from './IconedResourceType.svelte';
|
|
3
3
|
import TextInput from './text_input/TextInput.svelte';
|
|
4
|
+
import Password from './Password.svelte';
|
|
4
5
|
import Toggle from './Toggle.svelte';
|
|
5
6
|
import SettingCard from './instanceSettings/SettingCard.svelte';
|
|
6
7
|
let { value = $bindable() } = $props();
|
|
@@ -71,11 +72,12 @@ function handleToggle(e) {
|
|
|
71
72
|
bind:value={value['id']}
|
|
72
73
|
/>
|
|
73
74
|
</label>
|
|
74
|
-
<label class="flex flex-col gap-1">
|
|
75
|
+
<label for="pocketid_client_secret" class="flex flex-col gap-1">
|
|
75
76
|
<span class="text-emphasis font-semibold text-xs">Client Secret</span>
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
<Password
|
|
78
|
+
id="pocketid_client_secret"
|
|
79
|
+
placeholder="Client Secret"
|
|
80
|
+
bind:password={value['secret']}
|
|
79
81
|
/>
|
|
80
82
|
</label>
|
|
81
83
|
</SettingCard>
|
|
@@ -5,6 +5,7 @@ import { workspaceStore } from '../stores';
|
|
|
5
5
|
import { CancelablePromise, HelpersService } from '../gen';
|
|
6
6
|
import { base } from '../base';
|
|
7
7
|
import { displayDate, displaySize, emptyString, parseS3Object, sendUserToast } from '../utils';
|
|
8
|
+
import { downloadViaClient, shouldDownloadViaClient } from '../utils/downloadFile';
|
|
8
9
|
import { Alert, Button } from './common';
|
|
9
10
|
import Section from './Section.svelte';
|
|
10
11
|
import { createEventDispatcher, untrack } from 'svelte';
|
|
@@ -577,14 +578,27 @@ $effect.pre(() => {
|
|
|
577
578
|
{#if filePreview !== undefined && (!hideS3SpecificDetails || !readOnlyMode || allowDelete)}
|
|
578
579
|
<div class="flex gap-2 shrink-0">
|
|
579
580
|
{#if !hideS3SpecificDetails}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
581
|
+
{@const downloadApiPath = `/w/${$workspaceStore}/job_helpers/download_s3_file?file_key=${encodeURIComponent(fileMetadata?.fileKey ?? '')}${storage ? `&storage=${storage}` : ''}`}
|
|
582
|
+
{@const downloadName =
|
|
583
|
+
fileMetadata?.fileKey.split('/').pop() ?? 'unnamed_download.file'}
|
|
584
|
+
{#if shouldDownloadViaClient()}
|
|
585
|
+
<Button
|
|
586
|
+
title="Download file from S3"
|
|
587
|
+
variant="default"
|
|
588
|
+
on:click={() => downloadViaClient(downloadApiPath, downloadName)}
|
|
589
|
+
startIcon={{ icon: Download }}
|
|
590
|
+
iconOnly={true}
|
|
591
|
+
/>
|
|
592
|
+
{:else}
|
|
593
|
+
<Button
|
|
594
|
+
title="Download file from S3"
|
|
595
|
+
variant="default"
|
|
596
|
+
href={`${base}/api${downloadApiPath}`}
|
|
597
|
+
download={downloadName}
|
|
598
|
+
startIcon={{ icon: Download }}
|
|
599
|
+
iconOnly={true}
|
|
600
|
+
/>
|
|
601
|
+
{/if}
|
|
588
602
|
{/if}
|
|
589
603
|
{#if !readOnlyMode}
|
|
590
604
|
<Button
|
|
@@ -408,6 +408,8 @@ let logPanel = $state(undefined);
|
|
|
408
408
|
let testIsLoading = $state(false);
|
|
409
409
|
let testJob = $state();
|
|
410
410
|
let pastPreviews = $state([]);
|
|
411
|
+
let historyTabActive = false;
|
|
412
|
+
let pastPreviewsRequest;
|
|
411
413
|
let validCode = $state(true);
|
|
412
414
|
// Recording
|
|
413
415
|
let scriptRecording = createScriptRecording();
|
|
@@ -511,7 +513,9 @@ export async function runTest() {
|
|
|
511
513
|
lastRecording = scriptRecording.stop();
|
|
512
514
|
setActiveRecording(undefined);
|
|
513
515
|
}
|
|
514
|
-
|
|
516
|
+
if (historyTabActive) {
|
|
517
|
+
loadPastTests();
|
|
518
|
+
}
|
|
515
519
|
},
|
|
516
520
|
doneError({ error }) {
|
|
517
521
|
if (scriptRecording.active) {
|
|
@@ -536,12 +540,31 @@ function downloadRecording() {
|
|
|
536
540
|
}
|
|
537
541
|
}
|
|
538
542
|
async function loadPastTests() {
|
|
539
|
-
|
|
543
|
+
pastPreviewsRequest?.cancel();
|
|
544
|
+
const req = JobService.listCompletedJobs({
|
|
540
545
|
workspace: $workspaceStore,
|
|
541
546
|
jobKinds: 'preview',
|
|
542
547
|
createdBy: $userStore?.username,
|
|
543
|
-
scriptPathExact: path
|
|
548
|
+
scriptPathExact: path,
|
|
549
|
+
hasNullParent: true
|
|
544
550
|
});
|
|
551
|
+
pastPreviewsRequest = req;
|
|
552
|
+
try {
|
|
553
|
+
const result = await req;
|
|
554
|
+
if (pastPreviewsRequest === req) {
|
|
555
|
+
pastPreviews = result;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
catch (err) {
|
|
559
|
+
if (!(err instanceof Error) || err.name !== 'CancelError') {
|
|
560
|
+
throw err;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
finally {
|
|
564
|
+
if (pastPreviewsRequest === req) {
|
|
565
|
+
pastPreviewsRequest = undefined;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
545
568
|
}
|
|
546
569
|
export async function inferSchema(code, { nlang, resetArgs = false, applyInitialArgs = false } = {}) {
|
|
547
570
|
let nschema = schema ?? emptySchema();
|
|
@@ -897,7 +920,6 @@ onMount(async () => {
|
|
|
897
920
|
if (!validCode && code && lang) {
|
|
898
921
|
await inferSchema(code, { applyInitialArgs: true });
|
|
899
922
|
}
|
|
900
|
-
loadPastTests();
|
|
901
923
|
aiChatManager.saveAndClear();
|
|
902
924
|
aiChatManager.changeMode(AIMode.SCRIPT);
|
|
903
925
|
});
|
|
@@ -964,6 +986,8 @@ export function disableCollaboration() {
|
|
|
964
986
|
wsProvider = undefined;
|
|
965
987
|
}
|
|
966
988
|
onDestroy(() => {
|
|
989
|
+
pastPreviewsRequest?.cancel();
|
|
990
|
+
pastPreviewsRequest = undefined;
|
|
967
991
|
disableCollaboration();
|
|
968
992
|
aiChatManager.scriptEditorApplyCode = undefined;
|
|
969
993
|
aiChatManager.scriptEditorShowDiffMode = undefined;
|
|
@@ -1413,6 +1437,12 @@ $effect(() => {
|
|
|
1413
1437
|
} as any)
|
|
1414
1438
|
: testJob}
|
|
1415
1439
|
{pastPreviews}
|
|
1440
|
+
onTabChange={(tab) => {
|
|
1441
|
+
historyTabActive = tab === 'history'
|
|
1442
|
+
if (historyTabActive) {
|
|
1443
|
+
loadPastTests()
|
|
1444
|
+
}
|
|
1445
|
+
}}
|
|
1416
1446
|
previewIsLoading={debugMode
|
|
1417
1447
|
? $debugState.running && !$debugState.stopped
|
|
1418
1448
|
: testIsLoading}
|
|
@@ -16,7 +16,7 @@ declare const ShareModal: $$__sveltets_2_IsomorphicComponent<Record<string, neve
|
|
|
16
16
|
} & {
|
|
17
17
|
[evt: string]: CustomEvent<any>;
|
|
18
18
|
}, {}, {
|
|
19
|
-
openDrawer: (newPath: string, kind_l: "resource" | "volume" | "script" | "flow" | "app" | "variable" | "schedule" | "raw_app" | "
|
|
19
|
+
openDrawer: (newPath: string, kind_l: "resource" | "volume" | "script" | "flow" | "app" | "variable" | "schedule" | "raw_app" | "http_trigger" | "websocket_trigger" | "kafka_trigger" | "nats_trigger" | "postgres_trigger" | "mqtt_trigger" | "sqs_trigger" | "gcp_trigger" | "azure_trigger" | "email_trigger" | "group_", isOwnerOverride?: boolean) => Promise<void>;
|
|
20
20
|
}, "">;
|
|
21
21
|
type ShareModal = InstanceType<typeof ShareModal>;
|
|
22
22
|
export default ShareModal;
|
|
@@ -51,7 +51,7 @@ export function setCode(value) {
|
|
|
51
51
|
{#if deployTo}
|
|
52
52
|
<Label
|
|
53
53
|
label="Workspace specific"
|
|
54
|
-
tooltip="Prevents this variable from being deployed to prod/staging"
|
|
54
|
+
tooltip="Prevents this variable from being deployed to prod/staging. May have been enabled automatically because a workspace-specific resource references this variable via $var:. Disabling this toggle does not retroactively un-mark the resource that referenced it."
|
|
55
55
|
>
|
|
56
56
|
<Toggle bind:checked={wsSpecific} />
|
|
57
57
|
</Label>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">import { run } from 'svelte/legacy';
|
|
2
2
|
import IconedResourceType from './IconedResourceType.svelte';
|
|
3
3
|
import TextInput from './text_input/TextInput.svelte';
|
|
4
|
+
import Password from './Password.svelte';
|
|
4
5
|
import Toggle from './Toggle.svelte';
|
|
5
6
|
import SettingCard from './instanceSettings/SettingCard.svelte';
|
|
6
7
|
let { value = $bindable() } = $props();
|
|
@@ -64,11 +65,12 @@ run(() => {
|
|
|
64
65
|
bind:value={value['id']}
|
|
65
66
|
/>
|
|
66
67
|
</label>
|
|
67
|
-
<label>
|
|
68
|
+
<label for="zitadel_client_secret">
|
|
68
69
|
<span class="text-emphasis font-semibold text-xs">Client Secret </span>
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
<Password
|
|
71
|
+
id="zitadel_client_secret"
|
|
72
|
+
placeholder="Client Secret"
|
|
73
|
+
bind:password={value['secret']}
|
|
72
74
|
/>
|
|
73
75
|
</label>
|
|
74
76
|
</SettingCard>
|
|
@@ -1,25 +1,34 @@
|
|
|
1
1
|
<script lang="ts">import { workspaceStore } from '../../../stores';
|
|
2
2
|
import { Download } from 'lucide-svelte';
|
|
3
3
|
import { base } from '../../../base';
|
|
4
|
+
import { downloadViaClient, shouldDownloadViaClient } from '../../../utils/downloadFile';
|
|
4
5
|
let { s3object, workspaceId = undefined, appPath = undefined } = $props();
|
|
6
|
+
let workspace = $derived(workspaceId ?? $workspaceStore);
|
|
7
|
+
let filename = $derived(s3object?.s3?.split?.('/')?.pop() ?? 'unnamed_download.file');
|
|
8
|
+
let apiPath = $derived(`${appPath
|
|
9
|
+
? `/w/${workspace}/apps_u/download_s3_file/${appPath}`
|
|
10
|
+
: `/w/${workspace}/job_helpers/download_s3_file`}?${appPath ? 's3' : 'file_key'}=${encodeURIComponent(s3object?.s3 ?? '')}${s3object?.storage ? `&storage=${s3object.storage}` : ''}${appPath && s3object?.presigned ? `&${s3object?.presigned}` : ''}`);
|
|
11
|
+
let href = $derived(`${base}/api${apiPath}`);
|
|
12
|
+
const sharedClass = `relative center-center flex w-full text-center font-normal text-primary text-sm
|
|
13
|
+
border border-dashed border-gray-400 hover:border-blue-500
|
|
14
|
+
focus-within:border-blue-500 hover:bg-blue-50 dark:hover:bg-frost-900 focus-within:bg-blue-50
|
|
15
|
+
duration-200 rounded-lg p-1 gap-2`;
|
|
5
16
|
</script>
|
|
6
17
|
|
|
7
18
|
{#if s3object && s3object?.s3}
|
|
8
|
-
|
|
9
|
-
class=
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
</span>
|
|
24
|
-
</a>
|
|
19
|
+
{#if shouldDownloadViaClient()}
|
|
20
|
+
<button class={sharedClass} onclick={() => downloadViaClient(apiPath, filename)}>
|
|
21
|
+
<Download />
|
|
22
|
+
<span>
|
|
23
|
+
{s3object?.storage ? `s3://${s3object.storage}/${s3object.s3}` : `s3:///${s3object.s3}`}
|
|
24
|
+
</span>
|
|
25
|
+
</button>
|
|
26
|
+
{:else}
|
|
27
|
+
<a class={sharedClass} {href} download={filename}>
|
|
28
|
+
<Download />
|
|
29
|
+
<span>
|
|
30
|
+
{s3object?.storage ? `s3://${s3object.storage}/${s3object.s3}` : `s3:///${s3object.s3}`}
|
|
31
|
+
</span>
|
|
32
|
+
</a>
|
|
33
|
+
{/if}
|
|
25
34
|
{/if}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dfs, getPreviousModule, getStepPropPicker } from './previousResults';
|
|
1
|
+
import { dfs, getPreviousModule, getStepPropPicker, getFailureStepPropPicker } from './previousResults';
|
|
2
2
|
import { evalValue } from './utils.svelte';
|
|
3
3
|
export class StepsInputArgs {
|
|
4
4
|
#stepsEvaluated = $state({});
|
|
@@ -103,6 +103,11 @@ export class StepsInputArgs {
|
|
|
103
103
|
this.#steps[mod.id] = argsSnapshot;
|
|
104
104
|
}
|
|
105
105
|
updateStepArgs(id, flowState, flow, previewArgs) {
|
|
106
|
+
if (id === 'failure' && flow && flow.value.failure_module && flowState) {
|
|
107
|
+
const picker = getFailureStepPropPicker(flowState, flow, previewArgs);
|
|
108
|
+
this.initializeFromSchema(flow.value.failure_module, flowState['failure']?.schema ?? {}, picker.pickableProperties);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
106
111
|
if (!flowState || !flow) {
|
|
107
112
|
return;
|
|
108
113
|
}
|
|
@@ -54,6 +54,7 @@ function setBackendType(type) {
|
|
|
54
54
|
address: $values['secret_backend']?.address ?? '',
|
|
55
55
|
mount_path: $values['secret_backend']?.mount_path ?? 'windmill',
|
|
56
56
|
jwt_role: $values['secret_backend']?.jwt_role ?? 'windmill-secrets',
|
|
57
|
+
jwt_mount_path: $values['secret_backend']?.jwt_mount_path ?? null,
|
|
57
58
|
namespace: $values['secret_backend']?.namespace ?? null,
|
|
58
59
|
token: $values['secret_backend']?.token ?? null,
|
|
59
60
|
skip_ssl_verify: $values['secret_backend']?.skip_ssl_verify ?? false
|
|
@@ -105,6 +106,7 @@ function getVaultSettings() {
|
|
|
105
106
|
address: $values['secret_backend'].address,
|
|
106
107
|
mount_path: $values['secret_backend'].mount_path,
|
|
107
108
|
jwt_role: $values['secret_backend'].jwt_role,
|
|
109
|
+
jwt_mount_path: $values['secret_backend'].jwt_mount_path || undefined,
|
|
108
110
|
namespace: $values['secret_backend'].namespace || undefined,
|
|
109
111
|
token: $values['secret_backend'].token || undefined,
|
|
110
112
|
skip_ssl_verify: $values['secret_backend'].skip_ssl_verify || undefined
|
|
@@ -317,6 +319,8 @@ function isAwsSmConfigValid() {
|
|
|
317
319
|
return $values['secret_backend'].region?.trim() !== '';
|
|
318
320
|
}
|
|
319
321
|
let baseUrl = $derived($values['base_url'] ?? 'https://your-windmill-instance.com');
|
|
322
|
+
let jwtMount = $derived(($values['secret_backend']?.jwt_mount_path?.trim() || 'jwt'));
|
|
323
|
+
let vaultAudience = $derived(($values['secret_backend']?.address?.trim() || 'https://vault.example.com:8200'));
|
|
320
324
|
</script>
|
|
321
325
|
|
|
322
326
|
<div class="space-y-6">
|
|
@@ -465,6 +469,24 @@ let baseUrl = $derived($values['base_url'] ?? 'https://your-windmill-instance.co
|
|
|
465
469
|
}}
|
|
466
470
|
bind:value={$values['secret_backend'].jwt_role}
|
|
467
471
|
/>
|
|
472
|
+
<label for="vault_jwt_mount_path" class="block text-xs font-semibold text-emphasis"
|
|
473
|
+
>JWT Auth Mount Path (optional)</label
|
|
474
|
+
>
|
|
475
|
+
<span class="text-2xs text-secondary"
|
|
476
|
+
>Mount path of the JWT auth method in Vault. Defaults to <code>jwt</code>. Set this
|
|
477
|
+
only if you mounted the JWT auth method at a non-default path (<code
|
|
478
|
+
>vault auth enable -path=<mount> jwt</code
|
|
479
|
+
>).</span
|
|
480
|
+
>
|
|
481
|
+
<TextInput
|
|
482
|
+
inputProps={{
|
|
483
|
+
type: 'text',
|
|
484
|
+
id: 'vault_jwt_mount_path',
|
|
485
|
+
placeholder: 'jwt',
|
|
486
|
+
disabled
|
|
487
|
+
}}
|
|
488
|
+
bind:value={$values['secret_backend'].jwt_mount_path}
|
|
489
|
+
/>
|
|
468
490
|
<details class="mt-2">
|
|
469
491
|
<summary class="text-xs font-medium text-secondary cursor-pointer hover:text-primary"
|
|
470
492
|
>Vault JWT Setup Instructions</summary
|
|
@@ -475,29 +497,32 @@ let baseUrl = $derived($values['base_url'] ?? 'https://your-windmill-instance.co
|
|
|
475
497
|
class="bg-gray-100 dark:bg-gray-800 p-2 rounded font-mono text-2xs overflow-x-auto"
|
|
476
498
|
>
|
|
477
499
|
<pre
|
|
478
|
-
># Enable JWT auth method
|
|
479
|
-
|
|
500
|
+
># Enable JWT auth method{jwtMount === 'jwt'
|
|
501
|
+
? ''
|
|
502
|
+
: ` at custom mount '${jwtMount}'`}
|
|
503
|
+
vault auth enable {jwtMount === 'jwt' ? 'jwt' : `-path=${jwtMount} jwt`}
|
|
480
504
|
|
|
481
505
|
# Configure JWT auth with Windmill's JWKS endpoint
|
|
482
|
-
vault write auth/
|
|
483
|
-
jwks_url="{baseUrl}
|
|
484
|
-
bound_issuer="{baseUrl}"
|
|
506
|
+
vault write auth/{jwtMount}/config \
|
|
507
|
+
jwks_url="{baseUrl}/api/oidc/jwks" \
|
|
508
|
+
bound_issuer="{baseUrl}/api/oidc/"
|
|
485
509
|
|
|
486
510
|
# Create a policy for Windmill secrets
|
|
487
511
|
vault policy write windmill-secrets - <<EOF
|
|
488
|
-
path "windmill/data/*" {
|
|
512
|
+
path "{$values['secret_backend']?.mount_path ?? 'windmill'}/data/*" {
|
|
489
513
|
capabilities = ["create", "read", "update", "delete"]
|
|
490
514
|
}
|
|
491
|
-
path "windmill/metadata/*" {
|
|
515
|
+
path "{$values['secret_backend']?.mount_path ?? 'windmill'}/metadata/*" {
|
|
492
516
|
capabilities = ["list", "delete"]
|
|
493
517
|
}
|
|
494
518
|
EOF
|
|
495
519
|
|
|
496
|
-
# Create the JWT role
|
|
497
|
-
|
|
520
|
+
# Create the JWT role. bound_audiences must match the Vault server
|
|
521
|
+
# address — Windmill signs the JWT with `aud` = your Vault address.
|
|
522
|
+
vault write auth/{jwtMount}/role/{$values['secret_backend']?.jwt_role || 'windmill-secrets'} \
|
|
498
523
|
role_type="jwt" \
|
|
499
|
-
bound_audiences="{
|
|
500
|
-
user_claim="
|
|
524
|
+
bound_audiences="{vaultAudience}" \
|
|
525
|
+
user_claim="sub" \
|
|
501
526
|
policies="windmill-secrets" \
|
|
502
527
|
ttl="1h"</pre
|
|
503
528
|
>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">import ObjectViewer from './ObjectViewer.svelte';
|
|
2
2
|
import { copyToClipboard, truncate } from '../../utils';
|
|
3
|
+
import { downloadViaClient, shouldDownloadViaClient } from '../../utils/downloadFile';
|
|
3
4
|
import { createEventDispatcher, tick, untrack, setContext, getContext } from 'svelte';
|
|
4
5
|
import { computeKey, keepByKeyOrValue } from './utils';
|
|
5
6
|
import { NEVER_TESTED_THIS_FAR } from '../flows/models';
|
|
@@ -335,15 +336,28 @@ let menuItems = $derived([
|
|
|
335
336
|
<div class="flex">
|
|
336
337
|
<span class="text-primary">{closeBracket}</span>
|
|
337
338
|
{#if getTypeAsString(jsonFiltered) === 's3object'}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
339
|
+
{@const s3DownloadApiPath = `/w/${$workspaceStore}/job_helpers/download_s3_file?file_key=${encodeURIComponent(jsonFiltered?.s3 ?? '')}${jsonFiltered?.storage ? `&storage=${jsonFiltered.storage}` : ''}`}
|
|
340
|
+
{@const s3DownloadName = jsonFiltered?.s3.split('/').pop() ?? 'unnamed_download.file'}
|
|
341
|
+
{#if shouldDownloadViaClient()}
|
|
342
|
+
<button
|
|
343
|
+
class="text-secondary underline font-semibold text-2xs whitespace-nowrap ml-1 w-fit"
|
|
344
|
+
onclick={() => downloadViaClient(s3DownloadApiPath, s3DownloadName)}
|
|
345
|
+
>
|
|
346
|
+
<span class="flex items-center gap-1"
|
|
347
|
+
><Download size={12} />download</span
|
|
348
|
+
>
|
|
349
|
+
</button>
|
|
350
|
+
{:else}
|
|
351
|
+
<a
|
|
352
|
+
class="text-secondary underline font-semibold text-2xs whitespace-nowrap ml-1 w-fit"
|
|
353
|
+
href={`/api${s3DownloadApiPath}`}
|
|
354
|
+
download={s3DownloadName}
|
|
355
|
+
>
|
|
356
|
+
<span class="flex items-center gap-1"
|
|
357
|
+
><Download size={12} />download</span
|
|
358
|
+
>
|
|
359
|
+
</a>
|
|
360
|
+
{/if}
|
|
347
361
|
<button
|
|
348
362
|
class="text-secondary underline text-2xs whitespace-nowrap ml-1"
|
|
349
363
|
onclick={() => {
|
|
@@ -178,7 +178,7 @@ export declare function buildRunsFilterSearchbarSchema({ paths, usernames, folde
|
|
|
178
178
|
label: string;
|
|
179
179
|
icon: typeof Zap;
|
|
180
180
|
options: {
|
|
181
|
-
label:
|
|
181
|
+
label: any;
|
|
182
182
|
value: JobTriggerKind;
|
|
183
183
|
}[];
|
|
184
184
|
allowNegative: true;
|
|
@@ -43,6 +43,7 @@ export function useJobsLoader(args) {
|
|
|
43
43
|
let lookback = $derived(_args.lookback ?? 0);
|
|
44
44
|
let timeframe = $derived(_args?.timeframe);
|
|
45
45
|
let perPage = $derived(_args?.perPage ?? 1000);
|
|
46
|
+
let excludesEntrypointOverride = $derived(_args.excludesEntrypointOverride ?? false);
|
|
46
47
|
let label = $derived(filters?.label ?? null);
|
|
47
48
|
let worker = $derived(filters?.worker ?? null);
|
|
48
49
|
let success = $derived(filters?.status ?? null);
|
|
@@ -212,7 +213,8 @@ export function useJobsLoader(args) {
|
|
|
212
213
|
allWorkspaces: allWorkspaces ? true : undefined,
|
|
213
214
|
perPage: perPageOverride ?? perPage,
|
|
214
215
|
allowWildcards: allowWildcards ? true : undefined,
|
|
215
|
-
broadFilter
|
|
216
|
+
broadFilter,
|
|
217
|
+
excludesEntrypointOverride: excludesEntrypointOverride ? true : undefined
|
|
216
218
|
});
|
|
217
219
|
promise = CancelablePromiseUtils.catchErr(promise, (e) => {
|
|
218
220
|
if (e instanceof CancelError)
|
|
@@ -21,10 +21,13 @@ import Head from '../table/Head.svelte';
|
|
|
21
21
|
import WorkflowTimeline from '../WorkflowTimeline.svelte';
|
|
22
22
|
import Tooltip from '../Tooltip.svelte';
|
|
23
23
|
import { getStringError } from '../copilot/chat/utils';
|
|
24
|
-
let { lang, previewIsLoading = false, previewJob, pastPreviews = [], editor = undefined, diffEditor = undefined, args = undefined, workspace = undefined, showCaptures = false, customUi = undefined, children, capturesTab, customResultPanel, showCustomResultPanel = false } = $props();
|
|
24
|
+
let { lang, previewIsLoading = false, previewJob, pastPreviews = [], editor = undefined, diffEditor = undefined, args = undefined, workspace = undefined, showCaptures = false, customUi = undefined, children, capturesTab, customResultPanel, showCustomResultPanel = false, onTabChange } = $props();
|
|
25
25
|
let selectedTab = $state('logs');
|
|
26
26
|
let drawerOpen = $state(false);
|
|
27
27
|
let drawerContent = $state(undefined);
|
|
28
|
+
$effect(() => {
|
|
29
|
+
onTabChange?.(selectedTab);
|
|
30
|
+
});
|
|
28
31
|
export function setFocusToLogs() {
|
|
29
32
|
selectedTab = 'logs';
|
|
30
33
|
}
|
|
@@ -21,6 +21,7 @@ interface Props {
|
|
|
21
21
|
capturesTab?: import('svelte').Snippet;
|
|
22
22
|
customResultPanel?: import('svelte').Snippet;
|
|
23
23
|
showCustomResultPanel?: boolean;
|
|
24
|
+
onTabChange?: (tab: string) => void;
|
|
24
25
|
}
|
|
25
26
|
declare const LogPanel: import("svelte").Component<Props, {
|
|
26
27
|
setFocusToLogs: () => void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">import MenuLink from './MenuLink.svelte';
|
|
2
|
-
import { superadmin, usedTriggerKinds, userStore, workspaceStore, isCriticalAlertsUIOpen, enterpriseLicense, devopsRole, tutorialsToDo, skippedAll } from '../../stores';
|
|
2
|
+
import { superadmin, usedTriggerKinds, userStore, userWorkspaces, workspaceStore, isCriticalAlertsUIOpen, enterpriseLicense, devopsRole, tutorialsToDo, skippedAll } from '../../stores';
|
|
3
|
+
import { findWorkspaceDescendants } from '../../utils/workspaceHierarchy';
|
|
3
4
|
import { syncTutorialsTodos } from '../../tutorialUtils';
|
|
4
5
|
import { SIDEBAR_SHOW_SCHEDULES } from '../../consts';
|
|
5
6
|
import { BookOpen, ServerCog, Boxes, Calendar, DollarSign, Eye, Logs, FolderCog, FolderOpen, Github, GraduationCap, HelpCircle, Home, LogOut, Newspaper, Play, Route, Settings, UserCog, Plus, Unplug, AlertCircle, Database, Pyramid, Trash2, MailIcon } from 'lucide-svelte';
|
|
@@ -64,11 +65,24 @@ async function deleteFork() {
|
|
|
64
65
|
sendUserToast(err, true);
|
|
65
66
|
}
|
|
66
67
|
}
|
|
68
|
+
if (deleteForkedChildren && forkedDescendants.length > 0) {
|
|
69
|
+
for (const child of forkedDescendants) {
|
|
70
|
+
try {
|
|
71
|
+
await WorkspaceService.deleteWorkspace({ workspace: child.id });
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
sendUserToast(`Failed to delete forked child ${child.id}: ${err}`, true);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
67
79
|
await WorkspaceService.deleteWorkspace({ workspace });
|
|
68
80
|
sendUserToast('You deleted the workspace');
|
|
69
81
|
clearStores();
|
|
70
82
|
goto('/user/workspaces');
|
|
71
83
|
}
|
|
84
|
+
let deleteForkedChildren = $state(false);
|
|
85
|
+
const forkedDescendants = $derived($workspaceStore ? findWorkspaceDescendants($workspaceStore, $userWorkspaces ?? []) : []);
|
|
72
86
|
let hasNewChangelogs = $state(false);
|
|
73
87
|
let recentChangelogs = $state([]);
|
|
74
88
|
let lastOpened = localStorage.getItem('changelogsLastOpened');
|
|
@@ -206,7 +220,6 @@ let mainMenuLinks = $derived([
|
|
|
206
220
|
label: 'Assets',
|
|
207
221
|
href: `${base}/assets`,
|
|
208
222
|
icon: Pyramid,
|
|
209
|
-
disabled: $userStore?.operator,
|
|
210
223
|
aiId: 'sidebar-menu-link-assets',
|
|
211
224
|
aiDescription: 'Button to navigate to assets'
|
|
212
225
|
},
|
|
@@ -403,6 +416,7 @@ let secondaryMenuLinks = $derived([
|
|
|
403
416
|
label: 'Delete Forked Workspace',
|
|
404
417
|
action: async () => {
|
|
405
418
|
await loadForkedDatatables();
|
|
419
|
+
deleteForkedChildren = false;
|
|
406
420
|
deleteWorkspaceForkModal = true;
|
|
407
421
|
},
|
|
408
422
|
icon: Trash2,
|
|
@@ -718,6 +732,30 @@ let secondaryMenuLinks = $derived([
|
|
|
718
732
|
>
|
|
719
733
|
<div class="flex flex-col w-full space-y-4">
|
|
720
734
|
<span>Are you sure you want to delete this workspace fork? (deleting {$workspaceStore})</span>
|
|
735
|
+
{#if forkedDescendants.length > 0}
|
|
736
|
+
<div class="border rounded-md divide-y">
|
|
737
|
+
<div class="px-4 py-2 flex items-center justify-between gap-2">
|
|
738
|
+
<div class="flex flex-col min-w-0">
|
|
739
|
+
<span class="text-xs font-semibold text-secondary">Forked children</span>
|
|
740
|
+
<span class="text-3xs text-hint">
|
|
741
|
+
This fork has {forkedDescendants.length} forked
|
|
742
|
+
{forkedDescendants.length === 1 ? 'child' : 'children'} (transitively).
|
|
743
|
+
</span>
|
|
744
|
+
</div>
|
|
745
|
+
<Toggle
|
|
746
|
+
class="shrink-0"
|
|
747
|
+
size="xs"
|
|
748
|
+
bind:checked={deleteForkedChildren}
|
|
749
|
+
options={{ right: 'Also delete children' }}
|
|
750
|
+
/>
|
|
751
|
+
</div>
|
|
752
|
+
<ul class="px-4 py-2 text-3xs text-hint max-h-32 overflow-y-auto">
|
|
753
|
+
{#each forkedDescendants as child}
|
|
754
|
+
<li class="font-mono truncate" title={child.id}>{child.id}</li>
|
|
755
|
+
{/each}
|
|
756
|
+
</ul>
|
|
757
|
+
</div>
|
|
758
|
+
{/if}
|
|
721
759
|
{#if forkedDatatables.length > 0}
|
|
722
760
|
<div class="border rounded-md divide-y">
|
|
723
761
|
<div class="px-4 py-2 text-xs font-semibold text-secondary"> Forked databases </div>
|