mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-17 16:03:06 +00:00
* 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>
194 lines
4.8 KiB
Svelte
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>
|