fix: harden browser file picker and remove legacy
This commit is contained in:
Vendored
+35
-4
@@ -475,7 +475,7 @@ function unzipSync(data, opts) {
|
||||
// upload/frontend/table/rebase_tree.js
|
||||
function rebaseTree(root, prefix) {
|
||||
root.path = `${prefix}/${root.path}`;
|
||||
if (root.file) root.file = new File([root.file], root.path, { lastModified: root.file.lastModified });
|
||||
if (root.file) root.file = new File([root.file], root.file.name, { lastModified: root.file.lastModified });
|
||||
root.children.forEach((child) => rebaseTree(child, prefix));
|
||||
return root;
|
||||
}
|
||||
@@ -492,7 +492,7 @@ function extensionAllowed(name, allowedExt) {
|
||||
return allowedExt.some((extension) => lowerName.endsWith(extension.toLowerCase()));
|
||||
}
|
||||
function makeFile(data, name) {
|
||||
return new File([data], name);
|
||||
return new File([data], name.split("/").at(-1));
|
||||
}
|
||||
function safeEntryParts(entryName) {
|
||||
if (!entryName || entryName.startsWith("/") || entryName.includes("\\")) return null;
|
||||
@@ -521,6 +521,8 @@ async function listEntries(data, zipName, allowedExt, depth, limits, budget) {
|
||||
filter: (entry) => {
|
||||
budget.entries += 1;
|
||||
if (budget.entries > limits.maxEntries) throw new Error("\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u043D\u043E\u0433\u043E ZIP entries");
|
||||
const lowerName = entry.name.toLowerCase();
|
||||
if (!extensionAllowed(lowerName, allowedExt) && !lowerName.endsWith(".zip")) return false;
|
||||
if (entry.originalSize > limits.maxEntryBytes) return false;
|
||||
if (budget.totalBytes + entry.originalSize > limits.maxTotalBytes) {
|
||||
throw new Error("\u041F\u0440\u0435\u0432\u044B\u0448\u0435\u043D \u0441\u0443\u043C\u043C\u0430\u0440\u043D\u044B\u0439 \u0440\u0430\u0437\u043C\u0435\u0440 \u0440\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u0430\u043D\u043D\u044B\u0445 ZIP entries");
|
||||
@@ -570,9 +572,28 @@ function addFileWithDedup(state, fileNode) {
|
||||
};
|
||||
const accepted = accept(fileNode);
|
||||
if (!accepted || accepted.kind !== "file" && !accepted.children.length) return false;
|
||||
if (accepted.kind !== "file") {
|
||||
const existing = state.nodes.find((node2) => node2.kind !== "file" && node2.path === accepted.path);
|
||||
if (existing) {
|
||||
mergeChildren(existing, accepted);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
state.nodes.push(accepted);
|
||||
return true;
|
||||
}
|
||||
function mergeChildren(target, incoming) {
|
||||
incoming.children.forEach((child) => {
|
||||
if (child.kind === "file") {
|
||||
const duplicate = target.children.some((existing2) => existing2.kind === "file" && existing2.path === child.path && existing2.file.size === child.file.size);
|
||||
if (!duplicate) target.children.push(child);
|
||||
return;
|
||||
}
|
||||
const existing = target.children.find((candidate) => candidate.kind !== "file" && candidate.path === child.path);
|
||||
if (existing) mergeChildren(existing, child);
|
||||
else target.children.push(child);
|
||||
});
|
||||
}
|
||||
|
||||
// upload/frontend/table/esc.js
|
||||
function esc(value) {
|
||||
@@ -682,7 +703,11 @@ async function addFiles(state, cfg, files, elements) {
|
||||
const zipTree = await listZipFiles(file, cfg.allowedExt, cfg.limits);
|
||||
if (zipTree) addFileWithDedup(state, zipTree);
|
||||
} catch (error) {
|
||||
if (typeof cfg.onError === "function") cfg.onError(error, file);
|
||||
if (typeof cfg.onError === "function") {
|
||||
cfg.onError(error, file);
|
||||
} else if (elements.statusEl) {
|
||||
elements.statusEl.textContent = `\u041E\u0448\u0438\u0431\u043A\u0430 \u0447\u0442\u0435\u043D\u0438\u044F \u0430\u0440\u0445\u0438\u0432\u0430 ${file.name}: ${error.message}`;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -695,6 +720,7 @@ function onFilesChange(state, cfg, elements) {
|
||||
try {
|
||||
await addFiles(state, cfg, elements.fileInputEl.files, elements);
|
||||
} finally {
|
||||
elements.fileInputEl.value = "";
|
||||
state.busy = false;
|
||||
}
|
||||
};
|
||||
@@ -751,7 +777,11 @@ function onFolderChange(state, cfg, elements) {
|
||||
addToFolder(zip);
|
||||
}
|
||||
} catch (error) {
|
||||
if (typeof cfg.onError === "function") cfg.onError(error, file);
|
||||
if (typeof cfg.onError === "function") {
|
||||
cfg.onError(error, file);
|
||||
} else if (elements.statusEl) {
|
||||
elements.statusEl.textContent = `\u041E\u0448\u0438\u0431\u043A\u0430 \u0447\u0442\u0435\u043D\u0438\u044F \u0430\u0440\u0445\u0438\u0432\u0430 ${file.name}: ${error.message}`;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (cfg.allowedExt.some((extension) => lowerPath.endsWith(extension))) {
|
||||
@@ -884,6 +914,7 @@ function initFilePicker(config) {
|
||||
const elements = {
|
||||
fileInputEl: root.querySelector(".fp-file-input"),
|
||||
folderInputEl: root.querySelector(".fp-folder-input"),
|
||||
statusEl: root.querySelector(".fp-status"),
|
||||
tableBodyEl: root.querySelector(".fp-table-body"),
|
||||
countEl: root.querySelector(".fp-count")
|
||||
};
|
||||
|
||||
Vendored
+35
-4
@@ -501,7 +501,7 @@ var FilePicker = (() => {
|
||||
// upload/frontend/table/rebase_tree.js
|
||||
function rebaseTree(root, prefix) {
|
||||
root.path = `${prefix}/${root.path}`;
|
||||
if (root.file) root.file = new File([root.file], root.path, { lastModified: root.file.lastModified });
|
||||
if (root.file) root.file = new File([root.file], root.file.name, { lastModified: root.file.lastModified });
|
||||
root.children.forEach((child) => rebaseTree(child, prefix));
|
||||
return root;
|
||||
}
|
||||
@@ -518,7 +518,7 @@ var FilePicker = (() => {
|
||||
return allowedExt.some((extension) => lowerName.endsWith(extension.toLowerCase()));
|
||||
}
|
||||
function makeFile(data, name) {
|
||||
return new File([data], name);
|
||||
return new File([data], name.split("/").at(-1));
|
||||
}
|
||||
function safeEntryParts(entryName) {
|
||||
if (!entryName || entryName.startsWith("/") || entryName.includes("\\")) return null;
|
||||
@@ -547,6 +547,8 @@ var FilePicker = (() => {
|
||||
filter: (entry) => {
|
||||
budget.entries += 1;
|
||||
if (budget.entries > limits.maxEntries) throw new Error("\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u043D\u043E\u0433\u043E ZIP entries");
|
||||
const lowerName = entry.name.toLowerCase();
|
||||
if (!extensionAllowed(lowerName, allowedExt) && !lowerName.endsWith(".zip")) return false;
|
||||
if (entry.originalSize > limits.maxEntryBytes) return false;
|
||||
if (budget.totalBytes + entry.originalSize > limits.maxTotalBytes) {
|
||||
throw new Error("\u041F\u0440\u0435\u0432\u044B\u0448\u0435\u043D \u0441\u0443\u043C\u043C\u0430\u0440\u043D\u044B\u0439 \u0440\u0430\u0437\u043C\u0435\u0440 \u0440\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u0430\u043D\u043D\u044B\u0445 ZIP entries");
|
||||
@@ -596,9 +598,28 @@ var FilePicker = (() => {
|
||||
};
|
||||
const accepted = accept(fileNode);
|
||||
if (!accepted || accepted.kind !== "file" && !accepted.children.length) return false;
|
||||
if (accepted.kind !== "file") {
|
||||
const existing = state.nodes.find((node2) => node2.kind !== "file" && node2.path === accepted.path);
|
||||
if (existing) {
|
||||
mergeChildren(existing, accepted);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
state.nodes.push(accepted);
|
||||
return true;
|
||||
}
|
||||
function mergeChildren(target, incoming) {
|
||||
incoming.children.forEach((child) => {
|
||||
if (child.kind === "file") {
|
||||
const duplicate = target.children.some((existing2) => existing2.kind === "file" && existing2.path === child.path && existing2.file.size === child.file.size);
|
||||
if (!duplicate) target.children.push(child);
|
||||
return;
|
||||
}
|
||||
const existing = target.children.find((candidate) => candidate.kind !== "file" && candidate.path === child.path);
|
||||
if (existing) mergeChildren(existing, child);
|
||||
else target.children.push(child);
|
||||
});
|
||||
}
|
||||
|
||||
// upload/frontend/table/esc.js
|
||||
function esc(value) {
|
||||
@@ -708,7 +729,11 @@ var FilePicker = (() => {
|
||||
const zipTree = await listZipFiles(file, cfg.allowedExt, cfg.limits);
|
||||
if (zipTree) addFileWithDedup(state, zipTree);
|
||||
} catch (error) {
|
||||
if (typeof cfg.onError === "function") cfg.onError(error, file);
|
||||
if (typeof cfg.onError === "function") {
|
||||
cfg.onError(error, file);
|
||||
} else if (elements.statusEl) {
|
||||
elements.statusEl.textContent = `\u041E\u0448\u0438\u0431\u043A\u0430 \u0447\u0442\u0435\u043D\u0438\u044F \u0430\u0440\u0445\u0438\u0432\u0430 ${file.name}: ${error.message}`;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -721,6 +746,7 @@ var FilePicker = (() => {
|
||||
try {
|
||||
await addFiles(state, cfg, elements.fileInputEl.files, elements);
|
||||
} finally {
|
||||
elements.fileInputEl.value = "";
|
||||
state.busy = false;
|
||||
}
|
||||
};
|
||||
@@ -777,7 +803,11 @@ var FilePicker = (() => {
|
||||
addToFolder(zip);
|
||||
}
|
||||
} catch (error) {
|
||||
if (typeof cfg.onError === "function") cfg.onError(error, file);
|
||||
if (typeof cfg.onError === "function") {
|
||||
cfg.onError(error, file);
|
||||
} else if (elements.statusEl) {
|
||||
elements.statusEl.textContent = `\u041E\u0448\u0438\u0431\u043A\u0430 \u0447\u0442\u0435\u043D\u0438\u044F \u0430\u0440\u0445\u0438\u0432\u0430 ${file.name}: ${error.message}`;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (cfg.allowedExt.some((extension) => lowerPath.endsWith(extension))) {
|
||||
@@ -910,6 +940,7 @@ var FilePicker = (() => {
|
||||
const elements = {
|
||||
fileInputEl: root.querySelector(".fp-file-input"),
|
||||
folderInputEl: root.querySelector(".fp-folder-input"),
|
||||
statusEl: root.querySelector(".fp-status"),
|
||||
tableBodyEl: root.querySelector(".fp-table-body"),
|
||||
countEl: root.querySelector(".fp-count")
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user