diff --git a/Tasks.md b/Tasks.md
index 9a4b56b..1122da5 100644
--- a/Tasks.md
+++ b/Tasks.md
@@ -75,7 +75,7 @@ This document defines the complete project roadmap and task tracking system for
| [NUT-004](#nut-004) | Implement OCS Share Link Generation | Fixed | Feature |
| [NUT-005](#nut-005) | Implement Direct Download URL Builder | Fixed | Feature |
| [NUT-006](#nut-006) | Implement Credential Storage System | Fixed | Feature |
-| [NUT-007](#nut-007) | Implement Multi-Account Support (Backend) | Triage | Feature |
+| [NUT-007](#nut-007) | Implement Multi-Account Support (Backend) | Fixed | Feature |
| [NUT-008](#nut-008) | Implement CLI Frontend | Triage | Feature |
| [NUT-009](#nut-009) | Implement CLI Output Formatting Options | Triage | Feature |
| [NUT-010](#nut-010) | Implement CLI Multi-file Upload Support | Triage | Feature |
@@ -213,19 +213,19 @@ Implement secure credential storage using OS keychain when available, falling ba
- NUT-002
-
+
### Implement Multi-Account Support (Backend)
**ID:** NUT-007
-**Status:** Triage
+**Status:** Fixed
**Type:** Feature
**Description:**
Support multiple Nextcloud accounts in the backend credential system.
**Requirements:**
-- [ ] Add account list structure
-- [ ] Add default account selection
-- [ ] Add account switching API
+- [x] Add account list structure
+- [x] Add default account selection
+- [x] Add account switching API
**Dependencies:**
- NUT-006
diff --git a/nextcloud_client/src/credentials.rs b/nextcloud_client/src/credentials.rs
index 4626590..36eaf0a 100644
--- a/nextcloud_client/src/credentials.rs
+++ b/nextcloud_client/src/credentials.rs
@@ -2,8 +2,10 @@ use keyring::Entry;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
+use url::Url;
-use crate::config::AccountCredentials;
+use crate::client::NextcloudClient;
+use crate::config::{AccountCredentials, ClientConfig};
use crate::error::{NextcloudError, Result};
/// Service name identifier for OS Keychain / Keyring storage.
@@ -12,6 +14,13 @@ pub const KEYRING_SERVICE_NAME: &str = "me.majinnaibu.nut";
/// Metadata record of a configured Nextcloud account.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StoredAccount {
+ /// Unique identifier for this account (e.g. `"tom@cloud.example.com"`).
+ pub id: String,
+
+ /// Optional user-friendly label/alias (e.g. `"Personal"`, `"Work"`).
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub label: Option,
+
/// Username for this account on Nextcloud.
pub username: String,
@@ -27,8 +36,43 @@ pub struct StoredAccount {
pub fallback_password: Option,
}
-/// Manages account credentials using system keychain (macOS Keychain, Windows Credential Manager,
-/// Linux Secret Service) with fallback file persistence.
+impl StoredAccount {
+ /// Create a new `StoredAccount` with a generated canonical ID (`username@host`).
+ pub fn new(username: impl Into, server_url: impl Into) -> Self {
+ let username = username.into();
+ let server_url = server_url.into();
+
+ let host = Url::parse(&server_url)
+ .ok()
+ .and_then(|u| u.host_str().map(|h| h.to_string()))
+ .unwrap_or_else(|| "unknown-host".to_string());
+
+ let id = format!("{username}@{host}");
+
+ Self {
+ id,
+ label: None,
+ username,
+ server_url,
+ is_default: false,
+ fallback_password: None,
+ }
+ }
+
+ /// Set a friendly label for the account.
+ pub fn with_label(mut self, label: impl Into) -> Self {
+ self.label = Some(label.into());
+ self
+ }
+
+ /// Keyring account key identifier for secure storage.
+ pub fn keyring_key(&self) -> &str {
+ &self.id
+ }
+}
+
+/// Manages multiple Nextcloud account credentials using system keychain (macOS Keychain,
+/// Windows Credential Manager, Linux Secret Service) with fallback file persistence.
#[derive(Debug, Clone, Default)]
pub struct CredentialStore;
@@ -73,26 +117,45 @@ impl CredentialStore {
Ok(())
}
- /// Retrieve full credentials (including secret app password) for a specific username.
- pub fn get_credentials(username: &str) -> Result