Files
windmill/frontend/src/lib/components/workspaceSettings/OAuthClientConfig.svelte
T
6f24f1939d feat: google native triggers (#7837)
* feat: google native triggers

* nit skill

* better native trigger abstraction

* use resources for workspace integrations

* better and better

* better tests

* update native trigger skill

* sqlx

* less tx and google update fix

* refactor a bit the external logic

* nits

* fix

* fix google native trigger update

* fix oauth

* review fixes

* sqlx fix

* nit

* chore: update ee-repo-ref to a10eda4251610cceee67fbe05463b8be82ffa9e0

This commit updates the EE repository reference after PR #416 was merged in windmill-ee-private.

Previous ee-repo-ref: bf3696d5f2a39a3cb84dbbee81e092155f2a8c75

New ee-repo-ref: a10eda4251610cceee67fbe05463b8be82ffa9e0

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
2026-02-16 15:52:02 +00:00

194 lines
4.8 KiB
Svelte

<script lang="ts">
import { sendUserToast } from '$lib/utils'
import { Button, Alert } from '$lib/components/common'
import Label from '$lib/components/Label.svelte'
import ClipboardPanel from '$lib/components/details/ClipboardPanel.svelte'
import { Save, ExternalLink } from 'lucide-svelte'
interface Props {
serviceName: string
serviceDisplayName: string
existingConfig?: {
client_id: string
client_secret: string
base_url: string
} | null
redirectUri: string
onConfigSaved: (oauthData: any) => Promise<void>
requiresBaseUrl?: boolean
baseUrlLabel?: string
baseUrlPlaceholder?: string
baseUrlSettingsPath?: string
clientIdPlaceholder?: string
clientSecretPlaceholder?: string
setupInstructions?: string[]
}
let {
serviceName,
serviceDisplayName,
existingConfig,
onConfigSaved,
redirectUri,
requiresBaseUrl = true,
baseUrlLabel = `${serviceDisplayName} base URL`,
baseUrlPlaceholder = `https://your-${serviceName}.example.com`,
baseUrlSettingsPath = '/settings/admin/security',
clientIdPlaceholder = `Generated by ${serviceDisplayName} OAuth app`,
clientSecretPlaceholder = `Generated by ${serviceDisplayName} OAuth app`,
setupInstructions
}: Props = $props()
let saving = $state(false)
let clientId = $state('')
let clientSecret = $state('')
let baseUrl = $state('')
$effect(() => {
if (existingConfig) {
clientId = existingConfig.client_id
clientSecret = existingConfig.client_secret
baseUrl = existingConfig.base_url
}
})
let canSave = $derived(
!saving &&
!!clientId.trim() &&
!!clientSecret.trim() &&
(!requiresBaseUrl || !!baseUrl.trim())
)
async function saveConfig() {
if (!canSave) {
sendUserToast('Please fill in all required fields', true)
return
}
saving = true
try {
const oauthData: Record<string, string> = {
client_id: clientId.trim(),
client_secret: clientSecret.trim(),
base_url: requiresBaseUrl ? baseUrl.trim().replace(/\/+$/, '') : '',
redirect_uri: redirectUri.trim()
}
await onConfigSaved(oauthData)
resetForm()
} catch (err: any) {
sendUserToast(`Failed to configure OAuth client: ${err.message}`, true)
} finally {
saving = false
}
}
function resetForm() {
clientId = ''
clientSecret = ''
}
</script>
<div class="space-y-4">
<div class="flex justify-between items-center">
<h4 class="text-md font-medium text-primary">{serviceDisplayName} OAuth Client Configuration</h4
>
</div>
{#if !existingConfig}
<Alert type="warning" title="OAuth client required">
<p class="text-sm mb-2">
Before you can connect to {serviceDisplayName}, you need to configure an OAuth client.
</p>
{#if setupInstructions}
<ol class="list-decimal list-inside space-y-1 text-sm">
{#each setupInstructions as instruction}
<li>{@html instruction}</li>
{/each}
</ol>
{/if}
</Alert>
{/if}
<div class="space-y-4">
<div class="space-y-4">
{#if requiresBaseUrl}
<div class="flex flex-col gap-1">
<Label label={baseUrlLabel} />
<p class="text-xs font-normal text-secondary"
>The base URL of your {serviceDisplayName} instance (without trailing slash)</p
>
<input
type="url"
bind:value={baseUrl}
placeholder={baseUrlPlaceholder}
class="windmill-input"
disabled={saving}
/>
{#if baseUrl && baseUrlSettingsPath}
<Button
wrapperClasses="self-start"
href={`${baseUrl}${baseUrlSettingsPath}`}
target="_blank"
startIcon={{ icon: ExternalLink }}
>
{serviceDisplayName} OAuth Settings
</Button>
{/if}
</div>
{/if}
<div class="flex flex-col gap-1">
<Label label="Redirect URI" />
<p class="text-xs font-normal text-secondary"
>Use this URL when creating the OAuth app in {serviceDisplayName}</p
>
<ClipboardPanel content={redirectUri} size="sm" />
</div>
<div class="flex flex-col gap-1">
<Label label="Client ID" />
<input
type="text"
bind:value={clientId}
placeholder={clientIdPlaceholder}
class="windmill-input"
disabled={saving}
/>
</div>
<div class="flex flex-col gap-1">
<Label label="Client secret" />
<input
type="password"
bind:value={clientSecret}
placeholder={clientSecretPlaceholder}
class="windmill-input"
disabled={saving}
/>
</div>
<div class="flex gap-2">
<Button
color="blue"
onclick={saveConfig}
disabled={!canSave}
startIcon={{ icon: Save }}
>
{saving ? 'Saving...' : 'Save configuration'}
</Button>
</div>
</div>
</div>
{#if existingConfig}
<Alert type="success" title="OAuth client configured">
<p class="text-sm">
OAuth client is configured for {serviceDisplayName}. You can now connect this workspace to
{serviceDisplayName}.
</p>
</Alert>
{/if}
</div>