MyFSIO v0.1.9 Release #10
@@ -306,6 +306,12 @@ class ReplicationManager:
|
|||||||
if self._shutdown:
|
if self._shutdown:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Re-check if rule is still enabled (may have been paused after task was submitted)
|
||||||
|
current_rule = self.get_rule(bucket_name)
|
||||||
|
if not current_rule or not current_rule.enabled:
|
||||||
|
logger.debug(f"Replication skipped for {bucket_name}/{object_key}: rule disabled or removed")
|
||||||
|
return
|
||||||
|
|
||||||
if ".." in object_key or object_key.startswith("/") or object_key.startswith("\\"):
|
if ".." in object_key or object_key.startswith("/") or object_key.startswith("\\"):
|
||||||
logger.error(f"Invalid object key in replication (path traversal attempt): {object_key}")
|
logger.error(f"Invalid object key in replication (path traversal attempt): {object_key}")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1052,9 +1052,10 @@ class ObjectStorage:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
shutil.rmtree(upload_root, ignore_errors=True)
|
shutil.rmtree(upload_root, ignore_errors=True)
|
||||||
|
|
||||||
self._invalidate_bucket_stats_cache(bucket_id)
|
self._invalidate_bucket_stats_cache(bucket_id)
|
||||||
|
self._invalidate_object_cache(bucket_id)
|
||||||
|
|
||||||
stat = destination.stat()
|
stat = destination.stat()
|
||||||
return ObjectMeta(
|
return ObjectMeta(
|
||||||
key=safe_key.as_posix(),
|
key=safe_key.as_posix(),
|
||||||
|
|||||||
@@ -3801,41 +3801,40 @@
|
|||||||
|
|
||||||
selectAllCheckbox?.addEventListener('change', (event) => {
|
selectAllCheckbox?.addEventListener('change', (event) => {
|
||||||
const shouldSelect = Boolean(event.target?.checked);
|
const shouldSelect = Boolean(event.target?.checked);
|
||||||
|
|
||||||
if (hasFolders()) {
|
|
||||||
|
|
||||||
const objectsInCurrentView = allObjects.filter(obj => obj.key.startsWith(currentPrefix));
|
// Get all file items in the current view (works with virtual scrolling)
|
||||||
objectsInCurrentView.forEach(obj => {
|
const filesInView = visibleItems.filter(item => item.type === 'file');
|
||||||
const checkbox = obj.element.querySelector('[data-object-select]');
|
|
||||||
if (checkbox && !checkbox.disabled) {
|
|
||||||
checkbox.checked = shouldSelect;
|
|
||||||
}
|
|
||||||
toggleRowSelection(obj.element, shouldSelect);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll('[data-folder-select]').forEach(cb => {
|
// Update selectedRows directly using object keys (not DOM elements)
|
||||||
cb.checked = shouldSelect;
|
filesInView.forEach(item => {
|
||||||
});
|
if (shouldSelect) {
|
||||||
} else {
|
selectedRows.set(item.data.key, item.data);
|
||||||
|
} else {
|
||||||
|
selectedRows.delete(item.data.key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
document.querySelectorAll('[data-object-row]').forEach((row) => {
|
// Update folder checkboxes in DOM (folders are always rendered)
|
||||||
if (row.style.display === 'none') return;
|
document.querySelectorAll('[data-folder-select]').forEach(cb => {
|
||||||
const checkbox = row.querySelector('[data-object-select]');
|
cb.checked = shouldSelect;
|
||||||
if (!checkbox || checkbox.disabled) {
|
});
|
||||||
return;
|
|
||||||
}
|
// Update any currently rendered object checkboxes
|
||||||
|
document.querySelectorAll('[data-object-row]').forEach((row) => {
|
||||||
|
const checkbox = row.querySelector('[data-object-select]');
|
||||||
|
if (checkbox) {
|
||||||
checkbox.checked = shouldSelect;
|
checkbox.checked = shouldSelect;
|
||||||
toggleRowSelection(row, shouldSelect);
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
updateBulkDeleteState();
|
||||||
setTimeout(updateBulkDownloadState, 0);
|
setTimeout(updateBulkDownloadState, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
bulkDownloadButton?.addEventListener('click', async () => {
|
bulkDownloadButton?.addEventListener('click', async () => {
|
||||||
if (!bulkDownloadEndpoint) return;
|
if (!bulkDownloadEndpoint) return;
|
||||||
const selected = Array.from(document.querySelectorAll('[data-object-select]:checked')).map(
|
// Use selectedRows which tracks all selected objects (not just rendered ones)
|
||||||
(cb) => cb.closest('tr').dataset.key
|
const selected = Array.from(selectedRows.keys());
|
||||||
);
|
|
||||||
if (selected.length === 0) return;
|
if (selected.length === 0) return;
|
||||||
|
|
||||||
bulkDownloadButton.disabled = true;
|
bulkDownloadButton.disabled = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user