feat: change the klavis Linear to LobeHub oauth Linear (#11339)

feat: change the klavis Linear to LobeHub oauth Linear
This commit is contained in:
Shinji-Li
2026-01-08 15:37:02 +08:00
committed by GitHub
parent dd065fc991
commit ec8ff26148
5 changed files with 89 additions and 11 deletions

View File

@@ -32,5 +32,6 @@
"login.title": "Login to {{clientName}}",
"login.userWelcome": "Welcome back, ",
"success.subTitle": "You have successfully authorized the application to access your account. You may now close this page.",
"success.subTitleWithCountdown": "Authorization successful. Auto-closing in {{countdown}}s...",
"success.title": "Authorization Successful"
}

View File

@@ -32,5 +32,6 @@
"login.title": "登录 {{clientName}}",
"login.userWelcome": "欢迎回来,",
"success.subTitle": "应用已获得授权。你可以关闭此页面了",
"success.subTitleWithCountdown": "应用已获得授权。{{countdown}}秒后自动关闭…",
"success.title": "授权完成"
}

View File

@@ -1,4 +1,4 @@
import { IconType, SiCaldotcom, SiGithub, SiLinear } from '@icons-pack/react-simple-icons';
import { IconType, SiCaldotcom, SiGithub } from '@icons-pack/react-simple-icons';
import { Klavis } from 'klavis';
export interface KlavisServerType {
@@ -40,12 +40,6 @@ export const KLAVIS_SERVER_TYPES: KlavisServerType[] = [
label: 'Airtable',
serverName: Klavis.McpServerName.Airtable,
},
{
icon: SiLinear,
identifier: 'linear',
label: 'Linear',
serverName: Klavis.McpServerName.Linear,
},
{
icon: 'https://hub-apac-1.lobeobjects.space/assets/logos/googlesheets.svg',
identifier: 'google-sheets',

View File

@@ -2,11 +2,48 @@
import { FluentEmoji, Text } from '@lobehub/ui';
import { Result } from 'antd';
import React, { memo } from 'react';
import { useSearchParams } from 'next/navigation';
import React, { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
const SuccessPage = memo(() => {
const { t } = useTranslation('oauth');
const searchParams = useSearchParams();
const [countdown, setCountdown] = useState(3);
useEffect(() => {
// Check if this is a LobeHub Skill OAuth callback
const provider = searchParams.get('provider');
if (provider && window.opener) {
// Notify parent window about successful OAuth
window.opener.postMessage(
{
provider,
type: 'LOBEHUB_SKILL_AUTH_SUCCESS',
},
window.location.origin,
);
// Start countdown and close window after 3 seconds
let timeLeft = 3;
setCountdown(timeLeft);
const countdownTimer = setInterval(() => {
timeLeft -= 1;
setCountdown(timeLeft);
if (timeLeft <= 0) {
clearInterval(countdownTimer);
window.close();
}
}, 1000);
return () => clearInterval(countdownTimer);
}
}, [searchParams]);
const provider = searchParams.get('provider');
return (
<Result
@@ -14,7 +51,12 @@ const SuccessPage = memo(() => {
status="success"
subTitle={
<Text fontSize={16} type="secondary">
{t('success.subTitle')}
{provider
? t('success.subTitleWithCountdown', {
countdown,
defaultValue: `You may close this page. Auto-closing in ${countdown}s...`,
})
: t('success.subTitle')}
</Text>
}
title={

View File

@@ -135,13 +135,52 @@ const LobehubSkillServerItem = memo<LobehubSkillServerItemProps>(({ provider, la
s.togglePlugin,
]);
// Listen for OAuth success message from popup window
useEffect(() => {
const handleMessage = async (event: MessageEvent) => {
// Verify origin for security
if (event.origin !== window.location.origin) return;
if (event.data?.type === 'LOBEHUB_SKILL_AUTH_SUCCESS' && event.data?.provider === provider) {
console.log('[LobehubSkill] OAuth success message received for provider:', provider);
// Cleanup polling/window monitoring
cleanup();
// Refresh status to get the connected state
await checkStatus(provider);
// Auto-enable the plugin after successful OAuth
// Need to get the latest server state after checkStatus
const latestServer = useToolStore
.getState()
.lobehubSkillServers?.find((s) => s.identifier === provider);
if (latestServer?.status === LobehubSkillStatus.CONNECTED) {
const newPluginId = latestServer.identifier;
const isAlreadyEnabled = agentSelectors
.currentAgentPlugins(useAgentStore.getState())
.includes(newPluginId);
if (!isAlreadyEnabled) {
console.log('[LobehubSkill] Auto-enabling plugin:', newPluginId);
togglePlugin(newPluginId);
}
}
}
};
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, [provider, cleanup, checkStatus, togglePlugin]);
const handleConnect = async () => {
// 只有已连接状态才阻止重新连接
if (server?.isConnected) return;
setIsConnecting(true);
try {
const { authorizeUrl } = await getAuthorizeUrl(provider);
// Use /oauth/callback/success as redirect URI with provider param for auto-enable
const redirectUri = `${window.location.origin}/oauth/callback/success?provider=${encodeURIComponent(provider)}`;
const { authorizeUrl } = await getAuthorizeUrl(provider, { redirectUri });
openOAuthWindow(authorizeUrl);
} catch (error) {
console.error('[LobehubSkill] Failed to get authorize URL:', error);
@@ -236,7 +275,8 @@ const LobehubSkillServerItem = memo<LobehubSkillServerItemProps>(({ provider, la
onClick={async (e) => {
e.stopPropagation();
try {
const { authorizeUrl } = await getAuthorizeUrl(provider);
const redirectUri = `${window.location.origin}/oauth/callback/success?provider=${encodeURIComponent(provider)}`;
const { authorizeUrl } = await getAuthorizeUrl(provider, { redirectUri });
openOAuthWindow(authorizeUrl);
} catch (error) {
console.error('[LobehubSkill] Failed to get authorize URL:', error);