mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat: auto-fill detected env variables for sites/functions
This commit is contained in:
+3
-2
@@ -11,6 +11,7 @@
|
||||
|
||||
export let show = false;
|
||||
export let variables: Partial<Models.Variable>[];
|
||||
export let productLabel = 'site';
|
||||
|
||||
let newVariables: Partial<Models.Variable>[] = [{ key: '', value: '' }];
|
||||
let secret = false;
|
||||
@@ -65,8 +66,8 @@
|
||||
|
||||
<Modal bind:show onSubmit={handleVariable} title="Create variables" bind:error>
|
||||
<span slot="description">
|
||||
Set the environment variables or secret that will be passed to your site. Global variables
|
||||
can be set in <Link
|
||||
Set the environment variables or secret that will be passed to your {productLabel}. Global
|
||||
variables can be set in <Link
|
||||
variant="muted"
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/settings`}
|
||||
>project settings</Link
|
||||
@@ -0,0 +1,260 @@
|
||||
<script lang="ts">
|
||||
import { Empty, Paginator } from '$lib/components';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import {
|
||||
ActionMenu,
|
||||
Accordion,
|
||||
Badge,
|
||||
InteractiveText,
|
||||
Icon,
|
||||
Layout,
|
||||
Popover,
|
||||
Skeleton,
|
||||
Table,
|
||||
Tooltip,
|
||||
Button as PinkButton
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconDotsHorizontal,
|
||||
IconCode,
|
||||
IconUpload,
|
||||
IconPlus,
|
||||
IconTrash,
|
||||
IconEyeOff,
|
||||
IconPencil
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import VariableEditorModal from './variableEditorModal.svelte';
|
||||
import SecretVariableModal from './secretVariableModal.svelte';
|
||||
import ImportVariablesModal from './importVariablesModal.svelte';
|
||||
import CreateVariableModal from './createVariableModal.svelte';
|
||||
import DeleteVariableModal from './deleteVariableModal.svelte';
|
||||
import UpdateVariableModal from './updateVariableModal.svelte';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
|
||||
export let variables: Partial<Models.Variable>[] = [];
|
||||
export let productLabel = 'site';
|
||||
export let docsLink =
|
||||
'https://appwrite.io/docs/products/sites/develop#accessing-environment-variables';
|
||||
export let analyticsSource = 'site_configuration';
|
||||
export let analyticsCreateSource = 'site_settings';
|
||||
export let isLoading = false;
|
||||
|
||||
let showEditorModal = false;
|
||||
let showImportModal = false;
|
||||
let showSecretModal = false;
|
||||
let showCreate = false;
|
||||
let showUpdate = false;
|
||||
let showDelete = false;
|
||||
|
||||
let currentVariable: Partial<Models.Variable>;
|
||||
|
||||
$: createSource = analyticsCreateSource || analyticsSource;
|
||||
</script>
|
||||
|
||||
<Accordion title="Environment variables" badge="Optional" hideDivider>
|
||||
<Layout.Stack gap="xl">
|
||||
Set up environment variables to securely manage keys and settings for your project.
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.Stack direction="row">
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => {
|
||||
showEditorModal = true;
|
||||
trackEvent(Click.VariablesUpdateClick, {
|
||||
source: analyticsSource
|
||||
});
|
||||
}}>
|
||||
<Icon slot="start" icon={IconCode} /> Editor
|
||||
</Button>
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => {
|
||||
showImportModal = true;
|
||||
trackEvent(Click.VariablesImportClick, {
|
||||
source: analyticsSource
|
||||
});
|
||||
}}>
|
||||
<Icon slot="start" icon={IconUpload} /> Import .env
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
{#if variables?.length}
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => {
|
||||
showCreate = true;
|
||||
trackEvent(Click.VariablesCreateClick, {
|
||||
source: createSource
|
||||
});
|
||||
}}>
|
||||
<Icon slot="start" icon={IconPlus} /> Create variable
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
{#if isLoading && !variables?.length}
|
||||
<Table.Root
|
||||
let:root
|
||||
columns={[
|
||||
{ id: 'key', width: 200 },
|
||||
{ id: 'value' },
|
||||
{ id: 'actions', width: 40 }
|
||||
]}>
|
||||
<svelte:fragment slot="header" let:root>
|
||||
<Table.Header.Cell column="key" {root}>Key</Table.Header.Cell>
|
||||
<Table.Header.Cell column="value" {root}>Value</Table.Header.Cell>
|
||||
<Table.Header.Cell column="actions" {root}></Table.Header.Cell>
|
||||
</svelte:fragment>
|
||||
{#each Array(3) as _}
|
||||
<Table.Row.Base {root}>
|
||||
<Table.Cell column="key" {root}>
|
||||
<Skeleton variant="line" width={120} height={14} />
|
||||
</Table.Cell>
|
||||
<Table.Cell column="value" {root}>
|
||||
<Skeleton variant="line" width="100%" height={14} />
|
||||
</Table.Cell>
|
||||
<Table.Cell column="actions" {root}>
|
||||
<Skeleton variant="line" width={24} height={14} />
|
||||
</Table.Cell>
|
||||
</Table.Row.Base>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
{:else if variables?.length}
|
||||
<Paginator items={variables} limit={6} hideFooter={variables.length <= 6}>
|
||||
{#snippet children(paginatedItems)}
|
||||
<Table.Root
|
||||
let:root
|
||||
columns={[
|
||||
{ id: 'key', width: 200 },
|
||||
{ id: 'value' },
|
||||
{ id: 'actions', width: 40 }
|
||||
]}>
|
||||
<svelte:fragment slot="header" let:root>
|
||||
<Table.Header.Cell column="key" {root}>Key</Table.Header.Cell>
|
||||
<Table.Header.Cell column="value" {root}>Value</Table.Header.Cell>
|
||||
<Table.Header.Cell column="actions" {root}></Table.Header.Cell>
|
||||
</svelte:fragment>
|
||||
{#each paginatedItems as variable}
|
||||
<Table.Row.Base {root}>
|
||||
<Table.Cell column="key" {root}>{variable.key}</Table.Cell>
|
||||
<Table.Cell column="value" {root}>
|
||||
<!-- TODO: fix max width -->
|
||||
<div style="max-width: 20rem">
|
||||
{#if variable.secret}
|
||||
<Tooltip maxWidth="26rem">
|
||||
<Badge
|
||||
content="Secret"
|
||||
variant="secondary"
|
||||
size="s" />
|
||||
<svelte:fragment slot="tooltip">
|
||||
This value is secret, you cannot see its
|
||||
value.
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<InteractiveText
|
||||
variant="secret"
|
||||
isVisible={false}
|
||||
text={variable.value} />
|
||||
{/if}
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell column="actions" {root}>
|
||||
<div style="margin-inline-start: auto">
|
||||
<Popover
|
||||
padding="none"
|
||||
placement="bottom-end"
|
||||
let:toggle>
|
||||
<PinkButton.Button
|
||||
icon
|
||||
variant="text"
|
||||
size="s"
|
||||
aria-label="More options"
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
toggle(e);
|
||||
}}>
|
||||
<Icon icon={IconDotsHorizontal} size="s" />
|
||||
</PinkButton.Button>
|
||||
|
||||
<svelte:fragment slot="tooltip" let:toggle>
|
||||
<ActionMenu.Root>
|
||||
{#if !variable?.secret}
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconPencil}
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
currentVariable = variable;
|
||||
showUpdate = true;
|
||||
}}>
|
||||
Update
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
{#if !variable?.secret}
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconEyeOff}
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
currentVariable = variable;
|
||||
showSecretModal = true;
|
||||
}}>
|
||||
Secret
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
<ActionMenu.Item.Button
|
||||
status="danger"
|
||||
leadingIcon={IconTrash}
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
currentVariable = variable;
|
||||
showDelete = true;
|
||||
}}>
|
||||
Delete
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
</Table.Row.Base>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
{/snippet}
|
||||
</Paginator>
|
||||
{:else}
|
||||
<Empty on:click={() => (showCreate = true)}>Create variables to get started</Empty>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
|
||||
{#if showEditorModal}
|
||||
<VariableEditorModal bind:variables bind:showEditor={showEditorModal} {docsLink} />
|
||||
{/if}
|
||||
|
||||
{#if showSecretModal}
|
||||
<SecretVariableModal bind:show={showSecretModal} bind:currentVariable bind:variables />
|
||||
{/if}
|
||||
|
||||
{#if showImportModal}
|
||||
<ImportVariablesModal bind:show={showImportModal} bind:variables />
|
||||
{/if}
|
||||
|
||||
{#if showCreate}
|
||||
<CreateVariableModal bind:show={showCreate} bind:variables {productLabel} />
|
||||
{/if}
|
||||
{#if showUpdate}
|
||||
<UpdateVariableModal
|
||||
bind:show={showUpdate}
|
||||
bind:variables
|
||||
bind:selectedVar={currentVariable}
|
||||
{productLabel} />
|
||||
{/if}
|
||||
|
||||
{#if showDelete}
|
||||
<DeleteVariableModal bind:show={showDelete} bind:variables bind:currentVariable />
|
||||
{/if}
|
||||
+3
-1
@@ -11,6 +11,7 @@
|
||||
export let show = false;
|
||||
export let selectedVar: Partial<Models.Variable>;
|
||||
export let variables: Partial<Models.Variable>[];
|
||||
export let productLabel = 'site';
|
||||
|
||||
let pair = {
|
||||
$id: selectedVar?.$id,
|
||||
@@ -40,7 +41,8 @@
|
||||
|
||||
<Modal bind:show onSubmit={handleVariable} title="Update variable">
|
||||
<span slot="description">
|
||||
Update the environment variable for your site. Global variables can be set in <Link
|
||||
Update the environment variable for your {productLabel}. Global variables can be set in
|
||||
<Link
|
||||
variant="muted"
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/settings`}
|
||||
>project settings</Link
|
||||
+3
-5
@@ -13,6 +13,8 @@
|
||||
|
||||
export let showEditor = false;
|
||||
export let variables: Partial<Models.Variable>[];
|
||||
export let docsLink =
|
||||
'https://appwrite.io/docs/products/sites/develop#accessing-environment-variables';
|
||||
|
||||
const editableVariables = variables.filter((variable) => !variable.secret);
|
||||
const secretVariables = variables.filter((variable) => variable.secret);
|
||||
@@ -122,11 +124,7 @@
|
||||
{#if secretVariables?.length > 0}
|
||||
<Alert.Inline status="info">
|
||||
{secretVariables.length} secret variables are hidden from the editor. Their values will
|
||||
remain unchanged. <Link
|
||||
href="https://appwrite.io/docs/products/sites/develop#accessing-environment-variables"
|
||||
external
|
||||
variant="muted">Learn more</Link
|
||||
>.
|
||||
remain unchanged. <Link href={docsLink} external variant="muted">Learn more</Link>.
|
||||
</Alert.Inline>
|
||||
{/if}
|
||||
<Layout.Stack gap="s">
|
||||
+48
-1
@@ -59,6 +59,45 @@
|
||||
|
||||
let detectingRuntime = true;
|
||||
|
||||
type DetectedVariable = {
|
||||
key?: string;
|
||||
name?: string;
|
||||
value?: string;
|
||||
secret?: boolean;
|
||||
};
|
||||
|
||||
function normalizeDetectedVariables(detected: DetectedVariable[] = []) {
|
||||
const normalized: Partial<Models.Variable>[] = [];
|
||||
detected.forEach((variable) => {
|
||||
const key = variable.key ?? variable.name;
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
normalized.push({
|
||||
key,
|
||||
value: variable.value ?? '',
|
||||
secret: variable.secret ?? false
|
||||
});
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function mergeVariables(
|
||||
existing: Partial<Models.Variable>[],
|
||||
detected: Partial<Models.Variable>[]
|
||||
) {
|
||||
const map = new Map(existing.map((variable) => [variable.key, variable]));
|
||||
detected.forEach((variable) => {
|
||||
if (!variable.key) {
|
||||
return;
|
||||
}
|
||||
if (!map.has(variable.key)) {
|
||||
map.set(variable.key, variable);
|
||||
}
|
||||
});
|
||||
return Array.from(map.values());
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
installation.set(data.installation);
|
||||
repository.set(data.repository);
|
||||
@@ -82,6 +121,10 @@
|
||||
entrypoint = detections.entrypoint;
|
||||
buildCommand = detections.commands;
|
||||
runtime = detections.runtime as Runtime;
|
||||
const detectedVariables = normalizeDetectedVariables(detections?.variables);
|
||||
if (detectedVariables.length) {
|
||||
variables = mergeVariables(variables, detectedVariables);
|
||||
}
|
||||
|
||||
trackEvent(Submit.FrameworkDetect, { runtime, source: 'repository' });
|
||||
} catch (error) {
|
||||
@@ -189,7 +232,11 @@
|
||||
installationId={data.installation.$id}
|
||||
repositoryId={data.repository.id} />
|
||||
|
||||
<Configuration bind:buildCommand bind:roles />
|
||||
<Configuration
|
||||
bind:buildCommand
|
||||
bind:roles
|
||||
bind:variables
|
||||
isVariablesLoading={detectingRuntime} />
|
||||
</Layout.Stack>
|
||||
</Form>
|
||||
<svelte:fragment slot="aside">
|
||||
|
||||
+11
@@ -3,9 +3,13 @@
|
||||
import { Link } from '$lib/elements';
|
||||
import { InputText } from '$lib/elements/forms';
|
||||
import { Accordion, Fieldset, Layout } from '@appwrite.io/pink-svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import EnvironmentVariables from '$lib/components/variables/environmentVariables.svelte';
|
||||
|
||||
export let buildCommand = '';
|
||||
export let roles: string[] = [];
|
||||
export let variables: Partial<Models.Variable>[] = [];
|
||||
export let isVariablesLoading = false;
|
||||
</script>
|
||||
|
||||
<Fieldset legend="Settings">
|
||||
@@ -31,5 +35,12 @@
|
||||
<Roles bind:roles />
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
<EnvironmentVariables
|
||||
bind:variables
|
||||
productLabel="function"
|
||||
docsLink="https://appwrite.io/docs/products/functions/develop"
|
||||
analyticsSource="function_configuration"
|
||||
analyticsCreateSource="function_configuration"
|
||||
isLoading={isVariablesLoading} />
|
||||
</Layout.Stack>
|
||||
</Fieldset>
|
||||
|
||||
+4
-230
@@ -1,38 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { Empty, Paginator } from '$lib/components';
|
||||
import { Button, InputSelect, InputText } from '$lib/elements/forms';
|
||||
import {
|
||||
Fieldset,
|
||||
Layout,
|
||||
Popover,
|
||||
Icon,
|
||||
Table,
|
||||
Badge,
|
||||
InteractiveText,
|
||||
ActionMenu,
|
||||
Accordion,
|
||||
Tooltip,
|
||||
Button as PinkButton
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import {
|
||||
IconDotsHorizontal,
|
||||
IconCode,
|
||||
IconUpload,
|
||||
IconPlus,
|
||||
IconTrash,
|
||||
IconEyeOff,
|
||||
IconPencil
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { Fieldset, Layout, Accordion } from '@appwrite.io/pink-svelte';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { iconPath } from '$lib/stores/app';
|
||||
import VariableEditorModal from './variableEditorModal.svelte';
|
||||
import SecretVariableModal from './secretVariableModal.svelte';
|
||||
import ImportSiteVariablesModal from './importSiteVariablesModal.svelte';
|
||||
import CreateVariableModal from './createVariableModal.svelte';
|
||||
import DeleteVariableModal from './deleteVariableModal.svelte';
|
||||
import UpdateVariableModal from './updateVariableModal.svelte';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
import { getFrameworkIcon } from '$lib/stores/sites';
|
||||
import EnvironmentVariables from '$lib/components/variables/environmentVariables.svelte';
|
||||
|
||||
export let frameworks: Models.Framework[];
|
||||
export let selectedFramework: Models.Framework;
|
||||
@@ -42,18 +14,11 @@
|
||||
frameworkData?.adapters.find((adapter) => adapter.key === 'static');
|
||||
|
||||
export let variables: Partial<Models.Variable>[] = [];
|
||||
export let isVariablesLoading = false;
|
||||
export let installCommand = '';
|
||||
export let buildCommand = '';
|
||||
export let outputDirectory = '';
|
||||
|
||||
let showEditorModal = false;
|
||||
let showImportModal = false;
|
||||
let showSecretModal = false;
|
||||
let showCreate = false;
|
||||
let showUpdate = false;
|
||||
let showDelete = false;
|
||||
|
||||
let currentVariable: Partial<Models.Variable>;
|
||||
let frameworkId = selectedFramework.key;
|
||||
|
||||
$: if (!installCommand || !buildCommand || !outputDirectory) {
|
||||
@@ -138,198 +103,7 @@
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Environment variables" badge="Optional" hideDivider>
|
||||
<Layout.Stack gap="xl">
|
||||
Set up environment variables to securely manage keys and settings for your
|
||||
project.
|
||||
<Layout.Stack gap="l">
|
||||
<Layout.Stack direction="row">
|
||||
<Layout.Stack direction="row" gap="s">
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => {
|
||||
showEditorModal = true;
|
||||
trackEvent(Click.VariablesUpdateClick, {
|
||||
source: 'site_configuration'
|
||||
});
|
||||
}}>
|
||||
<Icon slot="start" icon={IconCode} /> Editor
|
||||
</Button>
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => {
|
||||
showImportModal = true;
|
||||
trackEvent(Click.VariablesImportClick, {
|
||||
source: 'site_configuration'
|
||||
});
|
||||
}}>
|
||||
<Icon slot="start" icon={IconUpload} /> Import .env
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
{#if variables?.length}
|
||||
<Button
|
||||
secondary
|
||||
size="s"
|
||||
on:mousedown={() => {
|
||||
showCreate = true;
|
||||
trackEvent(Click.VariablesCreateClick, {
|
||||
source: 'site_settings'
|
||||
});
|
||||
}}>
|
||||
<Icon slot="start" icon={IconPlus} /> Create variable
|
||||
</Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
|
||||
{#if variables?.length}
|
||||
<Paginator
|
||||
items={variables}
|
||||
limit={6}
|
||||
hideFooter={variables.length <= 6}>
|
||||
{#snippet children(paginatedItems)}
|
||||
<Table.Root
|
||||
let:root
|
||||
columns={[
|
||||
{ id: 'key', width: 200 },
|
||||
{ id: 'value' },
|
||||
{ id: 'actions', width: 40 }
|
||||
]}>
|
||||
<svelte:fragment slot="header" let:root>
|
||||
<Table.Header.Cell column="key" {root}
|
||||
>Key</Table.Header.Cell>
|
||||
<Table.Header.Cell column="value" {root}
|
||||
>Value</Table.Header.Cell>
|
||||
<Table.Header.Cell column="actions" {root}
|
||||
></Table.Header.Cell>
|
||||
</svelte:fragment>
|
||||
{#each paginatedItems as variable}
|
||||
<Table.Row.Base {root}>
|
||||
<Table.Cell column="key" {root}
|
||||
>{variable.key}</Table.Cell>
|
||||
<Table.Cell column="value" {root}>
|
||||
<!-- TODO: fix max width -->
|
||||
<div style="max-width: 20rem">
|
||||
{#if variable.secret}
|
||||
<Tooltip maxWidth="26rem">
|
||||
<Badge
|
||||
content="Secret"
|
||||
variant="secondary"
|
||||
size="s" />
|
||||
<svelte:fragment slot="tooltip">
|
||||
This value is secret, you cannot
|
||||
see its value.
|
||||
</svelte:fragment>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<InteractiveText
|
||||
variant="secret"
|
||||
isVisible={false}
|
||||
text={variable.value} />
|
||||
{/if}
|
||||
</div>
|
||||
</Table.Cell>
|
||||
<Table.Cell column="actions" {root}>
|
||||
<div style="margin-inline-start: auto">
|
||||
<Popover
|
||||
padding="none"
|
||||
placement="bottom-end"
|
||||
let:toggle>
|
||||
<PinkButton.Button
|
||||
icon
|
||||
variant="text"
|
||||
size="s"
|
||||
aria-label="More options"
|
||||
on:click={(e) => {
|
||||
e.preventDefault();
|
||||
toggle(e);
|
||||
}}>
|
||||
<Icon
|
||||
icon={IconDotsHorizontal}
|
||||
size="s" />
|
||||
</PinkButton.Button>
|
||||
|
||||
<svelte:fragment
|
||||
slot="tooltip"
|
||||
let:toggle>
|
||||
<ActionMenu.Root>
|
||||
{#if !variable?.secret}
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconPencil}
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
currentVariable =
|
||||
variable;
|
||||
showUpdate = true;
|
||||
}}>
|
||||
Update
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
{#if !variable?.secret}
|
||||
<ActionMenu.Item.Button
|
||||
leadingIcon={IconEyeOff}
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
|
||||
currentVariable =
|
||||
variable;
|
||||
showSecretModal = true;
|
||||
}}>
|
||||
Secret
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
<ActionMenu.Item.Button
|
||||
status="danger"
|
||||
leadingIcon={IconTrash}
|
||||
on:click={(e) => {
|
||||
toggle(e);
|
||||
currentVariable =
|
||||
variable;
|
||||
showDelete = true;
|
||||
}}>
|
||||
Delete
|
||||
</ActionMenu.Item.Button>
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
</Table.Row.Base>
|
||||
{/each}
|
||||
</Table.Root>
|
||||
{/snippet}
|
||||
</Paginator>
|
||||
{:else}
|
||||
<Empty on:click={() => (showCreate = true)}
|
||||
>Create variables to get started</Empty>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
<EnvironmentVariables bind:variables isLoading={isVariablesLoading} />
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Fieldset>
|
||||
|
||||
{#if showEditorModal}
|
||||
<VariableEditorModal bind:variables bind:showEditor={showEditorModal} />
|
||||
{/if}
|
||||
|
||||
{#if showSecretModal}
|
||||
<SecretVariableModal bind:show={showSecretModal} bind:currentVariable bind:variables />
|
||||
{/if}
|
||||
|
||||
{#if showImportModal}
|
||||
<ImportSiteVariablesModal bind:show={showImportModal} bind:variables />
|
||||
{/if}
|
||||
|
||||
{#if showCreate}
|
||||
<CreateVariableModal bind:show={showCreate} bind:variables />
|
||||
{/if}
|
||||
{#if showUpdate}
|
||||
<UpdateVariableModal bind:show={showUpdate} bind:variables bind:selectedVar={currentVariable} />
|
||||
{/if}
|
||||
|
||||
{#if showDelete}
|
||||
<DeleteVariableModal bind:show={showDelete} bind:variables bind:currentVariable />
|
||||
{/if}
|
||||
|
||||
+48
@@ -47,6 +47,46 @@
|
||||
let silentMode = false;
|
||||
let domain = data.domain;
|
||||
let domainIsValid = true;
|
||||
let isVariablesLoading = true;
|
||||
|
||||
type DetectedVariable = {
|
||||
key?: string;
|
||||
name?: string;
|
||||
value?: string;
|
||||
secret?: boolean;
|
||||
};
|
||||
|
||||
function normalizeDetectedVariables(detected: DetectedVariable[] = []) {
|
||||
const normalized: Partial<Models.Variable>[] = [];
|
||||
detected.forEach((variable) => {
|
||||
const key = variable.key ?? variable.name;
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
normalized.push({
|
||||
key,
|
||||
value: variable.value ?? '',
|
||||
secret: variable.secret ?? false
|
||||
});
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function mergeVariables(
|
||||
existing: Partial<Models.Variable>[],
|
||||
detected: Partial<Models.Variable>[]
|
||||
) {
|
||||
const map = new Map(existing.map((variable) => [variable.key, variable]));
|
||||
detected.forEach((variable) => {
|
||||
if (!variable.key) {
|
||||
return;
|
||||
}
|
||||
if (!map.has(variable.key)) {
|
||||
map.set(variable.key, variable);
|
||||
}
|
||||
});
|
||||
return Array.from(map.values());
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
installation.set(data.installation);
|
||||
@@ -58,6 +98,7 @@
|
||||
|
||||
async function detectFramework() {
|
||||
try {
|
||||
isVariablesLoading = true;
|
||||
const response = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.vcs.createRepositoryDetection({
|
||||
@@ -71,6 +112,10 @@
|
||||
installCommand = adapter?.installCommand;
|
||||
buildCommand = adapter?.buildCommand;
|
||||
outputDirectory = adapter?.outputDirectory;
|
||||
const detectedVariables = normalizeDetectedVariables(response?.variables);
|
||||
if (detectedVariables.length) {
|
||||
variables = mergeVariables(variables, detectedVariables);
|
||||
}
|
||||
trackEvent(Submit.FrameworkDetect, {
|
||||
source: 'repository',
|
||||
framework: framework.key
|
||||
@@ -78,6 +123,8 @@
|
||||
} catch (error) {
|
||||
framework = data.frameworks.frameworks.find((f) => f.key === 'other');
|
||||
trackError(error, Submit.FrameworkDetect);
|
||||
} finally {
|
||||
isVariablesLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +248,7 @@
|
||||
bind:outputDirectory
|
||||
bind:selectedFramework={framework}
|
||||
bind:variables
|
||||
{isVariablesLoading}
|
||||
frameworks={data.frameworks.frameworks} />
|
||||
{/key}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user