mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-11 18:08:27 +00:00
116 lines
3.3 KiB
TypeScript
116 lines
3.3 KiB
TypeScript
import { type LoaderFunctionArgs } from "@remix-run/node";
|
|
import { getIntegrationDefinitionWithId } from "~/services/integrationDefinition.server";
|
|
import { runIntegrationTrigger } from "~/services/integration.server";
|
|
import { IntegrationEventType } from "@core/types";
|
|
import { createMCPAuthClient } from "@core/mcp-proxy";
|
|
import { logger } from "~/services/logger.service";
|
|
import { env } from "~/env.server";
|
|
import { getIntegrationDefinitionForState } from "~/services/oauth/oauth.server";
|
|
import {
|
|
getIntegrationAccount,
|
|
getIntegrationAccountForId,
|
|
} from "~/services/integrationAccount.server";
|
|
|
|
const CALLBACK_URL = `${env.APP_ORIGIN}/api/v1/oauth/callback`;
|
|
const MCP_CALLBACK_URL = `${CALLBACK_URL}/mcp`;
|
|
|
|
export async function loader({ request }: LoaderFunctionArgs) {
|
|
const url = new URL(request.url);
|
|
const authorizationCode = url.searchParams.get("code");
|
|
const state = url.searchParams.get("state");
|
|
|
|
if (!authorizationCode || !state) {
|
|
return new Response(null, {
|
|
status: 302,
|
|
headers: {
|
|
Location: `${env.APP_ORIGIN}/integrations?success=false&error=${encodeURIComponent(
|
|
"Missing authorization code or state",
|
|
)}`,
|
|
},
|
|
});
|
|
}
|
|
|
|
const {
|
|
integrationDefinitionId,
|
|
redirectURL,
|
|
userId,
|
|
workspaceId,
|
|
integrationAccountId,
|
|
} = await getIntegrationDefinitionForState(state);
|
|
|
|
try {
|
|
// For now, we'll assume Linear integration - in the future this should be derived from state
|
|
const integrationDefinition = await getIntegrationDefinitionWithId(
|
|
integrationDefinitionId,
|
|
);
|
|
|
|
const integrationAccount =
|
|
await getIntegrationAccountForId(integrationAccountId);
|
|
|
|
if (!integrationDefinition) {
|
|
throw new Error("Integration definition not found");
|
|
}
|
|
|
|
const spec = integrationDefinition.spec as any;
|
|
|
|
if (!spec.mcp) {
|
|
throw new Error("MCP auth configuration not found for this integration");
|
|
}
|
|
|
|
const { transportStrategy, url } = spec.mcp;
|
|
|
|
const authClient = createMCPAuthClient({
|
|
serverUrl: url,
|
|
transportStrategy: transportStrategy || "sse-first",
|
|
redirectUrl: MCP_CALLBACK_URL,
|
|
});
|
|
|
|
const result = await authClient.completeOAuthFlow({
|
|
authorizationCode,
|
|
state,
|
|
scope: "read write",
|
|
});
|
|
|
|
// Run integration trigger with MCP OAuth response
|
|
await runIntegrationTrigger(
|
|
integrationDefinition,
|
|
{
|
|
event: IntegrationEventType.SETUP,
|
|
eventBody: {
|
|
oauthResponse: result,
|
|
oauthParams: {
|
|
code: authorizationCode,
|
|
state,
|
|
redirect_uri: MCP_CALLBACK_URL,
|
|
},
|
|
mcp: true,
|
|
},
|
|
},
|
|
// We need to get userId from somewhere - for now using undefined
|
|
userId,
|
|
workspaceId,
|
|
integrationAccount ?? undefined,
|
|
);
|
|
|
|
return new Response(null, {
|
|
status: 302,
|
|
headers: {
|
|
Location: `${redirectURL}?success=true&integrationName=${encodeURIComponent(
|
|
integrationDefinition.name,
|
|
)}`,
|
|
},
|
|
});
|
|
} catch (error: any) {
|
|
logger.error("MCP OAuth callback error:", error);
|
|
|
|
return new Response(null, {
|
|
status: 302,
|
|
headers: {
|
|
Location: `${redirectURL}?success=false&error=${encodeURIComponent(
|
|
error.message || "OAuth callback failed",
|
|
)}`,
|
|
},
|
|
});
|
|
}
|
|
}
|