feat(auth): 当 API 返回 401 错误时自动打开设置对话框

当 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 <noreply@anthropic.com>
This commit is contained in:
liquidliangandClaude committed 2026-01-15 20:50:49 +08:00
1 parent c2e367fea1
commit ec60835ebf
4 files changed
+66

No files matched your search

+26
View File
@@ -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<DrawnixProps> = ({
}
}, [board]);
// 监听 API 认证错误事件,自动打开设置对话框
useEffect(() => {
const handleApiAuthError = (event: Event) => {
const customEvent = event as CustomEvent<ApiAuthErrorDetail>;
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(() => {
@@ -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,
@@ -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 }));
}
@@ -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,