From ef9e38f1677f62536d506931fd4ad9c9694f9193 Mon Sep 17 00:00:00 2001 From: whyour Date: Sun, 22 Jun 2025 14:25:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=87=E4=BB=BD=E6=95=B0=E6=8D=AE=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=80=89=E6=8B=A9=E6=A8=A1=E5=9D=97=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=B8=85=E9=99=A4=E4=BE=9D=E8=B5=96=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/api/system.ts | 25 ++- back/config/index.ts | 2 + back/services/system.ts | 22 +- src/locales/en-US.json | 13 +- src/locales/zh-CN.json | 13 +- src/pages/setting/dependence.tsx | 53 ++++- src/pages/setting/other.tsx | 350 ++++++++++++++++++------------- 7 files changed, 329 insertions(+), 149 deletions(-) diff --git a/back/api/system.ts b/back/api/system.ts index 33f157a7..965dd3b9 100644 --- a/back/api/system.ts +++ b/back/api/system.ts @@ -316,10 +316,15 @@ export default (app: Router) => { route.put( '/data/export', + celebrate({ + body: Joi.object({ + type: Joi.array().items(Joi.string()).optional(), + }), + }), async (req: Request, res: Response, next: NextFunction) => { try { const systemService = Container.get(SystemService); - await systemService.exportData(res); + await systemService.exportData(res, req.body.type); } catch (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); + } + }, + ); }; diff --git a/back/config/index.ts b/back/config/index.ts index c8c8bc80..090349ac 100644 --- a/back/config/index.ts +++ b/back/config/index.ts @@ -86,6 +86,7 @@ const dbPath = path.join(dataPath, 'db/'); const uploadPath = path.join(dataPath, 'upload/'); const sshdPath = path.join(dataPath, 'ssh.d/'); const systemLogPath = path.join(dataPath, 'syslog/'); +const dependenceCachePath = path.join(dataPath, 'dep_cache/'); const envFile = path.join(preloadPath, 'env.sh'); const jsEnvFile = path.join(preloadPath, 'env.js'); @@ -174,4 +175,5 @@ export default { sqliteFile, sshdPath, systemLogPath, + dependenceCachePath, }; diff --git a/back/services/system.ts b/back/services/system.ts index b557cda7..aa6a2d94 100644 --- a/back/services/system.ts +++ b/back/services/system.ts @@ -415,10 +415,17 @@ export default class SystemService { } } - public async exportData(res: Response) { + public async exportData(res: Response, type?: string[]) { 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( - `cd ${config.dataPath} && cd ../ && tar -zcvf ${config.dataTgzFile} data/`, + `cd ${config.dataPath} && cd ../ && tar -zcvf ${ + config.dataTgzFile + } ${dataPaths.join(' ')}`, ); res.download(config.dataTgzFile); } catch (error: any) { @@ -503,4 +510,15 @@ export default class SystemService { 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 }; + } } diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 9d2968bc..9a21ef3a 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -510,5 +510,16 @@ "强制打开可能会导致编辑器显示异常": "Force opening may cause display issues in the editor", "确认离开": "Confirm 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" } diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 5baff0a1..c19d6984 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -510,5 +510,16 @@ "强制打开可能会导致编辑器显示异常": "强制打开可能会导致编辑器显示异常", "确认离开": "确认离开", "当前文件未保存,确认离开吗": "当前文件未保存,确认离开吗", - "收件邮箱地址,多个分号分隔,默认发送给发件邮箱地址": "收件邮箱地址,多个分号分隔,默认发送给发件邮箱地址" + "收件邮箱地址,多个分号分隔,默认发送给发件邮箱地址": "收件邮箱地址,多个分号分隔,默认发送给发件邮箱地址", + "选择备份模块": "选择备份模块", + "开始备份": "开始备份", + "基础数据": "基础数据", + "脚本文件": "脚本文件", + "日志文件": "日志文件", + "依赖缓存": "依赖缓存", + "远程脚本缓存": "远程脚本缓存", + "远程仓库缓存": "远程仓库缓存", + "SSH 文件缓存": "SSH 文件缓存", + "清除依赖缓存": "清除依赖缓存", + "清除成功": "清除成功" } diff --git a/src/pages/setting/dependence.tsx b/src/pages/setting/dependence.tsx index dddc5557..536c7861 100644 --- a/src/pages/setting/dependence.tsx +++ b/src/pages/setting/dependence.tsx @@ -1,6 +1,6 @@ import intl from 'react-intl-universal'; 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 { request } from '@/utils/http'; import './index.less'; @@ -25,6 +25,7 @@ const Dependence = () => { const [form] = Form.useForm(); const [log, setLog] = useState(''); const [loading, setLoading] = useState(false); + const [cleanType, setCleanType] = useState('node'); const getSystemConfig = () => { 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(() => { const ws = WebSocketManager.getInstance(); ws.subscribe('updateNodeMirror', handleMessage); @@ -222,6 +241,38 @@ const Dependence = () => { + + + { + setSystemConfig({ ...systemConfig, timezone: value }); + }} + options={TIMEZONES.map((timezone) => ({ + value: timezone, + label: timezone, + }))} + showSearch + filterOption={(input, option) => + option?.value?.toLowerCase().indexOf(input.toLowerCase()) >= 0 + } + /> + + + + - - - - { - if (event?.percent) { - showUploadProgress( - Math.min(parseFloat(event?.percent.toFixed(1)), 99), - ); - } - if (file.status === 'done') { - showUploadProgress(100); - showReloadModal(); - } - if (file.status === 'error') { - message.error('上传失败'); - } + { + if (event?.percent) { + showUploadProgress( + Math.min(parseFloat(event?.percent.toFixed(1)), 99), + ); + } + if (file.status === 'done') { + showUploadProgress(100); + showReloadModal(); + } + if (file.status === 'error') { + message.error('上传失败'); + } + }} + name="data" + headers={{ + Authorization: `Bearer ${localStorage.getItem(config.authKey)}`, + }} + > + + + + + + + + setVisible(false)} + okText={intl.get('开始备份')} + cancelText={intl.get('取消')} + okButtonProps={{ loading: exportLoading }} // 绑定加载状态到按钮 + > + { + setSelectedModules(v as string[]); }} - name="data" - headers={{ - Authorization: `Bearer ${localStorage.getItem(config.authKey)}`, + style={{ + width: '100%', + display: 'flex', + flexWrap: 'wrap', + gap: '8px 16px', }} > - - - - - - - + {exportModules.map((module) => ( + + {module.label} + + ))} + + + ); };