fix: variables

This commit is contained in:
Arman
2025-03-13 11:51:57 +01:00
parent cce768e989
commit d8744dc9f0
9 changed files with 267 additions and 46 deletions
@@ -2,10 +2,8 @@
import { base } from '$app/paths';
import { Button } from '$lib/elements/forms';
import { Container } from '$lib/layout';
import Create from './createProjectCloud.svelte';
import CreateProject from './createProject.svelte';
import CreateOrganization from '../createOrganization.svelte';
import { wizard } from '$lib/stores/wizard';
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
import { page } from '$app/stores';
import { registerCommands } from '$lib/commandCenter';
@@ -0,0 +1,115 @@
<script lang="ts">
import { Button, InputPassword } from '$lib/elements/forms';
import { Modal } from '$lib/components';
import { InputText } from '$lib/elements/forms';
import { createEventDispatcher } from 'svelte';
import type { Models } from '@appwrite.io/console';
import { project } from './store';
import { base } from '$app/paths';
import { Alert, Layout, Selector, Button as PinkButton, Icon } from '@appwrite.io/pink-svelte';
import { Link } from '$lib/elements';
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
export let isGlobal: boolean;
export let product: 'function' | 'site' = 'function';
export let showCreate = false;
let newVariables: Partial<Models.Variable>[] = [{ key: '', value: '' }];
let secret = false;
const dispatch = createEventDispatcher();
function close() {
showCreate = false;
}
function handleVariable() {
if (secret) {
newVariables = newVariables.map((variable) => ({ ...variable, secret: true }));
}
newVariables.forEach((variable) => {
if (('' + variable.value).length > 8192) {
throw new Error(`Variable ${variable.key} is longer than 8192 allowed characters`);
}
});
dispatch('created', newVariables);
close();
}
function removeVariable(index: number) {
if (newVariables.length === 1) {
newVariables[0].key = '';
newVariables[0].value = '';
} else {
newVariables.splice(index, 1);
newVariables = [...newVariables];
}
}
</script>
<Modal
bind:show={showCreate}
onSubmit={handleVariable}
title={`Create' ${isGlobal ? 'global' : 'environment'} variable`}>
<svelte:fragment slot="description">
<span>
Set the environment variables or secret keys that will be passed to {!isGlobal
? `your ${product}`
: `all functions and sites within your project`}.
</span>
</svelte:fragment>
<Layout.Stack>
{#if !isGlobal}
<Alert.Inline>
When there is a naming conflict with a global variable in your <Link
href={`${base}/project-${$project.$id}/settings/variables`}>
project settings</Link>
and a {product} environment variable, the global variable will be ignored.
</Alert.Inline>
{/if}
<Layout.Stack gap="xs">
{#each newVariables as pair, i}
<Layout.Stack direction="row" gap="s" alignItems="flex-end" inline>
<InputText
id="key"
label={`${i === 0 ? 'Key' : ''}`}
placeholder="ENTER_KEY"
bind:value={pair.key}
autocomplete={false}
autofocus />
<InputPassword
id="value"
label={`${i === 0 ? 'Value' : ''}`}
placeholder="Enter value"
bind:value={pair.value}
minlength={0} />
<PinkButton.Button
icon
variant="secondary"
type="button"
size="s"
disabled={newVariables.length === 1 && !pair.key && !pair.value}
on:click={() => removeVariable(i)}>
<Icon icon={IconX} />
</PinkButton.Button>
</Layout.Stack>
{/each}
<div>
<Button
text
compact
disabled={newVariables.some((pair) => !pair.key)}
on:click={() => (newVariables = [...newVariables, { key: '', value: '' }])}>
<Icon slot="start" icon={IconPlus} />Add variable</Button>
</div>
</Layout.Stack>
<Selector.Checkbox
id="secret"
label="Secret"
bind:checked={secret}
description="If selected, you and your team won't be able to read the values after creation." />
</Layout.Stack>
<svelte:fragment slot="footer">
<Button secondary on:click={close}>Cancel</Button>
<Button submit>Create</Button>
</svelte:fragment>
</Modal>
@@ -10,7 +10,6 @@
import UpdateSchedule from './updateSchedule.svelte';
import UpdateScopes from './updateScopes.svelte';
import UpdateTimeout from './updateTimeout.svelte';
import UpdateVariables from '../../../updateVariables.svelte';
import { sdk } from '$lib/stores/sdk';
import { Dependencies } from '$lib/constants';
import { invalidate } from '$app/navigation';
@@ -21,17 +20,29 @@
import UpdateBuildCommand from './updateBuildCommand.svelte';
import UpdateResourceLimits from './updateResourceLimits.svelte';
import { isCloud } from '$lib/system';
import UpdateVariables from '$routes/(console)/project-[project]/updateVariables.svelte';
export let data;
let showAlert = true;
const sdkCreateVariable = async (key: string, value: string, secret: boolean) => {
const sdkCreateVariable = async (key: string, value: string, secret?: boolean) => {
await sdk.forProject.functions.createVariable(data.function.$id, key, value, secret);
await Promise.all([invalidate(Dependencies.VARIABLES), invalidate(Dependencies.FUNCTION)]);
};
const sdkUpdateVariable = async (variableId: string, key: string, value: string) => {
await sdk.forProject.functions.updateVariable(data.function.$id, variableId, key, value);
const sdkUpdateVariable = async (
variableId: string,
key: string,
value: string,
secret?: boolean
) => {
await sdk.forProject.functions.updateVariable(
data.function.$id,
variableId,
key,
value,
secret
);
await Promise.all([invalidate(Dependencies.VARIABLES), invalidate(Dependencies.FUNCTION)]);
};
@@ -0,0 +1,43 @@
<script lang="ts">
import { Button } from '$lib/elements/forms';
import type { Models } from '@appwrite.io/console';
import { Dialog, Layout } from '@appwrite.io/pink-svelte';
import { createEventDispatcher } from 'svelte';
export let show = false;
export let selectedVar: Partial<Models.Variable>;
let pair = {
$id: selectedVar?.$id,
key: selectedVar?.key,
value: selectedVar?.value,
secret: true
};
const dispatch = createEventDispatcher();
function markAsSecret() {
dispatch('updated', pair);
selectedVar = null;
show = false;
}
$: if (!show) {
selectedVar = null;
}
</script>
<Dialog title="Secret variable" bind:open={show}>
<Layout.Stack gap="l">
<p>
Secret variables are hidden from both the UI and API. Once a variable is marked as
secret, this action cannot be reversed.
</p>
<p>Are you sure you want to make this variable secret?</p>
</Layout.Stack>
<svelte:fragment slot="footer">
<Layout.Stack direction="row" gap="s" justifyContent="flex-end">
<Button text on:click={() => (show = false)}>Cancel</Button>
<Button on:click={markAsSecret}>Mark as secret</Button>
</Layout.Stack>
</svelte:fragment>
</Dialog>
@@ -9,11 +9,11 @@
import UpdateName from './updateName.svelte';
import UpdateServices from './updateServices.svelte';
import UpdateInstallations from './updateInstallations.svelte';
import UpdateVariables from '../updateVariables.svelte';
import DeleteProject from './deleteProject.svelte';
import { Submit, trackEvent } from '$lib/actions/analytics';
import { canWriteProjects } from '$lib/stores/roles';
import ChangeOrganization from './changeOrganization.svelte';
import UpdateVariables from '../updateVariables.svelte';
export let data;
@@ -59,8 +59,13 @@
await invalidate(Dependencies.PROJECT_VARIABLES);
}
async function sdkUpdateVariable(variableId: string, key: string, value: string) {
await sdk.forProject.projectApi.updateVariable(variableId, key, value);
async function sdkUpdateVariable(
variableId: string,
key: string,
value: string,
secret: boolean
) {
await sdk.forProject.projectApi.updateVariable(variableId, key, value, secret);
await invalidate(Dependencies.PROJECT_VARIABLES);
}
@@ -23,6 +23,7 @@
import { base } from '$app/paths';
import { isCloud } from '$lib/system';
import { getApiEndpoint } from '$lib/stores/sdk';
import { capitalize } from '$lib/helpers/string';
export let deployment: Models.Deployment;
export let proxyRuleList: Models.ProxyRuleList;
@@ -86,7 +87,9 @@
Status
</Typography.Text>
<Typography.Text variant="m-400" color="--fgcolor-neutral-primary">
<Status status={deployment.status} label={deployment.status} />
<Status
status={deployment.status}
label={capitalize(deployment.status)} />
</Typography.Text>
</Layout.Stack>
{:else}
@@ -2,7 +2,6 @@
import { Container } from '$lib/layout';
import DangerZone from './dangerZone.svelte';
import UpdateName from './updateName.svelte';
import UpdateVariables from '../../../updateVariables.svelte';
import { sdk } from '$lib/stores/sdk';
import { Dependencies } from '$lib/constants';
import { invalidate } from '$app/navigation';
@@ -15,6 +14,7 @@
import { showConnectRepo } from './store';
import { isCloud } from '$lib/system';
import UpdateResourceLimits from './updateResourceLimits.svelte';
import UpdateVariables from '$routes/(console)/project-[project]/updateVariables.svelte';
export let data;
@@ -10,7 +10,7 @@
import { addNotification } from '$lib/stores/notifications';
import { project } from '$routes/(console)/project-[project]/store';
import PromoteVariableModal from './promoteVariableModal.svelte';
import CreateVariable from './createVariable.svelte';
import CreateVariable from './createVariableModal.svelte';
import RawVariableEditor from './rawVariableEditor.svelte';
import { base } from '$app/paths';
import {
@@ -26,6 +26,8 @@
import {
IconCode,
IconDotsHorizontal,
IconEye,
IconEyeOff,
IconGlobeAlt,
IconPencil,
IconPlus,
@@ -35,6 +37,8 @@
import Link from '$lib/elements/link.svelte';
import Copy from '$lib/components/copy.svelte';
import { page } from '$app/stores';
import UpdateVariablesModal from './updateVariablesModal.svelte';
import SecretVariableModal from './secretVariableModal.svelte';
export let variableList: Models.VariableList;
export let globalVariableList: Models.VariableList | undefined = undefined;
@@ -54,20 +58,24 @@
export let sdkDeleteVariable: (variableId: string) => Promise<unknown>;
export let product: 'function' | 'site' = 'function';
let showVariablesDropdown = [];
let selectedVar: Models.Variable = null;
let showVariablesUpload = false;
let showVariablesModal = false;
let showPromoteModal = false;
let showEditorModal = false;
let showUpdate = false;
let showSecretModal = false;
let offset = 0;
const limit = 10;
async function handleVariableCreated(event: CustomEvent<Models.Variable>) {
const variable = event.detail;
async function handleVariableCreated(event: CustomEvent<Models.Variable[]>) {
const variables = event.detail;
console.log(variables);
try {
await sdkCreateVariable(variable.key, variable.value, variable.secret);
selectedVar = null;
const promises = variables.map((variable) =>
sdkCreateVariable(variable.key, variable.value, variable?.secret || false)
);
await Promise.all(promises);
showVariablesModal = false;
addNotification({
type: 'success',
@@ -106,6 +114,28 @@
trackError(error, Submit.VariableUpdate);
}
}
async function handleVariableSecret(event: CustomEvent<Models.Variable>) {
const variable = event.detail;
console.log(variable);
try {
await sdkUpdateVariable(variable.$id, variable.key, variable.value, variable.secret);
selectedVar = null;
showVariablesModal = false;
addNotification({
type: 'success',
message: `${$project.name} ${
isGlobal ? 'global variable' : 'variable'
} has been marked as secret.`
});
trackEvent(Submit.VariableUpdate);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
trackError(error, Submit.VariableUpdate);
}
}
async function handleVariableDeleted(variable: Models.Variable) {
try {
@@ -285,7 +315,7 @@
<Table.Header.Cell>Value</Table.Header.Cell>
<Table.Header.Cell width="30px" />
</svelte:fragment>
{#each variableList.variables.slice(offset, offset + limit) as variable, i}
{#each variableList.variables.slice(offset, offset + limit) as variable}
<Table.Row>
<Table.Cell>
{@const isConflicting = globalVariableList
@@ -332,22 +362,31 @@
</Button>
<svelte:fragment slot="tooltip" let:toggle>
<ActionMenu.Root>
<ActionMenu.Item.Button
trailingIcon={IconPencil}
on:click={(e) => {
selectedVar = variable;
showVariablesDropdown[i] = false;
showVariablesModal = true;
toggle(e);
}}>
Update
</ActionMenu.Item.Button>
{#if !variable.secret}
<ActionMenu.Item.Button
trailingIcon={IconPencil}
on:click={(e) => {
selectedVar = variable;
showUpdate = true;
toggle(e);
}}>
Update
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
trailingIcon={IconEyeOff}
on:click={(e) => {
selectedVar = variable;
showSecretModal = true;
toggle(e);
}}>
Secret
</ActionMenu.Item.Button>
{/if}
{#if !isGlobal}
<ActionMenu.Item.Button
trailingIcon={IconGlobeAlt}
on:click={async (e) => {
selectedVar = variable;
showVariablesDropdown[i] = false;
showPromoteModal = true;
toggle(e);
}}>
@@ -359,7 +398,6 @@
trailingIcon={IconTrash}
on:click={async (e) => {
handleVariableDeleted(variable);
showVariablesDropdown[i] = false;
toggle(e);
}}>
Delete
@@ -390,12 +428,24 @@
<CreateVariable
{isGlobal}
{product}
bind:selectedVar
bind:showCreate={showVariablesModal}
on:created={handleVariableCreated}
on:updated={handleVariableUpdated} />
on:created={handleVariableCreated} />
{/if}
{#if showUpdate}
<UpdateVariablesModal
{isGlobal}
{product}
bind:selectedVar
bind:show={showUpdate}
on:updated={handleVariableUpdated} />
{/if}
{#if showSecretModal}
<SecretVariableModal
bind:show={showSecretModal}
{selectedVar}
on:updated={handleVariableSecret} />
{/if}
{#if showEditorModal}
<RawVariableEditor
{isGlobal}
@@ -11,8 +11,8 @@
export let isGlobal: boolean;
export let product: 'function' | 'site' = 'function';
export let showCreate = false;
export let selectedVar: Partial<Models.Variable> = null;
export let show = false;
export let selectedVar: Partial<Models.Variable>;
let pair = {
$id: selectedVar?.$id,
@@ -24,29 +24,25 @@
const dispatch = createEventDispatcher();
function close() {
showCreate = false;
show = false;
selectedVar = null;
}
function handleVariable() {
if (selectedVar) {
dispatch('updated', pair);
} else {
dispatch('created', pair);
}
dispatch('updated', pair);
close();
}
$: if (!showCreate) {
console.log('test');
$: if (!show) {
selectedVar = null;
}
</script>
<Modal
bind:show={showCreate}
bind:show
onSubmit={handleVariable}
title={`${selectedVar ? 'Update' : 'Create'} ${isGlobal ? 'global' : 'environment'} variable`}>
title={`Update ${isGlobal ? 'global' : 'environment'} variable`}>
<svelte:fragment slot="description">
<span>
Set the environment variables or secret keys that will be passed to {!isGlobal
@@ -85,6 +81,6 @@
</Layout.Stack>
<svelte:fragment slot="footer">
<Button secondary on:click={close}>Cancel</Button>
<Button submit>{selectedVar ? 'Update' : 'Create'}</Button>
<Button submit>Update</Button>
</svelte:fragment>
</Modal>