fix(wiki): preserve user-owned files when deleting directory-scanned raws (#84)

This commit is contained in:
matevip 2026-05-09 17:15:57 +08:00
parent 932f3de402
commit f7c1af80a4

View File

@ -411,9 +411,13 @@ public class WikiRawMaterialService {
log.warn("[Wiki] Failed to cascade-delete chunks for raw={}: {}", id, e.getMessage()); log.warn("[Wiki] Failed to cascade-delete chunks for raw={}: {}", id, e.getMessage());
} }
// Source file last DB pointer is gone, no other row references this // Source file last. cleanupFile is sandboxed to the upload dir, so:
// path (each upload gets a timestamp-prefixed unique name), so // - uploaded raws (server-managed copy under uploadDir) are removed
// leaving it on disk would just accumulate as the upload tree grows. // each upload has a timestamp-prefixed unique name, no other row
// references it, leaving it would just accumulate disk garbage.
// - directory-scanned raws (sourcePath points at the user's own file
// outside uploadDir) are left untouched the scanner references
// the original in place; the user's file is theirs to keep.
// Failure here is soft-logged and non-blocking operator can run a // Failure here is soft-logged and non-blocking operator can run a
// sweep later if disk usage matters more than the delete RTT. // sweep later if disk usage matters more than the delete RTT.
if (entity != null) { if (entity != null) {
@ -636,16 +640,37 @@ public class WikiRawMaterialService {
} }
/** /**
* Best-effort delete of an upload-tree file. Used both when a fresh * Best-effort delete of a raw material's source file on disk. Used both
* upload turns out to be a duplicate (the new file is redundant) and * when a fresh upload turns out to be a duplicate (the new file is
* when a raw material row is deleted (its source file becomes a * redundant) and when a raw material row is deleted (its source file
* disk orphan with no DB pointer to it). Idempotent silently * becomes a disk orphan with no DB pointer to it).
* succeeds when the path is null or the file is already gone. * <p>
* Sandboxed to {@link WikiProperties#getUploadDir()}: only deletes files
* that live under the configured upload directory i.e. files this
* service is responsible for (uploaded raws + KB pipeline outputs).
* Files outside the upload tree are left alone, because the directory
* scanner imports raws by referencing the user's local file in place
* (no copy); deleting them would wipe the user's original document, not
* just our internal cache. See {@code WikiDirectoryScanService}.
* <p>
* Idempotent silently succeeds when the path is null, the file is
* already gone, or the path is outside the upload sandbox.
*/ */
private void cleanupFile(String path) { private void cleanupFile(String path) {
if (path == null || path.isBlank()) return; if (path == null || path.isBlank()) return;
try { try {
java.nio.file.Files.deleteIfExists(java.nio.file.Paths.get(path)); java.nio.file.Path target = java.nio.file.Paths.get(path).toAbsolutePath().normalize();
java.nio.file.Path uploadRoot = java.nio.file.Paths.get(properties.getUploadDir())
.toAbsolutePath().normalize();
if (!target.startsWith(uploadRoot)) {
// User-owned file (imported by directory scan in place). The DB row is
// gone but the file on the user's disk must stay that's their data,
// not ours.
log.info("[Wiki] Skip file delete (outside upload dir, user-owned): path={} uploadDir={}",
target, uploadRoot);
return;
}
java.nio.file.Files.deleteIfExists(target);
} catch (Exception e) { } catch (Exception e) {
log.warn("[Wiki] Failed to clean up upload file {}: {}", path, e.getMessage()); log.warn("[Wiki] Failed to clean up upload file {}: {}", path, e.getMessage());
} }