Add provider vault settings UI

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-05-07 18:59:05 -05:00
1 parent 57db4863ba
commit 6000f0cb6a
4 files changed
+1037 -70

No files matched your search

+32
View File
@@ -1,9 +1,12 @@
import type {
CompanySecret,
CompanySecretBinding,
CompanySecretProviderConfig,
SecretAccessEvent,
SecretManagedMode,
SecretProvider,
SecretProviderConfigStatus,
SecretProviderConfigHealthResponse,
SecretProviderDescriptor,
SecretStatus,
} from "@paperclipai/shared";
@@ -23,6 +26,7 @@ export interface CreateSecretInput {
description?: string | null;
externalRef?: string | null;
providerVersionRef?: string | null;
providerConfigId?: string | null;
providerMetadata?: Record<string, unknown> | null;
}
@@ -50,6 +54,22 @@ export interface RotateSecretInput {
value?: string | null;
externalRef?: string | null;
providerVersionRef?: string | null;
providerConfigId?: string | null;
}
export interface CreateSecretProviderConfigInput {
provider: SecretProvider;
displayName: string;
status?: SecretProviderConfigStatus;
isDefault?: boolean;
config?: Record<string, unknown>;
}
export interface UpdateSecretProviderConfigInput {
displayName?: string;
status?: SecretProviderConfigStatus;
isDefault?: boolean;
config?: Record<string, unknown>;
}
export const secretsApi = {
@@ -58,6 +78,18 @@ export const secretsApi = {
api.get<SecretProviderDescriptor[]>(`/companies/${companyId}/secret-providers`),
providerHealth: (companyId: string) =>
api.get<SecretProviderHealthResponse>(`/companies/${companyId}/secret-providers/health`),
providerConfigs: (companyId: string) =>
api.get<CompanySecretProviderConfig[]>(`/companies/${companyId}/secret-provider-configs`),
createProviderConfig: (companyId: string, data: CreateSecretProviderConfigInput) =>
api.post<CompanySecretProviderConfig>(`/companies/${companyId}/secret-provider-configs`, data),
updateProviderConfig: (id: string, data: UpdateSecretProviderConfigInput) =>
api.patch<CompanySecretProviderConfig>(`/secret-provider-configs/${id}`, data),
disableProviderConfig: (id: string) =>
api.delete<CompanySecretProviderConfig>(`/secret-provider-configs/${id}`),
setDefaultProviderConfig: (id: string) =>
api.post<CompanySecretProviderConfig>(`/secret-provider-configs/${id}/default`, {}),
checkProviderConfigHealth: (id: string) =>
api.post<SecretProviderConfigHealthResponse>(`/secret-provider-configs/${id}/health`, {}),
create: (companyId: string, data: CreateSecretInput) =>
api.post<CompanySecret>(`/companies/${companyId}/secrets`, data),
update: (id: string, data: UpdateSecretInput) =>
+1
View File
@@ -135,6 +135,7 @@ export const queryKeys = {
secrets: {
list: (companyId: string) => ["secrets", companyId] as const,
providers: (companyId: string) => ["secret-providers", companyId] as const,
providerConfigs: (companyId: string) => ["secret-provider-configs", companyId] as const,
usage: (secretId: string) => ["secrets", "usage", secretId] as const,
accessEvents: (secretId: string) => ["secrets", "access-events", secretId] as const,
},
+57
View File
@@ -5,6 +5,8 @@ import type { SecretProviderDescriptor } from "@paperclipai/shared";
import {
getAwsManagedPathPreview,
getCreateProviderBlockReason,
getDefaultProviderConfigId,
getProviderConfigBlockReason,
} from "./Secrets";
import type { SecretProviderHealthResponse } from "../api/secrets";
@@ -53,6 +55,29 @@ describe("Secrets page provider helpers", () => {
).toBe("AWS Secrets Manager is not configured in this deployment.");
});
it("uses provider health copy when an unconfigured provider reports missing bootstrap inputs", () => {
const health: SecretProviderHealthResponse = {
providers: [
{
provider: "aws_secrets_manager",
status: "warn",
message:
"AWS Secrets Manager provider is not ready: missing PAPERCLIP_SECRETS_AWS_DEPLOYMENT_ID.",
},
],
};
expect(
getCreateProviderBlockReason(
{ ...awsProvider, configured: false },
"managed",
health,
),
).toBe(
"AWS Secrets Manager is not configured in this deployment. AWS Secrets Manager provider is not ready: missing PAPERCLIP_SECRETS_AWS_DEPLOYMENT_ID.",
);
});
it("blocks provider modes the backend does not support", () => {
expect(
getCreateProviderBlockReason(
@@ -69,4 +94,36 @@ describe("Secrets page provider helpers", () => {
),
).toBe("Local encrypted (default) does not support linked external references.");
});
it("chooses the ready default provider vault for a provider", () => {
expect(
getDefaultProviderConfigId(
[
{
id: "draft",
provider: "aws_secrets_manager",
status: "disabled",
isDefault: true,
},
{
id: "prod",
provider: "aws_secrets_manager",
status: "ready",
isDefault: true,
},
] as never,
"aws_secrets_manager",
),
).toBe("prod");
});
it("explains why coming-soon provider vaults cannot be selected", () => {
expect(
getProviderConfigBlockReason({
id: "vault-draft",
provider: "vault",
status: "coming_soon",
} as never),
).toBe("This provider vault is saved as draft metadata only.");
});
});
+947 -70
View File
File diff suppressed because it is too large. Load diff