From 47e3ea3066b89379fcbc741a8766fc1341f4a899 Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 6 Oct 2026 21:59:23 -0500 Subject: [PATCH] refactor: isolate Slack registration credential cleanup Co-Authored-By: Paperclip --- .../chat-slack-registration-cleanup.ts | 19 +++++++++++++++++++ .../src/services/chat-slack-registration.ts | 14 +------------- server/src/services/tool-access.ts | 2 +- 3 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 server/src/services/chat-slack-registration-cleanup.ts diff --git a/server/src/services/chat-slack-registration-cleanup.ts b/server/src/services/chat-slack-registration-cleanup.ts new file mode 100644 index 00000000000..22a2b758598 --- /dev/null +++ b/server/src/services/chat-slack-registration-cleanup.ts @@ -0,0 +1,19 @@ +import { and, eq, like } from "drizzle-orm"; +import { chatEndpoints, chatSlackRegistrations, toolOauthStates, type Db } from "@paperclipai/db"; +import { secretService } from "./secrets.js"; +import type { CredentialMutationLeaseGuard } from "./chat-credential-mutation-lease.js"; + +// Keep generic connection removal independent of the registration service's +// authorization graph; access routes already load tool-access through the barrel. +export async function removeSlackRegistration(db: Db, endpointId: string, lease: CredentialMutationLeaseGuard) { + const vault = secretService(db); + const [row] = await db.select().from(chatSlackRegistrations).where(eq(chatSlackRegistrations.endpointId, endpointId)); + if (!row) return; + await lease.assertOwned(); + await db.update(chatSlackRegistrations).set({ status: "removed", updatedAt: new Date() }).where(eq(chatSlackRegistrations.endpointId, endpointId)); + const [current] = await db.select().from(chatEndpoints).where(eq(chatEndpoints.id, endpointId)); + if (current) await db.delete(toolOauthStates).where(and(eq(toolOauthStates.connectionId, current.connectionId), like(toolOauthStates.state, "slack-install.%"))); + for (const id of Object.values(row.secretIds)) { await lease.assertOwned(); await vault.remove(id); } + await lease.assertOwned(); + await db.update(chatSlackRegistrations).set({ secretIds: {} }).where(eq(chatSlackRegistrations.endpointId, endpointId)); + } diff --git a/server/src/services/chat-slack-registration.ts b/server/src/services/chat-slack-registration.ts index 4288e50e775..fca36a79599 100644 --- a/server/src/services/chat-slack-registration.ts +++ b/server/src/services/chat-slack-registration.ts @@ -1,3 +1,4 @@ +import { removeSlackRegistration } from "./chat-slack-registration-cleanup.js"; import { createHash, randomBytes } from "node:crypto"; import { and, eq, gt, like } from "drizzle-orm"; import { agents, chatEndpoints, chatSlackRegistrations, companies, toolConnections, toolOauthStates, type Db } from "@paperclipai/db"; @@ -324,16 +325,3 @@ export function slackChatRegistrationService(db: Db, options: { return { create, install, pending, expiredReturn, complete, cleanup, returnPath, registration, resume: (endpointId: string, actor: SlackSetupActor) => options.withLock(endpointId, lease => resumeLocked(endpointId, actor, lease)) }; } - -export async function removeSlackRegistration(db: Db, endpointId: string, lease: CredentialMutationLeaseGuard) { - const vault = secretService(db); - const [row] = await db.select().from(chatSlackRegistrations).where(eq(chatSlackRegistrations.endpointId, endpointId)); - if (!row) return; - await lease.assertOwned(); - await db.update(chatSlackRegistrations).set({ status: "removed", updatedAt: new Date() }).where(eq(chatSlackRegistrations.endpointId, endpointId)); - const [current] = await db.select().from(chatEndpoints).where(eq(chatEndpoints.id, endpointId)); - if (current) await db.delete(toolOauthStates).where(and(eq(toolOauthStates.connectionId, current.connectionId), like(toolOauthStates.state, `${prefix}%`))); - for (const id of Object.values(row.secretIds)) { await lease.assertOwned(); await vault.remove(id); } - await lease.assertOwned(); - await db.update(chatSlackRegistrations).set({ secretIds: {} }).where(eq(chatSlackRegistrations.endpointId, endpointId)); - } diff --git a/server/src/services/tool-access.ts b/server/src/services/tool-access.ts index 53bbe54002c..b01550aea82 100644 --- a/server/src/services/tool-access.ts +++ b/server/src/services/tool-access.ts @@ -1,5 +1,5 @@ import { chatCredentialMutationLease } from "./chat-credential-mutation-lease.js"; -import { removeSlackRegistration } from "./chat-slack-registration.js"; +import { removeSlackRegistration } from "./chat-slack-registration-cleanup.js"; import { chatEndpoints } from "@paperclipai/db"; import { AGGREGATOR_NAMES, isAppAggregator, type AggregatorAppsResponse, type ArcadeDiscoverySetupInput } from "@paperclipai/shared/aggregator-apps"; import { resolveAggregatorApp, type AppCatalogAggregator } from "@paperclipai/shared/aggregator-app-catalog";