feat(webapp): allow marking environment variables as secret after creation

Move the secret toggle into the edit form so it submits on Save
instead of firing a separate request immediately. Remove the standalone
makeSecret action/method and include isSecret as an optional field on
the existing editValue flow.
This commit is contained in:
Joan La Rosa
2026-01-29 10:17:51 -05:00
committed by Matt Aitken
parent b221719c09
commit 3072f35604
3 changed files with 35 additions and 1 deletions
@@ -28,6 +28,7 @@ import { Fieldset } from "~/components/primitives/Fieldset";
import { FormButtons } from "~/components/primitives/FormButtons";
import { FormError } from "~/components/primitives/FormError";
import { Header2 } from "~/components/primitives/Headers";
import { Hint } from "~/components/primitives/Hint";
import { InfoPanel } from "~/components/primitives/InfoPanel";
import { Input } from "~/components/primitives/Input";
import { InputGroup } from "~/components/primitives/InputGroup";
@@ -68,7 +69,6 @@ import { EnvironmentVariablesRepository } from "~/v3/environmentVariables/enviro
import {
DeleteEnvironmentVariableValue,
EditEnvironmentVariableValue,
EnvironmentVariable,
} from "~/v3/environmentVariables/repository";
export const meta: MetaFunction = () => {
@@ -404,6 +404,7 @@ function EditEnvironmentVariablePanel({
revealAll: boolean;
}) {
const [isOpen, setIsOpen] = useState(false);
const [isSecret, setIsSecret] = useState(variable.isSecret);
const fetcher = useFetcher<typeof action>();
const lastSubmission = fetcher.data as any;
@@ -437,6 +438,7 @@ function EditEnvironmentVariablePanel({
<DialogHeader>Edit environment variable</DialogHeader>
<fetcher.Form method="post" {...form.props}>
<input type="hidden" name="action" value="edit" />
<input type="hidden" name="isSecret" value={isSecret ? "true" : "false"} />
<input {...conform.input(id, { type: "hidden" })} value={variable.id} />
<input
{...conform.input(environmentId, { type: "hidden" })}
@@ -455,6 +457,22 @@ function EditEnvironmentVariablePanel({
<EnvironmentCombo environment={variable.environment} className="text-sm" />
</InputGroup>
<InputGroup className="w-auto">
<Switch
variant="medium"
label={<span className="text-text-bright">Secret value</span>}
checked={isSecret}
disabled={variable.isSecret}
className="-ml-2 inline-flex w-fit"
onCheckedChange={setIsSecret}
/>
<Hint className="-mt-1">
{variable.isSecret
? "This variable is secret and cannot be changed back."
: "Once enabled, the value will be hidden and cannot be revealed again."}
</Hint>
</InputGroup>
<InputGroup fullWidth>
<Label>Value</Label>
<Input
@@ -366,6 +366,7 @@ export class EnvironmentVariablesRepository implements Repository {
id: string;
environmentId: string;
value: string;
isSecret?: boolean;
}
): Promise<Result> {
const project = await this.prismaClient.project.findFirst({
@@ -426,6 +427,20 @@ export class EnvironmentVariablesRepository implements Repository {
await secretStore.setSecret<{ secret: string }>(key, {
secret: options.value,
});
if (options.isSecret) {
await tx.environmentVariableValue.update({
where: {
variableId_environmentId: {
variableId: environmentVariable.id,
environmentId: options.environmentId,
},
},
data: {
isSecret: true,
},
});
}
});
return {
@@ -51,6 +51,7 @@ export const EditEnvironmentVariableValue = z.object({
id: z.string(),
environmentId: z.string(),
value: z.string(),
isSecret: z.preprocess((val) => val === "true" || val === true, z.boolean()).optional(),
});
export type EditEnvironmentVariableValue = z.infer<typeof EditEnvironmentVariableValue>;