diff --git a/server/src/__tests__/secrets-routes.test.ts b/server/src/__tests__/secrets-routes.test.ts index cd7152e3b0e..86d4b7cb1ed 100644 --- a/server/src/__tests__/secrets-routes.test.ts +++ b/server/src/__tests__/secrets-routes.test.ts @@ -18,6 +18,7 @@ const mockSecretService = vi.hoisted(() => ({ getById: vi.fn(), create: vi.fn(), update: vi.fn(), + remove: vi.fn(), previewRemoteImport: vi.fn(), importRemoteSecrets: vi.fn(), })); @@ -418,4 +419,36 @@ describe("secret routes", () => { expect(mockLogActivity).not.toHaveBeenCalled(); expect(JSON.stringify(mockLogActivity.mock.calls)).not.toContain("shared/repointed"); }); + + it("allows DELETE to retry cleanup for already soft-deleted secrets", async () => { + const secret = { + id: "33333333-3333-4333-8333-333333333333", + companyId: "company-1", + name: "OpenAI API Key__deleted__33333333-3333-4333-8333-333333333333", + key: "openai-api-key__deleted__33333333-3333-4333-8333-333333333333", + provider: "aws_secrets_manager", + managedMode: "paperclip_managed", + status: "deleted", + }; + mockSecretService.getById.mockResolvedValue(secret); + mockSecretService.remove.mockResolvedValue(secret); + + const res = await request(createApp()).delete( + "/api/secrets/33333333-3333-4333-8333-333333333333", + ); + + expect(res.status).toBe(200); + expect(res.body).toEqual({ ok: true }); + expect(mockSecretService.remove).toHaveBeenCalledWith( + "33333333-3333-4333-8333-333333333333", + ); + expect(mockLogActivity).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + action: "secret.deleted", + companyId: "company-1", + entityId: secret.id, + }), + ); + }); }); diff --git a/server/src/routes/secrets.ts b/server/src/routes/secrets.ts index a7f28c78914..be9d503f3de 100644 --- a/server/src/routes/secrets.ts +++ b/server/src/routes/secrets.ts @@ -454,10 +454,6 @@ export function secretRoutes(db: Db) { return; } assertCompanyAccess(req, existing.companyId); - if (existing.status === "deleted") { - res.status(404).json({ error: "Secret not found" }); - return; - } const removed = await svc.remove(id); if (!removed) { diff --git a/server/src/services/secrets.ts b/server/src/services/secrets.ts index 398316300f0..8bbfe892af6 100644 --- a/server/src/services/secrets.ts +++ b/server/src/services/secrets.ts @@ -1944,16 +1944,18 @@ export function secretService(db: Db) { const versionRow = await getSecretVersion(secret.id, secret.latestVersion); const providerId = secret.provider as SecretProvider; const provider = getSecretProvider(providerId); - await db - .update(companySecrets) - .set({ - key: `${secret.key}__deleted__${secret.id}`, - name: `${secret.name}__deleted__${secret.id}`, - status: "deleted", - deletedAt: secret.deletedAt ?? new Date(), - updatedAt: new Date(), - }) - .where(eq(companySecrets.id, secretId)); + if (secret.status !== "deleted") { + await db + .update(companySecrets) + .set({ + key: `${secret.key}__deleted__${secret.id}`, + name: `${secret.name}__deleted__${secret.id}`, + status: "deleted", + deletedAt: secret.deletedAt ?? new Date(), + updatedAt: new Date(), + }) + .where(eq(companySecrets.id, secretId)); + } const providerConfig = secret.providerConfigId ? await getProviderConfigById(secret.providerConfigId) : null;