mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-07 16:03:21 +00:00
* Remove $$props.field usage * Rename slots to ensure no hyphen * _props * _trigger * OnSelectedIteration type correct capitalization * rename _content * Remove afterUpdate * Migrate everything to svelte 5 * array bind * Fix popover * type never * nit fixes * Fixed many trivial errors * onClick * Fix errors * use let: * nit typing * fix: wrap state_referenced_locally vars with untrack() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add untrack import * Fix all syntax errors due to untrack migration * Fix undefined errors * Fix more undefined errors * untrack(() => initialOpen) * svelte-ignore * Fix state_descriptors_fixed error in Chart.svelte Use $state.snapshot() to pass plain copies of data/options to Chart.js instead of $state proxies. Chart.js's listenArrayEvents tries to define property descriptors on data arrays, which Svelte 5 proxies reject. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * nit typing * Merge issue * Fix "path is not set" error in resource picker / editor * Fix InputTransformForm error when rerunning some flows * fix npm run check --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
190 lines
5.4 KiB
Svelte
190 lines
5.4 KiB
Svelte
<script lang="ts">
|
|
import { run } from 'svelte/legacy'
|
|
|
|
import { Loader2, RotateCwIcon } from 'lucide-svelte'
|
|
|
|
import { Button, DrawerContent } from './common'
|
|
import Drawer from './common/drawer/Drawer.svelte'
|
|
import Path from './Path.svelte'
|
|
import { sendUserToast } from '$lib/toast'
|
|
import { Highlight } from 'svelte-highlight'
|
|
import { json } from 'svelte-highlight/languages'
|
|
|
|
import autosize from '$lib/autosize'
|
|
import { ResourceService, VariableService } from '$lib/gen'
|
|
import { oauthStore, workspaceStore } from '$lib/stores'
|
|
import Password from './Password.svelte'
|
|
import { createEventDispatcher } from 'svelte'
|
|
import HighlightTheme from './HighlightTheme.svelte'
|
|
|
|
let drawer: Drawer | undefined = $state()
|
|
let token: undefined | string = $state(undefined)
|
|
export async function open() {
|
|
token = $oauthStore?.access_token ?? ''
|
|
drawer?.openDrawer?.()
|
|
step = 'init'
|
|
description = ''
|
|
}
|
|
|
|
let step: 'init' | 'resource' = $state('init')
|
|
|
|
async function listDatabases() {
|
|
if (!token) return
|
|
databases = undefined
|
|
const res = await fetch('/api/oauth/list_supabase', {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Supabase-Token': token
|
|
}
|
|
})
|
|
databases = await res.json()
|
|
}
|
|
|
|
type Database = {
|
|
name: string
|
|
database?: { host: string }
|
|
region: string
|
|
id: string
|
|
}
|
|
let databases: undefined | Database[] = $state(undefined)
|
|
|
|
run(() => {
|
|
token != undefined && listDatabases()
|
|
})
|
|
|
|
let selectedDatabase: undefined | Database = $state(undefined)
|
|
|
|
let description = $state('')
|
|
let pathError = $state('')
|
|
let password = $state('')
|
|
let path: string | undefined = $state(undefined)
|
|
|
|
/**
|
|
* https://github.com/orgs/supabase/discussions/17817
|
|
* host is in the format of `aws-0-${region}.pooler.supabase.com`
|
|
* user is in the format of `postgres.${id}`
|
|
*/
|
|
let resourceValue = $derived.by(() => ({
|
|
host: `aws-0-${selectedDatabase?.region}.pooler.supabase.com`,
|
|
user: `postgres.${selectedDatabase?.id}`,
|
|
port: 5432,
|
|
dbname: 'postgres',
|
|
sslmode: 'prefer',
|
|
password: `$var:${path}`
|
|
}))
|
|
let disabled = $derived(path == undefined || pathError != '' || path == '')
|
|
|
|
const dispatch = createEventDispatcher()
|
|
async function save() {
|
|
if (!path) return
|
|
await VariableService.createVariable({
|
|
workspace: $workspaceStore!,
|
|
requestBody: {
|
|
path,
|
|
value: password,
|
|
is_secret: true,
|
|
description: 'Password for supabase postgres database',
|
|
is_oauth: false
|
|
}
|
|
})
|
|
|
|
await ResourceService.createResource({
|
|
workspace: $workspaceStore!,
|
|
requestBody: {
|
|
resource_type: 'postgresql',
|
|
path,
|
|
value: resourceValue,
|
|
description
|
|
}
|
|
})
|
|
sendUserToast('Saved postgres resource')
|
|
dispatch('refresh')
|
|
drawer?.closeDrawer?.()
|
|
}
|
|
</script>
|
|
|
|
<HighlightTheme />
|
|
|
|
<Drawer bind:this={drawer} size="800px">
|
|
<DrawerContent title="Add a Supabase Database" on:close={drawer.closeDrawer}>
|
|
{#if step === 'init' || selectedDatabase == undefined}
|
|
<h2
|
|
>Connect an existing database <div class="inline-block ml-2"
|
|
><Button variant="default" wrapperClasses="self-stretch" on:click={listDatabases}
|
|
><RotateCwIcon size={12} /></Button
|
|
></div
|
|
>
|
|
</h2>
|
|
<div class="mt-6"></div>
|
|
|
|
{#if databases == undefined}
|
|
<Loader2 class="animate-spin" />
|
|
{:else}
|
|
<div class=" flex flex-col gap-y-2"></div>
|
|
{#each databases as database}
|
|
<button
|
|
class="btn btn-outline-primary mt-2 border p-2 w-full border-secondary-inverse hover:border-secondary rounded"
|
|
onclick={() => {
|
|
selectedDatabase = database
|
|
|
|
step = 'resource'
|
|
}}
|
|
>
|
|
<div class="flex flex-row items-center">
|
|
<div class="flex-grow">
|
|
<h3 class="text-lg font-semibold">{database.name}</h3>
|
|
|
|
<p class="text-sm text-secondary">id: {database.id} - region: {database.region}</p>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
{/each}
|
|
{/if}
|
|
|
|
<h3 class="mt-8 mb-2">Create a new database</h3>
|
|
<p class="text-sm text-secondary"
|
|
><a href="https://supabase.com/dashboard/projects" target="_blank" rel="noopener noreferrer"
|
|
>Create a new database in your Supabase account
|
|
</a>
|
|
</p>
|
|
{:else if step === 'resource'}
|
|
<Path
|
|
bind:error={pathError}
|
|
bind:path
|
|
initialPath=""
|
|
fullNamePlaceholder={'supabase_' +
|
|
selectedDatabase?.name?.replace(/\s+/g, '').replace(/[^\w\s]/gi, '')}
|
|
kind="resource"
|
|
/>
|
|
|
|
<h2 class="mt-8 mb-2">Database Password</h2>
|
|
<p class="text-sm text-secondary mb-1"
|
|
>For security reasons from supabase, the password of the database cannot be retrieved
|
|
automatically. In a future update, a dedicated role for windmill will be created and the
|
|
password for it will be generated automatically. The password of the database is shown
|
|
during the project creation.</p
|
|
>
|
|
<Password required bind:password />
|
|
|
|
<h3 class="mt-6 mb-2">Description</h3>
|
|
<textarea autocomplete="off" use:autosize bind:value={description}></textarea>
|
|
|
|
<div class="mt-12"></div>
|
|
<p class="my-1 text-sm text-secondary"
|
|
>A resource and a variable will be created at path: {path}. The content of the resource will
|
|
be:</p
|
|
>
|
|
<Highlight language={json} code={JSON.stringify(resourceValue, null, 4)} />
|
|
{/if}
|
|
{#snippet actions()}
|
|
<div class="flex gap-1">
|
|
{#if step == 'resource' && selectedDatabase != undefined}
|
|
<Button variant="default" on:click={() => (step = 'init')}>Back</Button>
|
|
|
|
<Button {disabled} on:click={save} variant="accent">Save</Button>
|
|
{/if}
|
|
</div>
|
|
{/snippet}
|
|
</DrawerContent>
|
|
</Drawer>
|