feat: 完成发布流程
This commit is contained in:
parent
63452fe1ab
commit
9c9ffdd77e
11 changed files with 454 additions and 52 deletions
|
@ -16,9 +16,40 @@ public class DirFileConfig
|
|||
/// 除此外全部忽略,最高优先级,若有值,ExcludeFiles 将被忽略,它是根目录的相对路径
|
||||
/// </summary>
|
||||
public List<string>? CherryPicks { get; set; }
|
||||
|
||||
///
|
||||
public Dir? DirInfo { get; set; }
|
||||
}
|
||||
|
||||
public class MSSqlConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库地址
|
||||
/// </summary>
|
||||
public required string ServerName { get; set; }
|
||||
/// <summary>
|
||||
/// db名称
|
||||
/// </summary>
|
||||
public required string DatebaseName { get; set; }
|
||||
/// <summary>
|
||||
/// 用户
|
||||
/// </summary>
|
||||
public required string User { get; set; }
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
public required string Password { get; set; }
|
||||
/// <summary>
|
||||
/// 通常是:True
|
||||
/// </summary>
|
||||
public required string TrustServerCertificate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 同步数据的表格 !!! 通常是 dbo.TableName !!! 注意dbo.
|
||||
/// </summary>
|
||||
public List<string>? SyncTablesData{get;set;}
|
||||
}
|
||||
|
||||
public class Config
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -44,19 +75,14 @@ public class Config
|
|||
public required bool IsDeployDb { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 源数据库连接字符串(ip地址相对LocalServer)
|
||||
/// 源数据库连接(ip地址相对LocalServer)
|
||||
/// </summary>
|
||||
public required string SrcDbConnection { get; set; }
|
||||
public required MSSqlConfig SrcDb { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目标数据库连接字符串(ip地址相对RemoteServer)
|
||||
/// 目标数据库(ip地址相对RemoteServer)
|
||||
/// </summary>
|
||||
public required string DstDbConnection { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 同步的表
|
||||
/// </summary>
|
||||
public required List<string>? SyncDataTables { get; set; }
|
||||
public required MSSqlConfig DstDb { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否发布项目
|
||||
|
|
|
@ -27,6 +27,7 @@ public abstract class AbsPipeLine(bool isAES)
|
|||
/// <returns></returns>
|
||||
public abstract Task SendMsg(SyncMsg msg);
|
||||
|
||||
public abstract Task UploadFile(string filePath, string url, Func<double, bool> progressCb);
|
||||
protected readonly bool IsAES = isAES;
|
||||
}
|
||||
|
||||
|
@ -35,6 +36,37 @@ public class WebSocPipeLine<TSocket>(TSocket socket, bool isAES) : AbsPipeLine(i
|
|||
{
|
||||
public readonly TSocket Socket = socket;
|
||||
|
||||
public override async Task UploadFile(
|
||||
string filePath,
|
||||
string url,
|
||||
Func<double, bool> progressCb
|
||||
)
|
||||
{
|
||||
if (Socket is HttpClient)
|
||||
{
|
||||
using var client = new HttpClient();
|
||||
using var content = new MultipartFormDataContent();
|
||||
using var fileStream = new FileStream(filePath, FileMode.Open);
|
||||
var progress = new Progress<double>(
|
||||
(current) =>
|
||||
{
|
||||
progressCb(current);
|
||||
}
|
||||
);
|
||||
var fileContent = new ProgressStreamContent(fileStream, progress);
|
||||
content.Add(fileContent, "file", Path.GetFileName(filePath));
|
||||
var it = await client.PostAsync(url, content);
|
||||
if (it.StatusCode != System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception(it.Content.ReadAsStringAsync().Result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("只支持HttpClient!");
|
||||
}
|
||||
}
|
||||
|
||||
public override async IAsyncEnumerable<int> Work(Func<byte[], bool> receiveCb, string addr = "")
|
||||
{
|
||||
if (Socket is ClientWebSocket CSocket)
|
||||
|
|
|
@ -60,7 +60,7 @@ public class Dir(string path, List<AFileOrDir>? children = null, NextOpType? nex
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// clone
|
||||
/// clone,不克隆文件
|
||||
/// </summary>
|
||||
/// <param name="optype"></param>
|
||||
/// <param name="IsResetNextOpType"></param>
|
||||
|
|
|
@ -27,7 +27,7 @@ public abstract class FileDirOpStra
|
|||
/// 文件目录打包
|
||||
/// </summary>
|
||||
/// <param name="dstRootPath"></param>
|
||||
public class FileDirOpForPack(string srcRootPath, string dstRootPath, string syncId = "")
|
||||
public class FileDirOpForPack(string srcRootPath, string dstRootPath)
|
||||
: FileDirOpStra
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -40,14 +40,10 @@ public class FileDirOpForPack(string srcRootPath, string dstRootPath, string syn
|
|||
/// </summary>
|
||||
public readonly string SrcRootPath = srcRootPath;
|
||||
|
||||
public readonly string SyncId = string.IsNullOrEmpty(syncId)
|
||||
? Guid.NewGuid().ToString()
|
||||
: syncId;
|
||||
|
||||
/// <summary>
|
||||
/// 最终完成时的压缩
|
||||
/// </summary>
|
||||
public void FinallyCompress()
|
||||
public static void FinallyCompress(string dstPath, string Id)
|
||||
{
|
||||
static List<string> GetFilesResus(string dirPath)
|
||||
{
|
||||
|
@ -65,9 +61,9 @@ public class FileDirOpForPack(string srcRootPath, string dstRootPath, string syn
|
|||
}
|
||||
return files;
|
||||
}
|
||||
var fileNames = GetFilesResus(SrcRootPath);
|
||||
var OuptPutFile = Path.GetDirectoryName(DstRootPath) + $"/{SyncId}.zip";
|
||||
using FileStream fsOut = new(OuptPutFile, FileMode.Create);
|
||||
var fileNames = GetFilesResus(dstPath);
|
||||
var OuptPutFile = Path.GetDirectoryName(dstPath) + $"/{Id}.zip";
|
||||
using FileStream fsOut = new(OuptPutFile, System.IO.FileMode.Create);
|
||||
using ZipOutputStream zipStream = new(fsOut);
|
||||
{
|
||||
zipStream.SetLevel(9); // 设置压缩级别
|
||||
|
@ -78,7 +74,7 @@ public class FileDirOpForPack(string srcRootPath, string dstRootPath, string syn
|
|||
{
|
||||
// Using GetFileName makes the result compatible with XP
|
||||
// as the resulting path is not absolute.
|
||||
var entry = new ZipEntry(file.Replace(SrcRootPath, ""));
|
||||
var entry = new ZipEntry(file.Replace(dstPath, ""));
|
||||
|
||||
// Setup the entry data as required.
|
||||
|
||||
|
@ -170,15 +166,15 @@ public class FileDirOpForPack(string srcRootPath, string dstRootPath, string syn
|
|||
public override void DirDel(Dir dir, bool IsRecursion = true) { }
|
||||
}
|
||||
|
||||
public class FileDirOpForUnpack(string srcRootPath, string dstRootPath, string syncId)
|
||||
public class FileDirOpForUnpack(string srcRootPath, string dstRootPath)
|
||||
: FileDirOpStra
|
||||
{
|
||||
/// <summary>
|
||||
/// 解压缩,必须首先调用
|
||||
/// </summary>
|
||||
public void FirstUnComparess()
|
||||
public static void FirstUnComparess(string dstPath, string Id)
|
||||
{
|
||||
string zipFilePath = $"{SrcRootPath}/{SyncId}.zip";
|
||||
string zipFilePath = $"{dstPath}/{Id}.zip";
|
||||
|
||||
using (ZipInputStream s = new ZipInputStream(System.IO.File.OpenRead(zipFilePath)))
|
||||
{
|
||||
|
@ -189,7 +185,7 @@ public class FileDirOpForUnpack(string srcRootPath, string dstRootPath, string s
|
|||
Console.WriteLine(theEntry.Name);
|
||||
|
||||
string directoryName =
|
||||
DstRootPath + $"/{SyncId}/" + Path.GetDirectoryName(theEntry.Name)
|
||||
dstPath + $"/{Id}/" + Path.GetDirectoryName(theEntry.Name)
|
||||
?? throw new NullReferenceException("无法得到父文件目录!");
|
||||
string fileName = Path.GetFileName(theEntry.Name);
|
||||
|
||||
|
@ -198,7 +194,6 @@ public class FileDirOpForUnpack(string srcRootPath, string dstRootPath, string s
|
|||
{
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
|
||||
if (fileName != String.Empty)
|
||||
{
|
||||
using (
|
||||
|
@ -227,8 +222,6 @@ public class FileDirOpForUnpack(string srcRootPath, string dstRootPath, string s
|
|||
}
|
||||
}
|
||||
|
||||
public readonly string SyncId = syncId;
|
||||
|
||||
/// <summary>
|
||||
/// 目标根目录
|
||||
/// </summary>
|
||||
|
@ -244,27 +237,57 @@ public class FileDirOpForUnpack(string srcRootPath, string dstRootPath, string s
|
|||
/// </summary>
|
||||
public override void FileCreate(string absolutePath, DateTime mtime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var srcPath = absolutePath.Replace(DstRootPath, SrcRootPath);
|
||||
|
||||
var dstDirPath =
|
||||
Path.GetDirectoryName(absolutePath) ?? throw new NullReferenceException("父路径不存在!");
|
||||
if (!Directory.Exists(dstDirPath))
|
||||
{
|
||||
Directory.CreateDirectory(dstDirPath);
|
||||
}
|
||||
|
||||
//文件时间不会更改
|
||||
System.IO.File.Copy(srcPath, absolutePath, true);
|
||||
}
|
||||
|
||||
public override void DirCreate(Dir dir, bool IsRecursion = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// var srcPath = dir.FormatedPath.Replace(DstRootPath, SrcRootPath);
|
||||
var dstDirPath =
|
||||
Path.GetDirectoryName(dir.FormatedPath) ?? throw new NullReferenceException("父路径不存在!");
|
||||
if (!Directory.Exists(dstDirPath))
|
||||
{
|
||||
Directory.CreateDirectory(dstDirPath);
|
||||
}
|
||||
if (IsRecursion)
|
||||
{
|
||||
foreach (var c in dir.Children)
|
||||
{
|
||||
if (c is Dir d)
|
||||
{
|
||||
this.DirCreate(d, true);
|
||||
}
|
||||
else if (c is File f)
|
||||
{
|
||||
this.FileCreate(f.FormatedPath, f.MTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void FileModify(string absolutePath, DateTime mtime)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
this.FileCreate(absolutePath,mtime);
|
||||
}
|
||||
|
||||
public override void FileDel(string absolutePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
System.IO.File.Delete(absolutePath);
|
||||
}
|
||||
|
||||
public override void DirDel(Dir dir, bool IsRecursion = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
System.IO.Directory.Delete(dir.FormatedPath,IsRecursion);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
29
Server/Common/ProgressStreamContent.cs
Normal file
29
Server/Common/ProgressStreamContent.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using System.Net;
|
||||
|
||||
namespace Common;
|
||||
|
||||
public class ProgressStreamContent(Stream stream_, IProgress<double> progress)
|
||||
: StreamContent(stream_, 4096)
|
||||
{
|
||||
private readonly Stream FileStream = stream_;
|
||||
private readonly int BufferSize = 4096;
|
||||
private readonly IProgress<double> Progress = progress;
|
||||
|
||||
protected override async Task SerializeToStreamAsync(
|
||||
Stream stream,
|
||||
TransportContext? context = null
|
||||
)
|
||||
{
|
||||
var buffer = new byte[BufferSize];
|
||||
long totalBytesRead = 0;
|
||||
long totalBytes = FileStream.Length;
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = await FileStream.ReadAsync(buffer.AsMemory())) != 0)
|
||||
{
|
||||
await stream.WriteAsync(buffer.AsMemory(0, bytesRead));
|
||||
totalBytesRead += bytesRead;
|
||||
Progress.Report((double)totalBytesRead / totalBytes);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ using System.Diagnostics;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using Common;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LocalServer;
|
||||
|
||||
|
@ -203,16 +204,163 @@ public class DiffFileAndPackHelper(LocalSyncServer context)
|
|||
|
||||
var PackOp = new FileDirOpForPack(
|
||||
Context.NotNullSyncConfig.LocalRootPath,
|
||||
Context.NotNullSyncConfig.RemoteRootPath
|
||||
+ "/"
|
||||
+ Context.NotNullSyncConfig.Id.ToString(),
|
||||
LocalSyncServer.TempRootFile + "/" + Context.NotNullSyncConfig.Id.ToString(),
|
||||
Context.NotNullSyncConfig.Id.ToString()
|
||||
);
|
||||
|
||||
|
||||
Context.NotNullSyncConfig.DirFileConfigs.ForEach(e =>
|
||||
{
|
||||
if (e.DirInfo != null)
|
||||
{
|
||||
e.DirInfo.ResetRootPath(
|
||||
Context.NotNullSyncConfig.RemoteRootPath,
|
||||
Context.NotNullSyncConfig.LocalRootPath
|
||||
);
|
||||
e.DirInfo.WriteByThisInfo(PackOp);
|
||||
}
|
||||
});
|
||||
var n = new DeployMSSqlHelper(Context);
|
||||
n.PackSqlServerProcess();
|
||||
Context.StateHelper = n;
|
||||
}
|
||||
}
|
||||
|
||||
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.StateHelper = h;
|
||||
h.UpLoadPackedFile();
|
||||
}
|
||||
|
||||
public void PackSqlServerProcess()
|
||||
{
|
||||
if (Context.NotNullSyncConfig.IsDeployDb == false)
|
||||
{
|
||||
Context.LocalPipe.SendMsg(CreateMsg("配置为不发布数据库跳过此步骤")).Wait();
|
||||
PackAndSwitchNext();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
var arguments =
|
||||
$"SqlPackage /Action:Extract /TargetFile:{LocalSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id.ToString()}/{Context.NotNullSyncConfig.Id.ToString()}.dacpac "
|
||||
+ $"/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)
|
||||
{
|
||||
arguments += $" /p:TableData={t}";
|
||||
}
|
||||
}
|
||||
|
||||
ProcessStartInfo startInfo =
|
||||
new()
|
||||
{
|
||||
FileName = "cmd.exe", // The command to execute (can be any command line tool)
|
||||
Arguments = arguments,
|
||||
// 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
|
||||
{
|
||||
Context.LocalPipe.SendMsg(CreateErrMsg(output)).Wait();
|
||||
throw new Exception("执行发布错误,错误信息参考上一条消息!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("只支持windows!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 + "/UploadPacked",
|
||||
$"{LocalSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id}/{Context.NotNullSyncConfig.Id}.zip",
|
||||
(double current) =>
|
||||
{
|
||||
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.StateHelper = h;
|
||||
}
|
||||
}
|
||||
|
||||
public class FinallyPublishHelper(LocalSyncServer context)
|
||||
: StateHelpBase(context, SyncProcessStep.Publish)
|
||||
{
|
||||
protected override void HandleLocalMsg(SyncMsg msg)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 由最初始的客户端断开连接,表示发布完成。
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
protected override void HandleRemoteMsg(SyncMsg msg)
|
||||
{
|
||||
Context.LocalPipe.SendMsg(msg).Wait();
|
||||
}
|
||||
}
|
||||
// /// <summary>
|
||||
// /// 0. 发布源验证密码
|
||||
// /// </summary>
|
||||
|
|
|
@ -22,7 +22,7 @@ public class SyncFilesController(RemoteSyncServerFactory factory, SqliteDbContex
|
|||
if (Factory.GetServerByName(Name) == null)
|
||||
{
|
||||
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
var pipeLine = new WebSocPipeLine<WebSocket>(webSocket,true);
|
||||
var pipeLine = new WebSocPipeLine<WebSocket>(webSocket, true);
|
||||
Factory.CreateRemoteSyncServer(pipeLine, Name);
|
||||
}
|
||||
else
|
||||
|
@ -161,4 +161,44 @@ public class SyncFilesController(RemoteSyncServerFactory factory, SqliteDbContex
|
|||
return Ok(new { IsSuccess = false, e.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("/UploadFile")]
|
||||
public async Task<IActionResult> UploadFile(IFormFile file, [FromQuery] string Id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (file == null || file.Length == 0)
|
||||
{
|
||||
throw new Exception("文件不存在!");
|
||||
}
|
||||
var uploadPath = Path.Combine(RemoteSyncServer.TempRootFile, Id);
|
||||
if (!Directory.Exists(uploadPath))
|
||||
Directory.CreateDirectory(uploadPath);
|
||||
var filePath = Path.Combine(uploadPath, file.FileName);
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
var server = Factory.GetServerById(Id);
|
||||
if (server == null)
|
||||
{
|
||||
throw new Exception("不存在的Id!");
|
||||
}
|
||||
else
|
||||
{
|
||||
var h = new UnPackAndReleaseHelper(server);
|
||||
server.StateHelper = h;
|
||||
h.UnPack();
|
||||
}
|
||||
|
||||
return Ok(new { IsSuccess = true, Message = "File uploaded successfully." });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(
|
||||
500,
|
||||
new { IsSuccess = false, Message = $"Internal server error: {ex.Message}" }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.OpenApi.Validations;
|
||||
using RemoteServer;
|
||||
using RemoteServer.Models;
|
||||
|
||||
|
@ -9,9 +10,10 @@ ConfigurationBuilder configurationBuilder = new();
|
|||
// Add services to the container.
|
||||
|
||||
//添加配置文件路径
|
||||
RemoteSyncServerFactory.NamePwd = [.. (
|
||||
builder.Configuration.GetSection("NamePwds").Get<Tuple<string, string>[]>() ?? []
|
||||
)];
|
||||
RemoteSyncServerFactory.NamePwd =
|
||||
[
|
||||
.. (builder.Configuration.GetSection("NamePwds").Get<Tuple<string, string>[]>() ?? [])
|
||||
];
|
||||
RemoteSyncServer.TempRootFile = builder.Configuration["TempDir"] ?? "C:/TempPack";
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddDbContext<SqliteDbContext>(opions =>
|
||||
|
@ -35,5 +37,4 @@ app.UseWebSockets();
|
|||
app.Urls.Clear();
|
||||
app.Urls.Add("http://0.0.0.0:6818");
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
app.Run();
|
|
@ -12,6 +12,7 @@ public class RemoteSyncServer
|
|||
public StateHelpBase StateHelper;
|
||||
|
||||
public Config? SyncConfig;
|
||||
public List<DirFileConfig> Diff = [];
|
||||
|
||||
public Config NotNullSyncConfig
|
||||
{
|
||||
|
|
|
@ -37,4 +37,9 @@ public class RemoteSyncServerFactory
|
|||
var it = Servers.Where(x => x.Name == name).FirstOrDefault();
|
||||
return it;
|
||||
}
|
||||
|
||||
public RemoteSyncServer? GetServerById(string Id)
|
||||
{
|
||||
return Servers.Where(x => x.NotNullSyncConfig.Id.ToString() == Id).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ public abstract class StateHelpBase(
|
|||
protected readonly RemoteSyncServer Context = context;
|
||||
|
||||
protected readonly SyncProcessStep Step = step;
|
||||
|
||||
public SyncMsg CreateErrMsg(string Body)
|
||||
{
|
||||
return new SyncMsg(SyncMsgType.Error, Step, Body);
|
||||
|
@ -76,6 +75,7 @@ public class DiffFileHelper(RemoteSyncServer context)
|
|||
protected override void HandleMsg(SyncMsg msg)
|
||||
{
|
||||
Context.SyncConfig = JsonSerializer.Deserialize<Config>(msg.Body);
|
||||
|
||||
//文件对比
|
||||
Context.NotNullSyncConfig.DirFileConfigs.ForEach(e =>
|
||||
{
|
||||
|
@ -92,19 +92,116 @@ public class DiffFileHelper(RemoteSyncServer context)
|
|||
);
|
||||
nd.ExtractInfo(e.CherryPicks, e.Excludes);
|
||||
var diff = e.DirInfo.Diff(nd);
|
||||
e.DirInfo = diff;
|
||||
e.DirInfo = nd;
|
||||
Context.Diff.Add(
|
||||
new DirFileConfig()
|
||||
{
|
||||
DirPath = e.DirPath,
|
||||
Excludes = e.Excludes,
|
||||
CherryPicks = e.CherryPicks,
|
||||
DirInfo = diff
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
//将对比结果发送到Local
|
||||
Context.Pipe.SendMsg(
|
||||
CreateMsg(JsonSerializer.Serialize(Context.NotNullSyncConfig.DirFileConfigs))
|
||||
);
|
||||
Context.Pipe.SendMsg(CreateMsg(JsonSerializer.Serialize(Context.Diff)));
|
||||
}
|
||||
}
|
||||
public class UnPackFilesHelper(RemoteSyncServer context):StateHelpBase(context,SyncProcessStep.UploadAndUnpack)
|
||||
|
||||
public class UnPackAndReleaseHelper(RemoteSyncServer context)
|
||||
: StateHelpBase(context, SyncProcessStep.UploadAndUnpack)
|
||||
{
|
||||
protected override void HandleMsg(SyncMsg msg)
|
||||
public void UnPack()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
FileDirOpForUnpack.FirstUnComparess(
|
||||
Path.Combine(RemoteSyncServer.TempRootFile, Context.NotNullSyncConfig.Id.ToString()),
|
||||
Context.NotNullSyncConfig.Id.ToString()
|
||||
);
|
||||
Context.Pipe.SendMsg(CreateMsg("解压完成!")).Wait();
|
||||
var h = new FinallyPublishHelper(Context);
|
||||
Context.StateHelper = h;
|
||||
h.FinallyPublish();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void HandleMsg(SyncMsg msg) { }
|
||||
}
|
||||
|
||||
public class FinallyPublishHelper(RemoteSyncServer context)
|
||||
: StateHelpBase(context, SyncProcessStep.Publish)
|
||||
{
|
||||
public void FinallyPublish()
|
||||
{
|
||||
// 发布数据库
|
||||
if (Context.NotNullSyncConfig.IsDeployDb)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
var arguments =
|
||||
$"SqlPackage /Action:Publish /SourceFile: {RemoteSyncServer.TempRootFile}/{Context.NotNullSyncConfig.Id}/{Context.NotNullSyncConfig.Id}.dacpac "
|
||||
+ $"/TargetServerName:{Context.NotNullSyncConfig.DstDb.ServerName} /TargetDatabaseName:{Context.NotNullSyncConfig.DstDb.DatebaseName}"
|
||||
+ $" /TargetUser:{Context.NotNullSyncConfig.DstDb.User} /TargetPassword:{Context.NotNullSyncConfig.DstDb.Password} /TargetTrustServerCertificate:True ";
|
||||
|
||||
ProcessStartInfo startInfo =
|
||||
new()
|
||||
{
|
||||
FileName = "cmd.exe", // The command to execute (can be any command line tool)
|
||||
Arguments = arguments,
|
||||
// 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.Pipe.SendMsg(CreateMsg("数据库发布成功!")).Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.Pipe.SendMsg(CreateErrMsg(output)).Wait();
|
||||
throw new Exception("执行发布错误,错误信息参考上一条消息!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("只支持windows!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.Pipe.SendMsg(CreateMsg("跳过数据库发布!")).Wait();
|
||||
}
|
||||
var DirFileOp = new FileDirOpForUnpack(
|
||||
Path.Combine(
|
||||
RemoteSyncServer.TempRootFile,
|
||||
Context.NotNullSyncConfig.Id.ToString(),
|
||||
Context.NotNullSyncConfig.Id.ToString()
|
||||
),
|
||||
Context.NotNullSyncConfig.RemoteRootPath
|
||||
);
|
||||
for (int i = 0; i < Context.NotNullSyncConfig.DirFileConfigs.Count; ++i)
|
||||
{
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
Context
|
||||
.NotNullSyncConfig.DirFileConfigs[i]
|
||||
.DirInfo.CombineJustDirFile(DirFileOp, Context.Diff[i].DirInfo);
|
||||
#pragma warning restore CS8604 // Possible null reference argument.
|
||||
#pragma warning restore CS8602 // Dereference of a possibly null reference.
|
||||
}
|
||||
|
||||
Context.Pipe.SendMsg(CreateMsg("发布完成!")).Wait();
|
||||
}
|
||||
|
||||
protected override void HandleMsg(SyncMsg msg) { }
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue