mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
gh app import export from other instance + ui improvements (#5518)
* updating hub paths for git sync scripts supporting github app * removing accidentally committed file * gh app import export from other instance + ui improvements * update repo ref * sqlx + ellipsis
This commit is contained in:
-23
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM log_file WHERE hostname = $1 AND log_ts = $2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Timestamp"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "dee32ce9c4010ae407b4d1ba2ecf2062c36ee5dab3d4894ad4b2aa8b2f5a0db9"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
6406a96fb6d0ef473915529300a757057ee19306
|
||||
94cbac33756c1fba56da037456f44a4d294475b1
|
||||
@@ -1381,6 +1381,63 @@ paths:
|
||||
'200':
|
||||
description: Installation successfully deleted
|
||||
|
||||
/w/{workspace}/github_app/export/{installationId}:
|
||||
get:
|
||||
summary: Export GitHub installation JWT token
|
||||
description: Exports the JWT token for a specific GitHub installation in the workspace
|
||||
operationId: exportInstallation
|
||||
tags:
|
||||
- Git Sync
|
||||
parameters:
|
||||
- name: workspace
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: installationId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'200':
|
||||
description: Successfully exported the JWT token
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
jwt_token:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/github_app/import:
|
||||
post:
|
||||
summary: Import GitHub installation from JWT token
|
||||
description: Imports a GitHub installation from a JWT token exported from another instance
|
||||
operationId: importInstallation
|
||||
tags:
|
||||
- Git Sync
|
||||
parameters:
|
||||
- name: workspace
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- jwt_token
|
||||
properties:
|
||||
jwt_token:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successfully imported the installation
|
||||
|
||||
/users/accept_invite:
|
||||
post:
|
||||
summary: accept invite to workspace
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
type ResourceType,
|
||||
type GetGlobalConnectedRepositoriesResponse
|
||||
} from '$lib/gen'
|
||||
import { workspaceStore, enterpriseLicense, userWorkspaces } from '$lib/stores'
|
||||
import { workspaceStore, enterpriseLicense, userWorkspaces, userStore } from '$lib/stores'
|
||||
import { base } from '$lib/base'
|
||||
import { emptySchema, emptyString } from '$lib/utils'
|
||||
import SchemaForm from './SchemaForm.svelte'
|
||||
@@ -16,7 +16,8 @@
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import Popover from './meltComponents/Popover.svelte'
|
||||
import Button from './common/button/Button.svelte'
|
||||
import { Loader2, Github, RotateCw, Plus, Minus } from 'lucide-svelte'
|
||||
import { Loader2, Github, RotateCw, Plus, Minus, Download } from 'lucide-svelte'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
export let resourceType: string
|
||||
export let resourceTypeInfo: ResourceType | undefined
|
||||
@@ -36,6 +37,10 @@
|
||||
let selectedGHAppAccountId: string | undefined = undefined
|
||||
let selectedGHAppRepository: string | undefined = undefined
|
||||
let githubInstallationUrl: string | undefined = undefined
|
||||
let installationCheckInterval: number | undefined = undefined
|
||||
let isCheckingInstallation = false
|
||||
let importJwt = ''
|
||||
let githubAppPopover: { open: () => void; close: () => void } | null = null
|
||||
|
||||
async function loadGithubInstallations() {
|
||||
if (!$enterpriseLicense) return
|
||||
@@ -70,6 +75,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
function startInstallationCheck() {
|
||||
isCheckingInstallation = true
|
||||
installationCheckInterval = window.setInterval(async () => {
|
||||
const installations = await GitSyncService.getGlobalConnectedRepositories()
|
||||
if (installations.length > 0) {
|
||||
stopInstallationCheck()
|
||||
githubInstallations = installations
|
||||
workspaceGithubInstallations = githubInstallations.filter(
|
||||
(_) => _.workspace_id === $workspaceStore
|
||||
)
|
||||
// Open the popover with a small delay as otherwise it doesn't open
|
||||
setTimeout(() => {
|
||||
githubAppPopover?.open()
|
||||
}, 100)
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
function stopInstallationCheck() {
|
||||
if (installationCheckInterval) {
|
||||
clearInterval(installationCheckInterval)
|
||||
installationCheckInterval = undefined
|
||||
}
|
||||
isCheckingInstallation = false
|
||||
}
|
||||
|
||||
// Clean up interval when component is destroyed
|
||||
onDestroy(() => {
|
||||
stopInstallationCheck()
|
||||
})
|
||||
|
||||
function getRepositories(accountId: string) {
|
||||
return githubInstallations.find((_) => _.account_id === accountId)?.repositories || []
|
||||
}
|
||||
@@ -153,7 +189,7 @@
|
||||
}
|
||||
|
||||
$: resourceType == 'postgresql' && isSupabaseAvailable()
|
||||
$: resourceType == 'git_repository' && loadGithubInstallations()
|
||||
$: resourceType == 'git_repository' && $userStore?.is_admin && loadGithubInstallations()
|
||||
|
||||
let connectionString = ''
|
||||
let validConnectionString = true
|
||||
@@ -235,6 +271,61 @@
|
||||
sendUserToast('Failed to delete installation', true)
|
||||
}
|
||||
}
|
||||
|
||||
async function exportInstallation(installationId: number) {
|
||||
if (!$workspaceStore) {
|
||||
sendUserToast('Failed to export installation', true)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const response = await GitSyncService.exportInstallation({
|
||||
workspace: $workspaceStore,
|
||||
installationId: installationId
|
||||
})
|
||||
if (!response.jwt_token) {
|
||||
sendUserToast('Failed to export installation', true)
|
||||
return
|
||||
}
|
||||
// Copy to clipboard
|
||||
await navigator.clipboard.writeText(response.jwt_token)
|
||||
sendUserToast(
|
||||
'JWT token copied to clipboard. This token is sensitive and should be kept secret!',
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
10000
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
sendUserToast('Failed to export installation', true)
|
||||
}
|
||||
}
|
||||
|
||||
async function importInstallation(jwt: string) {
|
||||
if (!$workspaceStore) {
|
||||
sendUserToast('Failed to import installation', true)
|
||||
return
|
||||
}
|
||||
try {
|
||||
await GitSyncService.importInstallation({
|
||||
workspace: $workspaceStore,
|
||||
requestBody: { jwt_token: jwt }
|
||||
})
|
||||
importJwt = ''
|
||||
sendUserToast('Installation imported successfully', false)
|
||||
await loadGithubInstallations()
|
||||
} catch (error) {
|
||||
sendUserToast('Failed to import installation', true)
|
||||
}
|
||||
}
|
||||
|
||||
function handleInstallClick() {
|
||||
if (githubInstallations.length === 0) {
|
||||
if (!isCheckingInstallation) {
|
||||
startInstallationCheck()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !notFound}
|
||||
@@ -303,7 +394,7 @@
|
||||
<div class="text-[#11181C] dark:text-[#EDEDED] font-semibold">Connect Supabase</div>
|
||||
</a>
|
||||
{/if}
|
||||
{#if resourceType == 'git_repository' && $workspaceStore}
|
||||
{#if resourceType == 'git_repository' && $workspaceStore && $userStore?.is_admin}
|
||||
{#if !loadingGithubInstallations}
|
||||
<Button
|
||||
color="light"
|
||||
@@ -318,6 +409,7 @@
|
||||
{/if}
|
||||
{#if githubInstallations.length > 0}
|
||||
<Popover
|
||||
bind:this={githubAppPopover}
|
||||
floatingConfig={{
|
||||
placement: 'bottom'
|
||||
}}
|
||||
@@ -340,7 +432,7 @@
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="content" let:close>
|
||||
<div class="block text-primary p-4">
|
||||
<div class="flex flex-col gap-4 w-[500px]">
|
||||
<div class="flex flex-col gap-4 w-[600px]">
|
||||
{#if workspaceGithubInstallations.length > 0}
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold text-secondary">Select Repository</p>
|
||||
@@ -414,9 +506,9 @@
|
||||
<thead>
|
||||
<tr class="text-left text-xs text-tertiary">
|
||||
<th class="pb-2 w-1/3">Org</th>
|
||||
<th class="pb-2 w-1/3">Workspace</th>
|
||||
<th class="pb-2 w-1/4">Repos</th>
|
||||
<th class="pb-2 w-1/12" />
|
||||
<th class="pb-2 w-1/6">Workspace</th>
|
||||
<th class="pb-2 w-1/6">Repos</th>
|
||||
<th class="pb-2 w-1/3" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -444,16 +536,29 @@
|
||||
<td class="py-2 text-tertiary">
|
||||
{installation.repositories.length} repos
|
||||
</td>
|
||||
<td class="pl-2 py-2 text-right">
|
||||
<Button
|
||||
size="xs2"
|
||||
color="red"
|
||||
iconOnly
|
||||
btnClasses="px-1"
|
||||
startIcon={{ icon: Minus }}
|
||||
on:click={() =>
|
||||
deleteInstallation(installation.installation_id)}
|
||||
/>
|
||||
<td class="py-2 text-right">
|
||||
<div class="flex justify-end gap-1">
|
||||
<Button
|
||||
size="xs2"
|
||||
color="blue"
|
||||
title="Export installation to other instance"
|
||||
startIcon={{ icon: Download }}
|
||||
on:click={() =>
|
||||
exportInstallation(installation.installation_id)}
|
||||
>
|
||||
Export
|
||||
</Button>
|
||||
<Button
|
||||
size="xs2"
|
||||
color="red"
|
||||
title="Remove installation from workspace"
|
||||
startIcon={{ icon: Minus }}
|
||||
on:click={() =>
|
||||
deleteInstallation(installation.installation_id)}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
@@ -472,9 +577,9 @@
|
||||
<thead>
|
||||
<tr class="text-left text-xs text-tertiary">
|
||||
<th class="pb-2 w-1/3">Org</th>
|
||||
<th class="pb-2 w-1/3">Workspace</th>
|
||||
<th class="pb-2 w-1/4">Repos</th>
|
||||
<th class="pb-2 w-1/12" />
|
||||
<th class="pb-2 w-1/6">Workspace</th>
|
||||
<th class="pb-2 w-1/6">Repos</th>
|
||||
<th class="pb-2 w-1/3" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -502,13 +607,12 @@
|
||||
<td class="py-2 text-tertiary">
|
||||
{installation.repositories.length} repos
|
||||
</td>
|
||||
<td class="pl-2 py-2 text-right">
|
||||
<td class="pl-8 py-2 text-right">
|
||||
<Button
|
||||
size="xs2"
|
||||
color="blue"
|
||||
btnClasses="px-1"
|
||||
startIcon={{ icon: Plus }}
|
||||
title="Add installation to workspace"
|
||||
startIcon={{ icon: Plus }}
|
||||
on:click={() =>
|
||||
addInstallationToWorkspace(
|
||||
installation.installation_id,
|
||||
@@ -527,6 +631,27 @@
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex flex-col gap-2">
|
||||
<p class="text-sm font-semibold text-secondary"
|
||||
>Import installation from other instance:</p
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Paste JWT token here"
|
||||
bind:value={importJwt}
|
||||
class="flex-1"
|
||||
/>
|
||||
<Button
|
||||
color="blue"
|
||||
on:click={() => importInstallation(importJwt)}
|
||||
disabled={!importJwt}
|
||||
>
|
||||
Import
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
@@ -538,13 +663,18 @@
|
||||
size="xs"
|
||||
disabled={!$enterpriseLicense || loadingGithubInstallations}
|
||||
startIcon={{
|
||||
icon: loadingGithubInstallations ? Loader2 : Github,
|
||||
classes: loadingGithubInstallations ? 'animate-spin' : ''
|
||||
icon: loadingGithubInstallations || isCheckingInstallation ? Loader2 : Github,
|
||||
classes: loadingGithubInstallations || isCheckingInstallation ? 'animate-spin' : ''
|
||||
}}
|
||||
href={githubInstallationUrl}
|
||||
target="_blank"
|
||||
on:click={handleInstallClick}
|
||||
>
|
||||
{$enterpriseLicense ? 'Install GitHub App' : 'GitHub App (ee only)'}
|
||||
{$enterpriseLicense
|
||||
? isCheckingInstallation
|
||||
? 'Waiting for installation...'
|
||||
: 'Install GitHub App'
|
||||
: 'GitHub App (ee only)'}
|
||||
</Button>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user