Add upload overwrite confirmation
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
PLUGIN="u-navigator"
|
PLUGIN="u-navigator"
|
||||||
VERSION="2026.06.23.r048"
|
VERSION="2026.06.23.r049"
|
||||||
PKG_DIR="packages"
|
PKG_DIR="packages"
|
||||||
WORK_DIR=".plugin-build"
|
WORK_DIR=".plugin-build"
|
||||||
PKG_NAME="${PLUGIN}-${VERSION}.tgz"
|
PKG_NAME="${PLUGIN}-${VERSION}.tgz"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -12,10 +12,11 @@ if (($_POST['uploadMode'] ?? '') === 'chunk') {
|
|||||||
$chunkIndex = filter_var($_POST['chunkIndex'] ?? null, FILTER_VALIDATE_INT);
|
$chunkIndex = filter_var($_POST['chunkIndex'] ?? null, FILTER_VALIDATE_INT);
|
||||||
$totalChunks = filter_var($_POST['totalChunks'] ?? null, FILTER_VALIDATE_INT);
|
$totalChunks = filter_var($_POST['totalChunks'] ?? null, FILTER_VALIDATE_INT);
|
||||||
$encoded = (string)($_POST['data'] ?? '');
|
$encoded = (string)($_POST['data'] ?? '');
|
||||||
|
$overwrite = unav_upload_bool($_POST['overwrite'] ?? false);
|
||||||
if ($relativeName === '' || $chunkIndex === false || $totalChunks === false || $chunkIndex < 0 || $totalChunks < 1 || $chunkIndex >= $totalChunks) {
|
if ($relativeName === '' || $chunkIndex === false || $totalChunks === false || $chunkIndex < 0 || $totalChunks < 1 || $chunkIndex >= $totalChunks) {
|
||||||
unav_error(400, 'Invalid upload chunk');
|
unav_error(400, 'Invalid upload chunk');
|
||||||
}
|
}
|
||||||
$destination = unav_prepare_upload_destination($target['path'], $relativeName);
|
$destination = unav_prepare_upload_destination($target['path'], $relativeName, $overwrite);
|
||||||
$temporary = $destination['path'] . '.u-nav-upload';
|
$temporary = $destination['path'] . '.u-nav-upload';
|
||||||
if ($chunkIndex === 0) {
|
if ($chunkIndex === 0) {
|
||||||
@unlink($temporary);
|
@unlink($temporary);
|
||||||
@@ -30,6 +31,16 @@ if (($_POST['uploadMode'] ?? '') === 'chunk') {
|
|||||||
unav_error(500, 'Could not write upload chunk');
|
unav_error(500, 'Could not write upload chunk');
|
||||||
}
|
}
|
||||||
$complete = $chunkIndex === $totalChunks - 1;
|
$complete = $chunkIndex === $totalChunks - 1;
|
||||||
|
if ($complete && file_exists($destination['path'])) {
|
||||||
|
if (!$overwrite) {
|
||||||
|
@unlink($temporary);
|
||||||
|
unav_error(409, 'Destination already exists');
|
||||||
|
}
|
||||||
|
if (is_dir($destination['path']) || !@unlink($destination['path'])) {
|
||||||
|
@unlink($temporary);
|
||||||
|
unav_error(409, 'Destination cannot be overwritten');
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($complete && !rename($temporary, $destination['path'])) {
|
if ($complete && !rename($temporary, $destination['path'])) {
|
||||||
@unlink($temporary);
|
@unlink($temporary);
|
||||||
unav_error(500, 'Could not finalize upload');
|
unav_error(500, 'Could not finalize upload');
|
||||||
@@ -47,7 +58,11 @@ if (($_POST['uploadMode'] ?? '') === 'chunk') {
|
|||||||
|
|
||||||
if (isset($_GET['relativePath']) || isset($_SERVER['HTTP_X_UNAV_RELATIVE_PATH'])) {
|
if (isset($_GET['relativePath']) || isset($_SERVER['HTTP_X_UNAV_RELATIVE_PATH'])) {
|
||||||
$relativeName = (string)($_GET['relativePath'] ?? $_SERVER['HTTP_X_UNAV_RELATIVE_PATH']);
|
$relativeName = (string)($_GET['relativePath'] ?? $_SERVER['HTTP_X_UNAV_RELATIVE_PATH']);
|
||||||
$destination = unav_prepare_upload_destination($target['path'], $relativeName);
|
$overwrite = unav_upload_bool($_GET['overwrite'] ?? ($_SERVER['HTTP_X_UNAV_OVERWRITE'] ?? false));
|
||||||
|
$destination = unav_prepare_upload_destination($target['path'], $relativeName, $overwrite);
|
||||||
|
if ($overwrite && file_exists($destination['path']) && !is_dir($destination['path']) && !@unlink($destination['path'])) {
|
||||||
|
unav_error(409, 'Destination cannot be overwritten');
|
||||||
|
}
|
||||||
$bytes = file_put_contents($destination['path'], file_get_contents('php://input'), LOCK_EX);
|
$bytes = file_put_contents($destination['path'], file_get_contents('php://input'), LOCK_EX);
|
||||||
if ($bytes === false) {
|
if ($bytes === false) {
|
||||||
unav_error(500, 'Could not store uploaded file');
|
unav_error(500, 'Could not store uploaded file');
|
||||||
@@ -61,6 +76,7 @@ if (isset($_GET['relativePath']) || isset($_SERVER['HTTP_X_UNAV_RELATIVE_PATH'])
|
|||||||
}
|
}
|
||||||
|
|
||||||
$uploaded = [];
|
$uploaded = [];
|
||||||
|
$overwrite = unav_upload_bool($_POST['overwrite'] ?? false);
|
||||||
$names = unav_upload_array($_FILES['files']['name'] ?? []);
|
$names = unav_upload_array($_FILES['files']['name'] ?? []);
|
||||||
$errors = unav_upload_array($_FILES['files']['error'] ?? []);
|
$errors = unav_upload_array($_FILES['files']['error'] ?? []);
|
||||||
$tmpNames = unav_upload_array($_FILES['files']['tmp_name'] ?? []);
|
$tmpNames = unav_upload_array($_FILES['files']['tmp_name'] ?? []);
|
||||||
@@ -70,7 +86,10 @@ foreach ($names as $index => $name) {
|
|||||||
unav_error(400, 'Upload failed');
|
unav_error(400, 'Upload failed');
|
||||||
}
|
}
|
||||||
$relativeName = $relativePaths[$index] ?? $name;
|
$relativeName = $relativePaths[$index] ?? $name;
|
||||||
$destination = unav_prepare_upload_destination($target['path'], (string)$relativeName);
|
$destination = unav_prepare_upload_destination($target['path'], (string)$relativeName, $overwrite);
|
||||||
|
if ($overwrite && file_exists($destination['path']) && !is_dir($destination['path']) && !@unlink($destination['path'])) {
|
||||||
|
unav_error(409, 'Destination cannot be overwritten');
|
||||||
|
}
|
||||||
if (!move_uploaded_file($tmpNames[$index] ?? '', $destination['path'])) {
|
if (!move_uploaded_file($tmpNames[$index] ?? '', $destination['path'])) {
|
||||||
unav_error(500, 'Could not store uploaded file');
|
unav_error(500, 'Could not store uploaded file');
|
||||||
}
|
}
|
||||||
@@ -82,7 +101,7 @@ foreach ($names as $index => $name) {
|
|||||||
|
|
||||||
unav_json(['uploaded' => $uploaded], 201);
|
unav_json(['uploaded' => $uploaded], 201);
|
||||||
|
|
||||||
function unav_prepare_upload_destination(string $targetPath, string $relativeName): array {
|
function unav_prepare_upload_destination(string $targetPath, string $relativeName, bool $overwrite = false): array {
|
||||||
$safeName = unav_upload_safe_relative_path($relativeName);
|
$safeName = unav_upload_safe_relative_path($relativeName);
|
||||||
$relativeParent = dirname($safeName);
|
$relativeParent = dirname($safeName);
|
||||||
if ($relativeParent !== '.') {
|
if ($relativeParent !== '.') {
|
||||||
@@ -94,8 +113,13 @@ function unav_prepare_upload_destination(string $targetPath, string $relativeNam
|
|||||||
}
|
}
|
||||||
$destination = unav_destination_path($targetPath . '/' . $safeName);
|
$destination = unav_destination_path($targetPath . '/' . $safeName);
|
||||||
if (file_exists($destination['path'])) {
|
if (file_exists($destination['path'])) {
|
||||||
|
if (!$overwrite) {
|
||||||
unav_error(409, 'Destination already exists');
|
unav_error(409, 'Destination already exists');
|
||||||
}
|
}
|
||||||
|
if (is_dir($destination['path'])) {
|
||||||
|
unav_error(409, 'Destination directory already exists');
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!is_dir(dirname($destination['path']))) {
|
if (!is_dir(dirname($destination['path']))) {
|
||||||
unav_error(500, 'Could not create upload directory');
|
unav_error(500, 'Could not create upload directory');
|
||||||
}
|
}
|
||||||
@@ -126,3 +150,7 @@ function unav_upload_safe_relative_path(string $name): string {
|
|||||||
}
|
}
|
||||||
return implode('/', $parts);
|
return implode('/', $parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unav_upload_bool($value): bool {
|
||||||
|
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||||
|
}
|
||||||
|
|||||||
+24
-4
@@ -904,13 +904,27 @@ async function uploadFileList(win, files, targetPath) {
|
|||||||
const uploadItems = normalizeUploadItems(files);
|
const uploadItems = normalizeUploadItems(files);
|
||||||
for (const [index, item] of uploadItems.entries()) {
|
for (const [index, item] of uploadItems.entries()) {
|
||||||
recordDebug('upload.file.request', { path: item.path, size: item.file.size, index: index + 1, total: uploadItems.length });
|
recordDebug('upload.file.request', { path: item.path, size: item.file.size, index: index + 1, total: uploadItems.length });
|
||||||
await uploadFileChunked(targetPath, item.file, item.path);
|
try {
|
||||||
recordDebug('upload.file.done', { path: item.path, index: index + 1, total: uploadItems.length });
|
await uploadFileChunked(targetPath, item.file, item.path, false);
|
||||||
|
recordDebug('upload.file.done', { path: item.path, index: index + 1, total: uploadItems.length, overwrite: false });
|
||||||
|
} catch (error) {
|
||||||
|
if (error.status !== 409) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
const overwrite = window.confirm(`"${item.path}" existiert bereits.\n\nVorhandene Datei überschreiben?`);
|
||||||
|
recordDebug('upload.overwrite.prompt', { path: item.path, overwrite });
|
||||||
|
if (!overwrite) {
|
||||||
|
recordDebug('upload.file.skipped', { path: item.path, index: index + 1, total: uploadItems.length });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await uploadFileChunked(targetPath, item.file, item.path, true);
|
||||||
|
recordDebug('upload.file.done', { path: item.path, index: index + 1, total: uploadItems.length, overwrite: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await loadExplorer(win, targetPath);
|
await loadExplorer(win, targetPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadFileChunked(targetPath, file, relativePath) {
|
async function uploadFileChunked(targetPath, file, relativePath, overwrite = false) {
|
||||||
const chunkSize = 256 * 1024;
|
const chunkSize = 256 * 1024;
|
||||||
const totalChunks = Math.max(1, Math.ceil(file.size / chunkSize));
|
const totalChunks = Math.max(1, Math.ceil(file.size / chunkSize));
|
||||||
let finalPayload = null;
|
let finalPayload = null;
|
||||||
@@ -924,6 +938,9 @@ async function uploadFileChunked(targetPath, file, relativePath) {
|
|||||||
body.set('chunkIndex', String(chunkIndex));
|
body.set('chunkIndex', String(chunkIndex));
|
||||||
body.set('totalChunks', String(totalChunks));
|
body.set('totalChunks', String(totalChunks));
|
||||||
body.set('data', data);
|
body.set('data', data);
|
||||||
|
if (overwrite) {
|
||||||
|
body.set('overwrite', '1');
|
||||||
|
}
|
||||||
appendCsrf(body);
|
appendCsrf(body);
|
||||||
const response = await fetch(apiUrl(`upload.php?path=${encodeURIComponent(targetPath)}`), {
|
const response = await fetch(apiUrl(`upload.php?path=${encodeURIComponent(targetPath)}`), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -941,7 +958,10 @@ async function uploadFileChunked(targetPath, file, relativePath) {
|
|||||||
body: text.slice(0, 1000)
|
body: text.slice(0, 1000)
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(text || `${response.status} ${response.statusText}`);
|
const error = new Error(text || `${response.status} ${response.statusText}`);
|
||||||
|
error.status = response.status;
|
||||||
|
error.path = relativePath;
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
finalPayload = JSON.parse(text);
|
finalPayload = JSON.parse(text);
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE PLUGIN [
|
<!DOCTYPE PLUGIN [
|
||||||
<!ENTITY name "u-navigator">
|
<!ENTITY name "u-navigator">
|
||||||
<!ENTITY author "Michael Roll">
|
<!ENTITY author "Michael Roll">
|
||||||
<!ENTITY version "2026.06.23.r048">
|
<!ENTITY version "2026.06.23.r049">
|
||||||
<!ENTITY package "&name;-&version;.tgz">
|
<!ENTITY package "&name;-&version;.tgz">
|
||||||
<!ENTITY pluginURL "https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/u-navigator.plg">
|
<!ENTITY pluginURL "https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/u-navigator.plg">
|
||||||
<!ENTITY support "https://git.casaderoll.de/michael/Unraid-Navigator">
|
<!ENTITY support "https://git.casaderoll.de/michael/Unraid-Navigator">
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<FILE Name="/boot/config/plugins/&name;/&package;">
|
<FILE Name="/boot/config/plugins/&name;/&package;">
|
||||||
<URL>https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/packages/&package;</URL>
|
<URL>https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/packages/&package;</URL>
|
||||||
<MD5>45fd0a1c4f8033d9daa56268f48742d4</MD5>
|
<MD5>3bf3f4752ed9cd8b86fd27f9e17fbe14</MD5>
|
||||||
</FILE>
|
</FILE>
|
||||||
|
|
||||||
<FILE Run="/bin/bash">
|
<FILE Run="/bin/bash">
|
||||||
|
|||||||
Reference in New Issue
Block a user