FileSqlServerSync/Server/LocalServer/StateHelper.cs

450 lines
16 KiB
C#
Raw Permalink Normal View History

2024-09-05 01:59:57 +00:00
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
2024-07-31 09:14:07 +00:00
using System.Text.Json;
using Common;
namespace LocalServer;
2024-09-05 01:59:57 +00:00
public abstract class StateHelpBase(
LocalSyncServer context,
SyncProcessStep step = SyncProcessStep.Connect
)
2024-07-31 09:14:07 +00:00
{
protected readonly LocalSyncServer Context = context;
public readonly SyncProcessStep Step = step;
2024-07-31 09:14:07 +00:00
2024-09-05 01:59:57 +00:00
public SyncMsg CreateErrMsg(string Body)
{
return new SyncMsg
{
Body = Body,
Step = Step,
Type = SyncMsgType.Error
};
2024-09-05 01:59:57 +00:00
}
public SyncMsg CreateMsg(string body, SyncMsgType type = SyncMsgType.General)
{
return new SyncMsg
{
Body = body,
Step = Step,
Type = type
};
2024-09-05 01:59:57 +00:00
}
public bool ReceiveLocalMsg(byte[] msg)
2024-09-05 01:59:57 +00:00
{
var syncMsg =
JsonSerializer.Deserialize<SyncMsg>(msg)
?? throw new NullReferenceException("msg is null");
if (syncMsg.Step != Step && syncMsg.Step != SyncProcessStep.Close)
2024-09-05 01:59:57 +00:00
{
throw new Exception("Sync step error!");
}
HandleLocalMsg(syncMsg);
return true;
2024-09-05 01:59:57 +00:00
}
public bool ReceiveRemoteMsg(byte[] bytes)
2024-09-05 01:59:57 +00:00
{
var msg = Encoding.UTF8.GetString(bytes);
2024-09-05 01:59:57 +00:00
var syncMsg =
JsonSerializer.Deserialize<SyncMsg>(msg)
?? throw new NullReferenceException("msg is null");
if (syncMsg.Step != Step && syncMsg.Step != SyncProcessStep.Close)
2024-09-05 01:59:57 +00:00
{
throw new Exception("Sync step error!");
}
HandleRemoteMsg(syncMsg);
return true;
2024-09-05 01:59:57 +00:00
}
protected abstract void HandleRemoteMsg(SyncMsg msg);
protected abstract void HandleLocalMsg(SyncMsg msg);
2024-07-31 09:14:07 +00:00
}
/// <summary>
2024-09-05 01:59:57 +00:00
/// 0. 链接验证
2024-07-31 09:14:07 +00:00
/// </summary>
/// <param name="context"></param>
2024-09-05 01:59:57 +00:00
public class ConnectAuthorityHelper(LocalSyncServer context)
: StateHelpBase(context, SyncProcessStep.Connect)
2024-07-31 09:14:07 +00:00
{
2024-09-05 01:59:57 +00:00
// 如果密码错误,那么就直接关闭连接,不会进入这个方法
protected override void HandleRemoteMsg(SyncMsg msg)
2024-07-31 09:14:07 +00:00
{
2024-09-05 01:59:57 +00:00
//将remote的消息传递到前端界面
Context.LocalPipe.SendMsg(msg);
2024-09-05 01:59:57 +00:00
//下一步
var deployHelper = new DeployHelper(Context);
Context.SetStateHelper(deployHelper);
2024-09-05 01:59:57 +00:00
deployHelper.DeployProcess();
2024-07-31 09:14:07 +00:00
}
2024-09-05 01:59:57 +00:00
protected override void HandleLocalMsg(SyncMsg msg)
2024-07-31 09:14:07 +00:00
{
2024-09-05 01:59:57 +00:00
//收到配置文件
var config = JsonSerializer.Deserialize<Config>(msg.Body);
Context.SyncConfig = config;
Task.Run(async () =>
2024-07-31 09:14:07 +00:00
{
try
2024-07-31 09:14:07 +00:00
{
var rs = Context.RemotePipe.Work(
(byte[] b) =>
{
var x = Context.GetStateHelper().Step;
return Context.GetStateHelper().ReceiveRemoteMsg(b);
},
Context.NotNullSyncConfig.RemoteUrl + "/websoc?Name=" + Context.Name
);
await foreach (var r in rs)
{
if (r == 0)
{
await Context.RemotePipe.SendMsg(
CreateMsg(Context.NotNullSyncConfig.RemotePwd)
);
}
}
2024-07-31 09:14:07 +00:00
}
catch (Exception e)
{
Context.Close(e.Message);
}
2024-09-05 01:59:57 +00:00
});
2024-07-31 09:14:07 +00:00
}
}
/// <summary>
2024-09-05 01:59:57 +00:00
/// 1. 执行发布步骤
2024-07-31 09:14:07 +00:00
/// </summary>
/// <param name="context"></param>
2024-09-05 01:59:57 +00:00
public class DeployHelper(LocalSyncServer context)
: StateHelpBase(context, SyncProcessStep.DeployProject)
2024-07-31 09:14:07 +00:00
{
2024-09-05 01:59:57 +00:00
public void DeployProcess()
2024-07-31 09:14:07 +00:00
{
2024-09-05 01:59:57 +00:00
if (Context.NotNullSyncConfig.IsDeployProject == false)
2024-07-31 09:14:07 +00:00
{
Context.LocalPipe.SendMsg(CreateMsg("配置为不发布跳过此步骤")).Wait();
2024-09-05 01:59:57 +00:00
var h = new DiffFileAndPackHelper(Context);
Context.SetStateHelper(h);
2024-09-05 01:59:57 +00:00
h.DiffProcess();
2024-07-31 09:14:07 +00:00
}
else
{ // msbuild 只在windows 才有
2024-09-05 01:59:57 +00:00
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
2024-07-31 09:14:07 +00:00
{
//构建
ProcessStartInfo startbuildInfo =
2024-09-05 01:59:57 +00:00
new()
{
//p:DeployOnBuild=true
FileName = LocalSyncServer.MSBuildAbPath, // The command to execute (can be any command line tool)
2024-09-05 01:59:57 +00:00
Arguments =
$" {Context.NotNullSyncConfig.LocalProjectAbsolutePath} /t:ResolveReferences"
+ $" /t:Compile /p:Configuration=Release /t:_CopyWebApplication /p:OutputPath={Context.NotNullSyncConfig.LocalRootPath}/bin"
+ $" /p:WebProjectOutputDir={Context.NotNullSyncConfig.LocalRootPath}",
2024-09-05 01:59:57 +00:00
// The arguments to pass to the command (e.g., list directory contents)
RedirectStandardOutput = true, // Redirect the standard output to a string
RedirectStandardError = true,
StandardOutputEncoding = System.Text.Encoding.UTF8,
2024-09-05 01:59:57 +00:00
UseShellExecute = false, // Do not use the shell to execute the command
CreateNoWindow = true // Do not create a new window for the command
};
using Process bprocess = new() { StartInfo = startbuildInfo };
2024-09-05 01:59:57 +00:00
// Start the process
bprocess.Start();
2024-09-05 01:59:57 +00:00
// Read the output from the process
string boutput = bprocess.StandardOutput.ReadToEnd();
2024-09-05 01:59:57 +00:00
// Wait for the process to exit
bprocess.WaitForExit();
2024-09-05 01:59:57 +00:00
if (bprocess.ExitCode == 0)
2024-07-31 09:14:07 +00:00
{
Context.LocalPipe.SendMsg(CreateMsg("本地编译成功!")).Wait();
var h = new DiffFileAndPackHelper(Context);
Context.SetStateHelper(h);
h.DiffProcess();
2024-07-31 09:14:07 +00:00
}
else
{
var aTexts = boutput.Split('\n');
if (aTexts.Length > 10)
{
boutput = string.Join('\n', aTexts.Skip(aTexts.Length - 10));
}
Context.LocalPipe.SendMsg(CreateErrMsg(boutput)).Wait();
throw new Exception("执行编译错误,错误信息参考上一条消息!");
2024-07-31 09:14:07 +00:00
}
//发布
//ProcessStartInfo startInfo =
// new()
// {
// FileName = LocalSyncServer.MsdeployAbPath, // The command to execute (can be any command line tool)
// Arguments =
// $" -verb:sync -source:contentPath={Context.NotNullSyncConfig.LocalProjectAbsolutePath} -dest:contentPath={Context.NotNullSyncConfig.LocalRootPath} -disablerule:BackupRule",
// // 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();
// var h = new DiffFileAndPackHelper(Context);
// Context.SetStateHelper(h);
// h.DiffProcess();
//}
//else
//{
// Context.LocalPipe.SendMsg(CreateErrMsg(output)).Wait();
// throw new Exception("执行发布错误,错误信息参考上一条消息!");
//}
2024-07-31 09:14:07 +00:00
}
else
{
2024-09-05 01:59:57 +00:00
throw new NotSupportedException("只支持windows!");
2024-07-31 09:14:07 +00:00
}
}
}
2024-09-05 01:59:57 +00:00
protected override void HandleRemoteMsg(SyncMsg msg) { }
protected override void HandleLocalMsg(SyncMsg msg) { }
2024-07-31 09:14:07 +00:00
}
2024-09-05 01:59:57 +00:00
public class DiffFileAndPackHelper(LocalSyncServer context)
: StateHelpBase(context, SyncProcessStep.DiffFileAndPack)
2024-07-31 09:14:07 +00:00
{
2024-09-05 01:59:57 +00:00
public void DiffProcess()
2024-07-31 09:14:07 +00:00
{
2024-09-05 01:59:57 +00:00
//提取本地文件的信息
Context.NotNullSyncConfig.DirFileConfigs.ForEach(e =>
2024-07-31 09:14:07 +00:00
{
e.LocalDirInfo = new Dir
{
Path = Context.NotNullSyncConfig.LocalRootPath + e.DirPath,
Children = []
};
e.LocalDirInfo.ExtractInfo(e.CherryPicks, e.Excludes);
2024-09-05 01:59:57 +00:00
});
2024-09-05 01:59:57 +00:00
//将配置信息发送到remoteServer
2024-09-24 09:46:52 +00:00
var options = new JsonSerializerOptions { WriteIndented = true };
2024-09-05 01:59:57 +00:00
Context
2024-09-24 09:46:52 +00:00
.RemotePipe.SendMsg(
CreateMsg(JsonSerializer.Serialize(Context.NotNullSyncConfig, options))
)
2024-09-05 01:59:57 +00:00
.Wait();
2024-07-31 09:14:07 +00:00
}
2024-09-05 01:59:57 +00:00
protected override void HandleLocalMsg(SyncMsg msg) { }
protected override void HandleRemoteMsg(SyncMsg msg)
{
var diffConfig =
JsonSerializer.Deserialize<List<DirFileConfig>>(msg.Body)
?? throw new Exception("LocalServer: DirFile为空");
for (var i = 0; i < diffConfig.Count; ++i)
{
Context.NotNullSyncConfig.DirFileConfigs[i].DiffDirInfo = diffConfig[i].DiffDirInfo;
}
var PackOp = new FileDirOpForPack(
Context.NotNullSyncConfig.LocalRootPath,
LocalSyncServer.TempRootFile + "/" + Context.NotNullSyncConfig.Id.ToString()
);
Directory.CreateDirectory(
LocalSyncServer.TempRootFile + "/" + Context.NotNullSyncConfig.Id.ToString()
);
2024-09-22 05:27:52 +00:00
Context.NotNullSyncConfig.DirFileConfigs.ForEach(e =>
{
if (e.DiffDirInfo != null)
2024-09-22 05:27:52 +00:00
{
e.DiffDirInfo.ResetRootPath(
2024-09-22 05:27:52 +00:00
Context.NotNullSyncConfig.RemoteRootPath,
Context.NotNullSyncConfig.LocalRootPath
);
e.DiffDirInfo.WriteByThisInfo(PackOp);
Context
.LocalPipe.SendMsg(
CreateMsg(JsonSerializer.Serialize(e.DiffDirInfo), SyncMsgType.DirFileDiff)
)
.Wait();
2024-09-22 05:27:52 +00:00
}
});
Context.LocalPipe.SendMsg(CreateMsg("文件差异比较成功!")).Wait();
2024-09-22 05:27:52 +00:00
var n = new DeployMSSqlHelper(Context);
Context.SetStateHelper(n);
2024-09-22 05:27:52 +00:00
n.PackSqlServerProcess();
}
}
2024-09-22 05:27:52 +00:00
public class DeployMSSqlHelper(LocalSyncServer context)
: StateHelpBase(context, SyncProcessStep.PackSqlServer)
{
private void PackAndSwitchNext()
{
FileDirOpForPack.FinallyCompress(
LocalSyncServer.TempRootFile + "/" + Context.NotNullSyncConfig.Id.ToString(),
Context.NotNullSyncConfig.Id.ToString()
);
var h = new UploadPackedHelper(Context);
Context.SetStateHelper(h);
2024-09-22 05:27:52 +00:00
h.UpLoadPackedFile();
}
public void PackSqlServerProcess()
{
if (Context.NotNullSyncConfig.IsDeployDb == false)
{
Context.LocalPipe.SendMsg(CreateMsg("配置为不发布数据库跳过此步骤")).Wait();
PackAndSwitchNext();
}
else
{
Context.LocalPipe.SendMsg(CreateMsg("正在打包数据库...")).Wait();
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}"
2024-10-28 09:28:18 +00:00
+ $" /SourceDatabaseName:{Context.NotNullSyncConfig.SrcDb.DatabaseName} /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)
2024-09-22 05:27:52 +00:00
{
foreach (var t in Context.NotNullSyncConfig.SrcDb.SyncTablesData)
2024-09-22 05:27:52 +00:00
{
arguments += $" /p:TableData={t}";
2024-09-22 05:27:52 +00:00
}
}
2024-09-22 05:27:52 +00:00
ProcessStartInfo startInfo =
new()
2024-09-22 05:27:52 +00:00
{
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();
2024-09-22 05:27:52 +00:00
}
else
{
Context.LocalPipe.SendMsg(CreateErrMsg(output)).Wait();
throw new Exception("执行发布错误,错误信息参考上一条消息!");
2024-09-22 05:27:52 +00:00
}
}
}
protected override void HandleLocalMsg(SyncMsg msg) { }
protected override void HandleRemoteMsg(SyncMsg msg)
{
throw new NotImplementedException();
}
}
public class UploadPackedHelper(LocalSyncServer context)
: StateHelpBase(context, SyncProcessStep.UploadAndUnpack)
{
public void UpLoadPackedFile()
{
Context
.LocalPipe.UploadFile(
Context.NotNullSyncConfig.RemoteUrl,
2024-09-24 09:46:52 +00:00
$"{LocalSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id}.zip",
2024-09-22 05:27:52 +00:00
(double current) =>
{
// 每上传1Mb 更新一下进度
2024-09-22 05:27:52 +00:00
Context
.LocalPipe.SendMsg(CreateMsg(current.ToString(), SyncMsgType.Process))
.Wait();
return true;
}
)
.Wait();
Context.LocalPipe.SendMsg(CreateMsg("上传完成!")).Wait();
}
protected override void HandleLocalMsg(SyncMsg msg)
{
throw new NotImplementedException();
}
protected override void HandleRemoteMsg(SyncMsg msg)
{
Context.LocalPipe.SendMsg(msg).Wait();
var h = new FinallyPublishHelper(Context);
Context.SetStateHelper(h);
}
2024-07-31 09:14:07 +00:00
}
2024-09-22 05:27:52 +00:00
public class FinallyPublishHelper(LocalSyncServer context)
: StateHelpBase(context, SyncProcessStep.Publish)
{
protected override void HandleLocalMsg(SyncMsg msg)
{
//throw new NotImplementedException();
2024-09-22 05:27:52 +00:00
}
/// <summary>
/// 由最初始的客户端断开连接,表示发布完成。
/// </summary>
/// <param name="msg"></param>
protected override void HandleRemoteMsg(SyncMsg msg)
{
if (msg.Body == "发布完成!")
{
Context.SetStateHelper(new NormalCloseHelper(Context));
}
2024-09-22 05:27:52 +00:00
Context.LocalPipe.SendMsg(msg).Wait();
}
2024-10-28 09:28:18 +00:00
}
public class NormalCloseHelper(LocalSyncServer context)
: StateHelpBase(context, SyncProcessStep.Close)
{
protected override void HandleRemoteMsg(SyncMsg msg) { }
protected override void HandleLocalMsg(SyncMsg msg) { }
}