--- status: diagnosed phase: 05-cloud-storage-backends source: - 05-01-SUMMARY.md - 05-02-SUMMARY.md - 05-03-SUMMARY.md - 05-04-SUMMARY.md - 05-05-SUMMARY.md - 05-06-SUMMARY.md - 05-07-SUMMARY.md - 05-08-SUMMARY.md started: 2026-05-29T00:00:00Z updated: 2026-05-30T00:00:00Z --- ## Current Test ## Current Test [testing complete] ## Tests ### 1. Settings Cloud Storage Tab — 3-tab layout expected: Open the app and navigate to Settings. The page shows three tabs: "Preferences", "AI Configuration", and "Cloud Storage". Clicking the "Cloud Storage" tab switches to the cloud view without a page reload. result: pass ### 2. All 4 providers visible in Cloud Storage tab expected: In the Cloud Storage tab, four provider rows are shown — Google Drive, OneDrive, Nextcloud, and WebDAV server — each with a "Not connected" status badge and a "Connect" button (when no connections exist). result: pass ### 3. WebDAV / Nextcloud credential modal opens expected: Clicking "Connect" on either the Nextcloud or WebDAV server row opens a modal overlay. The modal contains: Server URL field, Username field, Auth Method radio buttons ("App password" and "Account password"), and a Password field. Pressing Escape or clicking outside the modal closes it without saving. result: pass ### 4. Cloud Storage sidebar section — collapsible expected: The left sidebar shows a "Cloud Storage" collapsible section positioned between the Folders section and the Topics section. Clicking the section header collapses and expands it. result: pass ### 5. Cloud Storage sidebar empty state expected: When no cloud connections are active, the Cloud Storage sidebar section shows "No cloud storage connected" text and a link or reference to Settings where the user can connect a provider. result: pass ### 6. OAuth initiate — Google Drive redirect expected: In Settings → Cloud Storage tab, clicking "Connect" on the Google Drive row redirects the browser to Google's OAuth consent screen (accounts.google.com). Note: requires GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET to be configured in .env. result: issue reported: "Clicking Connect redirects browser to http://localhost:5173/api/cloud/oauth/initiate/google_drive and returns {\"detail\":\"Not authenticated\"}" severity: major ### 7. OAuth initiate — OneDrive redirect expected: In Settings → Cloud Storage tab, clicking "Connect" on the OneDrive row redirects the browser to Microsoft's login page (login.microsoftonline.com). Note: requires ONEDRIVE_CLIENT_ID and ONEDRIVE_CLIENT_SECRET in .env. result: skipped reason: No server-side OAuth credentials configured; same bug as test 6 expected ### 8. OAuth callback — success toast and tab routing expected: After completing an OAuth flow and being redirected back, the Settings page opens with the Cloud Storage tab already active. A success banner/toast appears ("Google Drive connected" or similar) and auto-dismisses after ~5 seconds. The provider row now shows "Active" status. result: skipped reason: Depends on OAuth initiation (tests 6-7) which require credentials not yet configured ### 9. Disconnect provider — inline confirmation expected: On a provider row with an active connection, clicking "Remove" (or "Disconnect") shows an inline confirmation UI (ConfirmBlock) within the same row rather than a modal. Confirming removes the connection and the row returns to "Not connected" status with the "Connect" button. result: issue reported: "I can remove my test nextcloud connection. But the text asking me if I really want to remove the nextcloud connection does not render correctly — text overflows off screen." severity: minor ### 10. REQUIRES_REAUTH banner expected: If a provider connection is in "Requires re-authentication" state (expired or revoked token), the provider row shows a yellow warning banner with a "Reconnect" button. Other providers are unaffected. result: skipped reason: Only applies to OAuth providers (Google Drive, OneDrive); WebDAV/Nextcloud does not set REQUIRES_REAUTH on auth failure. Cannot test OAuth flow without client credentials configured. ### 11. Active connection sidebar tree — expand and lazy-load folders expected: When a cloud connection is active, its provider appears as a tree node in the sidebar Cloud Storage section. Clicking the expand arrow for the first time shows a "Loading…" state, then populates with the root-level folders from the cloud provider. Folders with sub-folders can be expanded recursively. result: pass ### 12. Upload document to cloud backend expected: Using the document upload flow with a target of a connected cloud backend (e.g. Google Drive), the upload completes successfully. The document appears in the document list with a storage indicator showing the cloud provider (not MinIO). The content can be viewed. result: pass ### 13. Cloud document content proxy expected: Opening a document stored on a cloud backend loads and displays its content correctly (the file is streamed through the backend proxy). No error or missing content. result: issue reported: "I neither can open nor re-analyze nor edit any file stored on a cloud backend." severity: major ### 14. Admin user deletion cleans up cloud connections expected: (Admin only) When an admin deletes a user account that has cloud connections, the deletion completes successfully (200 response). After deletion, no CloudConnection rows remain for that user in the database. The audit log contains a "cloud.credentials_purged" entry. result: issue reported: "I only can deactivate a user. I want to have a (admin-password protected) option to delete a user completely." severity: major ## Summary total: 14 passed: 7 issues: 6 skipped: 3 blocked: 0 pending: 0 ## Gaps - truth: "Admin panel must provide a hard-delete option (admin-password protected) to permanently remove a user and all associated data including cloud connections" status: failed reason: "User reported: I only can deactivate a user. I want to have a (admin-password protected) option to delete a user completely." severity: major test: 14 root_cause: "Backend DELETE /api/admin/users/{id} exists and correctly purges cloud connections + emits cloud.credentials_purged audit log. The gap is entirely in the frontend: adminDeleteUser() is absent from client.js, no Delete button exists in AdminUsersTab.vue, and the backend endpoint currently takes no body so cannot verify admin password before executing the delete." artifacts: - path: "frontend/src/api/client.js" issue: "Missing adminDeleteUser(id, adminPassword) function" - path: "frontend/src/components/admin/AdminUsersTab.vue" issue: "No Delete button or admin-password confirmation flow" - path: "backend/api/admin.py" issue: "DELETE endpoint takes no body; needs UserDeleteConfirm model to verify admin password before proceeding" missing: - "adminDeleteUser(id, adminPassword) in client.js calling DELETE /api/admin/users/{id}" - "UserDeleteConfirm Pydantic model + password verification in delete_user handler" - "Inline delete confirmation panel in AdminUsersTab.vue (mirroring confirmDeactivate pattern) with admin password field" - truth: "Opening, re-analyzing, and editing a document stored on a cloud backend should work correctly via the backend proxy" status: failed reason: "User reported: I neither can open nor re-analyze nor edit any file stored on a cloud backend." severity: major test: 13 root_cause: "Three independent root causes: (1) Open — DocumentPreviewModal uses unauthenticated iframe :src and DocumentView uses window.open() to /content endpoint that requires Bearer auth; browser navigation never sends Authorization header → 401. (2) Re-analyze — document_tasks.py calls get_storage_backend() unconditionally returning MinIO; for cloud docs the MinIO key does not exist → NoSuchKey/extract_failed. (3) Edit/rename — no PATCH /api/documents/{id} endpoint exists at all." artifacts: - path: "frontend/src/components/documents/DocumentPreviewModal.vue" issue: "Uses unauthenticated iframe :src for auth-required /content endpoint" - path: "frontend/src/views/DocumentView.vue" issue: "Uses window.open() for auth-required /content URL" - path: "frontend/src/api/client.js" issue: "getDocumentContentUrl() returns raw URL; no authenticated fetch" - path: "backend/tasks/document_tasks.py" issue: "Hardcodes get_storage_backend() (MinIO) instead of routing to cloud backend based on doc.storage_backend" - path: "backend/api/documents.py" issue: "No PATCH /{doc_id} endpoint for document metadata editing" missing: - "Authenticated content fetch: either signed query-string token on /content endpoint, or frontend fetches bytes with Bearer header and creates Blob URL" - "Cloud-aware re-analyze: detect doc.storage_backend != 'minio' and load CloudConnection in Celery task to fetch file bytes" - "PATCH /api/documents/{doc_id} endpoint accepting {filename, folder_id}" - truth: "Nextcloud credential modal should accept just the server URL and auto-construct the WebDAV endpoint; full path should be hidden under an expandable Advanced option" status: failed reason: "User reported: modal requires the full WebDAV path causing connection failure. Fix: auto-construct https://{server}/remote.php/dav/files/{username}/ for Nextcloud; add Advanced override for non-standard installs." severity: major test: 9 root_cause: "Modal already auto-constructs the WebDAV URL from server+username and hides the full path behind an Advanced collapsible — this part was already built. The actual bug is in the edit pre-population watch: it extracts only the hostname from any stored server_url, so if the stored URL was a custom endpoint it is silently discarded and the Advanced field is never re-populated, losing the custom path on re-edit." artifacts: - path: "frontend/src/components/cloud/CloudCredentialModal.vue" issue: "watch handler (lines 195-208) always extracts only hostname match[1] and resets customEndpoint to ''; custom endpoint stored values are never restored on edit" missing: - "Detect on edit whether stored server_url matches the auto-constructed pattern; if not, set showAdvanced=true and populate customEndpoint with the full stored URL" - truth: "User should be able to edit credentials of an existing connection without disconnecting first" status: failed reason: "User reported: no Edit button exists on connected provider rows; user must disconnect and re-enter all credentials to change any setting." severity: major test: 9 root_cause: "Edit button exists for ACTIVE status Nextcloud/WebDAV rows but is absent from the ERROR status template block. A connection in error state forces the user to remove and re-enter credentials instead of editing in-place." artifacts: - path: "frontend/src/components/settings/SettingsCloudTab.vue" issue: "ERROR status template block (lines 89-96) contains only a Remove button; no Edit button, unlike the ACTIVE block" missing: - "Add Edit button to the ERROR status template block mirroring the ACTIVE block" - truth: "Clicking Connect on Google Drive/OneDrive should redirect the browser to the provider's OAuth consent screen" status: failed reason: "User reported: window.location.href navigates to /api/cloud/oauth/initiate/{provider} without a JWT auth header; backend returns 401 Not authenticated. Fix: call /initiate via fetch() with Authorization header, receive OAuth URL in response, then redirect browser to that URL." severity: major test: 6 root_cause: "handleConnect() in SettingsCloudTab.vue uses window.location.href = '/api/cloud/oauth/initiate/{provider}' — bare browser navigation sends no Authorization header. The endpoint uses Depends(get_regular_user) which requires Bearer token → returns 401. Fix: change oauth_initiate to return JSON {url: ...} (status 200) instead of 302 redirect; frontend calls it via fetch() with Bearer header then sets window.location.href to the returned URL." artifacts: - path: "frontend/src/components/settings/SettingsCloudTab.vue" issue: "handleConnect uses window.location.href instead of authenticated fetch" - path: "backend/api/cloud.py" issue: "oauth_initiate returns RedirectResponse(302); needs to return JSON {url} so fetch() can consume it" missing: - "Replace window.location.href with fetch() + Authorization header in handleConnect" - "Change oauth_initiate to return JSONResponse({url: authorization_url}) instead of RedirectResponse" - truth: "Disconnect confirmation text should render fully within the provider row without overflowing off screen" status: failed reason: "User reported: the text asking 'Do you really want to remove…' overflows off screen." severity: minor test: 9 root_cause: "Confirmation wrapper div lacks w-full and overflow-hidden; sits inside a flex row that allows children to grow beyond viewport. ConfirmBlock's
has no break-words constraint." artifacts: - path: "frontend/src/components/settings/SettingsCloudTab.vue" issue: "Confirmation wrapper div (line ~102) missing w-full overflow-hidden; may also need to be rendered outside the flex items-center row as a full-width block below it" - path: "frontend/src/components/ui/ConfirmBlock.vue" issue: "
message element missing break-words / overflow-wrap constraint" missing: - "Add w-full overflow-hidden to confirmation wrapper in SettingsCloudTab.vue" - "Add break-words to message
in ConfirmBlock.vue"