From 5fcd5212d58a015a71446f21675d3006cf01af45 Mon Sep 17 00:00:00 2001 From: Accusys Date: Sat, 4 Jul 2026 23:42:42 +0800 Subject: [PATCH] fix: FilesView merge logic and computed property Bug: When regFiles API fails, all files get 'unregistered' status even if they are actually registered. Also, computed property was using reference to files.value instead of a copy, which could cause mutation issues. Fix: - Fetch scan results FIRST (source of truth for files on disk) - Use scan API's is_registered field as fallback status - Only override with regFiles data if file exists in scan results - Computed property now uses [...files.value] to create a copy - Skip files from regFiles that don't exist on disk (deleted) --- portal/src/views/FilesView.vue | 59 +++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/portal/src/views/FilesView.vue b/portal/src/views/FilesView.vue index 710a9dd..1310372 100644 --- a/portal/src/views/FilesView.vue +++ b/portal/src/views/FilesView.vue @@ -220,7 +220,8 @@ function getMediaType(fileName: string): 'video' | 'photo' { } const displayFiles = computed(() => { - let result = files.value + // Start with a copy to avoid mutation issues + let result = [...files.value] // Filter by media type if (mediaType.value !== 'all') { @@ -263,7 +264,20 @@ async function fetchFiles() { try { const config = getCurrentConfig() - // Get registered files with real processing status FIRST + // Get scan results FIRST (source of truth for files on disk) + const scanResp = await httpFetch(`${config.api_base_url}/api/v1/files/scan`) + const scanFiles: any[] = (scanResp?.files || []).map((f: any) => { + const mediaType = getMediaType(f.file_name) + return { + ...f, + media_type: mediaType, + is_indexed: false, + // Use scan API's is_registered field as default status + status: f.is_registered ? 'pending' : 'unregistered' + } + }) + + // Get registered files with real processing status let regFiles: any[] = [] try { const regResp = await httpFetch(`${config.api_base_url}/api/v1/files?page=1&page_size=200`) @@ -277,38 +291,37 @@ async function fetchFiles() { } }) } catch { - // Registered files API may not be available + // Registered files API may not be available; scan data will be used as fallback + console.warn('Failed to fetch registered files, using scan data as fallback') } - // Get scan results - const scanResp = await httpFetch(`${config.api_base_url}/api/v1/files/scan`) - const scanFiles: any[] = (scanResp?.files || []).map((f: any) => { - const mediaType = getMediaType(f.file_name) - return { - ...f, - media_type: mediaType, - is_indexed: false - } - }) - - // Build a set of registered file paths for quick lookup - const registeredPaths = new Set(regFiles.map(f => f.file_path)) + // Build a map of registered files by file_path for quick lookup + const regMap = new Map() + for (const f of regFiles) { + regMap.set(f.file_path, f) + } // Merge: start with scan results, override with registered data where available const merged = new Map() // First pass: add all scan results for (const f of scanFiles) { - const isRegistered = registeredPaths.has(f.file_path) - merged.set(f.file_path, { - ...f, - status: isRegistered ? 'pending' : 'unregistered' - }) + merged.set(f.file_path, { ...f }) } - // Second pass: override with real registered data + // Second pass: override with real registered data (only for files that exist in scan) for (const f of regFiles) { - merged.set(f.file_path, f) + if (merged.has(f.file_path)) { + // File exists on disk, update with real status + const existing = merged.get(f.file_path) + merged.set(f.file_path, { + ...existing, + ...f, + media_type: f.media_type || existing.media_type, + is_indexed: f.is_indexed || existing.is_indexed + }) + } + // If file not in scan, it might have been deleted from disk - skip it } files.value = Array.from(merged.values())