From ec60835ebf4e222cb9d792146d550bcf68da8bfd Mon Sep 17 00:00:00 2001 From: liquidliang Date: Thu, 15 Jan 2026 20:50:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E5=BD=93=20API=20=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=20401=20=E9=94=99=E8=AF=AF=E6=97=B6=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=89=93=E5=BC=80=E8=AE=BE=E7=BD=AE=E5=AF=B9=E8=AF=9D=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当 API 返回 401 认证错误(Invalid Token)时,自动弹出设置对话框 让用户重新配置 API Key,同时显示错误提示信息。 - 新增 api-auth-error-event.ts 定义事件常量和工具函数 - apiCalls.ts 检测 401 错误并触发事件 - workflow-submission-service.ts 检测工作流失败的 401 错误 - drawnix.tsx 监听事件并打开设置对话框 + 显示 MessagePlugin 错误提示 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/drawnix/src/drawnix.tsx | 26 +++++++++++++++++ .../services/workflow-submission-service.ts | 6 ++++ .../drawnix/src/utils/api-auth-error-event.ts | 28 +++++++++++++++++++ .../drawnix/src/utils/gemini-api/apiCalls.ts | 6 ++++ 4 files changed, 66 insertions(+) create mode 100644 packages/drawnix/src/utils/api-auth-error-event.ts diff --git a/packages/drawnix/src/drawnix.tsx b/packages/drawnix/src/drawnix.tsx index dec6b92c..a67a5c57 100644 --- a/packages/drawnix/src/drawnix.tsx +++ b/packages/drawnix/src/drawnix.tsx @@ -71,6 +71,8 @@ import { withArrowLineAutoCompleteExtend } from './plugins/with-arrow-line-auto- import { AutoCompleteShapePicker } from './components/auto-complete-shape-picker'; import { useAutoCompleteShapePicker } from './hooks/useAutoCompleteShapePicker'; import { withDefaultFill } from './plugins/with-default-fill'; +import { API_AUTH_ERROR_EVENT, ApiAuthErrorDetail } from './utils/api-auth-error-event'; +import { MessagePlugin } from 'tdesign-react'; const TTDDialog = lazy(() => import('./components/ttd-dialog/ttd-dialog').then(module => ({ default: module.TTDDialog }))); const SettingsDialog = lazy(() => import('./components/settings-dialog/settings-dialog').then(module => ({ default: module.SettingsDialog }))); @@ -224,6 +226,30 @@ export const Drawnix: React.FC = ({ } }, [board]); + // 监听 API 认证错误事件,自动打开设置对话框 + useEffect(() => { + const handleApiAuthError = (event: Event) => { + const customEvent = event as CustomEvent; + const { message } = customEvent.detail; + + // 显示错误提示 + MessagePlugin.error({ + content: 'API Key 无效或已过期,请重新配置', + duration: 5000, + }); + + console.error('[Drawnix] API auth error:', message); + + // 打开设置对话框 + setAppState(prev => ({ ...prev, openSettings: true })); + }; + + window.addEventListener(API_AUTH_ERROR_EVENT, handleApiAuthError); + return () => { + window.removeEventListener(API_AUTH_ERROR_EVENT, handleApiAuthError); + }; + }, []); + // Handle interrupted WorkZone elements after page refresh // Query task status from Service Worker and restore workflow state useEffect(() => { diff --git a/packages/drawnix/src/services/workflow-submission-service.ts b/packages/drawnix/src/services/workflow-submission-service.ts index 67ab58f7..109eaddd 100644 --- a/packages/drawnix/src/services/workflow-submission-service.ts +++ b/packages/drawnix/src/services/workflow-submission-service.ts @@ -12,6 +12,7 @@ import { Subject, Observable, Subscription } from 'rxjs'; import { filter } from 'rxjs/operators'; import type { ParsedGenerationParams } from '../utils/ai-input-parser'; +import { isAuthError, dispatchApiAuthError } from '../utils/api-auth-error-event'; // ============================================================================ // Types @@ -570,6 +571,11 @@ class WorkflowSubmissionService { workflow.error = data.error; } + // 检测 401 认证错误,触发打开设置对话框 + if (data.error && isAuthError(data.error)) { + dispatchApiAuthError({ message: data.error, source: 'workflow' }); + } + this.events$.next({ type: 'failed', workflowId: data.workflowId, diff --git a/packages/drawnix/src/utils/api-auth-error-event.ts b/packages/drawnix/src/utils/api-auth-error-event.ts new file mode 100644 index 00000000..d5bcf17b --- /dev/null +++ b/packages/drawnix/src/utils/api-auth-error-event.ts @@ -0,0 +1,28 @@ +/** + * API 认证错误事件 + * 用于在 API 返回 401 错误时通知主线程打开设置对话框 + */ + +export const API_AUTH_ERROR_EVENT = 'api-auth-error'; + +export interface ApiAuthErrorDetail { + message: string; + source?: string; // 错误来源,如 'chat', 'workflow' 等 +} + +/** + * 检查错误信息是否包含 401 认证错误 + */ +export function isAuthError(error: string): boolean { + return error.includes('401') || + error.toLowerCase().includes('invalid token') || + error.toLowerCase().includes('unauthorized') || + error.toLowerCase().includes('authentication'); +} + +/** + * 触发 API 认证错误事件 + */ +export function dispatchApiAuthError(detail: ApiAuthErrorDetail): void { + window.dispatchEvent(new CustomEvent(API_AUTH_ERROR_EVENT, { detail })); +} diff --git a/packages/drawnix/src/utils/gemini-api/apiCalls.ts b/packages/drawnix/src/utils/gemini-api/apiCalls.ts index a6952875..d94c77c7 100644 --- a/packages/drawnix/src/utils/gemini-api/apiCalls.ts +++ b/packages/drawnix/src/utils/gemini-api/apiCalls.ts @@ -11,6 +11,7 @@ import { shouldUseSWTaskQueue } from '../../services/task-queue'; import { swTaskQueueClient } from '../../services/sw-client'; import { geminiSettings } from '../settings-manager'; import type { ChatParams, ChatMessage as SWChatMessage } from '../../services/sw-client/types'; +import { isAuthError, dispatchApiAuthError } from '../api-auth-error-event'; /** * 确保 SW 客户端已初始化 @@ -303,6 +304,11 @@ async function callApiStreamViaSW( const duration = Date.now() - startTime; console.error('[ApiCalls/SW] Stream error:', error); + // 检测 401 认证错误,触发打开设置对话框 + if (isAuthError(error)) { + dispatchApiAuthError({ message: error, source: 'chat' }); + } + analytics.trackAPICallFailure({ endpoint, model,