mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-05 07:07:38 +03:00
improve file manager
This commit is contained in:
parent
14972b34cb
commit
ec82ff209d
@ -3,6 +3,38 @@ import re
|
|||||||
|
|
||||||
SRC_DIR = "src"
|
SRC_DIR = "src"
|
||||||
|
|
||||||
|
|
||||||
|
def bytes_to_cpp_byte_array(input_file_bytes: bytes, input_file_path: str, output_header_file: str, variable_name: str):
|
||||||
|
# Format bytes into C++ byte array initialiser format (hex values)
|
||||||
|
hex_values = [f"0x{b:02x}" for b in input_file_bytes]
|
||||||
|
bytes_array_declaration = ", ".join(hex_values)
|
||||||
|
data_length = len(input_file_bytes)
|
||||||
|
|
||||||
|
# Generate the C++ header file content
|
||||||
|
header_content = f"""
|
||||||
|
#ifndef {variable_name.upper()}_H
|
||||||
|
#define {variable_name.upper()}_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// Embedded file: {os.path.basename(input_file_path)}
|
||||||
|
constexpr uint8_t {variable_name}_data[] = {{
|
||||||
|
{bytes_array_declaration}
|
||||||
|
}};
|
||||||
|
constexpr size_t {variable_name}_size = {data_length};
|
||||||
|
|
||||||
|
#endif // {variable_name.upper()}_H
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(output_header_file, 'w') as f:
|
||||||
|
f.write(header_content)
|
||||||
|
print(f"Successfully generated C++ header file: {output_header_file}")
|
||||||
|
except IOError as e:
|
||||||
|
print(f"Error writing header file: {e}")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
def minify_html(html: str) -> str:
|
def minify_html(html: str) -> str:
|
||||||
# Tags where whitespace should be preserved
|
# Tags where whitespace should be preserved
|
||||||
preserve_tags = ['pre', 'code', 'textarea', 'script', 'style']
|
preserve_tags = ['pre', 'code', 'textarea', 'script', 'style']
|
||||||
@ -31,21 +63,37 @@ def minify_html(html: str) -> str:
|
|||||||
|
|
||||||
return html.strip()
|
return html.strip()
|
||||||
|
|
||||||
|
def read_src_file(root: str, file: str) -> str:
|
||||||
|
file_path = os.path.join(root, file)
|
||||||
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
def build_static(src_path: str, dest_dir: str):
|
||||||
|
filename = os.path.basename(src_path)
|
||||||
|
base_name, extension = os.path.splitext(filename)
|
||||||
|
postfix = extension[1:].capitalize()
|
||||||
|
base_name = f"{os.path.splitext(filename)[0]}{postfix}"
|
||||||
|
output_cpp_header = os.path.join(dest_dir, f"{base_name}.generated.h")
|
||||||
|
cpp_variable_name = base_name
|
||||||
|
if not os.path.exists(dest_dir):
|
||||||
|
os.makedirs(dest_dir)
|
||||||
|
|
||||||
|
if extension == ".html":
|
||||||
|
# minify HTML content
|
||||||
|
src_string = open(src_path, "r").read()
|
||||||
|
minified_str = minify_html(src_string)
|
||||||
|
src_bytes = minified_str.encode("utf-8")
|
||||||
|
else:
|
||||||
|
src_bytes = open(src_path, "rb").read()
|
||||||
|
|
||||||
|
bytes_to_cpp_byte_array(
|
||||||
|
input_file_bytes=src_bytes,
|
||||||
|
input_file_path=src_path,
|
||||||
|
output_header_file=output_cpp_header,
|
||||||
|
variable_name=cpp_variable_name
|
||||||
|
)
|
||||||
|
|
||||||
for root, _, files in os.walk(SRC_DIR):
|
for root, _, files in os.walk(SRC_DIR):
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.endswith(".html"):
|
if file.endswith((".html", ".css", ".js")):
|
||||||
html_path = os.path.join(root, file)
|
build_static(os.path.join(root, file), root)
|
||||||
with open(html_path, "r", encoding="utf-8") as f:
|
|
||||||
html_content = f.read()
|
|
||||||
|
|
||||||
# minified = regex.sub("\g<1>", html_content)
|
|
||||||
minified = minify_html(html_content)
|
|
||||||
base_name = f"{os.path.splitext(file)[0]}Html"
|
|
||||||
header_path = os.path.join(root, f"{base_name}.generated.h")
|
|
||||||
|
|
||||||
with open(header_path, "w", encoding="utf-8") as h:
|
|
||||||
h.write(f"// THIS FILE IS AUTOGENERATED, DO NOT EDIT MANUALLY\n\n")
|
|
||||||
h.write(f"#pragma once\n")
|
|
||||||
h.write(f'constexpr char {base_name}[] PROGMEM = R"rawliteral({minified})rawliteral";\n')
|
|
||||||
|
|
||||||
print(f"Generated: {header_path}")
|
|
||||||
|
|||||||
@ -67,20 +67,23 @@ void CrossPointWebServer::begin() {
|
|||||||
|
|
||||||
// Setup routes
|
// Setup routes
|
||||||
Serial.printf("[%lu] [WEB] Setting up routes...\n", millis());
|
Serial.printf("[%lu] [WEB] Setting up routes...\n", millis());
|
||||||
server->on("/", HTTP_GET, [this] { handleRoot(); });
|
server->on("/", HTTP_GET, [this] { handleStatusPage(); });
|
||||||
server->on("/files", HTTP_GET, [this] { handleFileList(); });
|
server->on("/files", HTTP_GET, [this] { handleFileList(); });
|
||||||
|
|
||||||
server->on("/api/status", HTTP_GET, [this] { handleStatus(); });
|
server->on("/api/status", HTTP_GET, [this] { handleStatus(); });
|
||||||
server->on("/api/files", HTTP_GET, [this] { handleFileListData(); });
|
server->on("/api/files", HTTP_GET, [this] { handleFileListData(); });
|
||||||
|
|
||||||
// Upload endpoint with special handling for multipart form data
|
// Upload endpoint with special handling for multipart form data
|
||||||
server->on("/upload", HTTP_POST, [this] { handleUploadPost(); }, [this] { handleUpload(); });
|
server->on("/api/upload", HTTP_POST, [this] { handleUploadPost(); }, [this] { handleUpload(); });
|
||||||
|
|
||||||
// Create folder endpoint
|
// Create folder endpoint
|
||||||
server->on("/mkdir", HTTP_POST, [this] { handleCreateFolder(); });
|
server->on("/api/mkdir", HTTP_POST, [this] { handleCreateFolder(); });
|
||||||
|
|
||||||
|
// Move file/folder endpoint
|
||||||
|
server->on("/api/move", HTTP_POST, [this] { handleMove(); });
|
||||||
|
|
||||||
// Delete file/folder endpoint
|
// Delete file/folder endpoint
|
||||||
server->on("/delete", HTTP_POST, [this] { handleDelete(); });
|
server->on("/api/delete", HTTP_POST, [this] { handleDelete(); });
|
||||||
|
|
||||||
server->onNotFound([this] { handleNotFound(); });
|
server->onNotFound([this] { handleNotFound(); });
|
||||||
Serial.printf("[%lu] [WEB] [MEM] Free heap after route setup: %d bytes\n", millis(), ESP.getFreeHeap());
|
Serial.printf("[%lu] [WEB] [MEM] Free heap after route setup: %d bytes\n", millis(), ESP.getFreeHeap());
|
||||||
@ -150,8 +153,10 @@ void CrossPointWebServer::handleClient() const {
|
|||||||
server->handleClient();
|
server->handleClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrossPointWebServer::handleRoot() const {
|
void CrossPointWebServer::handleStatusPage() const {
|
||||||
server->send(200, "text/html", HomePageHtml);
|
server->setContentLength(HomePageHtml_size);
|
||||||
|
server->send(200, "text/html", "");
|
||||||
|
server->sendContent((char*)HomePageHtml_data, HomePageHtml_size);
|
||||||
Serial.printf("[%lu] [WEB] Served root page\n", millis());
|
Serial.printf("[%lu] [WEB] Served root page\n", millis());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +246,11 @@ bool CrossPointWebServer::isEpubFile(const String& filename) const {
|
|||||||
return lower.endsWith(".epub");
|
return lower.endsWith(".epub");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrossPointWebServer::handleFileList() const { server->send(200, "text/html", FilesPageHtml); }
|
void CrossPointWebServer::handleFileList() const {
|
||||||
|
server->setContentLength(FilesPageHtml_size);
|
||||||
|
server->send(200, "text/html", "");
|
||||||
|
server->sendContent((char*)FilesPageHtml_data, FilesPageHtml_size);
|
||||||
|
}
|
||||||
|
|
||||||
void CrossPointWebServer::handleFileListData() const {
|
void CrossPointWebServer::handleFileListData() const {
|
||||||
// Get current path from query string (default to root)
|
// Get current path from query string (default to root)
|
||||||
@ -555,3 +564,69 @@ void CrossPointWebServer::handleDelete() const {
|
|||||||
server->send(500, "text/plain", "Failed to delete item");
|
server->send(500, "text/plain", "Failed to delete item");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CrossPointWebServer::handleMove() const {
|
||||||
|
// Get path from form data
|
||||||
|
if (!server->hasArg("path")) {
|
||||||
|
server->send(400, "text/plain", "Missing path");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server->hasArg("new_path")) {
|
||||||
|
server->send(400, "text/plain", "Missing new path");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String itemPath = server->arg("path");
|
||||||
|
String newItemPath = server->arg("new_path");
|
||||||
|
const String itemType = server->hasArg("type") ? server->arg("type") : "file";
|
||||||
|
|
||||||
|
// Validate path
|
||||||
|
if (itemPath.isEmpty() || itemPath == "/") {
|
||||||
|
server->send(400, "text/plain", "Cannot move root directory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure path starts with /
|
||||||
|
if (!itemPath.startsWith("/")) {
|
||||||
|
itemPath = "/" + itemPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Security check: prevent renaming of protected items
|
||||||
|
const String itemName = itemPath.substring(itemPath.lastIndexOf('/') + 1);
|
||||||
|
|
||||||
|
// Check if item starts with a dot (hidden/system file)
|
||||||
|
if (itemName.startsWith(".")) {
|
||||||
|
Serial.printf("[%lu] [WEB] Move rejected - hidden/system item: %s\n", millis(), itemPath.c_str());
|
||||||
|
server->send(403, "text/plain", "Cannot move system files");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check against explicitly protected items
|
||||||
|
for (size_t i = 0; i < HIDDEN_ITEMS_COUNT; i++) {
|
||||||
|
if (itemName.equals(HIDDEN_ITEMS[i])) {
|
||||||
|
Serial.printf("[%lu] [WEB] Move rejected - protected item: %s\n", millis(), itemPath.c_str());
|
||||||
|
server->send(403, "text/plain", "Cannot move protected items");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if item exists
|
||||||
|
if (!SdMan.exists(itemPath.c_str())) {
|
||||||
|
Serial.printf("[%lu] [WEB] Move failed - item not found: %s\n", millis(), itemPath.c_str());
|
||||||
|
server->send(404, "text/plain", "Item not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [WEB] Attempting to move %s: %s\n", millis(), itemType.c_str(), itemPath.c_str());
|
||||||
|
|
||||||
|
bool success = SdMan.rename(itemPath.c_str(), newItemPath.c_str());
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
Serial.printf("[%lu] [WEB] Successfully moved: %s\n", millis(), itemPath.c_str());
|
||||||
|
server->send(200, "text/plain", "Moved successfully");
|
||||||
|
} else {
|
||||||
|
Serial.printf("[%lu] [WEB] Failed to move: %s\n", millis(), itemPath.c_str());
|
||||||
|
server->send(500, "text/plain", "Failed to move item");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ class CrossPointWebServer {
|
|||||||
bool isEpubFile(const String& filename) const;
|
bool isEpubFile(const String& filename) const;
|
||||||
|
|
||||||
// Request handlers
|
// Request handlers
|
||||||
void handleRoot() const;
|
void handleStatusPage() const;
|
||||||
void handleNotFound() const;
|
void handleNotFound() const;
|
||||||
void handleStatus() const;
|
void handleStatus() const;
|
||||||
void handleFileList() const;
|
void handleFileList() const;
|
||||||
@ -53,4 +53,5 @@ class CrossPointWebServer {
|
|||||||
void handleUploadPost() const;
|
void handleUploadPost() const;
|
||||||
void handleCreateFolder() const;
|
void handleCreateFolder() const;
|
||||||
void handleDelete() const;
|
void handleDelete() const;
|
||||||
|
void handleMove() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -52,6 +52,7 @@
|
|||||||
.breadcrumb-inline a {
|
.breadcrumb-inline a {
|
||||||
color: #3498db;
|
color: #3498db;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.breadcrumb-inline a:hover {
|
.breadcrumb-inline a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
@ -667,7 +668,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
// get current path from query parameter
|
// get current path from query parameter
|
||||||
const currentPath = decodeURIComponent(new URLSearchParams(window.location.search).get('path') || '/');
|
let currentPath = decodeURIComponent(new URLSearchParams(window.location.search).get('path') || '/');
|
||||||
|
|
||||||
function escapeHtml(unsafe) {
|
function escapeHtml(unsafe) {
|
||||||
return unsafe
|
return unsafe
|
||||||
@ -686,32 +687,45 @@
|
|||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)).toLocaleString() + ' ' + sizes[i];
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)).toLocaleString() + ' ' + sizes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hydrate() {
|
async function goToPath(path) {
|
||||||
// Close modals when clicking overlay
|
window.history.pushState({}, '', '?path=' + encodeURIComponent(path))
|
||||||
document.querySelectorAll('.modal-overlay').forEach(function(overlay) {
|
currentPath = path;
|
||||||
overlay.addEventListener('click', function(e) {
|
updateBreadcrumbs();
|
||||||
if (e.target === overlay) {
|
fillFilesTable();
|
||||||
overlay.classList.remove('open');
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
function reloadContent() {
|
||||||
|
goToPath(currentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateBreadcrumbs() {
|
||||||
const breadcrumbs = document.getElementById('directory-breadcrumbs');
|
const breadcrumbs = document.getElementById('directory-breadcrumbs');
|
||||||
const fileTable = document.getElementById('file-table');
|
|
||||||
|
|
||||||
let breadcrumbContent = '<span class="sep">/</span>';
|
let breadcrumbContent = '<span class="sep">/</span>';
|
||||||
if (currentPath === '/') {
|
if (currentPath === '/') {
|
||||||
breadcrumbContent += '<span class="current">🏠</span>';
|
breadcrumbContent += '<span class="current">🏠</span>';
|
||||||
} else {
|
} else {
|
||||||
breadcrumbContent += '<a href="/files">🏠</a>';
|
breadcrumbContent += `<a onclick="goToPath('/')">🏠</a>`;
|
||||||
const pathSegments = currentPath.split('/');
|
const pathSegments = currentPath.split('/');
|
||||||
pathSegments.slice(1, pathSegments.length - 1).forEach(function(segment, index) {
|
pathSegments.slice(1, pathSegments.length - 1).forEach(function(segment, index) {
|
||||||
breadcrumbContent += '<span class="sep">/</span><a href="/files?path=' + encodeURIComponent(pathSegments.slice(0, index + 2).join('/')) + '">' + escapeHtml(segment) + '</a>';
|
breadcrumbContent += `<span class="sep">/</span><a onclick="goToPath('${pathSegments.slice(0, index + 2).join('/')}')">${escapeHtml(segment)}</a>`;
|
||||||
});
|
});
|
||||||
breadcrumbContent += '<span class="sep">/</span>';
|
breadcrumbContent += '<span class="sep">/</span>';
|
||||||
breadcrumbContent += '<span class="current">' + escapeHtml(pathSegments[pathSegments.length - 1]) + '</span>';
|
breadcrumbContent += '<span class="current">' + escapeHtml(pathSegments[pathSegments.length - 1]) + '</span>';
|
||||||
}
|
}
|
||||||
breadcrumbs.innerHTML = breadcrumbContent;
|
breadcrumbs.innerHTML = breadcrumbContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fillFilesTable() {
|
||||||
|
const fileTable = document.getElementById('file-table');
|
||||||
|
const folderSummary = document.getElementById('folder-summary');
|
||||||
|
|
||||||
|
folderSummary.innerHTML = "";
|
||||||
|
fileTable.innerHTML = `
|
||||||
|
<div class="loader-container">
|
||||||
|
<span class="loader"></span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
let files = [];
|
let files = [];
|
||||||
try {
|
try {
|
||||||
@ -733,7 +747,7 @@
|
|||||||
totalSize += file.size;
|
totalSize += file.size;
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('folder-summary').innerHTML = `${folderCount} folders, ${files.length - folderCount} files, ${formatFileSize(totalSize)}`;
|
folderSummary.innerHTML = `${folderCount} folders, ${files.length - folderCount} files, ${formatFileSize(totalSize)}`;
|
||||||
|
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
fileTable.innerHTML = '<div class="no-files">This folder is empty</div>';
|
fileTable.innerHTML = '<div class="no-files">This folder is empty</div>';
|
||||||
@ -756,25 +770,37 @@
|
|||||||
if (!folderPath.endsWith("/")) folderPath += "/";
|
if (!folderPath.endsWith("/")) folderPath += "/";
|
||||||
folderPath += file.name;
|
folderPath += file.name;
|
||||||
|
|
||||||
fileTableContent += '<tr class="folder-row">';
|
|
||||||
fileTableContent += `<td><span class="file-icon">📁</span><a href="/files?path=${encodeURIComponent(folderPath)}" class="folder-link">${escapeHtml(file.name)}</a><span class="folder-badge">FOLDER</span></td>`;
|
fileTableContent += `
|
||||||
fileTableContent += '<td>Folder</td>';
|
<tr class="folder-row">
|
||||||
fileTableContent += '<td>-</td>';
|
<td>
|
||||||
fileTableContent += `<td class="actions-col"><button class="delete-btn" onclick="openDeleteModal('${file.name.replaceAll("'", "\\'")}', '${folderPath.replaceAll("'", "\\'")}', true)" title="Delete folder">🗑️</button></td>`;
|
<span class="file-icon">📁</span><a onclick="goToPath('${folderPath}')" class="folder-link">${escapeHtml(file.name)}</a><span class="folder-badge">FOLDER</span>
|
||||||
fileTableContent += '</tr>';
|
</td>
|
||||||
|
<td>Folder</td>
|
||||||
|
<td>-</td>
|
||||||
|
<td class="actions-col">
|
||||||
|
<button class="delete-btn" onclick="openDeleteModal('${file.name.replaceAll("'", "\\'")}', '${folderPath.replaceAll("'", "\\'")}', true)" title="Delete folder">🗑️</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
} else {
|
} else {
|
||||||
let filePath = currentPath;
|
let filePath = currentPath;
|
||||||
if (!filePath.endsWith("/")) filePath += "/";
|
if (!filePath.endsWith("/")) filePath += "/";
|
||||||
filePath += file.name;
|
filePath += file.name;
|
||||||
|
|
||||||
fileTableContent += `<tr class="${file.isEpub ? 'epub-file' : ''}">`;
|
fileTableContent += `
|
||||||
fileTableContent += `<td><span class="file-icon">${file.isEpub ? '📗' : '📄'}</span>${escapeHtml(file.name)}`;
|
<tr class="${file.isEpub ? 'epub-file' : ''}">
|
||||||
if (file.isEpub) fileTableContent += '<span class="epub-badge">EPUB</span>';
|
<td>
|
||||||
fileTableContent += '</td>';
|
<span class="file-icon">${file.isEpub ? '📗' : '📄'}</span>${escapeHtml(file.name)}
|
||||||
fileTableContent += `<td>${file.name.split('.').pop().toUpperCase()}</td>`;
|
${file.isEpub ? '<span class="epub-badge">EPUB</span>' : ''}
|
||||||
fileTableContent += `<td>${formatFileSize(file.size)}</td>`;
|
</td>
|
||||||
fileTableContent += `<td class="actions-col"><button class="delete-btn" onclick="openDeleteModal('${file.name.replaceAll("'", "\\'")}', '${filePath.replaceAll("'", "\\'")}', false)" title="Delete file">🗑️</button></td>`;
|
<td>${file.name.split('.').pop().toUpperCase()}</td>
|
||||||
fileTableContent += '</tr>';
|
<td>${formatFileSize(file.size)}</td>
|
||||||
|
<td class="actions-col">
|
||||||
|
<button class="delete-btn" onclick="openDeleteModal('${file.name.replaceAll("'", "\\'")}', '${filePath.replaceAll("'", "\\'")}', false)" title="Delete file">🗑️</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -783,6 +809,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function hydrate() {
|
||||||
|
// Close modals when clicking overlay
|
||||||
|
document.querySelectorAll('.modal-overlay').forEach(function(overlay) {
|
||||||
|
overlay.addEventListener('click', function(e) {
|
||||||
|
if (e.target === overlay) {
|
||||||
|
overlay.classList.remove('open');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await updateBreadcrumbs();
|
||||||
|
await fillFilesTable();
|
||||||
|
}
|
||||||
|
|
||||||
// Modal functions
|
// Modal functions
|
||||||
function openUploadModal() {
|
function openUploadModal() {
|
||||||
document.getElementById('uploadPathDisplay').textContent = currentPath === '/' ? '/ 🏠' : currentPath;
|
document.getElementById('uploadPathDisplay').textContent = currentPath === '/' ? '/ 🏠' : currentPath;
|
||||||
@ -845,7 +885,8 @@ function uploadFile() {
|
|||||||
progressText.textContent = 'All uploads complete!';
|
progressText.textContent = 'All uploads complete!';
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
closeUploadModal();
|
closeUploadModal();
|
||||||
hydrate(); // Refresh file list instead of reloading
|
// hydrate(); // Refresh file list instead of reloading
|
||||||
|
reloadContent();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
progressFill.style.backgroundColor = '#e74c3c';
|
progressFill.style.backgroundColor = '#e74c3c';
|
||||||
@ -858,7 +899,8 @@ function uploadFile() {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
closeUploadModal();
|
closeUploadModal();
|
||||||
showFailedUploadsBanner();
|
showFailedUploadsBanner();
|
||||||
hydrate(); // Refresh file list to show successfully uploaded files
|
// hydrate(); // Refresh file list to show successfully uploaded files
|
||||||
|
reloadContent();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -871,7 +913,7 @@ function uploadFile() {
|
|||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
// Include path as query parameter since multipart form data doesn't make
|
// Include path as query parameter since multipart form data doesn't make
|
||||||
// form fields available until after file upload completes
|
// form fields available until after file upload completes
|
||||||
xhr.open('POST', '/upload?path=' + encodeURIComponent(currentPath), true);
|
xhr.open('POST', '/api/upload?path=' + encodeURIComponent(currentPath), true);
|
||||||
|
|
||||||
progressFill.style.width = '0%';
|
progressFill.style.width = '0%';
|
||||||
progressFill.style.backgroundColor = '#4caf50';
|
progressFill.style.backgroundColor = '#4caf50';
|
||||||
@ -1008,11 +1050,12 @@ function retryAllFailedUploads() {
|
|||||||
formData.append('path', currentPath);
|
formData.append('path', currentPath);
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open('POST', '/mkdir', true);
|
xhr.open('POST', '/api/mkdir', true);
|
||||||
|
|
||||||
xhr.onload = function() {
|
xhr.onload = function() {
|
||||||
if (xhr.status === 200) {
|
if (xhr.status === 200) {
|
||||||
window.location.reload();
|
reloadContent();
|
||||||
|
closeFolderModal();
|
||||||
} else {
|
} else {
|
||||||
alert('Failed to create folder: ' + xhr.responseText);
|
alert('Failed to create folder: ' + xhr.responseText);
|
||||||
}
|
}
|
||||||
@ -1046,15 +1089,15 @@ function retryAllFailedUploads() {
|
|||||||
formData.append('type', itemType);
|
formData.append('type', itemType);
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open('POST', '/delete', true);
|
xhr.open('POST', '/api/delete', true);
|
||||||
|
|
||||||
xhr.onload = function() {
|
xhr.onload = function() {
|
||||||
if (xhr.status === 200) {
|
if (xhr.status === 200) {
|
||||||
window.location.reload();
|
reloadContent();
|
||||||
} else {
|
} else {
|
||||||
alert('Failed to delete: ' + xhr.responseText);
|
alert('Failed to delete: ' + xhr.responseText);
|
||||||
closeDeleteModal();
|
|
||||||
}
|
}
|
||||||
|
closeDeleteModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.onerror = function() {
|
xhr.onerror = function() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user