feat: allow users to choose to overwrite existing files

This commit is contained in:
killerprojecte 2024-08-07 21:56:25 +08:00
parent 5032ce7872
commit 955be538ad
9 changed files with 147 additions and 10 deletions

View File

@ -203,3 +203,18 @@ routerApp.on("file/compress", async (ctx, data) => {
protocol.responseError(ctx, error);
}
});
// Check file is exists in path
routerApp.on("file/exists", async (ctx, data) => {
try {
const fileName = data.uploadFilename;
const dir = data.uploadDir;
const fileManager = getFileManager(data.instanceUuid);
const exists = fileManager.exists(dir, fileName);
protocol.response(ctx, exists);
} catch (error: any) {
console.log(error);
protocol.responseError(ctx, error);
}
});

View File

@ -81,7 +81,9 @@ router.post("/upload/:key", async (ctx) => {
if (!fileManager.checkPath(fileSaveRelativePath))
throw new Error("Access denied: Invalid destination");
const fileSaveAbsolutePath = fileManager.toAbsolutePath(fileSaveRelativePath);
await fs.move(uploadedFile.filepath, fileSaveAbsolutePath);
await fs.move(uploadedFile.filepath, fileSaveAbsolutePath, {
overwrite: ctx.query.overwrite === "true"
});
if (unzip) {
const fileManager = new FileManager(instance.config.cwd);

View File

@ -251,4 +251,14 @@ export default class FileManager {
}
return true;
}
exists(uploadDir: string, fileName: string): boolean {
const fileSaveRelativePath = path.normalize(path.join(uploadDir, fileName));
if (!FileManager.checkFileName(path.basename(fileName)))
throw new Error("Access denied: Malformed file name");
if (!this.checkPath(fileSaveRelativePath))
throw new Error("Access denied: Invalid destination");
const fileSaveAbsolutePath = this.toAbsolutePath(fileSaveRelativePath);
return fs.existsSync(fileSaveAbsolutePath)
}
}

View File

@ -18,7 +18,8 @@ import {
uploadAddress,
uploadFile as uploadFileApi,
downloadAddress,
changePermission as changePermissionApi
changePermission as changePermissionApi,
prepareUploadFile as prepareUploadFileApi
} from "@/services/apis/fileManager";
import type {
DataType,
@ -349,14 +350,17 @@ export const useFileManager = (instanceId?: string, daemonId?: string) => {
const selectedFile = async (file: File) => {
const { execute: uploadFile } = uploadFileApi();
const { state: prepareUploadFileResp, execute: prepareUploadFile } = prepareUploadFileApi();
const { state: uploadCfg, execute: getUploadCfg } = uploadAddress();
try {
percentComplete.value = 1;
const uploadDir = breadcrumbs[breadcrumbs.length - 1].path;
await getUploadCfg({
params: {
upload_dir: breadcrumbs[breadcrumbs.length - 1].path,
upload_dir: uploadDir,
daemonId: daemonId!,
uuid: instanceId!
uuid: instanceId!,
file_name: file.name
}
});
if (!uploadCfg.value) {
@ -364,6 +368,48 @@ export const useFileManager = (instanceId?: string, daemonId?: string) => {
throw new Error(t("TXT_CODE_e8ce38c2"));
}
var shouldOverwrite = false;
await prepareUploadFile({
params: {
daemonId: daemonId!,
uuid: instanceId!,
uploadDir: uploadDir,
uploadFilename: file.name
}
}).then((bl) => {
console.log(bl);
});
console.log(prepareUploadFileResp.value);
if (prepareUploadFileResp.value == undefined) {
percentComplete.value = 0;
throw new Error(t("TXT_CODE_4caa5237"));
} else if (prepareUploadFileResp.value.exists) {
var complete: (value: boolean) => void, reject: (reason?: any) => void;
var promise: Promise<boolean> = new Promise((onComplete, onReject) => {
complete = onComplete;
reject = onReject;
})
Modal.confirm({
title: t("TXT_CODE_99ca8563"),
icon: createVNode(ExclamationCircleOutlined),
content: t("TXT_CODE_ec99ddaa") + ` ${file.name} ` + t("TXT_CODE_8bd1f5d2"),
onOk() {
complete(true);
},
onCancel() {
complete(false);
percentComplete.value = 0;
}
});
shouldOverwrite = await promise;
if (!shouldOverwrite) {
return reportErrorMsg(t("TXT_CODE_8b14426e"));
}
}
const uploadFormData = new FormData();
uploadFormData.append("file", file);
@ -376,6 +422,9 @@ export const useFileManager = (instanceId?: string, daemonId?: string) => {
onUploadProgress: (progressEvent: any) => {
const p = Math.round((progressEvent.loaded * 100) / progressEvent.total);
if (p >= 1) percentComplete.value = p;
},
params: {
overwrite: shouldOverwrite
}
});
await getFileList();

View File

@ -153,7 +153,8 @@ export const uploadAddress = useDefineApi<
params: {
upload_dir: string;
daemonId: string;
uuid: string;
uuid: string,
file_name: string;
};
},
{
@ -167,7 +168,10 @@ export const uploadAddress = useDefineApi<
export const uploadFile = useDefineApi<
{
data: FormData;
data: FormData,
params: {
overwrite: boolean;
};
},
any
>({
@ -227,3 +231,20 @@ export const changePermission = useDefineApi<
url: "/api/files/chmod",
method: "PUT"
});
export const prepareUploadFile = useDefineApi<
{
params: {
daemonId: string;
uuid: string,
uploadDir: string,
uploadFilename: string;
};
},
{
exists: boolean
}
>({
url: "/api/files/pre_upload",
method: "GET"
});

View File

@ -161,7 +161,8 @@ const selectedFile = async () => {
await getCfg({
params: {
upload_dir: ".",
daemonId: props.daemonId
daemonId: props.daemonId,
file_name: uFile.value?.name
},
data: formData
});

View File

@ -1901,5 +1901,10 @@
"TXT_CODE_930d2524": "Direct connection to web page",
"TXT_CODE_e039b9b5": "normal",
"TXT_CODE_a788e3eb": "Memory & Processor",
"TXT_CODE_c5ed896f": "The instantaneous output content is too long and has been rejected...."
"TXT_CODE_c5ed896f": "The instantaneous output content is too long and has been rejected....",
"TXT_CODE_4caa5237": "Failed to resolve file pre-upload status",
"TXT_CODE_99ca8563": "Overwriting File",
"TXT_CODE_ec99ddaa": "File",
"TXT_CODE_8bd1f5d2": "is already exists in this folder, should overwrite it?",
"TXT_CODE_8b14426e": "File already exists in this folder, Upload skipped"
}

View File

@ -1901,5 +1901,10 @@
"TXT_CODE_23a3bd72": "异常",
"TXT_CODE_6b4a27dd": "网页前端无法与节点建立 WebSocket 连接,请检查网络代理配置或防火墙配置!",
"TXT_CODE_a788e3eb": "内存 & 处理器",
"TXT_CODE_c5ed896f": "此刻瞬时输出内容过长,已拒绝显示..."
"TXT_CODE_c5ed896f": "此刻瞬时输出内容过长,已拒绝显示...",
"TXT_CODE_4caa5237": "获取文件存在状态失败",
"TXT_CODE_99ca8563": "覆盖文件",
"TXT_CODE_ec99ddaa": "您上传的文件",
"TXT_CODE_8bd1f5d2": "已经在目录中存在, 是否覆盖原文件?",
"TXT_CODE_8b14426e": "文件已存在, 跳过上传"
}

View File

@ -316,6 +316,7 @@ router.all(
const daemonId = String(ctx.query.daemonId);
const instanceUuid = String(ctx.query.uuid);
const uploadDir = String(ctx.query.upload_dir);
const uploadFilename = String(ctx.query.file_name);
const remoteService = RemoteServiceSubsystem.getInstance(daemonId);
const addr = `${remoteService?.config.ip}:${remoteService?.config.port}${
remoteService?.config.prefix ? removeTrail(remoteService.config.prefix, "/") : ""
@ -326,7 +327,8 @@ router.all(
password: password,
parameter: {
uploadDir,
instanceUuid
instanceUuid,
uploadFilename
}
});
ctx.body = {
@ -339,4 +341,31 @@ router.all(
}
);
router.get(
"/pre_upload",
permission({ level: ROLE.USER }),
validator({
query: { daemonId: String, uuid: String, uploadDir: String, uploadFilename: String },
}),
async (ctx) => {
try {
const daemonId = String(ctx.query.daemonId);
const instanceUuid = String(ctx.query.uuid);
const uploadDir = String(ctx.query.uploadDir);
const uploadFilename = String(ctx.query.uploadFilename);
const remoteService = RemoteServiceSubsystem.getInstance(daemonId);
const result = await new RemoteRequest(remoteService).request("file/exists", {
instanceUuid,
uploadDir,
uploadFilename
});
ctx.body = {
exists: result
};
} catch (err) {
ctx.body = err;
}
}
);
export default router;