diff --git a/packages/shared/src/validators/secret.test.ts b/packages/shared/src/validators/secret.test.ts index 8c03375d4bd..e41ee8b1296 100644 --- a/packages/shared/src/validators/secret.test.ts +++ b/packages/shared/src/validators/secret.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + createUserSecretDefinitionSchema, createUserSecretDeclarationSchema, createUserSecretValueSchema, createSecretProviderConfigSchema, @@ -63,6 +64,16 @@ describe("secret validators", () => { }); }); + it("does not allow user secret definitions to be created as deleted", () => { + expect(() => + createUserSecretDefinitionSchema.parse({ + key: "github_api_token", + name: "GitHub API token", + status: "deleted", + }), + ).toThrow(); + }); + it("requires secret rotation payloads to include rotation input", () => { expect(() => rotateSecretSchema.parse({})).toThrow(/requires value, externalRef/); expect(() => rotateUserSecretValueSchema.parse({})).toThrow(/requires value, externalRef/); diff --git a/packages/shared/src/validators/secret.ts b/packages/shared/src/validators/secret.ts index bb6bf4e04cf..164ea2b4006 100644 --- a/packages/shared/src/validators/secret.ts +++ b/packages/shared/src/validators/secret.ts @@ -9,6 +9,7 @@ import { const secretKeySchema = z.string().trim().min(1).max(120).regex(/^[a-zA-Z0-9_.-]+$/); const secretVersionSelectorSchema = z.union([z.literal("latest"), z.number().int().positive()]); +const creatableSecretStatusSchema = z.enum(["active", "disabled", "archived"]); export const envBindingPlainSchema = z.object({ type: z.literal("plain"), @@ -142,7 +143,7 @@ export const createUserSecretDefinitionSchema = z.object({ key: secretKeySchema, name: z.string().trim().min(1).max(160), description: z.string().trim().max(500).optional().nullable(), - status: z.enum(SECRET_STATUSES).optional(), + status: creatableSecretStatusSchema.optional(), provider: z.enum(SECRET_PROVIDERS).optional(), providerConfigId: z.string().uuid().optional().nullable(), managedMode: z.enum(SECRET_MANAGED_MODES).optional(), diff --git a/ui/src/api/secrets.ts b/ui/src/api/secrets.ts index 139d1afaf13..d0bdca591b7 100644 --- a/ui/src/api/secrets.ts +++ b/ui/src/api/secrets.ts @@ -32,7 +32,7 @@ export interface CreateUserSecretDefinitionInput { key: string; name: string; description?: string | null; - status?: SecretStatus; + status?: Exclude; provider?: SecretProvider; managedMode?: SecretManagedMode; providerConfigId?: string | null; @@ -41,7 +41,6 @@ export interface CreateUserSecretDefinitionInput { } export interface UpdateUserSecretDefinitionInput { - key?: string; name?: string; description?: string | null; status?: SecretStatus; diff --git a/ui/src/pages/secrets/UserSecretDefinitionsTab.tsx b/ui/src/pages/secrets/UserSecretDefinitionsTab.tsx index b0360bbd139..40129958826 100644 --- a/ui/src/pages/secrets/UserSecretDefinitionsTab.tsx +++ b/ui/src/pages/secrets/UserSecretDefinitionsTab.tsx @@ -107,14 +107,17 @@ export function UserSecretDefinitionsTab({ companyId }: { companyId: string }) { name: form.name.trim(), description: form.description.trim() || null, usageGuidance: form.usageGuidance.trim() || null, - status: form.status, }; if (editing) { - return secretsApi.updateUserSecretDefinition(companyId, editing.id, sharedPayload); + return secretsApi.updateUserSecretDefinition(companyId, editing.id, { + ...sharedPayload, + status: form.status, + }); } return secretsApi.createUserSecretDefinition(companyId, { ...sharedPayload, key: form.key.trim(), + status: form.status === "deleted" ? "active" : form.status, }); }, onSuccess: (definition) => {