From 88b87de391daf714b3c473adfbc8131e4b8a6937 Mon Sep 17 00:00:00 2001 From: whyour Date: Sun, 16 Jul 2023 00:23:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=95=B0=E6=8D=AE=E5=A4=87?= =?UTF-8?q?=E4=BB=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/api/system.ts | 12 +++++++++++ back/config/index.ts | 5 +++++ back/services/system.ts | 22 +++++++++++++++++--- package.json | 4 ++++ pnpm-lock.yaml | 40 +++++++++++++++++++++++++++++++++++++ src/pages/setting/other.tsx | 17 ++++++++++++++++ src/utils/http.ts | 23 +++++++++++---------- 7 files changed, 110 insertions(+), 13 deletions(-) diff --git a/back/api/system.ts b/back/api/system.ts index 7f84d8be..efc51545 100644 --- a/back/api/system.ts +++ b/back/api/system.ts @@ -209,4 +209,16 @@ export default (app: Router) => { } }, ); + + route.put( + '/data/export', + async (req: Request, res: Response, next: NextFunction) => { + try { + const systemService = Container.get(SystemService); + await systemService.exportData(res); + } catch (e) { + return next(e); + } + }, + ); }; diff --git a/back/config/index.ts b/back/config/index.ts index 94707302..ffdbb3e6 100644 --- a/back/config/index.ts +++ b/back/config/index.ts @@ -20,6 +20,7 @@ const rootPath = process.env.QL_DIR as string; const envFound = dotenv.config({ path: path.join(rootPath, '.env') }); const dataPath = path.join(rootPath, 'data/'); +const tmpPath = path.join(rootPath, '.tmp/'); const samplePath = path.join(rootPath, 'sample/'); const configPath = path.join(dataPath, 'config/'); const scriptPath = path.join(dataPath, 'scripts/'); @@ -42,6 +43,7 @@ const authError = '错误的用户名密码,请重试'; const loginFaild = '请先登录!'; const configString = 'config sample crontab shareCode diy'; const versionFile = path.join(rootPath, 'version.yaml'); +const dataTgzFile = path.join(tmpPath, 'data.tgz'); if (envFound.error) { throw new Error("⚠️ Couldn't find .env file ⚠️"); @@ -59,6 +61,9 @@ export default { prefix: '/api', }, rootPath, + tmpPath, + dataPath, + dataTgzFile, configString, loginFaild, authError, diff --git a/back/services/system.ts b/back/services/system.ts index 5cb69581..bd7c9a0a 100644 --- a/back/services/system.ts +++ b/back/services/system.ts @@ -1,7 +1,7 @@ +import { Response } from 'express'; import { Service, Inject } from 'typedi'; import winston from 'winston'; import config from '../config'; -import * as fs from 'fs'; import { AuthDataType, AuthInfo, @@ -23,6 +23,8 @@ import { } from '../config/util'; import { TASK_COMMAND } from '../config/const'; import taskLimit from '../shared/pLimit'; +import tar from 'tar'; +import fs from 'fs'; @Service() export default class SystemService { @@ -33,7 +35,7 @@ export default class SystemService { @Inject('logger') private logger: winston.Logger, private scheduleService: ScheduleService, private sockService: SockService, - ) {} + ) { } public async getSystemConfig() { const doc = await this.getDb({ type: AuthDataType.systemConfig }); @@ -108,7 +110,7 @@ export default class SystemService { }, ); lastVersionContent = await parseContentVersion(result.body); - } catch (error) {} + } catch (error) { } if (!lastVersionContent) { lastVersionContent = currentVersionContent; @@ -250,4 +252,18 @@ export default class SystemService { return { code: 400, message: '任务未找到' }; } } + + public async exportData(res: Response) { + try { + await tar.create({ gzip: true, file: config.dataTgzFile, cwd: config.rootPath }, ['data']) + const dataFile = fs.createReadStream(config.dataTgzFile); + res.writeHead(200, { + 'Content-Type': 'application/force-download', + 'Content-Disposition': 'attachment; filename=data.tgz' + }); + dataFile.pipe(res); + } catch (error: any) { + return res.send({ code: 400, message: error.message }); + } + } } diff --git a/package.json b/package.json index d2722147..59205e61 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "serve-handler": "^6.1.3", "sockjs": "^0.3.24", "sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3", + "tar": "^6.1.15", "toad-scheduler": "^1.6.0", "typedi": "^0.10.0", "uuid": "^8.3.2", @@ -108,6 +109,7 @@ "@types/cross-spawn": "^6.0.2", "@types/express": "^4.17.13", "@types/express-jwt": "^6.0.4", + "@types/file-saver": "^2.0.5", "@types/js-yaml": "^4.0.5", "@types/jsonwebtoken": "^8.5.8", "@types/lodash": "^4.14.185", @@ -123,6 +125,7 @@ "@types/serve-handler": "^6.1.1", "@types/sockjs": "^0.3.33", "@types/sockjs-client": "^1.5.1", + "@types/tar": "^6.1.5", "@types/uuid": "^8.3.4", "@umijs/max": "^4.0.55", "@umijs/ssr-darkreader": "^4.9.45", @@ -132,6 +135,7 @@ "codemirror": "^5.65.2", "compression-webpack-plugin": "9.2.0", "concurrently": "^7.0.0", + "file-saver": "^2.0.5", "lint-staged": "^13.0.3", "monaco-editor": "0.33.0", "nodemon": "^2.0.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5fe14b8..5a1bd5e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -109,6 +109,9 @@ dependencies: sqlite3: specifier: git+https://github.com/whyour/node-sqlite3.git#v1.0.3 version: github.com/whyour/node-sqlite3/3a00af0b5d7603b7f1b290032507320b18a6b741 + tar: + specifier: ^6.1.15 + version: 6.1.15 toad-scheduler: specifier: ^1.6.0 version: 1.6.1 @@ -156,6 +159,9 @@ devDependencies: '@types/express-jwt': specifier: ^6.0.4 version: 6.0.4 + '@types/file-saver': + specifier: ^2.0.5 + version: 2.0.5 '@types/js-yaml': specifier: ^4.0.5 version: 4.0.5 @@ -201,6 +207,9 @@ devDependencies: '@types/sockjs-client': specifier: ^1.5.1 version: 1.5.1 + '@types/tar': + specifier: ^6.1.5 + version: 6.1.5 '@types/uuid': specifier: ^8.3.4 version: 8.3.4 @@ -228,6 +237,9 @@ devDependencies: concurrently: specifier: ^7.0.0 version: 7.6.0 + file-saver: + specifier: ^2.0.5 + version: 2.0.5 lint-staged: specifier: ^13.0.3 version: 13.2.2 @@ -4248,6 +4260,10 @@ packages: '@types/qs': 6.9.7 '@types/serve-static': 1.15.1 + /@types/file-saver@2.0.5: + resolution: {integrity: sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==} + dev: true + /@types/graceful-fs@4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: @@ -4457,6 +4473,13 @@ packages: '@types/node': 17.0.45 dev: true + /@types/tar@6.1.5: + resolution: {integrity: sha512-qm2I/RlZij5RofuY7vohTpYNaYcrSQlN2MyjucQc7ZweDwaEWkdN/EeNh6e9zjK6uEm6PwjdMXkcj05BxZdX1Q==} + dependencies: + '@types/node': 17.0.45 + minipass: 4.2.8 + dev: true + /@types/triple-beam@1.3.2: resolution: {integrity: sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==} dev: false @@ -4850,6 +4873,7 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] requiresBuild: true dev: true optional: true @@ -4859,6 +4883,7 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] requiresBuild: true dev: true optional: true @@ -4868,6 +4893,7 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] requiresBuild: true dev: true optional: true @@ -4877,6 +4903,7 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] requiresBuild: true dev: true optional: true @@ -8085,6 +8112,10 @@ packages: flat-cache: 3.0.4 dev: true + /file-saver@2.0.5: + resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} + dev: true + /file-uri-to-path@2.0.0: resolution: {integrity: sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==} engines: {node: '>= 6'} @@ -9645,6 +9676,7 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] requiresBuild: true dev: true optional: true @@ -9654,6 +9686,7 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] requiresBuild: true dev: true optional: true @@ -9663,6 +9696,7 @@ packages: engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] requiresBuild: true dev: true optional: true @@ -9672,6 +9706,7 @@ packages: engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] requiresBuild: true dev: true optional: true @@ -10212,6 +10247,11 @@ packages: yallist: 4.0.0 dev: false + /minipass@4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} + dev: true + /minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} diff --git a/src/pages/setting/other.tsx b/src/pages/setting/other.tsx index 62b36e3e..9fda1235 100644 --- a/src/pages/setting/other.tsx +++ b/src/pages/setting/other.tsx @@ -5,6 +5,7 @@ import config from '@/utils/config'; import { request } from '@/utils/http'; import CheckUpdate from './checkUpdate'; import { SharedContext } from '@/layouts'; +import { saveAs } from 'file-saver'; import './index.less'; const optionsWithDisabled = [ @@ -76,6 +77,17 @@ const Other = ({ }); }; + const exportData = () => { + request + .put(`${config.apiPrefix}system/data/export`, { responseType: 'blob' }) + .then((res) => { + saveAs(res, 'data.tgz'); + }) + .catch((error: any) => { + console.log(error); + }); + }; + useEffect(() => { getSystemConfig(); }, []); @@ -127,6 +139,11 @@ const Other = ({ + + + diff --git a/src/utils/http.ts b/src/utils/http.ts index 20991189..4aa1a3dd 100644 --- a/src/utils/http.ts +++ b/src/utils/http.ts @@ -66,16 +66,19 @@ _request.interceptors.response.use(async (response) => { history.push('/login'); } } else { - const res = await response.clone().json(); - if (res.code !== 200) { - const msg = res.message || res.data; - msg && - message.error({ - content: msg, - style: { maxWidth: 500, margin: '0 auto' }, - }); - } - return res; + try { + const res = await response.clone().json(); + if (res.code !== 200) { + const msg = res.message || res.data; + msg && + message.error({ + content: msg, + style: { maxWidth: 500, margin: '0 auto' }, + }); + } + return res; + } catch (error) { } + return response; } return response; });