aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src-tauri/src/core/account_storage.rs
diff options
context:
space:
mode:
authorHsiangNianian <i@jyunko.cn>2026-01-13 20:19:34 +0800
committerHsiangNianian <i@jyunko.cn>2026-01-13 20:19:34 +0800
commit48d9d886c078a04ead31a9d10744a085307444fa (patch)
tree9d94019ec67f80a01c9587d330f287c2d05702f0 /src-tauri/src/core/account_storage.rs
parent5e09625902ad0fa1b1eb555a255a3720193dbb2c (diff)
downloadDropOut-48d9d886c078a04ead31a9d10744a085307444fa.tar.gz
DropOut-48d9d886c078a04ead31a9d10744a085307444fa.zip
feat: implement Microsoft account token refresh and storage management; add Java detection functionality
Diffstat (limited to 'src-tauri/src/core/account_storage.rs')
-rw-r--r--src-tauri/src/core/account_storage.rs159
1 files changed, 159 insertions, 0 deletions
diff --git a/src-tauri/src/core/account_storage.rs b/src-tauri/src/core/account_storage.rs
new file mode 100644
index 0000000..b8e15e1
--- /dev/null
+++ b/src-tauri/src/core/account_storage.rs
@@ -0,0 +1,159 @@
+use crate::core::auth::{Account, MicrosoftAccount, OfflineAccount};
+use serde::{Deserialize, Serialize};
+use std::fs;
+use std::path::PathBuf;
+
+/// Stored account data for persistence
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct AccountStore {
+ pub accounts: Vec<StoredAccount>,
+ pub active_account_id: Option<String>,
+}
+
+impl Default for AccountStore {
+ fn default() -> Self {
+ Self {
+ accounts: Vec::new(),
+ active_account_id: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(tag = "type")]
+pub enum StoredAccount {
+ Offline(OfflineAccount),
+ Microsoft(StoredMicrosoftAccount),
+}
+
+/// Microsoft account with refresh token for persistence
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct StoredMicrosoftAccount {
+ pub username: String,
+ pub uuid: String,
+ pub access_token: String,
+ pub refresh_token: Option<String>,
+ pub ms_refresh_token: Option<String>, // Microsoft OAuth refresh token
+ pub expires_at: i64,
+}
+
+impl StoredAccount {
+ pub fn id(&self) -> String {
+ match self {
+ StoredAccount::Offline(a) => a.uuid.clone(),
+ StoredAccount::Microsoft(a) => a.uuid.clone(),
+ }
+ }
+
+ pub fn to_account(&self) -> Account {
+ match self {
+ StoredAccount::Offline(a) => Account::Offline(a.clone()),
+ StoredAccount::Microsoft(a) => Account::Microsoft(MicrosoftAccount {
+ username: a.username.clone(),
+ uuid: a.uuid.clone(),
+ access_token: a.access_token.clone(),
+ refresh_token: a.refresh_token.clone(),
+ expires_at: a.expires_at,
+ }),
+ }
+ }
+
+ pub fn from_account(account: &Account, ms_refresh_token: Option<String>) -> Self {
+ match account {
+ Account::Offline(a) => StoredAccount::Offline(a.clone()),
+ Account::Microsoft(a) => StoredAccount::Microsoft(StoredMicrosoftAccount {
+ username: a.username.clone(),
+ uuid: a.uuid.clone(),
+ access_token: a.access_token.clone(),
+ refresh_token: a.refresh_token.clone(),
+ ms_refresh_token,
+ expires_at: a.expires_at,
+ }),
+ }
+ }
+}
+
+pub struct AccountStorage {
+ file_path: PathBuf,
+}
+
+impl AccountStorage {
+ pub fn new(app_data_dir: PathBuf) -> Self {
+ Self {
+ file_path: app_data_dir.join("accounts.json"),
+ }
+ }
+
+ pub fn load(&self) -> AccountStore {
+ if self.file_path.exists() {
+ let content = fs::read_to_string(&self.file_path).unwrap_or_default();
+ serde_json::from_str(&content).unwrap_or_default()
+ } else {
+ AccountStore::default()
+ }
+ }
+
+ pub fn save(&self, store: &AccountStore) -> Result<(), String> {
+ let content = serde_json::to_string_pretty(store).map_err(|e| e.to_string())?;
+ if let Some(parent) = self.file_path.parent() {
+ fs::create_dir_all(parent).map_err(|e| e.to_string())?;
+ }
+ fs::write(&self.file_path, content).map_err(|e| e.to_string())?;
+ Ok(())
+ }
+
+ pub fn add_or_update_account(
+ &self,
+ account: &Account,
+ ms_refresh_token: Option<String>,
+ ) -> Result<(), String> {
+ let mut store = self.load();
+ let stored = StoredAccount::from_account(account, ms_refresh_token);
+ let id = stored.id();
+
+ // Remove existing account with same ID
+ store.accounts.retain(|a| a.id() != id);
+ store.accounts.push(stored);
+ store.active_account_id = Some(id);
+
+ self.save(&store)
+ }
+
+ pub fn remove_account(&self, uuid: &str) -> Result<(), String> {
+ let mut store = self.load();
+ store.accounts.retain(|a| a.id() != uuid);
+ if store.active_account_id.as_deref() == Some(uuid) {
+ store.active_account_id = store.accounts.first().map(|a| a.id());
+ }
+ self.save(&store)
+ }
+
+ pub fn get_active_account(&self) -> Option<(StoredAccount, Option<String>)> {
+ let store = self.load();
+ if let Some(active_id) = &store.active_account_id {
+ store.accounts.iter().find(|a| &a.id() == active_id).map(|a| {
+ let ms_token = match a {
+ StoredAccount::Microsoft(m) => m.ms_refresh_token.clone(),
+ _ => None,
+ };
+ (a.clone(), ms_token)
+ })
+ } else {
+ None
+ }
+ }
+
+ pub fn set_active_account(&self, uuid: &str) -> Result<(), String> {
+ let mut store = self.load();
+ if store.accounts.iter().any(|a| a.id() == uuid) {
+ store.active_account_id = Some(uuid.to_string());
+ self.save(&store)
+ } else {
+ Err("Account not found".to_string())
+ }
+ }
+
+ pub fn get_all_accounts(&self) -> Vec<StoredAccount> {
+ self.load().accounts
+ }
+}