Disconnecting a sync provider now calls the provider's revoke endpoint (DELETE /v1/permissions for Wahoo) before dropping the local row, so tokens don't accumulate against Wahoo's per-(app,user) cap. The token exchange now classifies Wahoo's "Too many unrevoked access tokens" 400 as a distinct OAuthError code, and the connections settings page shows a localized banner for that and other connect failures instead of a silent ?error=sync_failed. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
32 lines
1.2 KiB
TypeScript
32 lines
1.2 KiB
TypeScript
import { redirect, data } from "react-router";
|
|
import type { Route } from "./+types/api.sync.disconnect.$provider";
|
|
import { getSessionUser } from "~/lib/auth.server";
|
|
import { getProvider } from "~/lib/sync/registry";
|
|
import { deleteConnection, getConnection } from "~/lib/sync/connections.server";
|
|
|
|
export async function action({ params, request }: Route.ActionArgs) {
|
|
const user = await getSessionUser(request);
|
|
if (!user) return redirect("/auth/login");
|
|
|
|
const provider = getProvider(params.provider);
|
|
if (!provider) return data({ error: "Unknown provider" }, { status: 404 });
|
|
|
|
if (provider.revoke) {
|
|
const conn = await getConnection(user.id, provider.id);
|
|
if (conn) {
|
|
try {
|
|
await provider.revoke({
|
|
accessToken: conn.accessToken,
|
|
refreshToken: conn.refreshToken,
|
|
expiresAt: conn.expiresAt,
|
|
});
|
|
} catch (e) {
|
|
// Best-effort: token may already be expired/revoked. Drop the local row regardless.
|
|
console.warn(`Failed to revoke ${provider.id} token (continuing with disconnect):`, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
await deleteConnection(user.id, provider.id);
|
|
return redirect("/settings");
|
|
}
|