mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
refactor: standardize error handling across components by utilizing CustomApiError for improved error messaging
This commit is contained in:
@@ -49,6 +49,7 @@ import {
|
||||
} from '@/components/ui/select';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import { CustomApiError } from '@/app/infra/entities/common';
|
||||
|
||||
const getFormSchema = (t: (key: string) => string) =>
|
||||
z.object({
|
||||
@@ -241,7 +242,9 @@ export default function BotForm({
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t('bots.getBotConfigError') + err.message);
|
||||
toast.error(
|
||||
t('bots.getBotConfigError') + (err as CustomApiError).msg,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
form.reset();
|
||||
@@ -384,7 +387,7 @@ export default function BotForm({
|
||||
toast.success(t('bots.saveSuccess'));
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t('bots.saveError') + err.message);
|
||||
toast.error(t('bots.saveError') + err.msg);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
@@ -410,7 +413,7 @@ export default function BotForm({
|
||||
onNewBotCreated(res.uuid);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t('bots.createError') + err.message);
|
||||
toast.error(t('bots.createError') + err.msg);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
@@ -429,7 +432,7 @@ export default function BotForm({
|
||||
toast.success(t('bots.deleteSuccess'));
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t('bots.deleteError') + err.message);
|
||||
toast.error(t('bots.deleteError') + err.msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { toast } from 'sonner';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import BotDetailDialog from '@/app/home/bots/BotDetailDialog';
|
||||
import { CustomApiError } from '@/app/infra/entities/common';
|
||||
|
||||
export default function BotConfigPage() {
|
||||
const { t } = useTranslation();
|
||||
@@ -54,10 +55,7 @@ export default function BotConfigPage() {
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('get bot list error', err);
|
||||
toast.error(t('bots.getBotListError') + err.message);
|
||||
})
|
||||
.finally(() => {
|
||||
// setIsLoading(false);
|
||||
toast.error(t('bots.getBotListError') + (err as CustomApiError).msg);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ export default function DynamicFormItemComponent({
|
||||
setLlmModels(models);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('Failed to get LLM model list: ' + err.message);
|
||||
toast.error('Failed to get LLM model list: ' + err.msg);
|
||||
});
|
||||
}
|
||||
}, [config.type]);
|
||||
@@ -124,7 +124,7 @@ export default function DynamicFormItemComponent({
|
||||
setKnowledgeBases(resp.bases);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('Failed to get knowledge base list: ' + err.message);
|
||||
toast.error('Failed to get knowledge base list: ' + err.msg);
|
||||
});
|
||||
|
||||
// Fetch plugin system status
|
||||
@@ -165,7 +165,7 @@ export default function DynamicFormItemComponent({
|
||||
setBots(resp.bots);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('Failed to get bot list: ' + err.message);
|
||||
toast.error('Failed to get bot list: ' + err.msg);
|
||||
});
|
||||
}
|
||||
}, [config.type]);
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
ProviderModels,
|
||||
LANGBOT_MODELS_PROVIDER_REQUESTER,
|
||||
} from './types';
|
||||
import { CustomApiError } from '@/app/infra/entities/common';
|
||||
|
||||
interface ModelsDialogProps {
|
||||
open: boolean;
|
||||
@@ -349,7 +350,8 @@ export default function ModelsDialog({
|
||||
const duration = Date.now() - startTime;
|
||||
setTestResult({ success: true, duration });
|
||||
} catch (err) {
|
||||
toast.error(t('models.testError') + ': ' + (err as Error).message);
|
||||
console.error('Failed to test model', err);
|
||||
toast.error(t('models.testError') + ': ' + (err as CustomApiError).msg);
|
||||
setTestResult(null);
|
||||
} finally {
|
||||
setIsTesting(false);
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
import { DialogFooter } from '@/components/ui/dialog';
|
||||
import { toast } from 'sonner';
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import { CustomApiError } from '@/app/infra/entities/common';
|
||||
|
||||
const getFormSchema = (t: (key: string) => string) =>
|
||||
z.object({
|
||||
@@ -124,7 +125,7 @@ export default function ProviderForm({
|
||||
}
|
||||
onFormSubmit();
|
||||
} catch (err) {
|
||||
toast.error(t('models.providerSaveError') + (err as Error).message);
|
||||
toast.error(t('models.providerSaveError') + (err as CustomApiError).msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -291,7 +291,7 @@ export default function ExternalKBForm({
|
||||
toast.success(t('knowledge.updateExternalSuccess'));
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('Failed to update KB: ' + err.message);
|
||||
toast.error('Failed to update KB: ' + err.msg);
|
||||
});
|
||||
} else {
|
||||
// Create new KB
|
||||
@@ -303,7 +303,7 @@ export default function ExternalKBForm({
|
||||
form.reset();
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('Failed to create KB: ' + err.message);
|
||||
toast.error('Failed to create KB: ' + err.msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -321,7 +321,7 @@ export default function ExternalKBForm({
|
||||
toast.success(t('knowledge.deleteExternalSuccess'));
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('Failed to delete KB: ' + err.message);
|
||||
toast.error('Failed to delete KB: ' + err.msg);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ export default function PipelineFormComponent({
|
||||
toast.success(t('pipelines.createSuccess'));
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t('pipelines.createError') + err.message);
|
||||
toast.error(t('pipelines.createError') + err.msg);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ export default function PipelineFormComponent({
|
||||
toast.success(t('pipelines.saveSuccess'));
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t('pipelines.saveError') + err.message);
|
||||
toast.error(t('pipelines.saveError') + err.msg);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ export default function PipelineFormComponent({
|
||||
toast.success(t('pipelines.deleteSuccess'));
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t('pipelines.deleteError') + err.message);
|
||||
toast.error(t('pipelines.deleteError') + err.msg);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -360,7 +360,7 @@ export default function PipelineFormComponent({
|
||||
onCancel();
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t('pipelines.createError') + err.message);
|
||||
toast.error(t('pipelines.createError') + err.msg);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
// 'use client';
|
||||
|
||||
// import * as React from 'react';
|
||||
// import { useState, useEffect } from 'react';
|
||||
// import { PluginCardVO } from '@/app/home/plugins/plugin-installed/PluginCardVO';
|
||||
// import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
// import { PluginReorderElement } from '@/app/infra/entities/api';
|
||||
// import { toast } from 'sonner';
|
||||
// import {
|
||||
// Dialog,
|
||||
// DialogContent,
|
||||
// DialogHeader,
|
||||
// DialogTitle,
|
||||
// DialogFooter,
|
||||
// } from '@/components/ui/dialog';
|
||||
// import { Button } from '@/components/ui/button';
|
||||
// import {
|
||||
// DndContext,
|
||||
// closestCenter,
|
||||
// KeyboardSensor,
|
||||
// PointerSensor,
|
||||
// useSensor,
|
||||
// useSensors,
|
||||
// DragEndEvent,
|
||||
// } from '@dnd-kit/core';
|
||||
// import {
|
||||
// arrayMove,
|
||||
// SortableContext,
|
||||
// sortableKeyboardCoordinates,
|
||||
// useSortable,
|
||||
// verticalListSortingStrategy,
|
||||
// } from '@dnd-kit/sortable';
|
||||
// import { CSS } from '@dnd-kit/utilities';
|
||||
// import { useTranslation } from 'react-i18next';
|
||||
// import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
|
||||
// interface PluginSortDialogProps {
|
||||
// open: boolean;
|
||||
// onOpenChange: (open: boolean) => void;
|
||||
// onSortComplete: () => void;
|
||||
// }
|
||||
|
||||
// function SortablePluginItem({ plugin }: { plugin: PluginCardVO }) {
|
||||
// const { attributes, listeners, setNodeRef, transform, transition } =
|
||||
// useSortable({
|
||||
// id: `${plugin.author}-${plugin.name}`,
|
||||
// });
|
||||
|
||||
// const style = {
|
||||
// transform: CSS.Transform.toString(transform),
|
||||
// transition,
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div
|
||||
// ref={setNodeRef}
|
||||
// style={style}
|
||||
// {...attributes}
|
||||
// {...listeners}
|
||||
// className="bg-white dark:bg-gray-800 p-4 rounded-md shadow-sm border mb-2 cursor-move"
|
||||
// >
|
||||
// <div className="flex flex-col">
|
||||
// <div className="text-sm text-gray-600 dark:text-gray-400">
|
||||
// {plugin.author}
|
||||
// </div>
|
||||
// <div className="text-lg font-medium">{plugin.name}</div>
|
||||
// <div className="text-sm line-clamp-2 text-gray-500 dark:text-gray-400 mt-1">
|
||||
// {plugin.description}
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
// export default function PluginSortDialog({
|
||||
// open,
|
||||
// onOpenChange,
|
||||
// onSortComplete,
|
||||
// }: PluginSortDialogProps) {
|
||||
// const { t } = useTranslation();
|
||||
// const [sortedPlugins, setSortedPlugins] = useState<PluginCardVO[]>([]);
|
||||
// const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// function getPluginList() {
|
||||
// httpClient.getPlugins().then((value) => {
|
||||
// setSortedPlugins(
|
||||
// value.plugins.map((plugin) => {
|
||||
// return new PluginCardVO({
|
||||
// author: plugin.manifest.manifest.metadata.author ?? '',
|
||||
// description: extractI18nObject(
|
||||
// plugin.manifest.manifest.metadata.description ?? {
|
||||
// en_US: '',
|
||||
// zh_Hans: '',
|
||||
// },
|
||||
// ),
|
||||
// enabled: plugin.enabled,
|
||||
// name: plugin.manifest.manifest.metadata.name,
|
||||
// version: plugin.manifest.manifest.metadata.version ?? '',
|
||||
// status: plugin.status,
|
||||
// components: plugin.components,
|
||||
// install_source: plugin.install_source,
|
||||
// install_info: plugin.install_info,
|
||||
// priority: plugin.priority,
|
||||
// debug: plugin.debug,
|
||||
// });
|
||||
// }),
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
// useEffect(() => {
|
||||
// if (open) {
|
||||
// getPluginList();
|
||||
// }
|
||||
// }, [open]);
|
||||
|
||||
// const sensors = useSensors(
|
||||
// useSensor(PointerSensor),
|
||||
// useSensor(KeyboardSensor, {
|
||||
// coordinateGetter: sortableKeyboardCoordinates,
|
||||
// }),
|
||||
// );
|
||||
|
||||
// function handleDragEnd(event: DragEndEvent) {
|
||||
// const { active, over } = event;
|
||||
|
||||
// if (over && active.id !== over.id) {
|
||||
// setSortedPlugins((items) => {
|
||||
// const oldIndex = items.findIndex(
|
||||
// (item) => `${item.author}-${item.name}` === active.id,
|
||||
// );
|
||||
// const newIndex = items.findIndex(
|
||||
// (item) => `${item.author}-${item.name}` === over.id,
|
||||
// );
|
||||
|
||||
// const newItems = arrayMove(items, oldIndex, newIndex);
|
||||
|
||||
// return newItems;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// function handleSave() {
|
||||
// setIsLoading(true);
|
||||
|
||||
// const reorderElements: PluginReorderElement[] = sortedPlugins.map(
|
||||
// (plugin, index) => ({
|
||||
// author: plugin.author,
|
||||
// name: plugin.name,
|
||||
// priority: index,
|
||||
// }),
|
||||
// );
|
||||
|
||||
// httpClient
|
||||
// .reorderPlugins(reorderElements)
|
||||
// .then(() => {
|
||||
// toast.success(t('plugins.pluginSortSuccess'));
|
||||
// onSortComplete();
|
||||
// onOpenChange(false);
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// toast.error(t('plugins.pluginSortError') + err.message);
|
||||
// })
|
||||
// .finally(() => {
|
||||
// setIsLoading(false);
|
||||
// });
|
||||
// }
|
||||
|
||||
// return (
|
||||
// <Dialog open={open} onOpenChange={onOpenChange}>
|
||||
// <DialogContent className="w-[700px] max-h-[80vh] p-0 flex flex-col">
|
||||
// <DialogHeader className="px-6 pt-6 pb-0">
|
||||
// <DialogTitle>{t('plugins.pluginSort')}</DialogTitle>
|
||||
// </DialogHeader>
|
||||
// <div className="flex-1 overflow-y-auto px-6 py-0">
|
||||
// <p className="text-sm text-gray-500 mb-4">
|
||||
// {t('plugins.pluginSortDescription')}
|
||||
// </p>
|
||||
// <DndContext
|
||||
// sensors={sensors}
|
||||
// collisionDetection={closestCenter}
|
||||
// onDragEnd={handleDragEnd}
|
||||
// >
|
||||
// <SortableContext
|
||||
// items={sortedPlugins.map(
|
||||
// (plugin) => `${plugin.author}-${plugin.name}`,
|
||||
// )}
|
||||
// strategy={verticalListSortingStrategy}
|
||||
// >
|
||||
// {sortedPlugins.map((plugin) => (
|
||||
// <SortablePluginItem
|
||||
// key={`${plugin.author}-${plugin.name}`}
|
||||
// plugin={plugin}
|
||||
// />
|
||||
// ))}
|
||||
// </SortableContext>
|
||||
// </DndContext>
|
||||
// </div>
|
||||
// <DialogFooter className="px-6 py-4">
|
||||
// <Button
|
||||
// variant="outline"
|
||||
// onClick={() => onOpenChange(false)}
|
||||
// disabled={isLoading}
|
||||
// >
|
||||
// {t('common.cancel')}
|
||||
// </Button>
|
||||
// <Button onClick={handleSave} disabled={isLoading}>
|
||||
// {isLoading ? t('common.saving') : t('common.save')}
|
||||
// </Button>
|
||||
// </DialogFooter>
|
||||
// </DialogContent>
|
||||
// </Dialog>
|
||||
// );
|
||||
// }
|
||||
@@ -41,7 +41,7 @@ export default function MCPCardComponent({
|
||||
setSwitchEnable(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t('mcp.modifyFailed') + err.message);
|
||||
toast.error(t('mcp.modifyFailed') + err.msg);
|
||||
setSwitchEnable(true);
|
||||
});
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export default function MCPCardComponent({
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t('mcp.refreshFailed') + err.message);
|
||||
toast.error(t('mcp.refreshFailed') + err.msg);
|
||||
setTesting(false);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
MCPServer,
|
||||
MCPSessionStatus,
|
||||
} from '@/app/infra/entities/api';
|
||||
import { CustomApiError } from '@/app/infra/entities/common';
|
||||
|
||||
// Status Display Component - 在测试中、连接中或连接失败时使用
|
||||
function StatusDisplay({
|
||||
@@ -409,7 +410,8 @@ export default function MCPFormDialog({
|
||||
} catch (err) {
|
||||
clearInterval(interval);
|
||||
setMcpTesting(false);
|
||||
const errorMsg = (err as Error).message || t('mcp.getTaskFailed');
|
||||
const errorMsg =
|
||||
(err as CustomApiError).msg || t('mcp.getTaskFailed');
|
||||
toast.error(`${t('mcp.testError')}: ${errorMsg}`);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
@@ -282,7 +282,7 @@ export default function PluginConfigPage() {
|
||||
watchTask(taskId);
|
||||
})
|
||||
.catch((err) => {
|
||||
setInstallError(err.message);
|
||||
setInstallError(err.msg);
|
||||
setPluginInstallStatus(PluginInstallStatus.ERROR);
|
||||
});
|
||||
} else if (installSource === 'local') {
|
||||
@@ -293,7 +293,7 @@ export default function PluginConfigPage() {
|
||||
watchTask(taskId);
|
||||
})
|
||||
.catch((err) => {
|
||||
setInstallError(err.message);
|
||||
setInstallError(err.msg);
|
||||
setPluginInstallStatus(PluginInstallStatus.ERROR);
|
||||
});
|
||||
} else if (installSource === 'marketplace') {
|
||||
|
||||
@@ -19,3 +19,7 @@ export interface ComponentManifest {
|
||||
};
|
||||
spec: Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
}
|
||||
|
||||
export interface CustomApiError {
|
||||
msg?: string;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ export abstract class BaseHttpClient {
|
||||
// 错误处理
|
||||
protected handleError(error: object): never {
|
||||
if (axios.isCancel(error)) {
|
||||
throw { code: -2, message: 'Request canceled', data: null };
|
||||
throw { code: -2, msg: 'Request canceled', data: null };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import langbotIcon from '@/app/assets/langbot-logo.webp';
|
||||
import { toast } from 'sonner';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
||||
import { CustomApiError } from '@/app/infra/entities/common';
|
||||
|
||||
const formSchema = (t: (key: string) => string) =>
|
||||
z.object({
|
||||
@@ -75,7 +76,7 @@ export default function Register() {
|
||||
router.push('/login');
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
toast.error(t('register.initFailed') + err.message);
|
||||
toast.error(t('register.initFailed') + (err as CustomApiError).msg);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user