Skip to content

Commit

Permalink
✨ feat: 实现配置导出功能
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed Jul 30, 2023
1 parent 6b7c0a0 commit c1f73fe
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 4 deletions.
7 changes: 7 additions & 0 deletions src/features/SideBar/BottomAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ import Router from 'next/router';
import { ReactNode, memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';

import { useExportConfig } from '@/hooks/useExportConfig';

import pkg from '../../../package.json';

const BottomAction = memo<{ children: ReactNode }>(({ children }) => {
const { t } = useTranslation('common');

const { exportSessions, exportSettings, exportAll, exportAgents } = useExportConfig();
const items: MenuProps['items'] = useMemo(
() => [
{
Expand All @@ -30,21 +33,25 @@ const BottomAction = memo<{ children: ReactNode }>(({ children }) => {
{
key: 'allAgent',
label: <div>{t('exportType.allAgent')}</div>,
onClick: exportAgents,
},
{
key: 'allAgentWithMessage',
label: <div>{t('exportType.allAgentWithMessage')}</div>,
onClick: exportSessions,
},
{
key: 'globalSetting',
label: <div>{t('exportType.globalSetting')}</div>,
onClick: exportSettings,
},
{
type: 'divider',
},
{
key: 'all',
label: <div>{t('exportType.all')}</div>,
onClick: exportAll,
},
],
icon: <Icon icon={FolderOutput} />,
Expand Down
69 changes: 69 additions & 0 deletions src/hooks/useExportConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { transform } from 'lodash-es';
import { useMemo } from 'react';
import { shallow } from 'zustand/shallow';

import { Migration } from '@/migrations';
import { useSessionStore } from '@/store/session';
import { useSettings } from '@/store/settings';
import {
ConfigFileAgents,
ConfigFileAll,
ConfigFileSessions,
ConfigFileSettings,
} from '@/types/exportConfig';
import { exportConfigFile } from '@/utils/config';

export const useExportConfig = () => {
const [sessions] = useSessionStore((s) => [s.sessions], shallow);

const [settings] = useSettings((s) => [s.settings, s.importSettings], shallow);

const exportAgents = () => {
const config: ConfigFileAgents = {
exportType: 'agents',
state: {
sessions: transform(sessions, (result, value, key) => {
result[key] = { ...value, chats: {}, topics: {} };
}),
},
version: Migration.targetVersion,
};

exportConfigFile(config, 'agents');
};

const exportSessions = () => {
const config: ConfigFileSessions = {
exportType: 'sessions',
state: { sessions },
version: Migration.targetVersion,
};

exportConfigFile(config, 'sessions');
};

const exportSettings = () => {
const config: ConfigFileSettings = {
exportType: 'settings',
state: { settings },
version: Migration.targetVersion,
};

exportConfigFile(config, 'settings');
};

const exportAll = () => {
// 将 入参转换为 配置文件格式
const config: ConfigFileAll = {
exportType: 'all',
state: { sessions, settings },
version: Migration.targetVersion,
};
exportConfigFile(config, 'config');
};

return useMemo(
() => ({ exportAgents, exportAll, exportSessions, exportSettings }),
[sessions, settings],
);
};
34 changes: 30 additions & 4 deletions src/types/exportConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,36 @@ export interface ConfigState {
settings: GlobalSettings;
}

export interface ConfigFile {
export interface SettingsConfigState {
settings: GlobalSettings;
}
export interface SessionsConfigState {
sessions: LobeSessions;
}

export type ExportType = 'agents' | 'sessions' | 'settings' | 'all';

export type ConfigFile = ConfigFileSettings | ConfigFileSessions | ConfigFileAll | ConfigFileAgents;

export interface ConfigFileSettings {
exportType: 'settings';
state: SettingsConfigState;
version: number;
}
export interface ConfigFileSessions {
exportType: 'sessions';
state: SessionsConfigState;
version: number;
}

export interface ConfigFileAgents {
exportType: 'agents';
state: SessionsConfigState;
version: number;
}

export interface ConfigFileAll {
exportType: 'all';
state: ConfigState;
/**
* 配置文件的版本号
*/
version: number;
}
49 changes: 49 additions & 0 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { notification } from 'antd';

import { CURRENT_CONFIG_VERSION, Migration } from '@/migrations';
import { ConfigState } from '@/types/exportConfig';

export const exportConfigFile = (config: object, fileName?: string) => {
const file = `LobeChat-${fileName || '-config'}-v${CURRENT_CONFIG_VERSION}.json`;

// 创建一个 Blob 对象
const blob = new Blob([JSON.stringify(config)], { type: 'application/json' });

// 创建一个 URL 对象,用于下载
const url = URL.createObjectURL(blob);

// 创建一个 <a> 元素,设置下载链接和文件名
const a = document.createElement('a');
a.href = url;
a.download = file;

// 触发 <a> 元素的点击事件,开始下载
document.body.append(a);
a.click();

// 下载完成后,清除 URL 对象
URL.revokeObjectURL(url);
a.remove();
};

export const handleImport = (info: any, onConfigImport: (config: ConfigState) => void) => {
const reader = new FileReader();
//读取完文件之后的回调函数
reader.onloadend = function (evt) {
const fileString = evt.target?.result;
const fileJson = fileString as string;

try {
const { state } = Migration.migrate(JSON.parse(fileJson));

onConfigImport(state);
} catch (error) {
notification.error({
description: `出错原因: ${(error as Error).message}`,
message: '导入失败',
});
}
};
//@ts-ignore file 类型不明确
reader.readAsText(info.file.originFileObj, 'utf8');
};

0 comments on commit c1f73fe

Please sign in to comment.