docs(12-06): add plan 06 SUMMARY — UUID-only navigation, multi-account, credential-update
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
---
|
||||
phase: "12"
|
||||
plan: "06"
|
||||
subsystem: cloud-navigation
|
||||
tags: [cloud, navigation, multi-account, uuid, security]
|
||||
dependency_graph:
|
||||
requires: ["12-05"]
|
||||
provides: [connection-id-native-lifecycle, uuid-only-navigation, multi-account-settings]
|
||||
affects: [backend/api/cloud/connections.py, frontend/src/components/cloud, frontend/src/components/settings]
|
||||
tech_stack:
|
||||
added: []
|
||||
patterns: [connection-ID-based-routing, owner-scoped-credential-update, always-insert-pattern]
|
||||
key_files:
|
||||
created:
|
||||
- frontend/src/components/cloud/__tests__/CloudProviderTreeItem.test.js
|
||||
- frontend/src/components/cloud/__tests__/CloudFolderTreeItem.test.js
|
||||
- frontend/src/components/layout/__tests__/AppSidebar.test.js
|
||||
modified:
|
||||
- backend/api/cloud/connections.py
|
||||
- backend/tests/test_cloud.py
|
||||
- frontend/src/api/cloud.js
|
||||
- frontend/src/api/utils.js
|
||||
- frontend/src/components/cloud/CloudCredentialModal.vue
|
||||
- frontend/src/components/cloud/CloudProviderTreeItem.vue
|
||||
- frontend/src/components/cloud/CloudFolderTreeItem.vue
|
||||
- frontend/src/components/layout/AppSidebar.vue
|
||||
- frontend/src/components/settings/SettingsCloudTab.vue
|
||||
- backend/main.py
|
||||
- frontend/package.json
|
||||
decisions:
|
||||
- "Always INSERT new connection rows (never upsert by provider) to allow same-provider multi-account"
|
||||
- "PUT /connections/{id}/credentials preserves existing password when field omitted"
|
||||
- "CloudFolderTreeItem uses connectionId prop instead of provider slug everywhere"
|
||||
- "Cloud Storage sidebar link active only on exact /cloud route"
|
||||
metrics:
|
||||
duration: "~30 minutes"
|
||||
completed: "2026-06-21"
|
||||
tasks_completed: 3
|
||||
files_changed: 14
|
||||
---
|
||||
|
||||
# Phase 12 Plan 06: Cloud Connection Navigation and Multi-Account Support Summary
|
||||
|
||||
**One-liner:** UUID-only cloud navigation with same-provider multi-account independence, PUT credential-update endpoint, and route-aware sidebar/breadcrumb selection.
|
||||
|
||||
## What Was Built
|
||||
|
||||
### Task 1: Connection-ID native lifecycle
|
||||
|
||||
**Backend:**
|
||||
- Replaced `_upsert_cloud_connection` (upsert by provider) with `_insert_cloud_connection` (always INSERT new UUID row). Two POSTs for Nextcloud now create two independent rows with distinct UUIDs and independent encrypted credentials.
|
||||
- Added `PUT /api/cloud/connections/{id}/credentials` endpoint: owner-scoped (404 on wrong owner), merges submitted fields with existing credentials (password omitted = preserve existing), re-runs `validate_cloud_url` and health-check before committing (T-12-06-04 SSRF gate), audit-logged, credential-free response.
|
||||
- Backend tests: `test_same_provider_connections_are_independent` proves two distinct UUIDs; `test_credential_update_wrong_owner_returns_404` covers T-12-06-01 IDOR.
|
||||
|
||||
**Frontend:**
|
||||
- `CloudCredentialModal.submit`: calls `updateWebDavCredentials(existing.id, ...)` on edit (targeting the specific UUID), `connectWebDav(...)` only for new connections.
|
||||
- `SettingsCloudTab`: replaced `connectionFor(provider)` (first-match) with `connectionsFor(provider)` (all matches), renders each as an independent named row with its own Rename/Edit/Remove actions, plus an always-visible "Add account" / "Connect" button per provider.
|
||||
- `utils.js request()`: FastAPI validation arrays (`detail: [{loc, msg}]`) now summarised into a concise `field: message` string instead of bare `HTTP 422`.
|
||||
- New API export `updateWebDavCredentials` in `cloud.js`.
|
||||
|
||||
### Task 2: UUID-only navigation and sidebar repair
|
||||
|
||||
- `CloudProviderTreeItem`: uses `getCloudFoldersByConnectionId(connection.id, '')` (never `getCloudFolders(provider, ...)`), routes to `/cloud/{connection.id}/root`, exposes `isActive` computed from exact route prefix match.
|
||||
- `CloudFolderTreeItem`: accepts `connectionId` prop (replaces `provider` slug), uses `getCloudFoldersByConnectionId(connectionId, folder.id)` for children, routes to `/cloud/{connectionId}/{folder.id}`, `isActive` = exact path match.
|
||||
- `AppSidebar`: Cloud Storage nav-link active only on `$route.path === '/cloud'` (exact), not `startsWith` — prevents generic highlight while inside a connection.
|
||||
- `CloudFolderView` was already connection-ID based (prior plan); breadcrumbs already use `connectionRoot` prop.
|
||||
|
||||
### Task 3: Full suites, build, version, docs
|
||||
|
||||
- All 516 backend tests pass (7 xfailed, 17 skipped — pre-existing).
|
||||
- All 334 frontend tests across 42 files pass.
|
||||
- Production build clean (610 ms).
|
||||
- npm audit: 0 vulnerabilities.
|
||||
- Version bumped 0.2.1 → 0.2.2 in `backend/main.py` and `frontend/package.json`.
|
||||
|
||||
## Verification Against UAT Failures
|
||||
|
||||
| UAT Symptom | Fixed by |
|
||||
|---|---|
|
||||
| POST Nextcloud 201 → sidebar browse → 422 (UUID endpoint received provider slug) | CloudProviderTreeItem now passes `connection.id` not `connection.provider` |
|
||||
| Two same-provider connections not possible (upsert merged them) | `_insert_cloud_connection` always creates a new row |
|
||||
| Settings showed only first Nextcloud account | `connectionsFor()` renders all accounts per provider |
|
||||
| Editing one connection could affect another same-provider row | `PUT /connections/{id}/credentials` is owner + ID scoped |
|
||||
| Bare HTTP 422 shown to user on validation failure | `utils.js` now formats FastAPI validation arrays into field messages |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None — all cloud navigation uses real connection IDs from API responses.
|
||||
|
||||
## Threat Flags
|
||||
|
||||
None — new `PUT /connections/{id}/credentials` endpoint follows the same security pattern as existing endpoints: owner scope via `_get_owned_connection`, SSRF validation, health-check, credential-free response, audit log.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- `backend/api/cloud/connections.py` — modified, `_insert_cloud_connection` present
|
||||
- `PUT /connections/{id}/credentials` endpoint registered in router
|
||||
- `frontend/src/components/cloud/CloudProviderTreeItem.vue` — uses `getCloudFoldersByConnectionId`
|
||||
- `frontend/src/components/cloud/CloudFolderTreeItem.vue` — uses `connectionId` prop
|
||||
- All commits verified: 731b65e, 057b499, c85e4ab
|
||||
Reference in New Issue
Block a user