mirror of
https://github.com/MCSManager/MCSManager.git
synced 2025-01-30 15:19:32 +08:00
Refactor: zip/unzip command
This commit is contained in:
parent
dc000d433e
commit
4d0c64df25
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,6 +16,7 @@ lib/
|
||||
production/
|
||||
.DS_Store
|
||||
production-code/
|
||||
test.js
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
@ -1,90 +1,38 @@
|
||||
import { $t } from "../i18n";
|
||||
import fs from "fs-extra";
|
||||
import path from "path";
|
||||
import * as compressing from "compressing";
|
||||
import child_process from "child_process";
|
||||
import os from "os";
|
||||
import archiver from "archiver";
|
||||
import StreamZip, { async } from "node-stream-zip";
|
||||
import { processWrapper } from "common";
|
||||
import { PTY_PATH } from "../const";
|
||||
// const StreamZip = require('node-stream-zip');
|
||||
import { t } from "i18next";
|
||||
import logger from "../service/log";
|
||||
|
||||
// Cross-platform high-efficiency/low-efficiency decompression scheme
|
||||
const system = os.platform();
|
||||
|
||||
const COMPRESS_ERROR_MSG = {
|
||||
invalidName: t("压缩或解压的文件名中包含非法字符,请重命名更改文件!"),
|
||||
exitErr: t("解压/压缩程序执行结果不正确,请检查文件权限并重试!"),
|
||||
startErr: t(
|
||||
"解压/压缩程序启动失败,请确保系统已安装 zip 和 unzip 命令并作用到 MCSManager 的节点!"
|
||||
),
|
||||
timeoutErr: t("解压/压缩任务超时,已自动结束!")
|
||||
};
|
||||
|
||||
function checkFileName(fileName: string) {
|
||||
const disableList = ['"', "/", "\\", "?", "|"];
|
||||
const disableList = ['"', "?", "|", "&"];
|
||||
for (const iterator of disableList) {
|
||||
if (fileName.includes(iterator)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function archiveZip(
|
||||
zipPath: string,
|
||||
files: string[],
|
||||
fileCode: string = "utf-8"
|
||||
): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const output = fs.createWriteStream(zipPath);
|
||||
const archive = archiver("zip", {
|
||||
zlib: { level: 9 }
|
||||
// encoding: fileCode
|
||||
});
|
||||
files.forEach((v) => {
|
||||
const basename = path.normalize(path.basename(v));
|
||||
if (!fs.existsSync(v)) return;
|
||||
if (fs.statSync(v)?.isDirectory()) {
|
||||
archive.directory(v, basename);
|
||||
} else {
|
||||
archive.file(v, { name: basename });
|
||||
}
|
||||
});
|
||||
output.on("close", function () {
|
||||
resolve(true);
|
||||
});
|
||||
archive.on("warning", function (err) {
|
||||
reject(err);
|
||||
});
|
||||
archive.on("error", function (err) {
|
||||
reject(err);
|
||||
});
|
||||
archive.pipe(output);
|
||||
archive.finalize();
|
||||
});
|
||||
}
|
||||
|
||||
function archiveUnZip(
|
||||
sourceZip: string,
|
||||
destDir: string,
|
||||
fileCode: string = "utf-8"
|
||||
): Promise<boolean> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const zip = new StreamZip.async({ file: sourceZip, nameEncoding: fileCode });
|
||||
if (!fs.existsSync(destDir)) fs.mkdirsSync(destDir);
|
||||
try {
|
||||
await zip.extract(null, destDir);
|
||||
return resolve(true);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
zip
|
||||
.close()
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
});
|
||||
}
|
||||
|
||||
export async function compress(
|
||||
sourceZip: string,
|
||||
files: string[],
|
||||
fileCode?: string
|
||||
): Promise<boolean> {
|
||||
// if (system === "linux" && haveLinuxZip()) return await linuxZip(sourceZip, files);
|
||||
// return await nodeCompress(sourceZip, files, fileCode);
|
||||
if (hasGolangProcess()) return golangProcessZip(files, sourceZip, fileCode);
|
||||
return await archiveZip(sourceZip, files, fileCode);
|
||||
if (!checkFileName(sourceZip) || files.some((v) => !checkFileName(v)))
|
||||
throw new Error(COMPRESS_ERROR_MSG.invalidName);
|
||||
if (system === "win32") return await use7zipCompress(sourceZip, files);
|
||||
return await useZip(sourceZip, files);
|
||||
}
|
||||
|
||||
export async function decompress(
|
||||
@ -92,155 +40,111 @@ export async function decompress(
|
||||
dest: string,
|
||||
fileCode?: string
|
||||
): Promise<boolean> {
|
||||
// if (system === "linux" && haveLinuxUnzip()) return await linuxUnzip(zipPath, dest);
|
||||
// return await nodeDecompress(zipPath, dest, fileCode);
|
||||
if (hasGolangProcess()) return await golangProcessUnzip(zipPath, dest, fileCode);
|
||||
return await archiveUnZip(zipPath, dest, fileCode);
|
||||
if (!checkFileName(zipPath) || !checkFileName(dest))
|
||||
throw new Error(COMPRESS_ERROR_MSG.invalidName);
|
||||
if (system === "win32") return await use7zipDecompress(zipPath, dest);
|
||||
return await useUnzip(zipPath, dest);
|
||||
}
|
||||
|
||||
async function _7zipCompress(zipPath: string, files: string[]) {
|
||||
const cmd = `7z.exe a ${zipPath} ${files.join(" ")}`.split(" ");
|
||||
console.log($t("TXT_CODE_common._7zip"), `${cmd.join(" ")}`);
|
||||
function setTimeoutTask(
|
||||
subProcess: child_process.ChildProcessWithoutNullStreams,
|
||||
target: string | string[],
|
||||
reject: (b: any) => void,
|
||||
id: number | string
|
||||
) {
|
||||
setTimeout(() => {
|
||||
if (!subProcess.exitCode && subProcess.exitCode !== 0) {
|
||||
subProcess.kill("SIGKILL");
|
||||
logger.error(
|
||||
`[ZIP] ID: ${id} ${JSON.stringify(target)} Task timeout, exitCode: ${subProcess.exitCode}`
|
||||
);
|
||||
reject(new Error(COMPRESS_ERROR_MSG.timeoutErr));
|
||||
} else {
|
||||
reject(new Error(COMPRESS_ERROR_MSG.exitErr));
|
||||
}
|
||||
}, 1000 * 60);
|
||||
}
|
||||
|
||||
async function useUnzip(sourceZip: string, destDir: string): Promise<boolean> {
|
||||
const id = Date.now();
|
||||
return new Promise((resolve, reject) => {
|
||||
const p = cmd.splice(1);
|
||||
const process = child_process.spawn(cmd[0], [...p], {
|
||||
cwd: "./7zip/"
|
||||
logger.info(
|
||||
`ID: ${id} Function useUnzip(): Command: unzip ${["-o", sourceZip, "-d", destDir].join(" ")}}`
|
||||
);
|
||||
const subProcess = child_process.spawn("unzip", ["-o", sourceZip, "-d", destDir], {
|
||||
cwd: path.normalize(path.dirname(sourceZip)),
|
||||
stdio: "pipe",
|
||||
windowsHide: true
|
||||
});
|
||||
if (!process || !process.pid) return reject(false);
|
||||
process.on("exit", (code) => {
|
||||
if (code) return reject(false);
|
||||
if (!subProcess || !subProcess.pid) return reject(new Error(COMPRESS_ERROR_MSG.startErr));
|
||||
subProcess.stdout.on("data", (text) => {});
|
||||
subProcess.stderr.on("data", (text) => {});
|
||||
subProcess.on("exit", (code) => {
|
||||
logger.info(`ID: ${id} Function useUnzip() Done, return code: ${code}`);
|
||||
if (code) return reject(new Error(COMPRESS_ERROR_MSG.exitErr));
|
||||
return resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function _7zipDecompress(sourceZip: string, destDir: string) {
|
||||
// ./7z.exe x archive.zip -oD:\7-Zip
|
||||
const cmd = `7z.exe x ${sourceZip} -o${destDir}`.split(" ");
|
||||
console.log($t("TXT_CODE_common._7unzip"), `${cmd.join(" ")}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
const process = child_process.spawn(cmd[0], [cmd[1], cmd[2], cmd[3]], {
|
||||
cwd: "./7zip/"
|
||||
});
|
||||
if (!process || !process.pid) return reject(false);
|
||||
process.on("exit", (code) => {
|
||||
if (code) return reject(false);
|
||||
return resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function haveLinuxUnzip() {
|
||||
try {
|
||||
const result = child_process.execSync("unzip -hh");
|
||||
return result?.toString("utf-8").toLowerCase().includes("extended help for unzip");
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function haveLinuxZip() {
|
||||
try {
|
||||
const result = child_process.execSync("zip -h2");
|
||||
return result?.toString("utf-8").toLowerCase().includes("extended help for zip");
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function linuxUnzip(sourceZip: string, destDir: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let end = false;
|
||||
const process = child_process.spawn("unzip", ["-o", sourceZip, "-d", destDir], {
|
||||
cwd: path.normalize(path.dirname(sourceZip))
|
||||
});
|
||||
if (!process || !process.pid) return reject(false);
|
||||
process.on("exit", (code) => {
|
||||
end = true;
|
||||
if (code) return reject(false);
|
||||
return resolve(true);
|
||||
});
|
||||
// timeout, terminate the task
|
||||
setTimeout(() => {
|
||||
if (end) return;
|
||||
process.kill("SIGKILL");
|
||||
reject(false);
|
||||
}, 1000 * 60 * 60);
|
||||
setTimeoutTask(subProcess, sourceZip, reject, id);
|
||||
});
|
||||
}
|
||||
|
||||
// zip -r a.zip css css_v1 js
|
||||
// The ZIP file compressed by this function and the directory where the file is located must be in the same directory
|
||||
async function linuxZip(sourceZip: string, files: string[]) {
|
||||
async function useZip(distZip: string, files: string[]): Promise<boolean> {
|
||||
const id = Date.now();
|
||||
if (!files || files.length == 0) return false;
|
||||
files = files.map((v) => path.basename(v));
|
||||
return new Promise((resolve, reject) => {
|
||||
let end = false;
|
||||
files = files.map((v) => path.normalize(path.basename(v)));
|
||||
const process = child_process.spawn("zip", ["-r", sourceZip, ...files], {
|
||||
cwd: path.normalize(path.dirname(sourceZip))
|
||||
logger.info(`ID: ${id} Function useZip(): Command: zip ${["-r", distZip, ...files].join(" ")}`);
|
||||
const subProcess = child_process.spawn("zip", ["-r", distZip, ...files], {
|
||||
cwd: path.normalize(path.dirname(distZip)),
|
||||
stdio: "pipe",
|
||||
windowsHide: true
|
||||
});
|
||||
if (!process || !process.pid) return reject(false);
|
||||
process.on("exit", (code) => {
|
||||
end = true;
|
||||
if (code) return reject(false);
|
||||
if (!subProcess || !subProcess.pid) return reject(new Error(COMPRESS_ERROR_MSG.startErr));
|
||||
subProcess.stdout.on("data", (text) => {});
|
||||
subProcess.stderr.on("data", (text) => {});
|
||||
subProcess.on("exit", (code) => {
|
||||
logger.info(`ID: ${id} Function useZip() Done, return code: ${code}`);
|
||||
if (code) return reject(new Error(COMPRESS_ERROR_MSG.exitErr));
|
||||
return resolve(true);
|
||||
});
|
||||
// timeout, terminate the task
|
||||
setTimeout(() => {
|
||||
if (end) return;
|
||||
process.kill("SIGKILL");
|
||||
reject(false);
|
||||
}, 1000 * 60 * 60);
|
||||
setTimeoutTask(subProcess, files, reject, id);
|
||||
});
|
||||
}
|
||||
|
||||
async function nodeCompress(zipPath: string, files: string[], fileCode: string = "utf-8") {
|
||||
const stream = new compressing.zip.Stream();
|
||||
files.forEach((v) => {
|
||||
stream.addEntry(v, {});
|
||||
});
|
||||
const destStream = fs.createWriteStream(zipPath);
|
||||
stream.pipe(destStream);
|
||||
}
|
||||
|
||||
async function nodeDecompress(sourceZip: string, destDir: string, fileCode: string = "utf-8") {
|
||||
return await compressing.zip.uncompress(sourceZip, destDir, {
|
||||
zipFileNameEncoding: fileCode
|
||||
async function use7zipCompress(zipPath: string, files: string[]): Promise<boolean> {
|
||||
const cmd = `7z.exe a ${zipPath} ${files.join(" ")}`.split(" ");
|
||||
console.log($t("TXT_CODE_common._7zip"), `${cmd.join(" ")}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
const p = cmd.splice(1);
|
||||
const subProcess = child_process.spawn(cmd[0], [...p], {
|
||||
cwd: path.normalize(path.join(process.cwd(), "7zip")),
|
||||
stdio: "pipe"
|
||||
});
|
||||
if (!subProcess || !subProcess.pid) return reject(new Error(COMPRESS_ERROR_MSG.startErr));
|
||||
subProcess.on("exit", (code) => {
|
||||
if (code) return reject(new Error(COMPRESS_ERROR_MSG.exitErr));
|
||||
return resolve(true);
|
||||
});
|
||||
setTimeoutTask(subProcess, files, reject, "");
|
||||
});
|
||||
}
|
||||
|
||||
function hasGolangProcess() {
|
||||
return fs.existsSync(PTY_PATH);
|
||||
// ./7z.exe x archive.zip -oD:\7-Zip
|
||||
async function use7zipDecompress(sourceZip: string, destDir: string): Promise<boolean> {
|
||||
const cmd = `7z.exe x ${sourceZip} -o${destDir}`.split(" ");
|
||||
console.log($t("TXT_CODE_common._7unzip"), `${cmd.join(" ")}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
const subProcess = child_process.spawn(cmd[0], [cmd[1], cmd[2], cmd[3]], {
|
||||
cwd: path.normalize(path.join(process.cwd(), "7zip")),
|
||||
stdio: "pipe"
|
||||
});
|
||||
if (!subProcess || !subProcess.pid) return reject(new Error(COMPRESS_ERROR_MSG.startErr));
|
||||
subProcess.on("exit", (code) => {
|
||||
if (code) return reject(new Error(COMPRESS_ERROR_MSG.exitErr));
|
||||
return resolve(true);
|
||||
});
|
||||
setTimeoutTask(subProcess, sourceZip, reject, "");
|
||||
});
|
||||
}
|
||||
|
||||
// ./pty_linux_arm64 -m unzip /Users/wangkun/Documents/OtherWork/MCSM-Daemon/data/InstanceData/3832159255b042da8cb3fd2012b0a996/tmp.zip /Users/wangkun/Documents/OtherWork/MCSM-Daemon/data/InstanceData/3832159255b042da8cb3fd2012b0a996
|
||||
async function golangProcessUnzip(zipPath: string, destDir: string, fileCode: string = "utf-8") {
|
||||
console.log("GO Zip Params", zipPath, destDir, fileCode);
|
||||
return await new processWrapper(
|
||||
PTY_PATH,
|
||||
["-coder", fileCode, "-m", "unzip", zipPath, destDir],
|
||||
".",
|
||||
60 * 30
|
||||
).start();
|
||||
}
|
||||
|
||||
async function golangProcessZip(files: string[], destZip: string, fileCode: string = "utf-8") {
|
||||
let p = ["-coder", fileCode, "-m", "zip"];
|
||||
p = p.concat(files);
|
||||
p.push(destZip);
|
||||
console.log("GO Unzip Params", p);
|
||||
return await new processWrapper(PTY_PATH, p, ".", 60 * 30).start();
|
||||
}
|
||||
|
||||
// async function test() {
|
||||
// console.log(
|
||||
// "UNZIP::",
|
||||
// await golangProcessUnzip(
|
||||
// "/Users/wangkun/Documents/OtherWork/MCSM-Daemon/data/InstanceData/3832159d255b042da8cb3fd2012b0a996/tmp.zip",
|
||||
// "/Users/wangkun/Documents/OtherWork/MCSM-Daemon/data/InstanceData/3832159255b042da8cb3fd2012b0a996",
|
||||
// "utf-8"
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
|
||||
// test();
|
||||
|
@ -180,16 +180,15 @@ routerApp.on("file/compress", async (ctx, data) => {
|
||||
globalEnv.fileTaskCount--;
|
||||
}
|
||||
|
||||
protocol.response(ctx, true);
|
||||
|
||||
// start decompressing or compressing the file
|
||||
fileTaskStart();
|
||||
try {
|
||||
if (type === 1) {
|
||||
await fileManager.promiseZip(source, targets, code);
|
||||
await fileManager.zip(source, targets, code);
|
||||
} else {
|
||||
await fileManager.promiseUnzip(source, targets, code);
|
||||
await fileManager.unzip(source, targets, code);
|
||||
}
|
||||
protocol.response(ctx, true);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
@ -197,6 +196,5 @@ routerApp.on("file/compress", async (ctx, data) => {
|
||||
}
|
||||
} catch (error) {
|
||||
protocol.responseError(ctx, error);
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
|
@ -67,7 +67,7 @@ export class QuickInstallTask extends AsyncTask {
|
||||
const fileManager = getFileManager(this.instance.instanceUuid);
|
||||
try {
|
||||
let result = await this.download();
|
||||
result = await fileManager.promiseUnzip(this.TMP_ZIP_NAME, ".", "UTF-8");
|
||||
result = await fileManager.unzip(this.TMP_ZIP_NAME, ".", "UTF-8");
|
||||
if (!result) throw new Error($t("TXT_CODE_quick_install.unzipError"));
|
||||
const config = JSON.parse(await fileManager.readFile(this.ZIP_CONFIG_JSON)) as InstanceConfig;
|
||||
|
||||
|
@ -187,16 +187,14 @@ export default class FileManager {
|
||||
throw new Error($t("TXT_CODE_system_file.unzipLimit", { max: MAX_ZIP_GB }));
|
||||
}
|
||||
|
||||
unzip(sourceZip: string, destDir: string, code?: string) {
|
||||
async unzip(sourceZip: string, destDir: string, code?: string) {
|
||||
if (!code) code = this.fileCode;
|
||||
if (!this.check(sourceZip) || !this.checkPath(destDir)) throw new Error(ERROR_MSG_01);
|
||||
this.zipFileCheck(this.toAbsolutePath(sourceZip));
|
||||
decompress(this.toAbsolutePath(sourceZip), this.toAbsolutePath(destDir), code)
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
return await decompress(this.toAbsolutePath(sourceZip), this.toAbsolutePath(destDir), code);
|
||||
}
|
||||
|
||||
zip(sourceZip: string, files: string[], code?: string, callback = (err: any, v?: boolean) => {}) {
|
||||
async zip(sourceZip: string, files: string[], code?: string) {
|
||||
if (!code) code = this.fileCode;
|
||||
if (!this.checkPath(sourceZip)) throw new Error(ERROR_MSG_01);
|
||||
const MAX_ZIP_GB = globalConfiguration.config.maxZipFileSize;
|
||||
@ -214,30 +212,7 @@ export default class FileManager {
|
||||
}
|
||||
if (totalSize > MAX_TOTAL_FIELS_SIZE)
|
||||
throw new Error($t("TXT_CODE_system_file.unzipLimit", { max: MAX_ZIP_GB }));
|
||||
compress(sourceZipPath, filesPath, code)
|
||||
.then((v) => {
|
||||
callback(null, v);
|
||||
})
|
||||
.catch((err) => {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
async promiseUnzip(sourceZip: string, destDir: string, code?: string): Promise<boolean> {
|
||||
if (!code) code = this.fileCode;
|
||||
if (!this.check(sourceZip) || !this.checkPath(destDir)) throw new Error(ERROR_MSG_01);
|
||||
this.zipFileCheck(this.toAbsolutePath(sourceZip));
|
||||
return await decompress(this.toAbsolutePath(sourceZip), this.toAbsolutePath(destDir), code);
|
||||
}
|
||||
|
||||
async promiseZip(sourceZip: string, files: string[], code?: string): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// The path is checked in the function.
|
||||
this.zip(sourceZip, files, code, (err, v) => {
|
||||
if (err) return reject(err);
|
||||
resolve(v);
|
||||
});
|
||||
});
|
||||
return await compress(sourceZipPath, filesPath, code);
|
||||
}
|
||||
|
||||
async edit(target: string, data?: string) {
|
||||
|
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@ -97,6 +97,7 @@ declare module 'vue' {
|
||||
SelectInstances: typeof import('./src/components/fc/SelectInstances.vue')['default']
|
||||
Style1: typeof import('./src/components/time/Style1.vue')['default']
|
||||
Style2: typeof import('./src/components/time/Style2.vue')['default']
|
||||
TaskLoadingDialog: typeof import('./src/components/fc/TaskLoadingDialog.vue')['default']
|
||||
UploadFileDialog: typeof import('./src/components/fc/UploadFileDialog.vue')['default']
|
||||
}
|
||||
}
|
||||
|
@ -144,4 +144,3 @@ const operation = (type: "add" | "del", index = 0) => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
../../tools/formV
|
||||
|
61
frontend/src/components/fc/TaskLoadingDialog.vue
Normal file
61
frontend/src/components/fc/TaskLoadingDialog.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import type { MountComponent } from "@/types";
|
||||
import { LoadingOutlined } from "@ant-design/icons-vue";
|
||||
|
||||
interface Props extends MountComponent {
|
||||
title: string;
|
||||
text: string;
|
||||
subTitle?: string;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const open = ref(true);
|
||||
|
||||
const cancel = async () => {
|
||||
open.value = false;
|
||||
if (props.destroyComponent) props.destroyComponent();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
cancel
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="open"
|
||||
centered
|
||||
width="600px"
|
||||
:title="props.title"
|
||||
:mask-closable="false"
|
||||
:closable="false"
|
||||
:footer="null"
|
||||
>
|
||||
<div class="dialog-overflow-container">
|
||||
<div class="flex flex-center">
|
||||
<div>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-title :level="5">
|
||||
<div class="flex flex-center mb-20">
|
||||
<LoadingOutlined style="font-size: 60px" />
|
||||
</div>
|
||||
<div class="flex flex-center" style="gap: 10px">
|
||||
{{ props.text }}
|
||||
</div>
|
||||
</a-typography-title>
|
||||
<a-typography-text v-if="props.subTitle">
|
||||
<div
|
||||
class="flex-center"
|
||||
style="font-size: 12px; opacity: 0.8; max-width: 300px; text-align: center"
|
||||
>
|
||||
{{ props.subTitle }}
|
||||
</div>
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
@ -7,6 +7,7 @@ import KvOptionsDialogVue from "@/components/fc/KvOptionsDialog.vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import type { AntColumnsType } from "@/types/ant";
|
||||
import UploadFileDialogVue from "./UploadFileDialog.vue";
|
||||
import TaskLoadingDialog from "./TaskLoadingDialog.vue";
|
||||
|
||||
interface DockerConfigItem {
|
||||
host: string;
|
||||
@ -90,3 +91,14 @@ export async function useVolumeEditDialog(data: DockerConfigItem[] = []) {
|
||||
}).mount<DockerConfigItem[]>(KvOptionsDialogVue)) || []
|
||||
);
|
||||
}
|
||||
|
||||
export async function openLoadingDialog(title: string, text: string, subTitle?: string) {
|
||||
const component = (
|
||||
await useMountComponent({
|
||||
title,
|
||||
text,
|
||||
subTitle
|
||||
})
|
||||
).load<InstanceType<typeof TaskLoadingDialog>>(TaskLoadingDialog);
|
||||
return component;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import type { UploadProps } from "ant-design-vue";
|
||||
import type { Key } from "ant-design-vue/es/table/interface";
|
||||
import { ref, createVNode, reactive, type VNodeRef } from "vue";
|
||||
import { ref, createVNode, reactive, type VNodeRef, onMounted } from "vue";
|
||||
import { ExclamationCircleOutlined } from "@ant-design/icons-vue";
|
||||
import { parseForwardAddress } from "@/tools/protocol";
|
||||
import { number2permission, permission2number } from "@/tools/permission";
|
||||
@ -27,6 +27,8 @@ import type {
|
||||
FileStatus,
|
||||
Permission
|
||||
} from "@/types/fileManager";
|
||||
import { reportError } from "@/tools/validator";
|
||||
import { openLoadingDialog } from "@/components/fc";
|
||||
|
||||
export const useFileManager = (instanceId?: string, daemonId?: string) => {
|
||||
const dataSource = ref<DataType[]>();
|
||||
@ -273,6 +275,11 @@ export const useFileManager = (instanceId?: string, daemonId?: string) => {
|
||||
return reportError(t("TXT_CODE_b152cd75"));
|
||||
const filename = await openDialog(t("TXT_CODE_f8a15a94"), t("TXT_CODE_366bad15"), "", "zip");
|
||||
const { execute } = compressFileApi();
|
||||
const loadingDialog = await openLoadingDialog(
|
||||
t("处理中.."),
|
||||
t("正在压缩文件,请耐心等待..."),
|
||||
t("我们正在全力处理文件,但是解压缩程序最多运行运行 40 分钟,超时将自动终止解压缩子进程。")
|
||||
);
|
||||
try {
|
||||
await execute({
|
||||
params: {
|
||||
@ -286,17 +293,24 @@ export const useFileManager = (instanceId?: string, daemonId?: string) => {
|
||||
targets: selectionData.value.map((e) => breadcrumbs[breadcrumbs.length - 1].path + e.name)
|
||||
}
|
||||
});
|
||||
message.success(t("TXT_CODE_377e142"));
|
||||
message.success(t("任务执行完毕!"));
|
||||
await getFileList();
|
||||
} catch (error: any) {
|
||||
message.error(t("压缩任务执行失败!"));
|
||||
reportError(error.message);
|
||||
} finally {
|
||||
loadingDialog.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
const unzipFile = async (name: string) => {
|
||||
const dirname = await openDialog(t("TXT_CODE_7669fd3f"), "", "", "unzip");
|
||||
const { execute } = compressFileApi();
|
||||
|
||||
const loadingDialog = await openLoadingDialog(
|
||||
t("处理中.."),
|
||||
t("正在解压文件,请耐心等待..."),
|
||||
t("我们正在全力处理文件,但是解压缩程序最多运行运行 40 分钟,超时将自动终止解压缩子进程。")
|
||||
);
|
||||
try {
|
||||
await execute({
|
||||
params: {
|
||||
@ -313,10 +327,13 @@ export const useFileManager = (instanceId?: string, daemonId?: string) => {
|
||||
: breadcrumbs[breadcrumbs.length - 1].path + dirname
|
||||
}
|
||||
});
|
||||
message.success(t("TXT_CODE_16f55a9b"));
|
||||
message.success(t("任务执行完毕!"));
|
||||
await getFileList();
|
||||
} catch (error: any) {
|
||||
message.error(t("解压任务执行失败!"));
|
||||
reportError(error.message);
|
||||
} finally {
|
||||
loadingDialog.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* eslint-disable vue/one-component-per-file */
|
||||
|
||||
import { createApp, type Component } from "vue";
|
||||
import { createApp, type Component, type App } from "vue";
|
||||
import { sleep } from "@/tools/common";
|
||||
|
||||
export function useMountComponent(data: Record<string, any> = {}) {
|
||||
@ -28,7 +28,23 @@ export function useMountComponent(data: Record<string, any> = {}) {
|
||||
});
|
||||
};
|
||||
|
||||
const load = <T extends Component>(component: Component): T => {
|
||||
const div = document.createElement("div");
|
||||
document.body.appendChild(div);
|
||||
const app = createApp(component, {
|
||||
...data,
|
||||
async destroyComponent(delay = 1000) {
|
||||
await sleep(delay);
|
||||
app.unmount();
|
||||
div.remove();
|
||||
}
|
||||
});
|
||||
const mountedComponent = app.mount(div);
|
||||
return mountedComponent as any;
|
||||
};
|
||||
|
||||
return {
|
||||
mount
|
||||
mount,
|
||||
load
|
||||
};
|
||||
}
|
||||
|
@ -144,7 +144,8 @@ export const compressFile = useDefineApi<
|
||||
boolean
|
||||
>({
|
||||
url: "/api/files/compress",
|
||||
method: "POST"
|
||||
method: "POST",
|
||||
timeout: Number.MAX_SAFE_INTEGER
|
||||
});
|
||||
|
||||
export const uploadAddress = useDefineApi<
|
||||
|
@ -370,7 +370,7 @@ onUnmounted(() => {
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button size="large">
|
||||
<a-button size="middle">
|
||||
{{ t("TXT_CODE_fe731dfc") }}
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
@ -439,9 +439,9 @@ onUnmounted(() => {
|
||||
{{ t("TXT_CODE_76a82338") }}
|
||||
</a-typography-text>
|
||||
<a-radio-group v-model:value="dialog.code">
|
||||
<a-radio-button value="utf-8">utf-8</a-radio-button>
|
||||
<a-radio-button value="gbk">gbk</a-radio-button>
|
||||
<a-radio-button value="big5">big5</a-radio-button>
|
||||
<a-radio-button value="utf-8">UTF-8</a-radio-button>
|
||||
<a-radio-button value="gbk">GBK</a-radio-button>
|
||||
<a-radio-button value="big5">BIG5</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-space>
|
||||
|
||||
|
@ -251,14 +251,18 @@ router.post(
|
||||
const type = Number(ctx.request.body.type);
|
||||
const code = String(ctx.request.body.code);
|
||||
const remoteService = RemoteServiceSubsystem.getInstance(daemonId);
|
||||
await new RemoteRequest(remoteService).request("file/compress", {
|
||||
instanceUuid,
|
||||
targets,
|
||||
source,
|
||||
type,
|
||||
code
|
||||
});
|
||||
ctx.body = true;
|
||||
const res = await new RemoteRequest(remoteService).request(
|
||||
"file/compress",
|
||||
{
|
||||
instanceUuid,
|
||||
targets,
|
||||
source,
|
||||
type,
|
||||
code
|
||||
},
|
||||
0
|
||||
);
|
||||
ctx.body = res;
|
||||
} catch (err) {
|
||||
ctx.body = err;
|
||||
}
|
||||
|
@ -32,16 +32,21 @@ export default class RemoteRequest {
|
||||
const protocolData: IRequestPacket = { uuid, data };
|
||||
|
||||
// Start countdown
|
||||
const countdownTask = setTimeout(
|
||||
() =>
|
||||
reject(new RemoteError(`Request daemon:(${this.rService.config.ip}) [${event}] timeout`)),
|
||||
timeout
|
||||
);
|
||||
let countdownTask: NodeJS.Timeout;
|
||||
if (timeout) {
|
||||
countdownTask = setTimeout(
|
||||
() =>
|
||||
reject(
|
||||
new RemoteError(`Request daemon:(${this.rService.config.ip}) [${event}] timeout`)
|
||||
),
|
||||
timeout
|
||||
);
|
||||
}
|
||||
|
||||
// define event function
|
||||
const fn = (msg: IPacket) => {
|
||||
if (msg.uuid === uuid) {
|
||||
clearTimeout(countdownTask);
|
||||
if (countdownTask) clearTimeout(countdownTask);
|
||||
// Whenever a message is returned, match the ID to ensure that the response corresponds to the request,
|
||||
// then delete its own event listener
|
||||
this.rService.socket.removeListener(event, fn);
|
||||
|
Loading…
Reference in New Issue
Block a user