mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-22 22:36:06 +08:00
系统设置增加数据恢复功能
This commit is contained in:
parent
88b87de391
commit
93e94ea94c
|
@ -14,8 +14,18 @@ import {
|
|||
promiseExec,
|
||||
} from '../config/util';
|
||||
import dayjs from 'dayjs';
|
||||
import multer from 'multer';
|
||||
|
||||
const route = Router();
|
||||
const storage = multer.diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
cb(null, config.tmpPath);
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
cb(null, 'data.tgz');
|
||||
},
|
||||
});
|
||||
const upload = multer({ storage: storage });
|
||||
|
||||
export default (app: Router) => {
|
||||
app.use('/system', route);
|
||||
|
@ -118,11 +128,16 @@ export default (app: Router) => {
|
|||
|
||||
route.put(
|
||||
'/reload',
|
||||
celebrate({
|
||||
body: Joi.object({
|
||||
type: Joi.string().required(),
|
||||
}),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
try {
|
||||
const systemService = Container.get(SystemService);
|
||||
const result = await systemService.reloadSystem();
|
||||
const result = await systemService.reloadSystem(req.body.type);
|
||||
res.send(result);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
|
@ -221,4 +236,18 @@ export default (app: Router) => {
|
|||
}
|
||||
},
|
||||
);
|
||||
|
||||
route.put(
|
||||
'/data/import',
|
||||
upload.single('data'),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const systemService = Container.get(SystemService);
|
||||
const result = await systemService.importData();
|
||||
res.send(result);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
|
|
@ -20,11 +20,13 @@ import {
|
|||
killTask,
|
||||
parseContentVersion,
|
||||
parseVersion,
|
||||
promiseExec,
|
||||
} from '../config/util';
|
||||
import { TASK_COMMAND } from '../config/const';
|
||||
import taskLimit from '../shared/pLimit';
|
||||
import tar from 'tar';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
@Service()
|
||||
export default class SystemService {
|
||||
|
@ -182,8 +184,8 @@ export default class SystemService {
|
|||
return { code: 200 };
|
||||
}
|
||||
|
||||
public async reloadSystem() {
|
||||
const cp = spawn('ql -l reload', { shell: '/bin/bash' });
|
||||
public async reloadSystem(target: 'system' | 'data') {
|
||||
const cp = spawn(`ql -l reload ${target || ''}`, { shell: '/bin/bash' });
|
||||
|
||||
cp.stdout.on('data', (data) => {
|
||||
this.sockService.sendMessage({
|
||||
|
@ -266,4 +268,14 @@ export default class SystemService {
|
|||
return res.send({ code: 400, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
public async importData() {
|
||||
try {
|
||||
await promiseExec(`rm -rf ${path.join(config.tmpPath, 'data')}`);
|
||||
await tar.x({ file: config.dataTgzFile, cwd: config.tmpPath });
|
||||
return { code: 200 };
|
||||
} catch (error: any) {
|
||||
return { code: 400, message: error.message };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -230,15 +230,23 @@ usage() {
|
|||
}
|
||||
|
||||
reload_qinglong() {
|
||||
local reload_target="${1}"
|
||||
local primary_branch="master"
|
||||
if [[ "${QL_BRANCH}" == "develop" ]]; then
|
||||
primary_branch="develop"
|
||||
fi
|
||||
|
||||
if [[ "$reload_target" == 'system' ]]; then
|
||||
cp -rf ${dir_tmp}/qinglong-${primary_branch}/* ${dir_root}/
|
||||
rm -rf $dir_static/*
|
||||
cp -rf ${dir_tmp}/qinglong-static-${primary_branch}/* ${dir_static}/
|
||||
cp -f $file_config_sample $dir_config/config.sample.sh
|
||||
fi
|
||||
|
||||
if [[ "$reload_target" == 'data' ]]; then
|
||||
rm -rf ${dir_data}
|
||||
cp -rf ${dir_tmp}/data ${dir_root}/
|
||||
fi
|
||||
|
||||
reload_pm2
|
||||
}
|
||||
|
@ -246,7 +254,6 @@ reload_qinglong() {
|
|||
## 更新qinglong
|
||||
update_qinglong() {
|
||||
rm -rf ${dir_tmp}/*
|
||||
local needRestart=${1:-"true"}
|
||||
local mirror="gitee"
|
||||
local downloadQLUrl="https://gitee.com/whyour/qinglong/repository/archive"
|
||||
local downloadStaticUrl="https://gitee.com/whyour/qinglong-static/repository/archive"
|
||||
|
@ -490,10 +497,11 @@ main() {
|
|||
case $p1 in
|
||||
update)
|
||||
fix_config
|
||||
eval update_qinglong "$2" $cmd
|
||||
local needRestart=${p2:-"true"}
|
||||
eval update_qinglong $cmd
|
||||
;;
|
||||
reload)
|
||||
eval reload_qinglong $cmd
|
||||
eval reload_qinglong "$p2" $cmd
|
||||
;;
|
||||
extra)
|
||||
eval run_extra_shell $cmd
|
||||
|
|
|
@ -129,9 +129,9 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
|||
okText: '重启',
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}system/reload`)
|
||||
.put(`${config.apiPrefix}system/reload`, { data: { type: 'system' } })
|
||||
.then((_data: any) => {
|
||||
message.warning({
|
||||
message.success({
|
||||
content: (
|
||||
<span>
|
||||
系统将在
|
||||
|
@ -147,7 +147,7 @@ const CheckUpdate = ({ socketMessage, systemInfo }: any) => {
|
|||
});
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 14);
|
||||
}, 14000);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Button, InputNumber, Form, Radio, message, Input } from 'antd';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
Button,
|
||||
InputNumber,
|
||||
Form,
|
||||
Radio,
|
||||
message,
|
||||
Input,
|
||||
Upload,
|
||||
Modal,
|
||||
Progress,
|
||||
} from 'antd';
|
||||
import * as DarkReader from '@umijs/ssr-darkreader';
|
||||
import config from '@/utils/config';
|
||||
import { request } from '@/utils/http';
|
||||
|
@ -7,6 +17,8 @@ import CheckUpdate from './checkUpdate';
|
|||
import { SharedContext } from '@/layouts';
|
||||
import { saveAs } from 'file-saver';
|
||||
import './index.less';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import Countdown from 'antd/lib/statistic/Countdown';
|
||||
|
||||
const optionsWithDisabled = [
|
||||
{ label: '亮色', value: 'light' },
|
||||
|
@ -25,6 +37,8 @@ const Other = ({
|
|||
cronConcurrency?: number | null;
|
||||
}>();
|
||||
const [form] = Form.useForm();
|
||||
const modalRef = useRef<any>();
|
||||
const [exportLoading, setExportLoading] = useState(false);
|
||||
|
||||
const {
|
||||
enable: enableDarkMode,
|
||||
|
@ -78,6 +92,7 @@ const Other = ({
|
|||
};
|
||||
|
||||
const exportData = () => {
|
||||
setExportLoading(true);
|
||||
request
|
||||
.put(`${config.apiPrefix}system/data/export`, { responseType: 'blob' })
|
||||
.then((res) => {
|
||||
|
@ -85,6 +100,72 @@ const Other = ({
|
|||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
})
|
||||
.finally(() => setExportLoading(false));
|
||||
};
|
||||
|
||||
const showUploadModal = (progress: number) => {
|
||||
if (modalRef.current) {
|
||||
modalRef.current.update({
|
||||
content: (
|
||||
<Progress
|
||||
style={{ display: 'flex', justifyContent: 'center' }}
|
||||
type="circle"
|
||||
percent={progress}
|
||||
/>
|
||||
),
|
||||
});
|
||||
} else {
|
||||
modalRef.current = Modal.info({
|
||||
width: 600,
|
||||
maskClosable: false,
|
||||
title: '上传中...',
|
||||
centered: true,
|
||||
content: (
|
||||
<Progress
|
||||
style={{ display: 'flex', justifyContent: 'center' }}
|
||||
type="circle"
|
||||
percent={progress}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const showReloadModal = () => {
|
||||
Modal.confirm({
|
||||
width: 600,
|
||||
maskClosable: false,
|
||||
title: '确认重启',
|
||||
centered: true,
|
||||
content: '备份数据上传成功,确认覆盖数据',
|
||||
okText: '重启',
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}system/reload`, { data: { type: 'data' } })
|
||||
.then((_data: any) => {
|
||||
message.success({
|
||||
content: (
|
||||
<span>
|
||||
系统将在
|
||||
<Countdown
|
||||
className="inline-countdown"
|
||||
format="ss"
|
||||
value={Date.now() + 1000 * 15}
|
||||
/>
|
||||
秒后自动刷新
|
||||
</span>
|
||||
),
|
||||
duration: 15,
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 14000);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -140,9 +221,32 @@ const Other = ({
|
|||
</Input.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="数据备份还原" name="frequency">
|
||||
<Button type="primary" onClick={exportData}>
|
||||
<Button type="primary" onClick={exportData} loading={exportLoading}>
|
||||
备份
|
||||
</Button>
|
||||
<Upload
|
||||
method="put"
|
||||
showUploadList={false}
|
||||
maxCount={1}
|
||||
action="/api/system/data/import"
|
||||
onChange={(e) => {
|
||||
if (e.event?.percent) {
|
||||
const percent = parseFloat(e.event?.percent.toFixed(1));
|
||||
showUploadModal(percent);
|
||||
if (e.event?.percent === 100) {
|
||||
showReloadModal();
|
||||
}
|
||||
}
|
||||
}}
|
||||
name="data"
|
||||
headers={{
|
||||
Authorization: `Bearer ${localStorage.getItem(config.authKey)}`,
|
||||
}}
|
||||
>
|
||||
<Button icon={<UploadOutlined />} style={{ marginLeft: 8 }}>
|
||||
还原数据
|
||||
</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Form.Item label="检查更新" name="update">
|
||||
<CheckUpdate systemInfo={systemInfo} socketMessage={socketMessage} />
|
||||
|
|
Loading…
Reference in New Issue
Block a user