(params: {
draft: { credentialId: string | null; workspaceId: string; displayName: string }
newAccountId: string
workspaceId: string
userId: string
now: Date
})
| 101 | * Handles unique constraint checks and orphaned account cleanup. |
| 102 | */ |
| 103 | export async function handleReconnectCredential(params: { |
| 104 | draft: { credentialId: string | null; workspaceId: string; displayName: string } |
| 105 | newAccountId: string |
| 106 | workspaceId: string |
| 107 | userId: string |
| 108 | now: Date |
| 109 | }) { |
| 110 | const { draft, newAccountId, workspaceId, userId, now } = params |
| 111 | if (!draft.credentialId) return |
| 112 | |
| 113 | const [existingCredential] = await db |
| 114 | .select({ id: schema.credential.id, accountId: schema.credential.accountId }) |
| 115 | .from(schema.credential) |
| 116 | .where(eq(schema.credential.id, draft.credentialId)) |
| 117 | .limit(1) |
| 118 | |
| 119 | if (!existingCredential) { |
| 120 | logger.warn('Credential not found for reconnect, skipping', { |
| 121 | credentialId: draft.credentialId, |
| 122 | }) |
| 123 | return |
| 124 | } |
| 125 | |
| 126 | const oldAccountId = existingCredential.accountId |
| 127 | |
| 128 | if (oldAccountId === newAccountId) { |
| 129 | logger.info('Account unchanged during reconnect, skipping update', { |
| 130 | credentialId: draft.credentialId, |
| 131 | accountId: newAccountId, |
| 132 | }) |
| 133 | return |
| 134 | } |
| 135 | |
| 136 | const [conflicting] = await db |
| 137 | .select({ id: schema.credential.id }) |
| 138 | .from(schema.credential) |
| 139 | .where( |
| 140 | and( |
| 141 | eq(schema.credential.workspaceId, workspaceId), |
| 142 | eq(schema.credential.accountId, newAccountId), |
| 143 | sql`${schema.credential.id} != ${draft.credentialId}` |
| 144 | ) |
| 145 | ) |
| 146 | .limit(1) |
| 147 | |
| 148 | if (conflicting) { |
| 149 | logger.warn('New account already used by another credential, skipping reconnect', { |
| 150 | credentialId: draft.credentialId, |
| 151 | newAccountId, |
| 152 | conflictingCredentialId: conflicting.id, |
| 153 | }) |
| 154 | return |
| 155 | } |
| 156 | |
| 157 | await db |
| 158 | .update(schema.credential) |
| 159 | .set({ accountId: newAccountId, updatedAt: now }) |
| 160 | .where(eq(schema.credential.id, draft.credentialId)) |
no test coverage detected