mirror of
https://github.com/whyour/qinglong.git
synced 2025-07-04 09:06:06 +08:00
备份数据支持选择模块,支持清除依赖缓存
This commit is contained in:
parent
c9bd053fbd
commit
ef9e38f167
|
@ -316,10 +316,15 @@ export default (app: Router) => {
|
||||||
|
|
||||||
route.put(
|
route.put(
|
||||||
'/data/export',
|
'/data/export',
|
||||||
|
celebrate({
|
||||||
|
body: Joi.object({
|
||||||
|
type: Joi.array().items(Joi.string()).optional(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
async (req: Request, res: Response, next: NextFunction) => {
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
try {
|
try {
|
||||||
const systemService = Container.get(SystemService);
|
const systemService = Container.get(SystemService);
|
||||||
await systemService.exportData(res);
|
await systemService.exportData(res, req.body.type);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return next(e);
|
return next(e);
|
||||||
}
|
}
|
||||||
|
@ -416,4 +421,22 @@ export default (app: Router) => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
route.put(
|
||||||
|
'/config/dependence-clean',
|
||||||
|
celebrate({
|
||||||
|
body: Joi.object({
|
||||||
|
type: Joi.string().allow(''),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const systemService = Container.get(SystemService);
|
||||||
|
const result = await systemService.cleanDependence(req.body.type);
|
||||||
|
res.send(result);
|
||||||
|
} catch (e) {
|
||||||
|
return next(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -86,6 +86,7 @@ const dbPath = path.join(dataPath, 'db/');
|
||||||
const uploadPath = path.join(dataPath, 'upload/');
|
const uploadPath = path.join(dataPath, 'upload/');
|
||||||
const sshdPath = path.join(dataPath, 'ssh.d/');
|
const sshdPath = path.join(dataPath, 'ssh.d/');
|
||||||
const systemLogPath = path.join(dataPath, 'syslog/');
|
const systemLogPath = path.join(dataPath, 'syslog/');
|
||||||
|
const dependenceCachePath = path.join(dataPath, 'dep_cache/');
|
||||||
|
|
||||||
const envFile = path.join(preloadPath, 'env.sh');
|
const envFile = path.join(preloadPath, 'env.sh');
|
||||||
const jsEnvFile = path.join(preloadPath, 'env.js');
|
const jsEnvFile = path.join(preloadPath, 'env.js');
|
||||||
|
@ -174,4 +175,5 @@ export default {
|
||||||
sqliteFile,
|
sqliteFile,
|
||||||
sshdPath,
|
sshdPath,
|
||||||
systemLogPath,
|
systemLogPath,
|
||||||
|
dependenceCachePath,
|
||||||
};
|
};
|
||||||
|
|
|
@ -415,10 +415,17 @@ export default class SystemService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async exportData(res: Response) {
|
public async exportData(res: Response, type?: string[]) {
|
||||||
try {
|
try {
|
||||||
|
let dataDirs = ['db', 'upload'];
|
||||||
|
if (type && type.length) {
|
||||||
|
dataDirs = dataDirs.concat(type.filter((x) => x !== 'base'));
|
||||||
|
}
|
||||||
|
const dataPaths = dataDirs.map((dir) => `data/${dir}`);
|
||||||
await promiseExec(
|
await promiseExec(
|
||||||
`cd ${config.dataPath} && cd ../ && tar -zcvf ${config.dataTgzFile} data/`,
|
`cd ${config.dataPath} && cd ../ && tar -zcvf ${
|
||||||
|
config.dataTgzFile
|
||||||
|
} ${dataPaths.join(' ')}`,
|
||||||
);
|
);
|
||||||
res.download(config.dataTgzFile);
|
res.download(config.dataTgzFile);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
@ -503,4 +510,15 @@ export default class SystemService {
|
||||||
return { code: 400, message: '设置时区失败' };
|
return { code: 400, message: '设置时区失败' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async cleanDependence(type: 'node' | 'python3') {
|
||||||
|
if (!type || !['node', 'python3'].includes(type)) {
|
||||||
|
return { code: 400, message: '参数错误' };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const finalPath = path.join(config.dependenceCachePath, type);
|
||||||
|
await fs.promises.rm(finalPath, { recursive: true });
|
||||||
|
} catch (error) {}
|
||||||
|
return { code: 200 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -510,5 +510,16 @@
|
||||||
"强制打开可能会导致编辑器显示异常": "Force opening may cause display issues in the editor",
|
"强制打开可能会导致编辑器显示异常": "Force opening may cause display issues in the editor",
|
||||||
"确认离开": "Confirm Leave",
|
"确认离开": "Confirm Leave",
|
||||||
"当前文件未保存,确认离开吗": "Current file is not saved, are you sure to leave?",
|
"当前文件未保存,确认离开吗": "Current file is not saved, are you sure to leave?",
|
||||||
"收件邮箱地址,多个分号分隔,默认发送给发件邮箱地址": "Receiving email address, multiple semicolon separated, sent to the sending email address by default"
|
"收件邮箱地址,多个分号分隔,默认发送给发件邮箱地址": "Receiving email address, multiple semicolon separated, sent to the sending email address by default",
|
||||||
|
"选择备份模块": "Select backup module",
|
||||||
|
"开始备份": "Start backup",
|
||||||
|
"基础数据": "Basic data",
|
||||||
|
"脚本文件": "Script files",
|
||||||
|
"日志文件": "Log files",
|
||||||
|
"依赖缓存": "Dependency cache",
|
||||||
|
"远程脚本缓存": "Remote script cache",
|
||||||
|
"远程仓库缓存": "Remote repository cache",
|
||||||
|
"SSH 文件缓存": "SSH file cache",
|
||||||
|
"清除依赖缓存": "Clean dependency cache",
|
||||||
|
"清除成功": "Clean successful"
|
||||||
}
|
}
|
||||||
|
|
|
@ -510,5 +510,16 @@
|
||||||
"强制打开可能会导致编辑器显示异常": "强制打开可能会导致编辑器显示异常",
|
"强制打开可能会导致编辑器显示异常": "强制打开可能会导致编辑器显示异常",
|
||||||
"确认离开": "确认离开",
|
"确认离开": "确认离开",
|
||||||
"当前文件未保存,确认离开吗": "当前文件未保存,确认离开吗",
|
"当前文件未保存,确认离开吗": "当前文件未保存,确认离开吗",
|
||||||
"收件邮箱地址,多个分号分隔,默认发送给发件邮箱地址": "收件邮箱地址,多个分号分隔,默认发送给发件邮箱地址"
|
"收件邮箱地址,多个分号分隔,默认发送给发件邮箱地址": "收件邮箱地址,多个分号分隔,默认发送给发件邮箱地址",
|
||||||
|
"选择备份模块": "选择备份模块",
|
||||||
|
"开始备份": "开始备份",
|
||||||
|
"基础数据": "基础数据",
|
||||||
|
"脚本文件": "脚本文件",
|
||||||
|
"日志文件": "日志文件",
|
||||||
|
"依赖缓存": "依赖缓存",
|
||||||
|
"远程脚本缓存": "远程脚本缓存",
|
||||||
|
"远程仓库缓存": "远程仓库缓存",
|
||||||
|
"SSH 文件缓存": "SSH 文件缓存",
|
||||||
|
"清除依赖缓存": "清除依赖缓存",
|
||||||
|
"清除成功": "清除成功"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { Button, InputNumber, Form, message, Input, Alert } from 'antd';
|
import { Button, InputNumber, Form, message, Input, Alert, Select } from 'antd';
|
||||||
import config from '@/utils/config';
|
import config from '@/utils/config';
|
||||||
import { request } from '@/utils/http';
|
import { request } from '@/utils/http';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
@ -25,6 +25,7 @@ const Dependence = () => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [log, setLog] = useState<string>('');
|
const [log, setLog] = useState<string>('');
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [cleanType, setCleanType] = useState<string>('node');
|
||||||
|
|
||||||
const getSystemConfig = () => {
|
const getSystemConfig = () => {
|
||||||
request
|
request
|
||||||
|
@ -84,6 +85,24 @@ const Dependence = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cleanDependenceCache = (type: string) => {
|
||||||
|
setLoading(true);
|
||||||
|
setLog('');
|
||||||
|
request
|
||||||
|
.put(`${config.apiPrefix}system/config/dependence-clean`, {
|
||||||
|
type,
|
||||||
|
})
|
||||||
|
.then(({ code, data }) => {
|
||||||
|
if (code === 200) {
|
||||||
|
message.success(intl.get('清除成功'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ws = WebSocketManager.getInstance();
|
const ws = WebSocketManager.getInstance();
|
||||||
ws.subscribe('updateNodeMirror', handleMessage);
|
ws.subscribe('updateNodeMirror', handleMessage);
|
||||||
|
@ -222,6 +241,38 @@ const Dependence = () => {
|
||||||
</Button>
|
</Button>
|
||||||
</Input.Group>
|
</Input.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={intl.get('清除依赖缓存')}
|
||||||
|
name="clean"
|
||||||
|
tooltip={{
|
||||||
|
title: intl.get('清除依赖缓存'),
|
||||||
|
placement: 'topLeft',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Input.Group compact>
|
||||||
|
<Select
|
||||||
|
defaultValue={'node'}
|
||||||
|
style={{ width: 100 }}
|
||||||
|
onChange={(value) => {
|
||||||
|
setCleanType(value);
|
||||||
|
}}
|
||||||
|
options={[
|
||||||
|
{ label: 'node', value: 'node' },
|
||||||
|
{ label: 'python3', value: 'python3' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
loading={loading}
|
||||||
|
onClick={() => {
|
||||||
|
cleanDependenceCache(cleanType);
|
||||||
|
}}
|
||||||
|
style={{ width: 100 }}
|
||||||
|
>
|
||||||
|
{intl.get('确认')}
|
||||||
|
</Button>
|
||||||
|
</Input.Group>
|
||||||
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
<pre
|
<pre
|
||||||
style={{
|
style={{
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
Upload,
|
Upload,
|
||||||
Modal,
|
Modal,
|
||||||
Select,
|
Select,
|
||||||
|
Checkbox,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import * as DarkReader from '@umijs/ssr-darkreader';
|
import * as DarkReader from '@umijs/ssr-darkreader';
|
||||||
import config from '@/utils/config';
|
import config from '@/utils/config';
|
||||||
|
@ -31,6 +32,19 @@ const dataMap = {
|
||||||
timezone: 'timezone',
|
timezone: 'timezone',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const exportModules = [
|
||||||
|
{ value: 'base', label: intl.get('基础数据'), disabled: true },
|
||||||
|
{ value: 'config', label: intl.get('配置文件') },
|
||||||
|
{ value: 'scripts', label: intl.get('脚本文件') },
|
||||||
|
{ value: 'log', label: intl.get('日志文件') },
|
||||||
|
{ value: 'deps', label: intl.get('依赖文件') },
|
||||||
|
{ value: 'syslog', label: intl.get('系统日志') },
|
||||||
|
{ value: 'dep_cache', label: intl.get('依赖缓存') },
|
||||||
|
{ value: 'raw', label: intl.get('远程脚本缓存') },
|
||||||
|
{ value: 'repo', label: intl.get('远程仓库缓存') },
|
||||||
|
{ value: 'ssh.d', label: intl.get('SSH 文件缓存') },
|
||||||
|
];
|
||||||
|
|
||||||
const Other = ({
|
const Other = ({
|
||||||
systemInfo,
|
systemInfo,
|
||||||
reloadTheme,
|
reloadTheme,
|
||||||
|
@ -45,6 +59,8 @@ const Other = ({
|
||||||
const [exportLoading, setExportLoading] = useState(false);
|
const [exportLoading, setExportLoading] = useState(false);
|
||||||
const showUploadProgress = useProgress(intl.get('上传'));
|
const showUploadProgress = useProgress(intl.get('上传'));
|
||||||
const showDownloadProgress = useProgress(intl.get('下载'));
|
const showDownloadProgress = useProgress(intl.get('下载'));
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [selectedModules, setSelectedModules] = useState<string[]>(['base']);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
enable: enableDarkMode,
|
enable: enableDarkMode,
|
||||||
|
@ -110,7 +126,7 @@ const Other = ({
|
||||||
request
|
request
|
||||||
.put<Blob>(
|
.put<Blob>(
|
||||||
`${config.apiPrefix}system/data/export`,
|
`${config.apiPrefix}system/data/export`,
|
||||||
{},
|
{ type: selectedModules },
|
||||||
{
|
{
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
timeout: 86400000,
|
timeout: 86400000,
|
||||||
|
@ -127,7 +143,10 @@ const Other = ({
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
})
|
})
|
||||||
.finally(() => setExportLoading(false));
|
.finally(() => {
|
||||||
|
setExportLoading(false);
|
||||||
|
setVisible(false);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const showReloadModal = () => {
|
const showReloadModal = () => {
|
||||||
|
@ -178,6 +197,7 @@ const Other = ({
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Form layout="vertical" form={form}>
|
<Form layout="vertical" form={form}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={intl.get('主题')}
|
label={intl.get('主题')}
|
||||||
|
@ -196,7 +216,10 @@ const Other = ({
|
||||||
>
|
>
|
||||||
{intl.get('亮色')}
|
{intl.get('亮色')}
|
||||||
</Radio.Button>
|
</Radio.Button>
|
||||||
<Radio.Button value="dark" style={{ width: 66, textAlign: 'center' }}>
|
<Radio.Button
|
||||||
|
value="dark"
|
||||||
|
style={{ width: 66, textAlign: 'center' }}
|
||||||
|
>
|
||||||
{intl.get('暗色')}
|
{intl.get('暗色')}
|
||||||
</Radio.Button>
|
</Radio.Button>
|
||||||
<Radio.Button
|
<Radio.Button
|
||||||
|
@ -296,7 +319,14 @@ const Other = ({
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={intl.get('数据备份还原')} name="frequency">
|
<Form.Item label={intl.get('数据备份还原')} name="frequency">
|
||||||
<Button type="primary" onClick={exportData} loading={exportLoading}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedModules(['base']);
|
||||||
|
setVisible(true);
|
||||||
|
}}
|
||||||
|
loading={exportLoading}
|
||||||
|
>
|
||||||
{exportLoading ? intl.get('生成数据中...') : intl.get('备份')}
|
{exportLoading ? intl.get('生成数据中...') : intl.get('备份')}
|
||||||
</Button>
|
</Button>
|
||||||
<Upload
|
<Upload
|
||||||
|
@ -332,6 +362,40 @@ const Other = ({
|
||||||
<CheckUpdate systemInfo={systemInfo} />
|
<CheckUpdate systemInfo={systemInfo} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
<Modal
|
||||||
|
title={intl.get('选择备份模块')}
|
||||||
|
open={visible}
|
||||||
|
onOk={exportData}
|
||||||
|
onCancel={() => setVisible(false)}
|
||||||
|
okText={intl.get('开始备份')}
|
||||||
|
cancelText={intl.get('取消')}
|
||||||
|
okButtonProps={{ loading: exportLoading }} // 绑定加载状态到按钮
|
||||||
|
>
|
||||||
|
<Checkbox.Group
|
||||||
|
value={selectedModules}
|
||||||
|
onChange={(v) => {
|
||||||
|
setSelectedModules(v as string[]);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: '8px 16px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{exportModules.map((module) => (
|
||||||
|
<Checkbox
|
||||||
|
key={module.value}
|
||||||
|
value={module.value}
|
||||||
|
disabled={module.disabled}
|
||||||
|
style={{ marginLeft: 0 }}
|
||||||
|
>
|
||||||
|
{module.label}
|
||||||
|
</Checkbox>
|
||||||
|
))}
|
||||||
|
</Checkbox.Group>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user