diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-01-02 02:22:01 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-01-02 02:22:01 +0800 |
| commit | f6a918848b499b9ec6fab8124d714d64af8afae2 (patch) | |
| tree | e7dab039d511d5ec17310af916fa941da4a9374f /crates/vcs_actions/src | |
| parent | 8644ba2ea292ef2aa3d49976f4f3916b7ecde938 (diff) | |
Add host mode authentication and reference sheet handling
- Return host mode status from auth_member to determine admin privileges
- Add reference sheet detection to get_current_sheet_name with allow_ref
parameter
- Prevent modifications to reference sheets unless in host mode
- Use VAULT_HOST_NAME as sheet holder for host mode operations
- Add share/merge share action registrations
Diffstat (limited to 'crates/vcs_actions/src')
| -rw-r--r-- | crates/vcs_actions/src/actions.rs | 122 | ||||
| -rw-r--r-- | crates/vcs_actions/src/actions/local_actions.rs | 90 | ||||
| -rw-r--r-- | crates/vcs_actions/src/actions/sheet_actions.rs | 258 | ||||
| -rw-r--r-- | crates/vcs_actions/src/actions/track_action.rs | 180 | ||||
| -rw-r--r-- | crates/vcs_actions/src/actions/user_actions.rs | 9 | ||||
| -rw-r--r-- | crates/vcs_actions/src/actions/vault_actions.rs | 1 | ||||
| -rw-r--r-- | crates/vcs_actions/src/registry/client_registry.rs | 5 | ||||
| -rw-r--r-- | crates/vcs_actions/src/registry/server_registry.rs | 5 |
8 files changed, 518 insertions, 152 deletions
diff --git a/crates/vcs_actions/src/actions.rs b/crates/vcs_actions/src/actions.rs index 260a6be..50281c9 100644 --- a/crates/vcs_actions/src/actions.rs +++ b/crates/vcs_actions/src/actions.rs @@ -5,9 +5,9 @@ use cfg_file::config::ConfigFile; use tcp_connection::{error::TcpTargetError, instance::ConnectionInstance}; use tokio::sync::{Mutex, mpsc::Sender}; use vcs_data::{ - constants::SERVER_PATH_MEMBER_PUB, + constants::{SERVER_PATH_MEMBER_PUB, VAULT_HOST_NAME}, data::{ - local::{LocalWorkspace, config::LocalConfig}, + local::{LocalWorkspace, config::LocalConfig, latest_info::LatestInfo}, member::MemberId, sheet::SheetName, user::UserDirectory, @@ -78,13 +78,25 @@ pub fn try_get_local_output(ctx: &ActionContext) -> Result<Arc<Sender<String>>, pub async fn auth_member( ctx: &ActionContext, instance: &Arc<Mutex<ConnectionInstance>>, -) -> Result<MemberId, TcpTargetError> { +) -> Result<(MemberId, bool), TcpTargetError> { + // Window开服Linux连接 -> 此函数内产生 early eof + // ~ WS # jv update + // 身份认证失败:I/O error: early eof! + + // 分析相应流程: + // 1. 服务端发起挑战,客户端接受 + // 2. 服务端发送结果,客户端接受 + // 3. 推测此时发生 early eof ---> 无 ack,导致客户端尝试拿到结果时,服务端已经结束 + // 这很有可能是 Windows 和 Linux 对于连接处理的方案差异导致的问题,需要进一步排查 + // Start Challenge (Remote) if ctx.is_proc_on_remote() { + let mut mut_instance = instance.lock().await; let vault = try_get_vault(ctx)?; - let result = instance - .lock() - .await + + let using_host_mode = mut_instance.read_msgpack::<bool>().await?; + + let result = mut_instance .challenge(vault.vault_path().join(SERVER_PATH_MEMBER_PUB)) .await; @@ -92,14 +104,28 @@ pub async fn auth_member( Ok((pass, member_id)) => { if !pass { // Send false to inform the client that authentication failed - instance.lock().await.write(false).await?; + mut_instance.write(false).await?; Err(TcpTargetError::Authentication( "Authenticate failed.".to_string(), )) } else { - // Send true to inform the client that authentication was successful - instance.lock().await.write(true).await?; - Ok(member_id) + if using_host_mode { + if vault.config().vault_admin_list().contains(&member_id) { + // Using Host mode authentication, and is indeed an administrator + mut_instance.write(true).await?; + Ok((member_id, true)) + } else { + // Using Host mode authentication, but not an administrator + mut_instance.write(false).await?; + Err(TcpTargetError::Authentication( + "Authenticate failed.".to_string(), + )) + } + } else { + // Not using Host mode authentication + mut_instance.write(true).await?; + Ok((member_id, false)) + } } } Err(e) => Err(e), @@ -108,22 +134,27 @@ pub async fn auth_member( // Accept Challenge (Local) if ctx.is_proc_on_local() { + let mut mut_instance = instance.lock().await; let local_workspace = try_get_local_workspace(ctx)?; + let (is_host_mode, member_name) = { + let cfg = local_workspace.config().lock_owned().await; + (cfg.is_host_mode(), cfg.current_account()) + }; let user_directory = try_get_user_directory(ctx)?; + // Inform remote whether to authenticate in Host mode + mut_instance.write_msgpack(is_host_mode).await?; + // Member name & Private key - let member_name = local_workspace.config().lock().await.current_account(); let private_key = user_directory.account_private_key_path(&member_name); - let _ = instance - .lock() - .await + let _ = mut_instance .accept_challenge(private_key, &member_name) .await?; // Read result - let challenge_result = instance.lock().await.read::<bool>().await?; + let challenge_result = mut_instance.read::<bool>().await?; if challenge_result { - return Ok(member_name.clone()); + return Ok((member_name.clone(), is_host_mode)); } else { return Err(TcpTargetError::Authentication( "Authenticate failed.".to_string(), @@ -136,34 +167,57 @@ pub async fn auth_member( /// Get the current sheet name based on the context (local or remote). /// This function handles the communication between local and remote instances -/// to verify and retrieve the current sheet name. +/// to verify and retrieve the current sheet name and whether it's a reference sheet. /// /// On local: /// - Reads the current sheet from local configuration /// - Sends the sheet name to remote for verification -/// - Returns the sheet name if remote confirms it exists +/// - Returns the sheet name and whether it's a reference sheet if remote confirms it exists /// /// On remote: /// - Receives sheet name from local /// - Verifies the sheet exists in the vault -/// - Sends confirmation back to local +/// - Checks if the sheet is a reference sheet +/// - If allow_ref is true, reference sheets are allowed to pass verification +/// - Sends confirmation and reference status back to local /// -/// Returns the verified sheet name or an error if the sheet doesn't exist +/// Returns a tuple of (SheetName, bool) where the bool indicates if it's a reference sheet, +/// or an error if the sheet doesn't exist or doesn't meet the verification criteria. pub async fn get_current_sheet_name( ctx: &ActionContext, instance: &Arc<Mutex<ConnectionInstance>>, member_id: &MemberId, -) -> Result<SheetName, TcpTargetError> { + allow_ref: bool, +) -> Result<(SheetName, bool), TcpTargetError> { let mut mut_instance = instance.lock().await; if ctx.is_proc_on_local() { + let workspace = try_get_local_workspace(ctx)?; let config = LocalConfig::read().await?; + let latest = LatestInfo::read_from(LatestInfo::latest_info_path( + workspace.local_path(), + member_id, + )) + .await?; if let Some(sheet_name) = config.sheet_in_use() { // Send sheet name mut_instance.write_msgpack(sheet_name).await?; // Read result if mut_instance.read_msgpack::<bool>().await? { - return Ok(sheet_name.clone()); + // Check if sheet is a reference sheet + let is_ref_sheet = latest.reference_sheets.contains(sheet_name); + if allow_ref { + // Allow reference sheets, directly return the determination result + return Ok((sheet_name.clone(), is_ref_sheet)); + } else if is_ref_sheet { + // Not allowed but it's a reference sheet, return an error + return Err(TcpTargetError::ReferenceSheetNotAllowed( + "Reference sheet not allowed".to_string(), + )); + } else { + // Not allowed but not a reference sheet, return normally + return Ok((sheet_name.clone(), false)); + } } else { return Err(TcpTargetError::NotFound("Sheet not found".to_string())); } @@ -185,11 +239,27 @@ pub async fn get_current_sheet_name( // Check if sheet exists if let Ok(sheet) = vault.sheet(&sheet_name).await && let Some(holder) = sheet.holder() - && holder == member_id { - // Tell local the check is passed - mut_instance.write_msgpack(true).await?; - return Ok(sheet_name.clone()); + let is_ref_sheet = holder == VAULT_HOST_NAME; + if allow_ref { + // Allow reference sheets, directly return the determination result + if holder == member_id || holder == VAULT_HOST_NAME { + mut_instance.write_msgpack(true).await?; + return Ok((sheet.name().clone(), is_ref_sheet)); + } + } else if is_ref_sheet { + // Not allowed but it's a reference sheet, return an error + mut_instance.write_msgpack(true).await?; + return Err(TcpTargetError::ReferenceSheetNotAllowed( + "Reference sheet not allowed".to_string(), + )); + } else { + // Not allowed but not a reference sheet, return normally + if holder == member_id { + mut_instance.write_msgpack(true).await?; + return Ok((sheet_name.clone(), false)); + } + } } // Tell local the check is not passed mut_instance.write_msgpack(false).await?; diff --git a/crates/vcs_actions/src/actions/local_actions.rs b/crates/vcs_actions/src/actions/local_actions.rs index 7eee64d..d3f718d 100644 --- a/crates/vcs_actions/src/actions/local_actions.rs +++ b/crates/vcs_actions/src/actions/local_actions.rs @@ -1,4 +1,10 @@ -use std::{collections::HashMap, io::ErrorKind, net::SocketAddr, path::PathBuf, time::SystemTime}; +use std::{ + collections::{HashMap, HashSet}, + io::ErrorKind, + net::SocketAddr, + path::PathBuf, + time::SystemTime, +}; use action_system::{action::ActionContext, macros::action_gen}; use cfg_file::config::ConfigFile; @@ -6,7 +12,9 @@ use log::info; use serde::{Deserialize, Serialize}; use tcp_connection::error::TcpTargetError; use vcs_data::{ - constants::{CLIENT_PATH_CACHED_SHEET, CLIENT_PATH_LOCAL_SHEET}, + constants::{ + CLIENT_PATH_CACHED_SHEET, CLIENT_PATH_LOCAL_SHEET, REF_SHEET_NAME, VAULT_HOST_NAME, + }, data::{ local::{ cached_sheet::CachedSheet, @@ -19,6 +27,7 @@ use vcs_data::{ sheet::{SheetData, SheetName}, vault::{ config::VaultUuid, + sheet_share::{Share, SheetShareId}, virtual_file::{VirtualFileId, VirtualFileVersion}, }, }, @@ -138,7 +147,7 @@ pub async fn update_to_latest_info_action( ) -> Result<UpdateToLatestInfoResult, TcpTargetError> { let instance = check_connection_instance(&ctx)?; - let member_id = match auth_member(&ctx, instance).await { + let (member_id, _is_host_mode) = match auth_member(&ctx, instance).await { Ok(id) => id, Err(e) => return Ok(UpdateToLatestInfoResult::AuthorizeFailed(e.to_string())), }; @@ -153,13 +162,43 @@ pub async fn update_to_latest_info_action( // Build latest info let mut latest_info = LatestInfo::default(); - // Sheet + // Sheet & Share + let mut shares_in_my_sheets: HashMap<SheetName, HashMap<SheetShareId, Share>> = + HashMap::new(); let mut member_owned = Vec::new(); let mut member_visible = Vec::new(); + let mut ref_sheets = HashSet::new(); for sheet in vault.sheets().await? { - if sheet.holder().is_some() && sheet.holder().unwrap() == &member_id { + // Build share parts + if let Some(holder) = sheet.holder() { + if holder == &member_id { + let mut sheet_shares: HashMap<SheetShareId, Share> = HashMap::new(); + for share in sheet.get_shares().await? { + // Get SharePath + let Some(share_path) = share.path.clone() else { + continue; + }; + // Get ShareId from SharePath + let Some(share_id) = share_path.file_name() else { + continue; + }; + sheet_shares.insert(share_id.display().to_string(), share); + } + shares_in_my_sheets.insert(sheet.name().clone(), sheet_shares); + } + } + + // Build sheet parts + let holder_is_host = + sheet.holder().unwrap_or(&String::default()) == &VAULT_HOST_NAME; + if sheet.holder().is_some() + && (sheet.holder().unwrap() == &member_id || holder_is_host) + { member_owned.push(sheet.name().clone()); + if holder_is_host { + ref_sheets.insert(sheet.name().clone()); + } } else { member_visible.push(SheetInfo { sheet_name: sheet.name().clone(), @@ -168,12 +207,15 @@ pub async fn update_to_latest_info_action( } } - latest_info.my_sheets = member_owned; - latest_info.other_sheets = member_visible; + // Record Share & Sheet + latest_info.visible_sheets = member_owned; + latest_info.invisible_sheets = member_visible; + latest_info.shares_in_my_sheets = shares_in_my_sheets; // RefSheet - let ref_sheet_data = vault.sheet(&"ref".to_string()).await?.to_data(); + let ref_sheet_data = vault.sheet(&REF_SHEET_NAME.to_string()).await?.to_data(); latest_info.ref_sheet_content = ref_sheet_data; + latest_info.reference_sheets = ref_sheets; // Members let members = vault.members().await?; @@ -222,7 +264,7 @@ pub async fn update_to_latest_info_action( // Collect all local versions let mut local_versions = vec![]; - for request_sheet in latest_info.my_sheets { + for request_sheet in latest_info.visible_sheets { let Ok(data) = CachedSheet::cached_sheet_data(&request_sheet).await else { // For newly created sheets, the version is 0. // Send -1 to distinguish from 0, ensuring the upstream will definitely send the sheet information @@ -244,29 +286,6 @@ pub async fn update_to_latest_info_action( let _: bool = mut_instance.read_msgpack().await?; } } else { - // Send data to local - if ctx.is_proc_on_remote() { - let vault = try_get_vault(&ctx)?; - let mut mut_instance = instance.lock().await; - - let local_versions = - mut_instance.read_msgpack::<Vec<(SheetName, i32)>>().await?; - - for (sheet_name, local_write_count) in local_versions.iter() { - let sheet = vault.sheet(sheet_name).await?; - if let Some(holder) = sheet.holder() - && holder == &member_id - && &sheet.write_count() != local_write_count - { - mut_instance.write_msgpack(true).await?; - mut_instance - .write_large_msgpack((sheet_name, sheet.to_data()), 1024u16) - .await?; - } - } - mut_instance.write_msgpack(false).await?; - } - // Receive data if ctx.is_proc_on_local() { let mut mut_instance = instance.lock().await; @@ -289,7 +308,8 @@ pub async fn update_to_latest_info_action( } } } - } else if ctx.is_proc_on_remote() { + } + if ctx.is_proc_on_remote() { let vault = try_get_vault(&ctx)?; let mut mut_instance = instance.lock().await; @@ -298,7 +318,7 @@ pub async fn update_to_latest_info_action( for (sheet_name, version) in local_versions.iter() { let sheet = vault.sheet(sheet_name).await?; if let Some(holder) = sheet.holder() - && holder == &member_id + && (holder == &member_id || holder == VAULT_HOST_NAME) && &sheet.write_count() != version { mut_instance.write_msgpack(true).await?; @@ -331,7 +351,7 @@ pub async fn update_to_latest_info_action( // Collect files that need to know the holder let mut holder_wants_know = Vec::new(); - for sheet_name in &latest_info.my_sheets { + for sheet_name in &latest_info.visible_sheets { if let Ok(sheet_data) = CachedSheet::cached_sheet_data(sheet_name).await { holder_wants_know .extend(sheet_data.mapping().values().map(|value| value.id.clone())); diff --git a/crates/vcs_actions/src/actions/sheet_actions.rs b/crates/vcs_actions/src/actions/sheet_actions.rs index aff06f5..759c275 100644 --- a/crates/vcs_actions/src/actions/sheet_actions.rs +++ b/crates/vcs_actions/src/actions/sheet_actions.rs @@ -3,12 +3,16 @@ use std::{collections::HashMap, io::ErrorKind}; use action_system::{action::ActionContext, macros::action_gen}; use serde::{Deserialize, Serialize}; use tcp_connection::error::TcpTargetError; -use vcs_data::data::{ - local::{ - vault_modified::sign_vault_modified, - workspace_analyzer::{FromRelativePathBuf, ToRelativePathBuf}, +use vcs_data::{ + constants::VAULT_HOST_NAME, + data::{ + local::{ + vault_modified::sign_vault_modified, + workspace_analyzer::{FromRelativePathBuf, ToRelativePathBuf}, + }, + sheet::SheetName, + vault::sheet_share::{ShareMergeMode, SheetShareId}, }, - sheet::SheetName, }; use crate::{ @@ -42,7 +46,7 @@ pub async fn make_sheet_action( let instance = check_connection_instance(&ctx)?; // Auth Member - let member_id = match auth_member(&ctx, instance).await { + let (member_id, is_host_mode) = match auth_member(&ctx, instance).await { Ok(id) => id, Err(e) => return Ok(MakeSheetActionResult::AuthorizeFailed(e.to_string())), }; @@ -54,7 +58,11 @@ pub async fn make_sheet_action( if let Ok(mut sheet) = vault.sheet(&sheet_name).await { // If the sheet has no holder, assign it to the current member (restore operation) if sheet.holder().is_none() { - sheet.set_holder(member_id); + sheet.set_holder(if is_host_mode { + VAULT_HOST_NAME.to_string() + } else { + member_id + }); match sheet.persist().await { Ok(_) => { write_and_return!(instance, MakeSheetActionResult::SuccessRestore); @@ -124,7 +132,7 @@ pub async fn drop_sheet_action( let instance = check_connection_instance(&ctx)?; // Auth Member - let member_id = match auth_member(&ctx, instance).await { + let (member_id, is_host_mode) = match auth_member(&ctx, instance).await { Ok(id) => id, Err(e) => { return Ok(DropSheetActionResult::AuthorizeFailed(e.to_string())); @@ -174,8 +182,9 @@ pub async fn drop_sheet_action( write_and_return!(instance, DropSheetActionResult::NoHolder); }; - // Verify the sheet's holder - if holder != &member_id { + // Verify that the sheet holder is either the current user or the host + // All sheets belong to the host + if holder != &member_id && !is_host_mode { write_and_return!(instance, DropSheetActionResult::NotOwner); } @@ -223,6 +232,7 @@ pub enum EditMappingActionResult { // Fail AuthorizeFailed(String), + EditNotAllowed, MappingNotFound(FromRelativePathBuf), InvalidMove(InvalidMoveReason), @@ -251,7 +261,7 @@ pub async fn edit_mapping_action( let instance = check_connection_instance(&ctx)?; // Auth Member - let member_id = match auth_member(&ctx, instance).await { + let (member_id, is_host_mode) = match auth_member(&ctx, instance).await { Ok(id) => id, Err(e) => { return Ok(EditMappingActionResult::AuthorizeFailed(e.to_string())); @@ -259,7 +269,15 @@ pub async fn edit_mapping_action( }; // Check sheet - let sheet_name = get_current_sheet_name(&ctx, instance, &member_id).await?; + let (sheet_name, is_ref_sheet) = + get_current_sheet_name(&ctx, instance, &member_id, true).await?; + + // Can modify Sheet when not in reference sheet or in Host mode + let can_modify_sheet = !is_ref_sheet || is_host_mode; + + if !can_modify_sheet { + return Ok(EditMappingActionResult::EditNotAllowed); + } if ctx.is_proc_on_remote() { let vault = try_get_vault(&ctx)?; @@ -340,3 +358,219 @@ pub async fn edit_mapping_action( Ok(EditMappingActionResult::Success) } + +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)] +pub struct ShareMappingArguments { + pub mappings: Vec<FromRelativePathBuf>, + pub description: String, + // None = current sheet, + // Some(sheet_name) = other ref(public) sheet + pub from_sheet: Option<SheetName>, + pub to_sheet: SheetName, +} + +#[derive(Serialize, Deserialize, Default)] +pub enum ShareMappingActionResult { + Success, + + // Fail + AuthorizeFailed(String), + TargetSheetNotFound(SheetName), + TargetIsSelf, + MappingNotFound(FromRelativePathBuf), + + #[default] + Unknown, +} + +#[action_gen] +pub async fn share_mapping_action( + ctx: ActionContext, + args: ShareMappingArguments, +) -> Result<ShareMappingActionResult, TcpTargetError> { + let instance = check_connection_instance(&ctx)?; + + // Auth Member + let (member_id, _is_host_mode) = match auth_member(&ctx, instance).await { + Ok(id) => id, + Err(e) => { + return Ok(ShareMappingActionResult::AuthorizeFailed(e.to_string())); + } + }; + + // Check sheet + let sheet_name = args.from_sheet.unwrap_or( + get_current_sheet_name(&ctx, instance, &member_id, false) + .await? + .0, + ); + + if ctx.is_proc_on_remote() { + let vault = try_get_vault(&ctx)?; + let sheet = vault.sheet(&sheet_name).await?; + + // Tip: Because sheet_name may specify a sheet that does not belong to the user, + // a secondary verification is required. + + // Check if the sheet holder is Some and matches the member_id or is the host + let Some(holder) = sheet.holder() else { + // If there's no holder, the sheet cannot be shared from + write_and_return!( + instance, + ShareMappingActionResult::AuthorizeFailed("Sheet has no holder".to_string()) + ); + }; + + // Verify the holder is either the current member or the host + if holder != &member_id && holder != VAULT_HOST_NAME { + write_and_return!( + instance, + ShareMappingActionResult::AuthorizeFailed( + "Not sheet holder or ref sheet".to_string() + ) + ); + } + + let to_sheet_name = args.to_sheet; + + // Verify target sheet exists + if !vault.sheet_names()?.contains(&to_sheet_name) { + // Does not exist + write_and_return!( + instance, + ShareMappingActionResult::TargetSheetNotFound(to_sheet_name.clone()) + ); + } + + // Verify sheet is not self + if sheet_name == to_sheet_name { + // Is self + write_and_return!(instance, ShareMappingActionResult::TargetIsSelf); + } + + // Verify all mappings are correct + for mapping in args.mappings.iter() { + if !sheet.mapping().contains_key(mapping) { + // If any mapping is invalid, indicate failure + write_and_return!( + instance, + ShareMappingActionResult::MappingNotFound(mapping.clone()) + ); + } + } + + // Execute sharing logic + sheet + .share_mappings(&to_sheet_name, args.mappings, &member_id, args.description) + .await?; + + // Sharing successful + write_and_return!(instance, ShareMappingActionResult::Success); + } + + if ctx.is_proc_on_local() { + let result = instance + .lock() + .await + .read::<ShareMappingActionResult>() + .await?; + return Ok(result); + } + + Ok(ShareMappingActionResult::Success) +} + +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)] +pub struct MergeShareMappingArguments { + pub share_id: SheetShareId, + pub share_merge_mode: ShareMergeMode, +} + +#[derive(Serialize, Deserialize, Default)] +pub enum MergeShareMappingActionResult { + Success, + + // Fail + HasConflicts, + AuthorizeFailed(String), + EditNotAllowed, + ShareIdNotFound(SheetShareId), + MergeFails(String), + + #[default] + Unknown, +} + +#[action_gen] +pub async fn merge_share_mapping_action( + ctx: ActionContext, + args: MergeShareMappingArguments, +) -> Result<MergeShareMappingActionResult, TcpTargetError> { + let instance = check_connection_instance(&ctx)?; + + // Auth Member + let (member_id, is_host_mode) = match auth_member(&ctx, instance).await { + Ok(id) => id, + Err(e) => { + return Ok(MergeShareMappingActionResult::AuthorizeFailed( + e.to_string(), + )); + } + }; + + // Check sheet + let (sheet_name, is_ref_sheet) = + get_current_sheet_name(&ctx, instance, &member_id, true).await?; + + // Can modify Sheet when not in reference sheet or in Host mode + let can_modify_sheet = !is_ref_sheet || is_host_mode; + + if !can_modify_sheet { + return Ok(MergeShareMappingActionResult::EditNotAllowed); + } + + if ctx.is_proc_on_remote() { + let vault = try_get_vault(&ctx)?; + let share_id = args.share_id; + + // Get the share and sheet + let (sheet, share) = if vault.share_file_path(&sheet_name, &share_id).exists() { + let sheet = vault.sheet(&sheet_name).await?; + let share = sheet.get_share(&share_id).await?; + (sheet, share) + } else { + // Share does not exist + write_and_return!( + instance, + MergeShareMappingActionResult::ShareIdNotFound(share_id.clone()) + ); + }; + + // Perform the merge + match sheet.merge_share(share, args.share_merge_mode).await { + Ok(_) => write_and_return!(instance, MergeShareMappingActionResult::Success), + Err(e) => match e.kind() { + ErrorKind::AlreadyExists => { + write_and_return!(instance, MergeShareMappingActionResult::HasConflicts); + } + _ => { + write_and_return!( + instance, + MergeShareMappingActionResult::MergeFails(e.to_string()) + ); + } + }, + } + } + + if ctx.is_proc_on_local() { + let result = instance + .lock() + .await + .read::<MergeShareMappingActionResult>() + .await?; + return Ok(result); + } + + Ok(MergeShareMappingActionResult::Success) +} diff --git a/crates/vcs_actions/src/actions/track_action.rs b/crates/vcs_actions/src/actions/track_action.rs index 63c1b67..e5f96b3 100644 --- a/crates/vcs_actions/src/actions/track_action.rs +++ b/crates/vcs_actions/src/actions/track_action.rs @@ -123,13 +123,17 @@ pub async fn track_file_action( let instance = check_connection_instance(&ctx)?; // Auth Member - let member_id = match auth_member(&ctx, instance).await { + let (member_id, is_host_mode) = match auth_member(&ctx, instance).await { Ok(id) => id, Err(e) => return Ok(TrackFileActionResult::AuthorizeFailed(e.to_string())), }; // Check sheet - let sheet_name = get_current_sheet_name(&ctx, instance, &member_id).await?; + let (sheet_name, is_ref_sheet) = + get_current_sheet_name(&ctx, instance, &member_id, true).await?; + + // Can modify Sheet when not in reference sheet or in Host mode + let can_modify_sheet = !is_ref_sheet || is_host_mode; if ctx.is_proc_on_local() { let workspace = try_get_local_workspace(&ctx)?; @@ -164,7 +168,7 @@ pub async fn track_file_action( .collect::<Vec<_>>(); // Filter out modified files that need to be updated - let update_task: Vec<PathBuf> = { + let mut update_task: Vec<PathBuf> = { let result = modified.iter().filter_map(|p| { if let Ok(local_data) = local_sheet.mapping_data(p) { let id = local_data.mapping_vfid(); @@ -187,7 +191,7 @@ pub async fn track_file_action( let mut skipped_task: Vec<PathBuf> = Vec::new(); // Filter out files that do not exist locally or have version inconsistencies and need to be synchronized - let sync_task: Vec<PathBuf> = { + let mut sync_task: Vec<PathBuf> = { let other: Vec<PathBuf> = relative_pathes .iter() .filter(|p| !created_task.contains(p) && !update_task.contains(p)) @@ -242,6 +246,18 @@ pub async fn track_file_action( result.collect() }; + // If the sheet cannot be modified, + // the update_task here should be considered invalid and changed to sync rollback + if !can_modify_sheet { + if arguments.allow_overwrite_modified { + sync_task.append(&mut update_task); + update_task.clear(); + } else { + skipped_task.append(&mut update_task); + update_task.clear(); + } + } + // Package tasks let tasks: (Vec<PathBuf>, Vec<PathBuf>, Vec<PathBuf>) = (created_task, update_task, sync_task); @@ -256,45 +272,51 @@ pub async fn track_file_action( } // Process create tasks - let success_create = match proc_create_tasks_local( - &ctx, - instance.clone(), - &member_id, - &sheet_name, - tasks.0, - arguments.print_infos, - ) - .await - { - Ok(r) => match r { - CreateTaskResult::Success(relative_pathes) => relative_pathes, - _ => { - return Ok(TrackFileActionResult::CreateTaskFailed(r)); - } - }, - Err(e) => return Err(e), - }; + let mut success_create = Vec::<PathBuf>::new(); + if can_modify_sheet { + success_create = match proc_create_tasks_local( + &ctx, + instance.clone(), + &member_id, + &sheet_name, + tasks.0, + arguments.print_infos, + ) + .await + { + Ok(r) => match r { + CreateTaskResult::Success(relative_pathes) => relative_pathes, + _ => { + return Ok(TrackFileActionResult::CreateTaskFailed(r)); + } + }, + Err(e) => return Err(e), + }; + } // Process update tasks - let success_update = match proc_update_tasks_local( - &ctx, - instance.clone(), - &member_id, - &sheet_name, - tasks.1, - arguments.print_infos, - arguments.file_update_info, - ) - .await - { - Ok(r) => match r { - UpdateTaskResult::Success(relative_pathes) => relative_pathes, - _ => { - return Ok(TrackFileActionResult::UpdateTaskFailed(r)); - } - }, - Err(e) => return Err(e), - }; + let mut success_update = Vec::<PathBuf>::new(); + if can_modify_sheet { + success_update = match proc_update_tasks_local( + &ctx, + instance.clone(), + &member_id, + &sheet_name, + tasks.1, + arguments.print_infos, + arguments.file_update_info, + ) + .await + { + Ok(r) => match r { + UpdateTaskResult::Success(relative_pathes) => relative_pathes, + _ => { + return Ok(TrackFileActionResult::UpdateTaskFailed(r)); + } + }, + Err(e) => return Err(e), + }; + } // Process sync tasks let success_sync = match proc_sync_tasks_local( @@ -333,43 +355,49 @@ pub async fn track_file_action( }; // Process create tasks - let success_create = match proc_create_tasks_remote( - &ctx, - instance.clone(), - &member_id, - &sheet_name, - created_task, - ) - .await - { - Ok(r) => match r { - CreateTaskResult::Success(relative_pathes) => relative_pathes, - _ => { - return Ok(TrackFileActionResult::CreateTaskFailed(r)); - } - }, - Err(e) => return Err(e), - }; + let mut success_create = Vec::<PathBuf>::new(); + if can_modify_sheet { + success_create = match proc_create_tasks_remote( + &ctx, + instance.clone(), + &member_id, + &sheet_name, + created_task, + ) + .await + { + Ok(r) => match r { + CreateTaskResult::Success(relative_pathes) => relative_pathes, + _ => { + return Ok(TrackFileActionResult::CreateTaskFailed(r)); + } + }, + Err(e) => return Err(e), + }; + } // Process update tasks - let success_update = match proc_update_tasks_remote( - &ctx, - instance.clone(), - &member_id, - &sheet_name, - update_task, - arguments.file_update_info, - ) - .await - { - Ok(r) => match r { - UpdateTaskResult::Success(relative_pathes) => relative_pathes, - _ => { - return Ok(TrackFileActionResult::UpdateTaskFailed(r)); - } - }, - Err(e) => return Err(e), - }; + let mut success_update = Vec::<PathBuf>::new(); + if can_modify_sheet { + success_update = match proc_update_tasks_remote( + &ctx, + instance.clone(), + &member_id, + &sheet_name, + update_task, + arguments.file_update_info, + ) + .await + { + Ok(r) => match r { + UpdateTaskResult::Success(relative_pathes) => relative_pathes, + _ => { + return Ok(TrackFileActionResult::UpdateTaskFailed(r)); + } + }, + Err(e) => return Err(e), + }; + } // Process sync tasks let success_sync = match proc_sync_tasks_remote( diff --git a/crates/vcs_actions/src/actions/user_actions.rs b/crates/vcs_actions/src/actions/user_actions.rs index febfeeb..dc0f71a 100644 --- a/crates/vcs_actions/src/actions/user_actions.rs +++ b/crates/vcs_actions/src/actions/user_actions.rs @@ -43,7 +43,7 @@ pub async fn change_virtual_file_edit_right_action( let (relative_paths, print_info) = arguments; // Auth Member - let member_id = match auth_member(&ctx, instance).await { + let (member_id, is_host_mode) = match auth_member(&ctx, instance).await { Ok(id) => id, Err(e) => { return Ok(ChangeVirtualFileEditRightResult::AuthorizeFailed( @@ -53,7 +53,8 @@ pub async fn change_virtual_file_edit_right_action( }; // Check sheet - let sheet_name = get_current_sheet_name(&ctx, instance, &member_id).await?; + let (sheet_name, _is_ref_sheet) = + get_current_sheet_name(&ctx, instance, &member_id, true).await?; if ctx.is_proc_on_remote() { let mut mut_instance = instance.lock().await; @@ -87,7 +88,9 @@ pub async fn change_virtual_file_edit_right_action( } } else // Throw file - if has_edit_right && behaviour == EditRightChangeBehaviour::Throw { + if (has_edit_right || is_host_mode) + && behaviour == EditRightChangeBehaviour::Throw + { match vault.revoke_virtual_file_edit_right(&mapping.id).await { Ok(_) => { success_throw.push(path.clone()); diff --git a/crates/vcs_actions/src/actions/vault_actions.rs b/crates/vcs_actions/src/actions/vault_actions.rs index e69de29..8b13789 100644 --- a/crates/vcs_actions/src/actions/vault_actions.rs +++ b/crates/vcs_actions/src/actions/vault_actions.rs @@ -0,0 +1 @@ + diff --git a/crates/vcs_actions/src/registry/client_registry.rs b/crates/vcs_actions/src/registry/client_registry.rs index 6667a74..05cb7f1 100644 --- a/crates/vcs_actions/src/registry/client_registry.rs +++ b/crates/vcs_actions/src/registry/client_registry.rs @@ -15,6 +15,7 @@ use crate::{ }, sheet_actions::{ register_drop_sheet_action, register_edit_mapping_action, register_make_sheet_action, + register_merge_share_mapping_action, register_share_mapping_action, }, track_action::register_track_file_action, user_actions::register_change_virtual_file_edit_right_action, @@ -34,6 +35,10 @@ fn register_actions(pool: &mut ActionPool) { register_drop_sheet_action(pool); register_edit_mapping_action(pool); + // Share / Merge Share Actions + register_share_mapping_action(pool); + register_merge_share_mapping_action(pool); + // Track Action register_track_file_action(pool); diff --git a/crates/vcs_actions/src/registry/server_registry.rs b/crates/vcs_actions/src/registry/server_registry.rs index 404625c..356e640 100644 --- a/crates/vcs_actions/src/registry/server_registry.rs +++ b/crates/vcs_actions/src/registry/server_registry.rs @@ -4,6 +4,7 @@ use crate::actions::{ local_actions::{register_set_upstream_vault_action, register_update_to_latest_info_action}, sheet_actions::{ register_drop_sheet_action, register_edit_mapping_action, register_make_sheet_action, + register_merge_share_mapping_action, register_share_mapping_action, }, track_action::register_track_file_action, user_actions::register_change_virtual_file_edit_right_action, @@ -21,6 +22,10 @@ pub fn server_action_pool() -> ActionPool { register_drop_sheet_action(&mut pool); register_edit_mapping_action(&mut pool); + // Share / Merge Share Actions + register_share_mapping_action(&mut pool); + register_merge_share_mapping_action(&mut pool); + // Track Action register_track_file_action(&mut pool); |
