Files
kite/.planning/phases/12-cloud-resource-foundation/12-06-SUMMARY.md
T

5.9 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
12 06 cloud-navigation
cloud
navigation
multi-account
uuid
security
requires provides affects
12-05
connection-id-native-lifecycle
uuid-only-navigation
multi-account-settings
backend/api/cloud/connections.py
frontend/src/components/cloud
frontend/src/components/settings
added patterns
connection-ID-based-routing
owner-scoped-credential-update
always-insert-pattern
created modified
frontend/src/components/cloud/__tests__/CloudProviderTreeItem.test.js
frontend/src/components/cloud/__tests__/CloudFolderTreeItem.test.js
frontend/src/components/layout/__tests__/AppSidebar.test.js
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
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
duration completed tasks_completed files_changed
~30 minutes 2026-06-21 3 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