mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 17:33:51 +02:00
Add provider vault settings UI
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
57db4863ba
commit
6000f0cb6a
4 files changed
+1037
-70
No files matched your search
@@ -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) =>
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user