diff --git a/Server/Common/Config.cs b/Server/Common/Config.cs index a206c47..040ee29 100644 --- a/Server/Common/Config.cs +++ b/Server/Common/Config.cs @@ -3,7 +3,7 @@ namespace Common; public class DirFileConfig { /// - /// 相对路径 + /// 相对路径,与根路径拼成一个完整的路径,注意 "/" 不要缺少 /// public required string DirPath { get; set; } @@ -17,9 +17,17 @@ public class DirFileConfig /// public List? CherryPicks { get; set; } - /// + /// + /// 本地文件信息,也是即将发布的文件信息,通常是最新的版本 + /// public Dir? LocalDirInfo { get; set; } + /// + /// 差异文件信息 + /// public Dir? DiffDirInfo { get; set; } + /// + /// 远程文件信息,通常是较旧的版本 + /// public Dir? RemoteDirInfo { get; set; } } @@ -63,6 +71,9 @@ public class Config /// public required string Name { get; set; } + /// + /// 本次发布的唯一id + /// public Guid Id { get; set; } = Guid.NewGuid(); /// diff --git a/Server/Common/ConnectPipeline.cs b/Server/Common/ConnectPipeline.cs index 144c0a2..7c3626b 100644 --- a/Server/Common/ConnectPipeline.cs +++ b/Server/Common/ConnectPipeline.cs @@ -6,28 +6,52 @@ namespace Common; public abstract class AbsPipeLine(bool isAES) { + + /// + /// pipeLine工作函数,生效期间永久阻塞 + /// + /// 回调函数,从pipeline接收到的消息 + /// 连接的远程地址 + /// 第一次返回连接信息,第二次当pipeline 被断开时返回,此时可能是正常完成断开,或异常发生断开 public abstract IAsyncEnumerable Work(Func receiveCb, string addr = ""); protected Func ReceiveMsg = (byte[] a) => { return true; }; + /// + /// 监听pipeline 消息,由Work 函数调用 + /// + /// 消息回调 + /// protected abstract Task Listen(Func receiveCb); /// /// 关闭连接 /// - /// + /// 关闭原因 /// public abstract Task Close(string? CloseReason); /// - /// 发送消息 + /// 向管道中发送消息 /// /// /// public abstract Task SendMsg(SyncMsg msg); + /// + /// 打包文件上传 + /// + /// 上传地址 + /// 上传的文件路径 + /// 上传进度回调(现在没有回调) + /// 上传完成时返回/ public abstract Task UploadFile(string url, string filePath, Func progressCb); + + + /// + /// 管道消息是否使用AES加密 + /// protected readonly bool IsAES = isAES; } @@ -42,16 +66,16 @@ public class WebSocPipeLine(TSocket socket, bool isAES) : AbsPipeLine(i Func progressCb ) { - //throw new Exception("sdfsdf"); using var client = new HttpClient(); using var content = new MultipartFormDataContent(); using var fileStream = new FileStream(filePath, FileMode.Open); - var progress = new Progress( - (current) => - { - progressCb(current); - } - ); + // TODO 上传进度回调 + // var progress = new Progress( + // (current) => + // { + // progressCb(current); + // } + // ); //var fileContent = new ProgressStreamContent(fileStream, progress); content.Add(new StreamContent(fileStream), "file", Path.GetFileName(filePath)); var it = await client.PostAsync("http://" + url + "/UploadFile", content); @@ -80,7 +104,7 @@ public class WebSocPipeLine(TSocket socket, bool isAES) : AbsPipeLine(i protected override async Task Listen(Func receiveCb) { - //最大1MB!= + //warning 最大支持1MB,这由需要同步的文件数量大小决定 UTF-8 每个字符,汉字视为4个字节,数字1个 ,英文字母2个。1MB=256KB*4,25万个字符能描述就行 var buffer = new byte[1024 * 1024]; while (Socket.State == WebSocketState.Open) @@ -100,8 +124,6 @@ public class WebSocPipeLine(TSocket socket, bool isAES) : AbsPipeLine(i System.Buffer.BlockCopy(buffer, 0, nbuffer, 0, receiveResult.Count); if (IsAES) { - //var msg1 = Encoding.UTF8.GetString(nbuffer); - //var n1Buffler = Encoding.UTF8.GetBytes(msg1); var nnbuffer = AESHelper.DecryptStringFromBytes_Aes(nbuffer); receiveCb(Encoding.UTF8.GetBytes(nnbuffer)); } @@ -117,14 +139,8 @@ public class WebSocPipeLine(TSocket socket, bool isAES) : AbsPipeLine(i { if (Socket.State == WebSocketState.Open) { - //await SendMsg( - // new SyncMsg - // { - // Type = SyncMsgType.Error, - // Step = SyncProcessStep.Finally, - // Body = CloseReason ?? "" - // } - //); + //CloseReason 最大不能超过123bytes.https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close + //若传递超过这个限制,此处表现WebSocket将会卡住,无法关闭。 if (Encoding.UTF8.GetBytes(CloseReason ?? "").Length > 120) { await SendMsg( @@ -156,9 +172,9 @@ public class WebSocPipeLine(TSocket socket, bool isAES) : AbsPipeLine(i { string msgStr = JsonSerializer.Serialize(msg); var it = AESHelper.EncryptStringToBytes_Aes(msgStr); - //var xx = new ArraySegment(it); if (IsAES) { + // 加密后的字符,使用Binary 发送,加密通常不会发送到最前端,通常是 js 写的websocket await Socket.SendAsync( new ArraySegment(it), WebSocketMessageType.Binary, diff --git a/Server/Common/DirExtension.cs b/Server/Common/DirExtension.cs index 8d9d27b..d7d4cfe 100644 --- a/Server/Common/DirExtension.cs +++ b/Server/Common/DirExtension.cs @@ -1,5 +1,8 @@ namespace Common; +/// +/// Dir比较方法 +/// public static class DirExtension { /// @@ -248,6 +251,13 @@ public static class DirExtension return cDir; } + + /// + /// 根据Dirobject记录的信息,写入磁盘 + /// + /// + /// 文件操作类,它是如何写入文件的方法 + /// public static void WriteByThisInfo(this Dir thisDir, FileDirOpStra fileDirOp) { static void f(Dir dir, FileDirOpStra fileDirOp) @@ -285,6 +295,13 @@ public static class DirExtension f(thisDir, fileDirOp); } + /// + /// 从文件夹中提取文件信息 + /// + /// + /// 只提取这些信息,最高级别 + /// 忽略这些文件信息 + /// public static void ExtractInfo( this Dir thisDir, List? cherryPicks = null, @@ -334,7 +351,12 @@ public static class DirExtension } } } - + /// + /// 添加一个子对象,这个不包含文件或文件夹的创建 + /// + /// + /// + /// public static void AddChild(this Dir thisDir, AFileOrDir child) { if (child.FormatedPath[..thisDir.FormatedPath.Length] != thisDir.FormatedPath) @@ -371,6 +393,16 @@ public static class DirExtension } } + + /// + /// 合并diffdir中的内容 + /// + /// + /// + /// 是否更新对象 + /// 是否更新文件夹和文件 + /// + /// public static void Combine( this Dir thisDir, FileDirOpStra? fileDirOp, @@ -495,6 +527,14 @@ public static class DirExtension } } + /// + /// clone一个新的对象 + /// + /// + /// 重设的操作类型 + /// 是否重设操作类型 + /// + /// public static Dir Clone( this Dir thisDir, NextOpType? optype = null, @@ -536,6 +576,13 @@ public static class DirExtension return ndir; } + /// + /// 改变根路径位置 + /// + /// + /// + /// + /// public static Dir ResetRootPath(this Dir thisDir, string oldPath, string newPath) { thisDir.FormatedPath = thisDir.FormatedPath.Replace(oldPath, newPath); @@ -553,6 +600,10 @@ public static class DirExtension return thisDir; } + /// + /// 文件操作权限检查(废弃) + /// + /// public static void AccessCheck(this Dir thisDir) { //不是核心关注点,下面实现有bug。不校验所有文件夹权限,创建时会抛出错误,此时手动处理吧。 @@ -639,247 +690,3 @@ public static class DirExtension //}); } } - -/// -/// 文件夹结构,它包含文件和文件夹 -/// -/// 绝对路径 -/// 子文件或文件夹 -// public class Dirxx(string path, List? children = null, NextOpType? nextOp = null) -// : AFileOrDir(path, DirOrFile.Dir, nextOp) -// { -// public List Children { get; set; } = children ?? []; - -/* public override bool IsEqual(AFileOrDir other) -{ - if (other is not Dir otherDir) - { - return false; - } - else - { - if (this.FormatedPath != otherDir.FormatedPath || this.NextOp != otherDir.NextOp) - { - return false; - } - if (this.Children.Count != otherDir.Children.Count) - { - return false; - } - this.Children.Sort(AFileOrDir.Compare); - otherDir.Children.Sort(AFileOrDir.Compare); - for (int i = 0; i < this.Children.Count; i++) - { - if (!this.Children[i].IsEqual(otherDir.Children[i])) - { - return false; - } - } - return true; - } -} */ - -/// -/// clone, 但是更改根目录 -/// -/// 操作步骤 -/// 旧根路径 -/// 新根路径 -/// 是否重置下步操作 -/// -// public Dir Clone( -// NextOpType? optype, -// string oldRootPath, -// string newRootPath, -// bool IsResetNextOpType = false -// ) -// { -// var ndir = this.Clone(optype, IsResetNextOpType); -// ndir.ResetRootPath(oldRootPath, newRootPath); -// return ndir; -// } - -/// -/// clone,不克隆文件 -/// -/// -/// -/// -/// -// public Dir Clone(NextOpType? optype = null, bool IsResetNextOpType = false) { } - -/// -/// 重设置根目录 -/// -/// -/// -// public void ResetRootPath(string oldPath, string newPath) -// { -// this.FormatedPath = this.FormatedPath.Replace(oldPath, newPath); -// this.Children.ForEach(e => -// { -// if (e is File file) -// { -// file.FormatedPath = file.FormatedPath.Replace(oldPath, newPath); -// } -// else if (e is Dir dir) -// { -// dir.ResetRootPath(oldPath, newPath); -// } -// }); -// } - -/// -/// 文件夹合并 -/// -/// 具体操作步骤 -/// 将要更新的内容 -/// 是否更新Object对象 -/// 是否更新文件目录树 -/// -// public void Combine( -// FileDirOpStra? fileDirOp, -// Dir diffdir, -// bool IsUpdateObject = true, -// bool IsUpdateDirFile = false -// ) { } - -// /// -// /// 合并两个文件夹,other不会发生改变,this将合并一个副本,这不会改变文件结构 -// /// -// /// 它的一个clone将被合并的dir,它的NextOp 不应该是空,否则什么都不会发生 -// /// -// public void CombineJustObject(Dir other) -// { -// Combine(null, other, true, false); -// } - -// /// -// /// 合并两个文件夹,other不会发生改变,this将不会改变,而文件结构会改变 -// /// -// /// 它的一个clone将被合并的dir,它的NextOp 不应该是空,否则什么都不会发生 -// /// -// public void CombineJustDirFile(FileDirOpStra fileDirOp, Dir diffDir) -// { -// Combine(fileDirOp, diffDir, false, true); -// } - -// /// -// /// 添加子节点,根目录相同,才会被添加进去 -// /// -// /// -// /// / -// protected void AddChild(AFileOrDir child) { } - -// /// -// /// 从文件夹中提取信息 -// /// -// /// 绝对路径,只包含的文件或者目录 -// /// 绝对路径,排除的文件或目录 -// /// - - -// /// -// /// 写入目录文件树,首先必须定义写入文件的策略,此目录结构不包含文件内容,但有一个 -// /// 文件的修改时间,是否修改文件的修改时间,需要定义文件的写入策略 WriteFileStrageFunc -// /// -// /// - - -// /// -// /// 校验文件夹和文件权限 -// /// -// // private void AccessCheck()j -// // { -// //不是核心关注点,下面实现有bug。不校验所有文件夹权限,创建时会抛出错误,此时手动处理吧。 -// // return; -// //this.Children.ForEach(e => -// //{ -// // if (e is File file) -// // { -// // if (file.NextOp == null) { } -// // else if (file.NextOp == NextOpType.Add) -// // { -// // if ( -// // !AccessWrapper.CheckDirAccess( -// // Path.GetDirectoryName(file.FormatedPath) -// // ?? throw new DirectoryNotFoundException( -// // $"{file.FormatedPath} 此父路径不存在" -// // ), -// // [DirAcess.CreateFiles] -// // ) -// // ) -// // { -// // throw new UnauthorizedAccessException($"{file.FormatedPath} 无权限创建文件"); -// // } -// // } -// // else if (file.NextOp == NextOpType.Modify) -// // { -// // if ( -// // !( -// // AccessWrapper.CheckFileAccess(file.FormatedPath, [FileAccess.Delete]) -// // && AccessWrapper.CheckDirAccess( -// // Path.GetDirectoryName(file.FormatedPath) -// // ?? throw new DirectoryNotFoundException( -// // $"{file.FormatedPath} 此父路径不存在" -// // ), -// // [DirAcess.CreateFiles] -// // ) -// // ) -// // ) -// // { -// // throw new UnauthorizedAccessException( -// // $"{file.FormatedPath} 无权限删除源文件或者创建新文件" -// // ); -// // } -// // } -// // else if (file.NextOp == NextOpType.Del) -// // { -// // if (!AccessWrapper.CheckFileAccess(file.FormatedPath, [FileAccess.Delete])) -// // { -// // throw new UnauthorizedAccessException($"{file.FormatedPath} 无权限删除源文件"); -// // } -// // } -// // } -// // else if (e is Dir dir) -// // { -// // if (dir.NextOp == null) { } -// // else if (dir.NextOp == NextOpType.Add) -// // { -// // if ( -// // !AccessWrapper.CheckDirAccess( -// // Path.GetDirectoryName(dir.FormatedPath) -// // ?? throw new DirectoryNotFoundException( -// // $"{dir.FormatedPath} 此父路径不存在" -// // ), -// // [DirAcess.CreateDirectories, DirAcess.CreateFiles] -// // ) -// // ) -// // { -// // throw new UnauthorizedAccessException($"{dir.FormatedPath} 无权限创建文件夹或者文件"); -// // } -// // } -// // else if (dir.NextOp == NextOpType.Del) -// // { -// // if (!AccessWrapper.CheckDirAccess(dir.FormatedPath, [DirAcess.Delete])) -// // { -// // throw new UnauthorizedAccessException($"{dir.FormatedPath} 无权限删除文件夹"); -// // } -// // else -// // { -// // //校验是否拥有子文件或者文件夹的删除权限, -// // dir.AccessCheck(); -// // } -// // } -// // } -// //}); -// // } - -// /// -// /// 比较两个目录文件树是否相同,不相同返回差异部分,左侧是右侧的下一个版本,任何一个节点的nextop != null,即所有 -// /// 节点都会打上标记 -// /// 文件夹的 NextOp 只有新增和删除 -// /// -// /// -// /// -// } diff --git a/Server/Common/FileDirBase.cs b/Server/Common/FileDirBase.cs index 8fa825f..2b3a788 100644 --- a/Server/Common/FileDirBase.cs +++ b/Server/Common/FileDirBase.cs @@ -39,10 +39,10 @@ public class AFileOrDir set { Path = value; } } - public bool IsEqual(AFileOrDir other) - { - return false; - } + // public bool IsEqual(AFileOrDir other) + // { + // return false; + // } public static int Compare(AFileOrDir l, AFileOrDir r) { @@ -69,7 +69,7 @@ public class File : AFileOrDir public required DateTime MTime { get; set; } - public new bool IsEqual(AFileOrDir other) + public bool IsEqual(AFileOrDir other) { if (other is not File otherFile) { @@ -96,7 +96,7 @@ public class Dir : AFileOrDir public required List Children { get; set; } - public new bool IsEqual(AFileOrDir other) + public bool IsEqual(AFileOrDir other) { if (other is not Dir otherDir) { @@ -116,7 +116,23 @@ public class Dir : AFileOrDir otherDir.Children.Sort(AFileOrDir.Compare); for (int i = 0; i < this.Children.Count; i++) { - if (!this.Children[i].IsEqual(otherDir.Children[i])) + var l = this.Children[i]; + var r = otherDir.Children[i]; + if (l is Dir ld && r is Dir rd) + { + if (!ld.IsEqual(rd)) + { + return false; + } + } + else if (l is File lf && r is File rf) + { + if (!lf.IsEqual(rf)) + { + return false; + } + } + else { return false; } diff --git a/Server/Common/FileDirOp.cs b/Server/Common/FileDirOp.cs index 68cd78e..26ba936 100644 --- a/Server/Common/FileDirOp.cs +++ b/Server/Common/FileDirOp.cs @@ -321,391 +321,391 @@ public class FileDirOpForUnpack(string srcRootPath, string dstRootPath) : FileDi } } -/// -/// 文件目录权限校验 -/// -public class FileDirOpForAccessCheck : FileDirOpStra -{ - public override void FileCreate(string absolutePath, DateTime mtime) - { - throw new NotImplementedException(); - } +// /// +// /// 文件目录权限校验 +// /// +// public class FileDirOpForAccessCheck : FileDirOpStra +// { +// public override void FileCreate(string absolutePath, DateTime mtime) +// { +// throw new NotImplementedException(); +// } - public override void DirCreate(Dir dir, bool IsRecursion = true) - { - throw new NotImplementedException(); - } +// public override void DirCreate(Dir dir, bool IsRecursion = true) +// { +// throw new NotImplementedException(); +// } - public override void FileModify(string absolutePath, DateTime mtime) - { - throw new NotImplementedException(); - } +// public override void FileModify(string absolutePath, DateTime mtime) +// { +// throw new NotImplementedException(); +// } - public override void FileDel(string absolutePath) - { - throw new NotImplementedException(); - } +// public override void FileDel(string absolutePath) +// { +// throw new NotImplementedException(); +// } - public override void DirDel(Dir dir, bool IsRecursion = true) - { - throw new NotImplementedException(); - } -} +// public override void DirDel(Dir dir, bool IsRecursion = true) +// { +// throw new NotImplementedException(); +// } +// } -public enum FileAccess -{ - Read, - Write, - Delete, - Execute -} +// public enum FileAccess +// { +// Read, +// Write, +// Delete, +// Execute +// } -public enum DirAcess -{ - /// - /// 读取权限 - /// - Read, +// public enum DirAcess +// { +// /// +// /// 读取权限 +// /// +// Read, - /// - /// 写入权限 - /// - Write, +// /// +// /// 写入权限 +// /// +// Write, - /// - /// 列出文件夹权限 - /// - ListDirectory, +// /// +// /// 列出文件夹权限 +// /// +// ListDirectory, - /// - /// 创建文件权限 - /// - CreateFiles, +// /// +// /// 创建文件权限 +// /// +// CreateFiles, - /// - /// 创建文件夹权限 - /// - CreateDirectories, +// /// +// /// 创建文件夹权限 +// /// +// CreateDirectories, - /// - /// 删除文件权限 - /// - Delete, +// /// +// /// 删除文件权限 +// /// +// Delete, - /// - /// 删除文件夹及其子文件 - /// - DeleteSubdirectoriesAndFiles, -} +// /// +// /// 删除文件夹及其子文件 +// /// +// DeleteSubdirectoriesAndFiles, +// } -/// -/// 运行此软件的用户与目标软件的用户最好是 一个用户,一个用户组,或者运行此软件的用户具备最高权限。 -/// -public class AccessWrapper -{ - /// - /// - /// - /// - public static void FreeThisDirAccess(string absolutePath) - { - if ( - CheckDirAccess( - absolutePath, - [ - DirAcess.Read, - DirAcess.Write, - DirAcess.Delete, - DirAcess.ListDirectory, - DirAcess.CreateFiles, - DirAcess.CreateDirectories, - DirAcess.DeleteSubdirectoriesAndFiles - ] - ) - ) { } - else - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - DirectoryInfo dirInfo = new(absolutePath); - //获得该文件的访问权限 - var dirSecurity = dirInfo.GetAccessControl(); - //设定文件ACL继承 - InheritanceFlags inherits = - InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit; +// /// +// /// 运行此软件的用户与目标软件的用户最好是 一个用户,一个用户组,或者运行此软件的用户具备最高权限。 +// /// +// public class AccessWrapper +// { +// /// +// /// +// /// +// /// +// public static void FreeThisDirAccess(string absolutePath) +// { +// if ( +// CheckDirAccess( +// absolutePath, +// [ +// DirAcess.Read, +// DirAcess.Write, +// DirAcess.Delete, +// DirAcess.ListDirectory, +// DirAcess.CreateFiles, +// DirAcess.CreateDirectories, +// DirAcess.DeleteSubdirectoriesAndFiles +// ] +// ) +// ) { } +// else +// { +// if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) +// { +// DirectoryInfo dirInfo = new(absolutePath); +// //获得该文件的访问权限 +// var dirSecurity = dirInfo.GetAccessControl(); +// //设定文件ACL继承 +// InheritanceFlags inherits = +// InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit; - var cUser = - ( - WindowsIdentity.GetCurrent().Groups - ?? throw new Exception("GetWindowsIdentity failed. 你需要手动处理发布内容!--检查权限!") - ).FirstOrDefault() ?? throw new NullReferenceException("can't be null"); - FileSystemAccessRule MdfRule = - new( - cUser, - FileSystemRights.Modify, - inherits, - PropagationFlags.InheritOnly, - AccessControlType.Allow - ); - if (dirSecurity.ModifyAccessRule(AccessControlModification.Set, MdfRule, out _)) { } - else - { - throw new Exception("AddAccessRule failed. 你需要手动处理发布内容!--检查权限!"); - } - //设置访问权限 - dirInfo.SetAccessControl(dirSecurity); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - //TODO Linux文件权限 - } - else - { - throw new NotSupportedException( - $"{RuntimeInformation.OSDescription} is not supported." - ); - } - } - } +// var cUser = +// ( +// WindowsIdentity.GetCurrent().Groups +// ?? throw new Exception("GetWindowsIdentity failed. 你需要手动处理发布内容!--检查权限!") +// ).FirstOrDefault() ?? throw new NullReferenceException("can't be null"); +// FileSystemAccessRule MdfRule = +// new( +// cUser, +// FileSystemRights.Modify, +// inherits, +// PropagationFlags.InheritOnly, +// AccessControlType.Allow +// ); +// if (dirSecurity.ModifyAccessRule(AccessControlModification.Set, MdfRule, out _)) { } +// else +// { +// throw new Exception("AddAccessRule failed. 你需要手动处理发布内容!--检查权限!"); +// } +// //设置访问权限 +// dirInfo.SetAccessControl(dirSecurity); +// } +// else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +// { +// //TODO Linux文件权限 +// } +// else +// { +// throw new NotSupportedException( +// $"{RuntimeInformation.OSDescription} is not supported." +// ); +// } +// } +// } - public static void FreeThisFileAccess(string absolutePath) - { - if ( - CheckFileAccess( - absolutePath, - [FileAccess.Read, FileAccess.Write, FileAccess.Delete, FileAccess.Execute] - ) - ) { } - else - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - FileInfo fileInfo = new(absolutePath); - //获得该文件的访问权限 - FileSecurity fileSecurity = fileInfo.GetAccessControl(); +// public static void FreeThisFileAccess(string absolutePath) +// { +// if ( +// CheckFileAccess( +// absolutePath, +// [FileAccess.Read, FileAccess.Write, FileAccess.Delete, FileAccess.Execute] +// ) +// ) { } +// else +// { +// if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) +// { +// FileInfo fileInfo = new(absolutePath); +// //获得该文件的访问权限 +// FileSecurity fileSecurity = fileInfo.GetAccessControl(); - var cUser = - ( - WindowsIdentity.GetCurrent().Groups - ?? throw new NullReferenceException( - "GetWindowsIdentity failed. 你需要手动处理发布内容!-- 检查权限" - ) - ).FirstOrDefault() ?? throw new NullReferenceException("can't be null"); - FileSystemAccessRule MdfRule = - new(cUser, FileSystemRights.Modify, AccessControlType.Allow); - if (fileSecurity.ModifyAccessRule(AccessControlModification.Set, MdfRule, out _)) - { } - else - { - throw new Exception("AddAccessRule failed. 你需要手动处理发布内容!--检查权限"); - } - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - //TODO Linux文件权限 - } - else - { - throw new NotSupportedException( - $"{RuntimeInformation.OSDescription} is not supported." - ); - } - } - } +// var cUser = +// ( +// WindowsIdentity.GetCurrent().Groups +// ?? throw new NullReferenceException( +// "GetWindowsIdentity failed. 你需要手动处理发布内容!-- 检查权限" +// ) +// ).FirstOrDefault() ?? throw new NullReferenceException("can't be null"); +// FileSystemAccessRule MdfRule = +// new(cUser, FileSystemRights.Modify, AccessControlType.Allow); +// if (fileSecurity.ModifyAccessRule(AccessControlModification.Set, MdfRule, out _)) +// { } +// else +// { +// throw new Exception("AddAccessRule failed. 你需要手动处理发布内容!--检查权限"); +// } +// } +// else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +// { +// //TODO Linux文件权限 +// } +// else +// { +// throw new NotSupportedException( +// $"{RuntimeInformation.OSDescription} is not supported." +// ); +// } +// } +// } - public static bool CheckDirAccess(string absolutePath, DirAcess[] access) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - DirectoryInfo dirInfo = new(absolutePath); - //获得该文件的访问权限 - var dirSecurity = dirInfo.GetAccessControl(); - var ac = dirSecurity - .GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)) - .Cast(); -#pragma warning disable CA1416 // Validate platform compatibility - var it = - from i in ac - where - ( - WindowsIdentity.GetCurrent().Groups - ?? throw new NullReferenceException("未能获取当前用户组!") - ).Contains(i.IdentityReference) - select i; -#pragma warning restore CA1416 // Validate platform compatibility - List caccess = []; - foreach (var i in it) - { - if (i.FileSystemRights.HasFlag(FileSystemRights.FullControl)) - { - return true; - } - if ( - i.FileSystemRights.HasFlag( - FileSystemRights.Read - | FileSystemRights.Modify - | FileSystemRights.ReadAndExecute - ) - ) - { - caccess.Add(DirAcess.Read); - } - if (i.FileSystemRights.HasFlag(FileSystemRights.Write | FileSystemRights.Modify)) - { - caccess.Add(DirAcess.Write); - } - if (i.FileSystemRights.HasFlag(FileSystemRights.Modify | FileSystemRights.Delete)) - { - caccess.Add(DirAcess.Delete); - } - if ( - i.FileSystemRights.HasFlag( - FileSystemRights.ListDirectory | FileSystemRights.Modify - ) - ) - { - caccess.Add(DirAcess.ListDirectory); - } - if ( - i.FileSystemRights.HasFlag( - FileSystemRights.CreateFiles | FileSystemRights.Modify - ) - ) - { - caccess.Add(DirAcess.CreateFiles); - } - if ( - i.FileSystemRights.HasFlag( - FileSystemRights.CreateDirectories | FileSystemRights.Modify - ) - ) - { - caccess.Add(DirAcess.CreateDirectories); - } - if ( - i.FileSystemRights.HasFlag( - FileSystemRights.DeleteSubdirectoriesAndFiles | FileSystemRights.Modify - ) - ) - { - caccess.Add(DirAcess.DeleteSubdirectoriesAndFiles); - } - } - foreach (var i in access) - { - if (!caccess.Contains(i)) - { - return false; - } - } - return true; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - //TODO Linux文件权限 - return true; - } - else - { - throw new NotSupportedException( - $"{RuntimeInformation.OSDescription} is not supported." - ); - } - } +// public static bool CheckDirAccess(string absolutePath, DirAcess[] access) +// { +// if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) +// { +// DirectoryInfo dirInfo = new(absolutePath); +// //获得该文件的访问权限 +// var dirSecurity = dirInfo.GetAccessControl(); +// var ac = dirSecurity +// .GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)) +// .Cast(); +// #pragma warning disable CA1416 // Validate platform compatibility +// var it = +// from i in ac +// where +// ( +// WindowsIdentity.GetCurrent().Groups +// ?? throw new NullReferenceException("未能获取当前用户组!") +// ).Contains(i.IdentityReference) +// select i; +// #pragma warning restore CA1416 // Validate platform compatibility +// List caccess = []; +// foreach (var i in it) +// { +// if (i.FileSystemRights.HasFlag(FileSystemRights.FullControl)) +// { +// return true; +// } +// if ( +// i.FileSystemRights.HasFlag( +// FileSystemRights.Read +// | FileSystemRights.Modify +// | FileSystemRights.ReadAndExecute +// ) +// ) +// { +// caccess.Add(DirAcess.Read); +// } +// if (i.FileSystemRights.HasFlag(FileSystemRights.Write | FileSystemRights.Modify)) +// { +// caccess.Add(DirAcess.Write); +// } +// if (i.FileSystemRights.HasFlag(FileSystemRights.Modify | FileSystemRights.Delete)) +// { +// caccess.Add(DirAcess.Delete); +// } +// if ( +// i.FileSystemRights.HasFlag( +// FileSystemRights.ListDirectory | FileSystemRights.Modify +// ) +// ) +// { +// caccess.Add(DirAcess.ListDirectory); +// } +// if ( +// i.FileSystemRights.HasFlag( +// FileSystemRights.CreateFiles | FileSystemRights.Modify +// ) +// ) +// { +// caccess.Add(DirAcess.CreateFiles); +// } +// if ( +// i.FileSystemRights.HasFlag( +// FileSystemRights.CreateDirectories | FileSystemRights.Modify +// ) +// ) +// { +// caccess.Add(DirAcess.CreateDirectories); +// } +// if ( +// i.FileSystemRights.HasFlag( +// FileSystemRights.DeleteSubdirectoriesAndFiles | FileSystemRights.Modify +// ) +// ) +// { +// caccess.Add(DirAcess.DeleteSubdirectoriesAndFiles); +// } +// } +// foreach (var i in access) +// { +// if (!caccess.Contains(i)) +// { +// return false; +// } +// } +// return true; +// } +// else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +// { +// //TODO Linux文件权限 +// return true; +// } +// else +// { +// throw new NotSupportedException( +// $"{RuntimeInformation.OSDescription} is not supported." +// ); +// } +// } - public static bool CheckFileAccess(string absolutePath, FileAccess[] access) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - FileInfo fileInfo = new(absolutePath); - //获得该文件的访问权限 - FileSecurity fileSecurity = fileInfo.GetAccessControl(); +// public static bool CheckFileAccess(string absolutePath, FileAccess[] access) +// { +// if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) +// { +// FileInfo fileInfo = new(absolutePath); +// //获得该文件的访问权限 +// FileSecurity fileSecurity = fileInfo.GetAccessControl(); - var ac = fileSecurity - .GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)) - .Cast(); -#pragma warning disable CA1416 // 验证平台兼容性 - var it = - from i in ac - where - ( - WindowsIdentity.GetCurrent().Groups - ?? throw new NullReferenceException("未能获取当前用户组!") - ).Contains(i.IdentityReference) - select i; -#pragma warning restore CA1416 // 验证平台兼容性 +// var ac = fileSecurity +// .GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)) +// .Cast(); +// #pragma warning disable CA1416 // 验证平台兼容性 +// var it = +// from i in ac +// where +// ( +// WindowsIdentity.GetCurrent().Groups +// ?? throw new NullReferenceException("未能获取当前用户组!") +// ).Contains(i.IdentityReference) +// select i; +// #pragma warning restore CA1416 // 验证平台兼容性 - List caccess = []; - foreach (var i in it) - { - if (i.FileSystemRights.HasFlag(FileSystemRights.FullControl)) - { - return true; - } - if (i.FileSystemRights.HasFlag(FileSystemRights.Read | FileSystemRights.Modify)) - { - caccess.Add(FileAccess.Read); - } - if (i.FileSystemRights.HasFlag(FileSystemRights.Write | FileSystemRights.Modify)) - { - caccess.Add(FileAccess.Write); - } - if (i.FileSystemRights.HasFlag(FileSystemRights.Write | FileSystemRights.Modify)) - { - caccess.Add(FileAccess.Delete); - } - if ( - i.FileSystemRights.HasFlag( - FileSystemRights.ExecuteFile | FileSystemRights.Modify - ) - ) - { - caccess.Add(FileAccess.Execute); - } - } - foreach (var i in access) - { - if (!caccess.Contains(i)) - { - return false; - } - } - return true; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - //TODO Linux文件夹权限 - return true; - } - else - { - throw new NotSupportedException( - $"{RuntimeInformation.OSDescription} is not supported." - ); - } - } +// List caccess = []; +// foreach (var i in it) +// { +// if (i.FileSystemRights.HasFlag(FileSystemRights.FullControl)) +// { +// return true; +// } +// if (i.FileSystemRights.HasFlag(FileSystemRights.Read | FileSystemRights.Modify)) +// { +// caccess.Add(FileAccess.Read); +// } +// if (i.FileSystemRights.HasFlag(FileSystemRights.Write | FileSystemRights.Modify)) +// { +// caccess.Add(FileAccess.Write); +// } +// if (i.FileSystemRights.HasFlag(FileSystemRights.Write | FileSystemRights.Modify)) +// { +// caccess.Add(FileAccess.Delete); +// } +// if ( +// i.FileSystemRights.HasFlag( +// FileSystemRights.ExecuteFile | FileSystemRights.Modify +// ) +// ) +// { +// caccess.Add(FileAccess.Execute); +// } +// } +// foreach (var i in access) +// { +// if (!caccess.Contains(i)) +// { +// return false; +// } +// } +// return true; +// } +// else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +// { +// //TODO Linux文件夹权限 +// return true; +// } +// else +// { +// throw new NotSupportedException( +// $"{RuntimeInformation.OSDescription} is not supported." +// ); +// } +// } - /// - /// 获取当前用户 - /// - /// - /// +// /// +// /// 获取当前用户 +// /// +// /// +// /// - public static string GetCurrentUser() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return System.Security.Principal.WindowsIdentity.GetCurrent().Name; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return Environment.UserName; - } - else - { - throw new NotSupportedException( - $"{RuntimeInformation.OSDescription} is not supported." - ); - } - } -} +// public static string GetCurrentUser() +// { +// if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) +// { +// return System.Security.Principal.WindowsIdentity.GetCurrent().Name; +// } +// else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +// { +// return Environment.UserName; +// } +// else +// { +// throw new NotSupportedException( +// $"{RuntimeInformation.OSDescription} is not supported." +// ); +// } +// } +// } diff --git a/Server/LocalServer/Controllers/LocalServerController.cs b/Server/LocalServer/Controllers/LocalServerController.cs index 19fee47..8867b9b 100644 --- a/Server/LocalServer/Controllers/LocalServerController.cs +++ b/Server/LocalServer/Controllers/LocalServerController.cs @@ -10,36 +10,11 @@ namespace LocalServer.Controllers { private readonly LocalSyncServerFactory Factory = factory; - private static async Task Echo(WebSocket webSocket) - { - var buffer = new byte[1024 * 4]; - var receiveResult = await webSocket.ReceiveAsync( - new ArraySegment(buffer), - CancellationToken.None - ); - - while (!receiveResult.CloseStatus.HasValue) - { - await webSocket.SendAsync( - new ArraySegment(buffer, 0, receiveResult.Count), - receiveResult.MessageType, - receiveResult.EndOfMessage, - CancellationToken.None - ); - - receiveResult = await webSocket.ReceiveAsync( - new ArraySegment(buffer), - CancellationToken.None - ); - } - - await webSocket.CloseAsync( - receiveResult.CloseStatus.Value, - receiveResult.CloseStatusDescription, - CancellationToken.None - ); - } - + /// + /// websoc 连接入口 + /// + /// + /// [Route("/websoc")] public async Task WebsocketConnection(string Name) { @@ -50,8 +25,8 @@ namespace LocalServer.Controllers if (Factory.GetServerByName(Name) == null) { var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); - //await Echo(webSocket); var pipeLine = new WebSocPipeLine(webSocket, false); + //必须在此保留连接的上下文,否则 websocket 就直接断了。。。微软 这个设计措不及防 await Factory.CreateLocalSyncServer( pipeLine, Name, @@ -75,18 +50,18 @@ namespace LocalServer.Controllers } } - [Route("/macaddr")] - public string GetMacAddress() - { - NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); - string macaddrs = ""; - foreach (NetworkInterface nic in nics) - { - PhysicalAddress physicalAddress = nic.GetPhysicalAddress(); - macaddrs += physicalAddress.ToString() + ";"; - } - return macaddrs; - } - //TODO 是否在本地记载同步日志? + // [Route("/macaddr")] + // public string GetMacAddress() + // { + // NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); + // string macaddrs = ""; + // foreach (NetworkInterface nic in nics) + // { + // PhysicalAddress physicalAddress = nic.GetPhysicalAddress(); + // macaddrs += physicalAddress.ToString() + ";"; + // } + // return macaddrs; + // } + // //TODO 是否在本地记载同步日志? } } diff --git a/Server/LocalServer/LocalSyncServer.cs b/Server/LocalServer/LocalSyncServer.cs index 219ce86..bdf9011 100644 --- a/Server/LocalServer/LocalSyncServer.cs +++ b/Server/LocalServer/LocalSyncServer.cs @@ -8,63 +8,76 @@ public class LocalSyncServer #pragma warning disable CA2211 // Non-constant fields should not be visible public static string TempRootFile = "C:/TempPack"; public static string SqlPackageAbPath = "sqlpackage"; - + // 使用msdeploy 将会打包当前可运行的内容,它很有可能不包含最新的构建 //public static string MsdeployAbPath = "msdeploy"; //与visual studio 匹配的Msbuild 路径。在vs 中打开power shell 命令行,使用 `(get-Command -Name msbuild).Source ` + //使用msbuild 会缺少.net frame的运行环境 bin\roslyn 里面的内容,第一次需要人为复制一下,后面就就好了。 public static string MSBuildAbPath = "MSBuild"; #pragma warning restore CA2211 // Non-constant fields should not be visible + + /// + /// 连接状态,流程管理,LocalPipe 和 remotePipe 也在此处交换信息 + /// private StateHelpBase StateHelper; + public void SetStateHelper(StateHelpBase helper) { StateHelper = helper; } - public static string GetProjectOutPath(string project) - { - try - { - XDocument xdoc = XDocument.Load(project); - // 获取根元素 - XElement rootElement = xdoc.Root ?? throw new NullReferenceException("Root"); - Console.WriteLine("根元素: " + rootElement.Name); + /// + /// 查找构建xml文件,获取构建信息,那这个不用了 + /// + /// + // public static string GetProjectOutPath(string project) + // { + // try + // { + // XDocument xdoc = XDocument.Load(project); + // // 获取根元素 + // XElement rootElement = xdoc.Root ?? throw new NullReferenceException("Root"); + // Console.WriteLine("根元素: " + rootElement.Name); - // 遍历子节点 - foreach (XElement element in rootElement.Elements()) - { - if (element.Name.LocalName.Contains("PropertyGroup")) - { - var Conditon = element.Attribute("Condition"); + // // 遍历子节点 + // foreach (XElement element in rootElement.Elements()) + // { + // if (element.Name.LocalName.Contains("PropertyGroup")) + // { + // var Conditon = element.Attribute("Condition"); - if (Conditon != null) - { - if (Conditon.Value.Contains("Release")) - { - foreach (XElement element2 in element.Elements()) - { - if (element2.Name.LocalName == "OutputPath") - { - return element2.Value; - } - } - } - } - } - } - return "bin/"; - } - catch (Exception) - { - return "bin/"; - } - } + // if (Conditon != null) + // { + // if (Conditon.Value.Contains("Release")) + // { + // foreach (XElement element2 in element.Elements()) + // { + // if (element2.Name.LocalName == "OutputPath") + // { + // return element2.Value; + // } + // } + // } + // } + // } + // } + // return "bin/"; + // } + // catch (Exception) + // { + // return "bin/"; + // } + // } public StateHelpBase GetStateHelper() { return StateHelper; } + /// + /// 此次发布的配置 + /// public Config? SyncConfig; public Config NotNullSyncConfig @@ -79,13 +92,19 @@ public class LocalSyncServer } } + /// + /// 发布的名称 + /// public string Name; /// - /// 发布源连接 + /// jswebsocket 和 local server 的连接,它没有加密 /// public readonly AbsPipeLine LocalPipe; + /// + /// local server 和 remote server 的连接,它有加密 + /// public readonly AbsPipeLine RemotePipe; /// @@ -107,6 +126,10 @@ public class LocalSyncServer RemotePipe = remotePipe; } + /// + /// 这个阻塞在,接口中有http上下文处 + /// + /// public async Task Connect() { try @@ -125,6 +148,10 @@ public class LocalSyncServer } } + /// + /// 关闭连接 + /// + /// public void Close(string? CloseReason) { try diff --git a/Server/LocalServer/StateHelper.cs b/Server/LocalServer/StateHelper.cs index dd7fc00..3981388 100644 --- a/Server/LocalServer/StateHelper.cs +++ b/Server/LocalServer/StateHelper.cs @@ -2,19 +2,10 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; -using System.Text.Json.Serialization; using Common; namespace LocalServer; -// enum StateWhenMsg -// { -// Authority = 0, -// ConfigInfo = 1, -// LocalPackAndUpload = 2, -// RemoteUnPackAndRelease = 3, -// } - public abstract class StateHelpBase( LocalSyncServer context, SyncProcessStep step = SyncProcessStep.Connect @@ -147,18 +138,11 @@ public class DeployHelper(LocalSyncServer context) h.DiffProcess(); } else - { + { // msbuild 只在windows 才有 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { //构建 - //var OutputPath = LocalSyncServer.GetProjectOutPath( - // Context.NotNullSyncConfig.LocalProjectAbsolutePath - //); - //var AbOutPath = Path.Combine( - // Context.NotNullSyncConfig.LocalProjectAbsolutePath, - // OutputPath - //); ProcessStartInfo startbuildInfo = new() { @@ -334,59 +318,52 @@ public class DeployMSSqlHelper(LocalSyncServer context) } else { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + var arguments = + $" /Action:Extract /TargetFile:{LocalSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id.ToString()}/{Context.NotNullSyncConfig.Id.ToString()}.dacpac" + // 不要log file 了 + //+ $" /DiagnosticsFile:{LocalSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id.ToString()}/{Context.NotNullSyncConfig.Id.ToString()}.log" + + $" /p:ExtractAllTableData=false /p:VerifyExtraction=true /SourceServerName:{Context.NotNullSyncConfig.SrcDb.ServerName}" + + $" /SourceDatabaseName:{Context.NotNullSyncConfig.SrcDb.DatebaseName} /SourceUser:{Context.NotNullSyncConfig.SrcDb.User}" + + $" /SourcePassword:{Context.NotNullSyncConfig.SrcDb.Password} /SourceTrustServerCertificate:{Context.NotNullSyncConfig.SrcDb.TrustServerCertificate}" + + $" /p:ExtractReferencedServerScopedElements=False /p:IgnoreUserLoginMappings=True /p:IgnorePermissions=True"; + if (Context.NotNullSyncConfig.SrcDb.SyncTablesData != null) { - var arguments = - $" /Action:Extract /TargetFile:{LocalSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id.ToString()}/{Context.NotNullSyncConfig.Id.ToString()}.dacpac" - // 不要log file 了 - //+ $" /DiagnosticsFile:{LocalSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id.ToString()}/{Context.NotNullSyncConfig.Id.ToString()}.log" - + $" /p:ExtractAllTableData=false /p:VerifyExtraction=true /SourceServerName:{Context.NotNullSyncConfig.SrcDb.ServerName}" - + $" /SourceDatabaseName:{Context.NotNullSyncConfig.SrcDb.DatebaseName} /SourceUser:{Context.NotNullSyncConfig.SrcDb.User}" - + $" /SourcePassword:{Context.NotNullSyncConfig.SrcDb.Password} /SourceTrustServerCertificate:{Context.NotNullSyncConfig.SrcDb.TrustServerCertificate}" - + $" /p:ExtractReferencedServerScopedElements=False /p:IgnoreUserLoginMappings=True /p:IgnorePermissions=True"; - if (Context.NotNullSyncConfig.SrcDb.SyncTablesData != null) + foreach (var t in Context.NotNullSyncConfig.SrcDb.SyncTablesData) { - foreach (var t in Context.NotNullSyncConfig.SrcDb.SyncTablesData) - { - arguments += $" /p:TableData={t}"; - } + arguments += $" /p:TableData={t}"; } + } - ProcessStartInfo startInfo = - new() - { - FileName = LocalSyncServer.SqlPackageAbPath, // The command to execute (can be any command line tool) - Arguments = arguments, - StandardOutputEncoding = System.Text.Encoding.UTF8, - // The arguments to pass to the command (e.g., list directory contents) - RedirectStandardOutput = true, // Redirect the standard output to a string - UseShellExecute = false, // Do not use the shell to execute the command - CreateNoWindow = true // Do not create a new window for the command - }; - using Process process = new() { StartInfo = startInfo }; - // Start the process - process.Start(); - - // Read the output from the process - string output = process.StandardOutput.ReadToEnd(); - - // Wait for the process to exit - process.WaitForExit(); - - if (process.ExitCode == 0) + ProcessStartInfo startInfo = + new() { - Context.LocalPipe.SendMsg(CreateMsg("数据库打包成功!")).Wait(); - PackAndSwitchNext(); - } - else - { - Context.LocalPipe.SendMsg(CreateErrMsg(output)).Wait(); - throw new Exception("执行发布错误,错误信息参考上一条消息!"); - } + FileName = LocalSyncServer.SqlPackageAbPath, // The command to execute (can be any command line tool) + Arguments = arguments, + StandardOutputEncoding = System.Text.Encoding.UTF8, + // The arguments to pass to the command (e.g., list directory contents) + RedirectStandardOutput = true, // Redirect the standard output to a string + UseShellExecute = false, // Do not use the shell to execute the command + CreateNoWindow = true // Do not create a new window for the command + }; + using Process process = new() { StartInfo = startInfo }; + // Start the process + process.Start(); + + // Read the output from the process + string output = process.StandardOutput.ReadToEnd(); + + // Wait for the process to exit + process.WaitForExit(); + + if (process.ExitCode == 0) + { + Context.LocalPipe.SendMsg(CreateMsg("数据库打包成功!")).Wait(); + PackAndSwitchNext(); } else { - throw new NotSupportedException("只支持windows!"); + Context.LocalPipe.SendMsg(CreateErrMsg(output)).Wait(); + throw new Exception("执行发布错误,错误信息参考上一条消息!"); } } } @@ -410,6 +387,7 @@ public class UploadPackedHelper(LocalSyncServer context) $"{LocalSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id}.zip", (double current) => { + //这里可能需要降低获取上传进度的频率 Context .LocalPipe.SendMsg(CreateMsg(current.ToString(), SyncMsgType.Process)) .Wait(); @@ -449,223 +427,4 @@ public class FinallyPublishHelper(LocalSyncServer context) { Context.LocalPipe.SendMsg(msg).Wait(); } -} -// /// -// /// 0. 发布源验证密码 -// /// -// /// -// public class LocalAuthorityState(LocalSyncServer context) : StateHelpBase(context) -// { -// public override void HandleRemoteMsg(SyncMsg? msg) -// { -// throw new NotImplementedException("error usage!"); -// } - -// public override void HandleLocalMsg(SyncMsg? msg) -// { -// if (msg == null) -// { -// return; -// } -// else -// { -// string Pwd = msg.Body; -// if (Pwd == "Xfs1%$@_fdYU.>>") -// { -// Context.LocalSocketSendMsg(new SyncMsg(SyncMsgType.Success, "源服务密码校验成功!")); -// Context.StateHelper = new WaitingConfigInfoState(Context); -// } -// else -// { -// throw new UnauthorizedAccessException("pwd error!"); -// } -// } -// } -// } - -// /// -// /// 1. 获取配置信息,它包含目标的服务器的配置信息 -// /// -// /// -// public class WaitingConfigInfoState(LocalSyncServer context) : StateHelpBase(context) -// { -// public override void HandleRemoteMsg(SyncMsg? msg) { } - -// public override void HandleLocalMsg(SyncMsg? msg) -// { -// if (msg == null) -// { -// return; -// } -// else -// { -// string ConfigInfo = msg.Body; -// Context.SyncConfig = -// JsonSerializer.Deserialize(ConfigInfo) -// ?? throw new NullReferenceException("ConfigInfo is null"); -// var task = Context.RemoteSocket.ConnectAsync( -// new Uri(Context.SyncConfig.RemoteUrl), -// CancellationToken.None -// ); -// if (task.Wait(10000)) -// { -// if (Context.RemoteSocket.State == WebSocketState.Open) -// { -// var state = new RemoteAuthorityState(Context); -// state.SendPwdToRemoteServer(); -// Context.StateHelper = state; -// } -// else -// { -// throw new Exception("connect remote server failed!"); -// } -// } -// else -// { -// throw new TimeoutException("connect remote server timeout"); -// } -// } -// } -// } - -// /// -// /// 2. 目标服务器权限校验 -// /// -// /// -// public class RemoteAuthorityState(LocalSyncServer context) : StateHelpBase(context) -// { -// public override void HandleRemoteMsg(SyncMsg? msg) -// { -// if (msg == null) -// { -// return; -// } -// else -// { -// if (msg.Type == SyncMsgType.Success) -// { -// Context.LocalSocketSendMsg(new SyncMsg(SyncMsgType.Success, "远程服务器校验成功!")); -// var diffState = new DirFilesDiffState(Context); -// diffState.SendSyncConfigToRemote(); -// Context.StateHelper = diffState; -// } -// else -// { -// throw new Exception("远程服务器权限校验失败,请检查Local Server 的Mac地址是否在 Remote Server 的允许列表内!"); -// } -// } -// } - -// public override void HandleLocalMsg(SyncMsg? msg) { } - -// public void SendPwdToRemoteServer() -// { -// var authorityInfo = new -// { -// Pwd = "xfs@#123hd??1>>|12#4", -// MacAdr = new LocalServer.Controllers.LocalServerController( -// Context.Factory -// ).GetMacAddress() -// }; -// Context.RemoteSocketSendMsg(authorityInfo); -// } -// } - -// /// -// /// 3. 文件比较 -// /// -// /// -// public class DirFilesDiffState(LocalSyncServer context) : StateHelpBase(context) -// { -// public override void HandleRemoteMsg(SyncMsg? msg) -// { -// if (msg == null) -// { -// return; -// } -// else -// { -// if (msg.IsSuccess) -// { -// var state = new LocalPackAndUploadState(Context); -// state.PackDiffDir(msg); -// Context.StateHelper = state; -// } -// else -// { -// throw new Exception(msg.Body); -// } -// } -// } - -// public override void HandleLocalMsg(SyncMsg? msg) { } - -// public void SendSyncConfigToRemote() -// { -// Context.RemoteSocketSendMsg( -// Context.SyncConfig -// ?? throw new NullReferenceException("SyncConfig should't be null here!") -// ); -// } -// } - -// /// -// /// 4. 本地打包并上传 -// /// -// /// -// public class LocalPackAndUploadState(LocalSyncServer context) : StateHelpBase(context) -// { -// public override void HandleRemoteMsg(SyncMsg? msg) -// { -// if (msg == null) -// { -// return; -// } -// else { } -// } - -// public override void HandleLocalMsg(SyncMsg? msg) { } - -// /// -// /// 打包文件 -// /// -// /// -// /// -// public void PackDiffDir(SyncMsg msg) -// { -// if (msg.IsSuccess) -// { -// var diff = JsonSerializer.Deserialize(msg.Body); - -// Context.LocalSocketSendMsg(new SyncMsg(SyncMsgType.Success, "文件打包完成!")); -// Context.LocalSocketSendMsg(new SyncMsg(SyncMsgType.Success, "文件上传完成!")); -// } -// else -// { -// throw new Exception(msg.Body); -// } -// } - -// private void UploadPackedFiles(string absolutePath) -// { -// //TODO 传递上传进度到前端。 -// } -// } - -// /// -// /// 5. 目标服务器解包并发布 -// /// -// /// -// public class RemoteUnPackAndReleaseState(LocalSyncServer context) : StateHelpBase(context) -// { -// public override void HandleRemoteMsg(SyncMsg? msg) -// { -// if (msg == null) -// { -// return; -// } -// else { } -// } - -// public override void HandleLocalMsg(SyncMsg? msg) { } -// } +} \ No newline at end of file diff --git a/Server/RemoteServer/Controllers/RemoteServerController.cs b/Server/RemoteServer/Controllers/RemoteServerController.cs index 8d3acfa..33f79c5 100644 --- a/Server/RemoteServer/Controllers/RemoteServerController.cs +++ b/Server/RemoteServer/Controllers/RemoteServerController.cs @@ -24,8 +24,6 @@ public class SyncFilesController(RemoteSyncServerFactory factory, SqliteDbContex var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); var pipeLine = new WebSocPipeLine(webSocket, true); await Factory.CreateRemoteSyncServer(pipeLine, Name); - - var x = 11; } else { @@ -43,127 +41,11 @@ public class SyncFilesController(RemoteSyncServerFactory factory, SqliteDbContex HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest; } } - - [HttpGet("/GetSyncFilesLogs")] - public IActionResult GetSyncFilesLogs( - string? ClientName, - int? Status, - DateTime? SyncTimeStart, - DateTime? SyncTimeEnd, - int page, - int rows - ) - { - var item = - from i in _db.SyncLogHeads - where - ( - string.IsNullOrEmpty(ClientName) - || (i.ClientName != null && i.ClientName.Contains(ClientName)) - ) - && (Status == null || i.Status == Status) - && (SyncTimeStart == null || i.SyncTime >= SyncTimeStart) - && (SyncTimeEnd == null || i.SyncTime <= SyncTimeEnd) - orderby i.Id descending - select new - { - Head = i, - Files = (from j in _db.SyncLogFiles where j.HeadId == i.Id select j).ToList() - }; - - return Ok(item.Skip((page - 1) * rows).Take(rows).ToList()); - } - - public class InputFileInfo - { - public required string RelativePath { get; set; } - public DateTime MTime { get; set; } - } - - public class OutputFileInfo : InputFileInfo - { - /// - /// 0 新增 1 修改 2 删除 - /// - public int ServerOpType { get; set; } - } - - public class ServerOpFileInfo : OutputFileInfo - { - public required string ServerRootDirPath { get; set; } - public required string ClientRootDirPath { get; set; } - } - - public class InputFiles - { - public required string ServerRootDirPath { get; set; } - - /// - /// 0 special 1 exclude - /// - public int Type { get; set; } - public List? Files { get; set; } - } - - public class ServerOpFiles - { - public required string ServerRootDirPath { get; set; } - public string? ClientRootDirPath { get; set; } - public List? Files { get; set; } - } - - [HttpPost("/GetFilesInfoByDir")] - public IActionResult GetFilesInfoByDir([FromBody] InputFiles inputFiles) - { - return Ok(new { IsSuccess = true }); - } - - [HttpPost("/InitASync")] - public IActionResult InitASync([FromBody] SyncLogHead head) - { - try - { - var CurrentSyncTaskCount = ( - from i in _db.SyncLogHeads - where i.Status == 0 - select i - ).Count(); - if (CurrentSyncTaskCount > 0) - { - throw new Exception("存在未完成的任务,请等待完成!"); - } - head.Id = Guid.NewGuid(); - head.SyncTime = DateTime.Now; - head.Status = 0; - _db.SyncLogHeads.Add(head); - _db.SaveChanges(); - return Ok(new { IsSuccess = true, head.Id }); - } - catch (Exception e) - { - return Ok(new { IsSuccess = false, e.Message }); - } - } - - [HttpGet("/CloseASync")] - public IActionResult CloseASync(Guid Id, string Message, int Status) - { - try - { - var current = - (from i in _db.SyncLogHeads where i.Id == Id select i).FirstOrDefault() - ?? throw new Exception("任务不存在!"); - current.Status = Status; - current.Message = Message; - _db.SaveChanges(); - return Ok(new { IsSuccess = true }); - } - catch (Exception e) - { - return Ok(new { IsSuccess = false, e.Message }); - } - } - + /// + /// 上传文件 + /// + /// + /// [HttpPost("/UploadFile")] public async Task UploadFile(IFormFile file) { @@ -199,4 +81,124 @@ public class SyncFilesController(RemoteSyncServerFactory factory, SqliteDbContex return StatusCode(500, new { IsSuccess = false, Message = $"上传文件失败: {ex.Message}" }); } } + + // [HttpGet("/GetSyncFilesLogs")] + // public IActionResult GetSyncFilesLogs( + // string? ClientName, + // int? Status, + // DateTime? SyncTimeStart, + // DateTime? SyncTimeEnd, + // int page, + // int rows + // ) + // { + // var item = + // from i in _db.SyncLogHeads + // where + // ( + // string.IsNullOrEmpty(ClientName) + // || (i.ClientName != null && i.ClientName.Contains(ClientName)) + // ) + // && (Status == null || i.Status == Status) + // && (SyncTimeStart == null || i.SyncTime >= SyncTimeStart) + // && (SyncTimeEnd == null || i.SyncTime <= SyncTimeEnd) + // orderby i.Id descending + // select new + // { + // Head = i, + // Files = (from j in _db.SyncLogFiles where j.HeadId == i.Id select j).ToList() + // }; + + // return Ok(item.Skip((page - 1) * rows).Take(rows).ToList()); + // } + + // public class InputFileInfo + // { + // public required string RelativePath { get; set; } + // public DateTime MTime { get; set; } + // } + + // public class OutputFileInfo : InputFileInfo + // { + // /// + // /// 0 新增 1 修改 2 删除 + // /// + // public int ServerOpType { get; set; } + // } + + // public class ServerOpFileInfo : OutputFileInfo + // { + // public required string ServerRootDirPath { get; set; } + // public required string ClientRootDirPath { get; set; } + // } + + // public class InputFiles + // { + // public required string ServerRootDirPath { get; set; } + + // /// + // /// 0 special 1 exclude + // /// + // public int Type { get; set; } + // public List? Files { get; set; } + // } + + // public class ServerOpFiles + // { + // public required string ServerRootDirPath { get; set; } + // public string? ClientRootDirPath { get; set; } + // public List? Files { get; set; } + // } + + // [HttpPost("/GetFilesInfoByDir")] + // public IActionResult GetFilesInfoByDir([FromBody] InputFiles inputFiles) + // { + // return Ok(new { IsSuccess = true }); + // } + + // [HttpPost("/InitASync")] + // public IActionResult InitASync([FromBody] SyncLogHead head) + // { + // try + // { + // var CurrentSyncTaskCount = ( + // from i in _db.SyncLogHeads + // where i.Status == 0 + // select i + // ).Count(); + // if (CurrentSyncTaskCount > 0) + // { + // throw new Exception("存在未完成的任务,请等待完成!"); + // } + // head.Id = Guid.NewGuid(); + // head.SyncTime = DateTime.Now; + // head.Status = 0; + // _db.SyncLogHeads.Add(head); + // _db.SaveChanges(); + // return Ok(new { IsSuccess = true, head.Id }); + // } + // catch (Exception e) + // { + // return Ok(new { IsSuccess = false, e.Message }); + // } + // } + + // [HttpGet("/CloseASync")] + // public IActionResult CloseASync(Guid Id, string Message, int Status) + // { + // try + // { + // var current = + // (from i in _db.SyncLogHeads where i.Id == Id select i).FirstOrDefault() + // ?? throw new Exception("任务不存在!"); + // current.Status = Status; + // current.Message = Message; + // _db.SaveChanges(); + // return Ok(new { IsSuccess = true }); + // } + // catch (Exception e) + // { + // return Ok(new { IsSuccess = false, e.Message }); + // } + // } } diff --git a/Server/RemoteServer/Program.cs b/Server/RemoteServer/Program.cs index 8e8aeb7..6c11bb7 100644 --- a/Server/RemoteServer/Program.cs +++ b/Server/RemoteServer/Program.cs @@ -10,10 +10,6 @@ ConfigurationBuilder configurationBuilder = new(); // Add services to the container. //添加配置文件路径 -RemoteSyncServerFactory.NamePwd = -[ - .. (builder.Configuration.GetSection("NamePwds").Get[]>() ?? []) -]; foreach (var x in builder.Configuration.GetSection("NamePwds").GetChildren()) { var it = x.GetChildren(); diff --git a/Server/RemoteServer/RemoteSyncServerFactory.cs b/Server/RemoteServer/RemoteSyncServerFactory.cs index 58f0389..c40f6fa 100644 --- a/Server/RemoteServer/RemoteSyncServerFactory.cs +++ b/Server/RemoteServer/RemoteSyncServerFactory.cs @@ -7,6 +7,10 @@ public class RemoteSyncServerFactory private readonly object Lock = new(); #pragma warning disable CA2211 // Non-constant fields should not be visible + + /// + /// 发布的名称和密码 + /// public static List> NamePwd = []; #pragma warning restore CA2211 // Non-constant fields should not be visible diff --git a/Server/RemoteServer/StateHelper.cs b/Server/RemoteServer/StateHelper.cs index 3f025cd..b6dfa2c 100644 --- a/Server/RemoteServer/StateHelper.cs +++ b/Server/RemoteServer/StateHelper.cs @@ -6,13 +6,6 @@ using Common; namespace RemoteServer; -// enum StateWhenMsg -// { -// Authority = 0, -// ConfigInfo = 1, -// LocalPackAndUpload = 2, -// RemoteUnPackAndRelease = 3, -// } public abstract class StateHelpBase( RemoteSyncServer context, @@ -151,8 +144,6 @@ public class FinallyPublishHelper(RemoteSyncServer context) public void FinallyPublish() { // 发布数据库 - if (Context.NotNullSyncConfig.IsDeployDb) - { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var arguments = @@ -189,11 +180,6 @@ public class FinallyPublishHelper(RemoteSyncServer context) Context.Pipe.SendMsg(CreateErrMsg(output)).Wait(); throw new Exception("执行发布错误,错误信息参考上一条消息!"); } - } - else - { - throw new NotSupportedException("只支持windows!"); - } } else { diff --git a/Server/ServerTest/PipeTest.cs b/Server/ServerTest/PipeTest.cs index cfc4386..0768820 100644 --- a/Server/ServerTest/PipeTest.cs +++ b/Server/ServerTest/PipeTest.cs @@ -1,3 +1,4 @@ +using System.Runtime.InteropServices; using System.Text.Json; using Common; using LocalServer; @@ -13,68 +14,75 @@ public class PipeTest [Fact] public async void TestCase() { - var p1 = new TestPipe(false, "1"); - var x = Task.Run(async () => + //msbuild ֻwindows + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - var rs = p1.Work( - (byte[] b) => - { - var msg = JsonSerializer.Deserialize(b); - if (msg.Body == "ɣ") - { - p1.Close("˳"); - } - Console.WriteLine(b); - return true; - } - ); - await foreach (var r in rs) + var p1 = new TestPipe(false, "1"); + var x = Task.Run(async () => { - Console.WriteLine(r); + var rs = p1.Work( + (byte[] b) => + { + var msg = JsonSerializer.Deserialize(b); +#pragma warning disable CS8602 // Dereference of a possibly null reference. + if (msg.Body == "ɣ") + { + _ = p1.Close("˳"); + } +#pragma warning restore CS8602 // Dereference of a possibly null reference. + Console.WriteLine(b); + return true; + } + ); + await foreach (var r in rs) + { + Console.WriteLine(r); + } + }); + //await p1.Close("sdf"); + //await x; + var p2 = new TestPipe(false, "2"); + p1.Other = p2; + p2.Other = p1; + var p3 = new TestPipe(true, "3"); + var p4 = new TestPipe(true, "4"); + p3.Other = p4; + p4.Other = p3; + LocalSyncServer.TempRootFile = "D:/FileSyncTest/stemp"; + RemoteSyncServer.SqlPackageAbPath = + "C:\\Users\\ZHAOLEI\\.dotnet\\tools\\sqlpackage.exe"; + //LocalSyncServer.MsdeployAbPath = + // "C:\\Program Files\\IIS\\Microsoft Web Deploy V3\\msdeploy.exe"; + LocalSyncServer.SqlPackageAbPath = "C:\\Users\\ZHAOLEI\\.dotnet\\tools\\sqlpackage.exe"; + LocalSyncServer.MSBuildAbPath = + "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\MSBuild.exe"; + RemoteSyncServer.TempRootFile = "D:/FileSyncTest/dtemp"; + RemoteSyncServerFactory.NamePwd = [new Tuple("Test", "t123")]; + var lf = new LocalSyncServerFactory(); + var task1 = Task.Run(async () => + { + await lf.CreateLocalSyncServer(p2, "Test", p3); + }); + + var rf = new RemoteSyncServerFactory(); + + var task2 = Task.Run(async () => + { + await rf.CreateRemoteSyncServer(p4, "Test"); + }); + TestPipe.syncServerFactory = rf; + var starter = new SyncMsg + { + Body = JsonSerializer.Serialize(new PipeSeed().TestConfig), + Type = SyncMsgType.General, + Step = SyncProcessStep.Connect + }; + await p1.SendMsg(starter); + await x; + if (p1.ErrResult != "˳") + { + Assert.Fail(p1.ErrResult); } - }); - //await p1.Close("sdf"); - //await x; - var p2 = new TestPipe(false, "2"); - p1.Other = p2; - p2.Other = p1; - var p3 = new TestPipe(true, "3"); - var p4 = new TestPipe(true, "4"); - p3.Other = p4; - p4.Other = p3; - LocalSyncServer.TempRootFile = "D:/FileSyncTest/stemp"; - RemoteSyncServer.SqlPackageAbPath = "C:\\Users\\ZHAOLEI\\.dotnet\\tools\\sqlpackage.exe"; - //LocalSyncServer.MsdeployAbPath = - // "C:\\Program Files\\IIS\\Microsoft Web Deploy V3\\msdeploy.exe"; - LocalSyncServer.SqlPackageAbPath = "C:\\Users\\ZHAOLEI\\.dotnet\\tools\\sqlpackage.exe"; - LocalSyncServer.MSBuildAbPath = - "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\MSBuild.exe"; - RemoteSyncServer.TempRootFile = "D:/FileSyncTest/dtemp"; - RemoteSyncServerFactory.NamePwd = [new Tuple("Test", "t123")]; - var lf = new LocalSyncServerFactory(); - var task1 = Task.Run(async () => - { - await lf.CreateLocalSyncServer(p2, "Test", p3); - }); - - var rf = new RemoteSyncServerFactory(); - - var task2 = Task.Run(async () => - { - await rf.CreateRemoteSyncServer(p4, "Test"); - }); - TestPipe.syncServerFactory = rf; - var starter = new SyncMsg - { - Body = JsonSerializer.Serialize(new PipeSeed().TestConfig), - Type = SyncMsgType.General, - Step = SyncProcessStep.Connect - }; - await p1.SendMsg(starter); - await x; - if (p1.ErrResult != "˳") - { - Assert.Fail(p1.ErrResult); } } } diff --git a/Server/ServerTest/TestPipe.cs b/Server/ServerTest/TestPipe.cs index f96897f..5bdae1e 100644 --- a/Server/ServerTest/TestPipe.cs +++ b/Server/ServerTest/TestPipe.cs @@ -15,7 +15,9 @@ namespace ServerTest public TestPipe? Other; public string? ErrResult; public string Id = id; +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. public static RemoteSyncServerFactory syncServerFactory; +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. public override async IAsyncEnumerable Work( Func receiveCb, @@ -44,7 +46,7 @@ namespace ServerTest //} Task.Run(() => { - var it = syncServerFactory.GetServerByName("Test"); + var it = syncServerFactory.GetServerByName("Test")?? throw new NullReferenceException("找不到服务名称!"); var h = new UnPackAndReleaseHelper(it); it.SetStateHelpBase(h); h.UnPack();