chore: 推送到coding
This commit is contained in:
parent
02c162ee8f
commit
62366f0d97
20 changed files with 1401 additions and 210 deletions
10
README.md
10
README.md
|
@ -1,5 +1,15 @@
|
||||||
> 这是一个基于 asp.net c# 的发布工具。
|
> 这是一个基于 asp.net c# 的发布工具。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 此行命令差一个运行环境复制。bin/roslyn
|
||||||
|
msbuild .\HMES-H7-HNFYMF.WEB\HMES_H7_HNFYMF.WEB.csproj /t:ResolveReferences /t:Compile /t:_CopyWebApplication /p:Configuration=Release /p:WebProjectOutputDir=C:\publish /p:OutputPath=C:\publish\bin
|
||||||
|
|
||||||
|
|
||||||
|
# 此命令是一个完整的发布命令
|
||||||
|
|
||||||
|
msdeploy.exe -verb:sync -source:contentPath=D:\git\HMES-H7-HNFY\HMES-H7-HNFYMF\HMES-H7-HNFYMF.WEB -dest:contentPath=D:\git\HMES-H7-HNFY\HMES-H7-HNFYMF\release -disablerule:BackupRule
|
||||||
|
```
|
||||||
|
|
||||||
```plantuml
|
```plantuml
|
||||||
package 服务器 {
|
package 服务器 {
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Common;
|
namespace Common;
|
||||||
|
|
||||||
|
@ -94,7 +91,7 @@ public class AESHelper
|
||||||
return encrypted;
|
return encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string DecryptStringFromBytes_Aes(byte[] cipherText)
|
public static string DecryptStringFromBytes_Aes(byte[] cipherText)
|
||||||
{
|
{
|
||||||
// Check arguments.
|
// Check arguments.
|
||||||
if (cipherText == null || cipherText.Length <= 0)
|
if (cipherText == null || cipherText.Length <= 0)
|
||||||
|
|
80
Server/Common/Config.cs
Normal file
80
Server/Common/Config.cs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
namespace Common;
|
||||||
|
|
||||||
|
public class DirFileConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 相对路径
|
||||||
|
/// </summary>
|
||||||
|
public required string DirPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排除的文件,它是根目录的相对路径
|
||||||
|
/// </summary>
|
||||||
|
public List<string>? Excludes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 除此外全部忽略,最高优先级,若有值,ExcludeFiles 将被忽略,它是根目录的相对路径
|
||||||
|
/// </summary>
|
||||||
|
public List<string>? CherryPicks { get; set; }
|
||||||
|
public Dir? LocalDirInfo { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Config
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 发布的项目名称
|
||||||
|
/// </summary>
|
||||||
|
public required string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程Url
|
||||||
|
/// </summary>
|
||||||
|
public required string RemoteUrl { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 链接到远程的密码
|
||||||
|
/// </summary>
|
||||||
|
public required string RemotePwd {get;set;}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否发布数据库
|
||||||
|
/// </summary>
|
||||||
|
public required bool IsDeployDb {get;set;}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 源数据库连接字符串(ip地址相对LocalServer)
|
||||||
|
/// </summary>
|
||||||
|
public required string SrcDbConnection { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 目标数据库连接字符串(ip地址相对RemoteServer)
|
||||||
|
/// </summary>
|
||||||
|
public required string DstDbConnection { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同步的表
|
||||||
|
/// </summary>
|
||||||
|
public required List<string>? SyncDataTables {get;set;}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否发布项目
|
||||||
|
/// </summary>
|
||||||
|
public required bool IsDeployProject {get;set;}
|
||||||
|
/// <summary>
|
||||||
|
/// 项目的绝对路径 空字符串表示不发布,不为空LocalRootPath将是发布路径。
|
||||||
|
/// </summary>
|
||||||
|
public required string LocalProjectAbsolutePath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 本地父文件路径
|
||||||
|
/// </summary>
|
||||||
|
public required string LocalRootPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 远程父路径
|
||||||
|
/// </summary>
|
||||||
|
public required string RemoteRootPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同步的文件夹配置
|
||||||
|
/// </summary>
|
||||||
|
public required List<DirFileConfig> DirFileConfigs { get; set; }
|
||||||
|
}
|
|
@ -309,11 +309,27 @@ public class Dir(string path, List<AFileOrDir>? children = null, NextOpType? nex
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从文件目录结构提起文件信息,注意,此目录文件树不包含文件内容,仅有修改时间mtime
|
/// 从文件夹中提取信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <param name="cherryPicks">绝对路径,只包含的文件或者目录</param>
|
||||||
public void ExtractInfo()
|
/// <param name="exculdes">绝对路径,排除的文件或目录</param>
|
||||||
|
/// <exception cref="NotSupportedException"></exception>
|
||||||
|
public void ExtractInfo(List<string>? cherryPicks = null, List<string>? exculdes = null)
|
||||||
{
|
{
|
||||||
|
bool filter(string path)
|
||||||
|
{
|
||||||
|
if (cherryPicks != null)
|
||||||
|
{
|
||||||
|
return cherryPicks.Contains(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exculdes != null)
|
||||||
|
{
|
||||||
|
return !exculdes.Contains(path);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.Children.Count != 0)
|
if (this.Children.Count != 0)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException("this dir is not empty.");
|
throw new NotSupportedException("this dir is not empty.");
|
||||||
|
@ -322,13 +338,19 @@ public class Dir(string path, List<AFileOrDir>? children = null, NextOpType? nex
|
||||||
string[] dirs = Directory.GetDirectories(this.FormatedPath);
|
string[] dirs = Directory.GetDirectories(this.FormatedPath);
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
this.Children.Add(new File(file, System.IO.File.GetLastWriteTime($"{file}")));
|
if (filter(file))
|
||||||
|
{
|
||||||
|
this.Children.Add(new File(file, System.IO.File.GetLastWriteTime($"{file}")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
foreach (var dir in dirs)
|
foreach (var dir in dirs)
|
||||||
{
|
{
|
||||||
var ndir = new Dir(dir);
|
if (filter(dir))
|
||||||
ndir.ExtractInfo();
|
{
|
||||||
this.Children.Add(ndir);
|
var ndir = new Dir(dir);
|
||||||
|
ndir.ExtractInfo();
|
||||||
|
this.Children.Add(ndir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,30 @@
|
||||||
namespace Common;
|
namespace Common;
|
||||||
|
|
||||||
public class SyncMsg(bool isSuccess, string body)
|
public enum SyncMsgType
|
||||||
{
|
{
|
||||||
public bool IsSuccess { get; set; } = isSuccess;
|
Error = 0,
|
||||||
|
General = 1,
|
||||||
|
Process = 2,
|
||||||
|
DirFilePack = 3
|
||||||
|
}
|
||||||
|
public enum SyncProcessStep
|
||||||
|
{
|
||||||
|
Connect = 1,
|
||||||
|
DeployProject = 2,
|
||||||
|
DiffFileAndPack = 3,
|
||||||
|
PackSqlServer = 4,
|
||||||
|
Upload = 5,
|
||||||
|
Publish = 6
|
||||||
|
}
|
||||||
|
public class SyncMsg(SyncMsgType msgType, SyncProcessStep step, string body)
|
||||||
|
{
|
||||||
|
public SyncMsgType? Type { get; set; } = msgType;
|
||||||
|
|
||||||
|
public SyncProcessStep Step {get;set;} = step;
|
||||||
|
|
||||||
|
public bool IsSuccess
|
||||||
|
{
|
||||||
|
get { return Type != SyncMsgType.Error; }
|
||||||
|
}
|
||||||
public string Body { get; set; } = body;
|
public string Body { get; set; } = body;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace LocalServer.Controllers
|
||||||
{
|
{
|
||||||
private readonly LocalSyncServerFactory Factory = factory;
|
private readonly LocalSyncServerFactory Factory = factory;
|
||||||
|
|
||||||
[Route("/")]
|
[Route("/websoc")]
|
||||||
public async Task WebsocketConnection(string Name)
|
public async Task WebsocketConnection(string Name)
|
||||||
{
|
{
|
||||||
if (HttpContext.WebSockets.IsWebSocketRequest)
|
if (HttpContext.WebSockets.IsWebSocketRequest)
|
||||||
|
|
|
@ -2,16 +2,26 @@ using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Common;
|
using Common;
|
||||||
using LocalServer.Models;
|
|
||||||
|
|
||||||
namespace LocalServer;
|
namespace LocalServer;
|
||||||
|
|
||||||
public class LocalSyncServer
|
public class LocalSyncServer
|
||||||
{
|
{
|
||||||
|
#pragma warning disable CA2211 // Non-constant fields should not be visible
|
||||||
|
public static string TempRootFile = "C:/TempPack";
|
||||||
|
#pragma warning restore CA2211 // Non-constant fields should not be visible
|
||||||
public StateHelpBase StateHelper;
|
public StateHelpBase StateHelper;
|
||||||
|
|
||||||
public Config? SyncConfig;
|
public Config? SyncConfig;
|
||||||
|
|
||||||
|
public Config NotNullSyncConfig {get {
|
||||||
|
if (SyncConfig == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("SyncConfig");
|
||||||
|
}
|
||||||
|
return SyncConfig;
|
||||||
|
}}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发布源连接
|
/// 发布源连接
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -20,7 +30,7 @@ public class LocalSyncServer
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发布源-缓冲区,存储数据 最大1MB
|
/// 发布源-缓冲区,存储数据 最大1MB
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] Buffer = new byte[1024 * 1024];
|
// public byte[] Buffer = new byte[1024 * 1024];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发布目标-连接
|
/// 发布目标-连接
|
||||||
|
@ -47,41 +57,96 @@ public class LocalSyncServer
|
||||||
LocalSocket = socket;
|
LocalSocket = socket;
|
||||||
Name = name;
|
Name = name;
|
||||||
Factory = factory;
|
Factory = factory;
|
||||||
StateHelper = new LocalAuthorityState(this);
|
StateHelper = new ConnectAuthorityHelper(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Start()
|
public async Task RemoteSocketConnect()
|
||||||
{
|
{
|
||||||
//最大1MB
|
if (SyncConfig != null)
|
||||||
var buffer = new byte[1024 * 1024];
|
|
||||||
var receiveResult = await LocalSocket.ReceiveAsync(
|
|
||||||
new ArraySegment<byte>(buffer),
|
|
||||||
CancellationToken.None
|
|
||||||
);
|
|
||||||
|
|
||||||
while (!receiveResult.CloseStatus.HasValue)
|
|
||||||
{
|
{
|
||||||
await LocalSocket.SendAsync(
|
await RemoteSocket.ConnectAsync(
|
||||||
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
|
new Uri(SyncConfig.RemoteUrl + "/websoc"),
|
||||||
receiveResult.MessageType,
|
|
||||||
receiveResult.EndOfMessage,
|
|
||||||
CancellationToken.None
|
|
||||||
);
|
|
||||||
|
|
||||||
receiveResult = await LocalSocket.ReceiveAsync(
|
|
||||||
new ArraySegment<byte>(buffer),
|
|
||||||
CancellationToken.None
|
CancellationToken.None
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Factory.RemoveLocalSyncServer(this);
|
else
|
||||||
await LocalSocket.CloseAsync(
|
{
|
||||||
receiveResult.CloseStatus.Value,
|
throw new ArgumentException("SyncConfig is null!");
|
||||||
receiveResult.CloseStatusDescription,
|
}
|
||||||
CancellationToken.None
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void LocalSocketSendMsg(object msgOb)
|
public async Task RemoteSocketLiten()
|
||||||
|
{
|
||||||
|
string CloseMsg = "任务结束关闭";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (RemoteSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
var buffer = new byte[1024 * 1024];
|
||||||
|
var receiveResult = await RemoteSocket.ReceiveAsync(
|
||||||
|
new ArraySegment<byte>(buffer),
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
if (receiveResult.MessageType == WebSocketMessageType.Close)
|
||||||
|
{
|
||||||
|
Close(receiveResult.CloseStatusDescription);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var nbuffer = new byte[receiveResult.Count];
|
||||||
|
System.Buffer.BlockCopy(buffer, 0, nbuffer, 0, receiveResult.Count);
|
||||||
|
StateHelper.ReceiveRemoteMsg(nbuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
CloseMsg = e.Message;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Close(CloseMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LocalSocketListen()
|
||||||
|
{
|
||||||
|
string CloseMsg = "任务结束关闭";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//最大1MB!=
|
||||||
|
var buffer = new byte[1024 * 1024];
|
||||||
|
|
||||||
|
while (LocalSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
var receiveResult = await LocalSocket.ReceiveAsync(
|
||||||
|
new ArraySegment<byte>(buffer),
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
|
||||||
|
if (receiveResult.MessageType == WebSocketMessageType.Close)
|
||||||
|
{
|
||||||
|
Close(receiveResult.CloseStatusDescription);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StateHelper.ReceiveLocalMsg(
|
||||||
|
Encoding.UTF8.GetString(buffer, 0, receiveResult.Count)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
CloseMsg = e.Message;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Close(CloseMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LocalSocketSendMsg(object msgOb)
|
||||||
{
|
{
|
||||||
string msg = JsonSerializer.Serialize(msgOb);
|
string msg = JsonSerializer.Serialize(msgOb);
|
||||||
await LocalSocket.SendAsync(
|
await LocalSocket.SendAsync(
|
||||||
|
@ -92,7 +157,7 @@ public class LocalSyncServer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void RemoteSocketSendMsg(object msgOb)
|
public async Task RemoteSocketSendMsg(object msgOb)
|
||||||
{
|
{
|
||||||
string msg = JsonSerializer.Serialize(msgOb);
|
string msg = JsonSerializer.Serialize(msgOb);
|
||||||
var buffer = AESHelper.EncryptStringToBytes_Aes(msg);
|
var buffer = AESHelper.EncryptStringToBytes_Aes(msg);
|
||||||
|
@ -104,5 +169,40 @@ public class LocalSyncServer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Close() { }
|
public void Close(string? CloseReason)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (LocalSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
LocalSocket
|
||||||
|
.CloseAsync(
|
||||||
|
WebSocketCloseStatus.NormalClosure,
|
||||||
|
CloseReason,
|
||||||
|
CancellationToken.None
|
||||||
|
)
|
||||||
|
.Wait(60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RemoteSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
RemoteSocket
|
||||||
|
.CloseAsync(
|
||||||
|
WebSocketCloseStatus.NormalClosure,
|
||||||
|
CloseReason,
|
||||||
|
CancellationToken.None
|
||||||
|
)
|
||||||
|
.Wait(60 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//TODO 日志
|
||||||
|
Console.WriteLine(e.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Factory.RemoveLocalSyncServer(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,18 +6,21 @@ public class LocalSyncServerFactory
|
||||||
{
|
{
|
||||||
private readonly object Lock = new();
|
private readonly object Lock = new();
|
||||||
|
|
||||||
public async void CreateLocalSyncServer(WebSocket socket, string name)
|
public void CreateLocalSyncServer(WebSocket socket, string name)
|
||||||
{
|
{
|
||||||
if (Servers.Select(x => x.Name == name).Any())
|
if (Servers.Select(x => x.Name == name).Any())
|
||||||
{
|
{
|
||||||
throw new Exception("there already is a server with that name is Runing!");
|
throw new Exception("LocalServer:存在同名发布源!");
|
||||||
}
|
}
|
||||||
var server = new LocalSyncServer(socket, name, this);
|
var server = new LocalSyncServer(socket, name, this);
|
||||||
lock (Lock)
|
lock (Lock)
|
||||||
{
|
{
|
||||||
Servers.Add(server);
|
Servers.Add(server);
|
||||||
}
|
}
|
||||||
await server.Start();
|
//脱离当前函数栈
|
||||||
|
Task.Run(async ()=>{
|
||||||
|
await server.LocalSocketListen();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<LocalSyncServer> Servers = [];
|
private readonly List<LocalSyncServer> Servers = [];
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
namespace LocalServer.Models;
|
|
||||||
|
|
||||||
public class DirFileConfig
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 本地-源根目录
|
|
||||||
/// </summary>
|
|
||||||
public required string LocalRootPath { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程-目标根目录
|
|
||||||
/// </summary>
|
|
||||||
public required string RemoteRootPath { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 排除的文件,它是根目录的相对路径
|
|
||||||
/// </summary>
|
|
||||||
public List<string>? ExcludeFiles { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 除此外全部忽略,最高优先级,若有值,ExcludeFiles 将被忽略,它是根目录的相对路径
|
|
||||||
/// </summary>
|
|
||||||
public List<DirFileConfig>? CherryPickFiles { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Config
|
|
||||||
{
|
|
||||||
public required string Name { get; set; }
|
|
||||||
public required string RemoteUrl { get; set; }
|
|
||||||
|
|
||||||
public List<DirFileConfig>? DirFileConfigs { get; set; }
|
|
||||||
}
|
|
|
@ -1,6 +1,14 @@
|
||||||
using LocalServer;
|
using LocalServer;
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
ConfigurationBuilder configurationBuilder = new ();
|
||||||
|
|
||||||
|
//添加配置文件路径
|
||||||
|
configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json");
|
||||||
|
|
||||||
|
//加载文件
|
||||||
|
IConfiguration _configuration = configurationBuilder.Build();
|
||||||
|
LocalSyncServer.TempRootFile = _configuration["TempDir"]??"C:/TempPack";;
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
@ -11,7 +19,6 @@ builder.Services.AddSwaggerGen();
|
||||||
builder.Services.AddSingleton<LocalSyncServerFactory>();
|
builder.Services.AddSingleton<LocalSyncServerFactory>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
|
@ -20,7 +27,8 @@ if (app.Environment.IsDevelopment())
|
||||||
}
|
}
|
||||||
app.UseWebSockets();
|
app.UseWebSockets();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
app.Urls.Clear();
|
||||||
|
app.Urls.Add("http://0.0.0.0:6818");
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
using System.Net.NetworkInformation;
|
using System.Diagnostics;
|
||||||
using System.Net.WebSockets;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Intrinsics.Arm;
|
|
||||||
using System.Security.AccessControl;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Common;
|
using Common;
|
||||||
using LocalServer.Models;
|
|
||||||
|
|
||||||
namespace LocalServer;
|
namespace LocalServer;
|
||||||
|
|
||||||
|
@ -17,173 +13,403 @@ namespace LocalServer;
|
||||||
// RemoteUnPackAndRelease = 3,
|
// RemoteUnPackAndRelease = 3,
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public abstract class StateHelpBase(LocalSyncServer context)
|
public abstract class StateHelpBase(
|
||||||
|
LocalSyncServer context,
|
||||||
|
SyncProcessStep step = SyncProcessStep.Connect
|
||||||
|
)
|
||||||
{
|
{
|
||||||
protected readonly LocalSyncServer Context = context;
|
protected readonly LocalSyncServer Context = context;
|
||||||
|
|
||||||
public abstract void HandleRemoteMsg(SyncMsg? msg);
|
protected readonly SyncProcessStep Step = step;
|
||||||
|
|
||||||
public abstract void HandleLocalMsg(SyncMsg? msg);
|
public SyncMsg CreateErrMsg(string Body)
|
||||||
|
{
|
||||||
|
return new SyncMsg(SyncMsgType.Error, Step, Body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyncMsg CreateMsg(string body, SyncMsgType type = SyncMsgType.General)
|
||||||
|
{
|
||||||
|
return new SyncMsg(type, Step, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReceiveLocalMsg(string msg)
|
||||||
|
{
|
||||||
|
var syncMsg =
|
||||||
|
JsonSerializer.Deserialize<SyncMsg>(msg)
|
||||||
|
?? throw new NullReferenceException("msg is null");
|
||||||
|
if (syncMsg.Step != Step)
|
||||||
|
{
|
||||||
|
throw new Exception("Sync step error!");
|
||||||
|
}
|
||||||
|
HandleLocalMsg(syncMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReceiveRemoteMsg(byte[] bytes)
|
||||||
|
{
|
||||||
|
var msg = AESHelper.DecryptStringFromBytes_Aes(bytes);
|
||||||
|
|
||||||
|
var syncMsg =
|
||||||
|
JsonSerializer.Deserialize<SyncMsg>(msg)
|
||||||
|
?? throw new NullReferenceException("msg is null");
|
||||||
|
if (syncMsg.Step != Step)
|
||||||
|
{
|
||||||
|
throw new Exception("Sync step error!");
|
||||||
|
}
|
||||||
|
HandleLocalMsg(syncMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void HandleRemoteMsg(SyncMsg msg);
|
||||||
|
|
||||||
|
protected abstract void HandleLocalMsg(SyncMsg msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 0. 发布源验证密码
|
/// 0. 链接验证
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
public class LocalAuthorityState(LocalSyncServer context) : StateHelpBase(context)
|
public class ConnectAuthorityHelper(LocalSyncServer context)
|
||||||
|
: StateHelpBase(context, SyncProcessStep.Connect)
|
||||||
{
|
{
|
||||||
public override void HandleRemoteMsg(SyncMsg? msg)
|
// 如果密码错误,那么就直接关闭连接,不会进入这个方法
|
||||||
|
protected override void HandleRemoteMsg(SyncMsg msg)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("error usage!");
|
//将remote的消息传递到前端界面
|
||||||
|
Context.LocalSocketSendMsg(msg).Wait();
|
||||||
|
//下一步
|
||||||
|
var deployHelper = new DeployHelper(Context);
|
||||||
|
Context.StateHelper = deployHelper;
|
||||||
|
deployHelper.DeployProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void HandleLocalMsg(SyncMsg? msg)
|
protected override void HandleLocalMsg(SyncMsg msg)
|
||||||
{
|
{
|
||||||
if (msg == null)
|
//收到配置文件
|
||||||
|
var config = JsonSerializer.Deserialize<Config>(msg.Body);
|
||||||
|
Context.SyncConfig = config;
|
||||||
|
Context.RemoteSocketConnect().Wait(60 * 1000);
|
||||||
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
return;
|
if (Context.SyncConfig != null)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string Pwd = msg.Body;
|
|
||||||
if (Pwd == "Xfs1%$@_fdYU.>>")
|
|
||||||
{
|
{
|
||||||
Context.LocalSocketSendMsg(new SyncMsg(true, "源服务密码校验成功!"));
|
await Context.RemoteSocketSendMsg(CreateMsg(Context.SyncConfig.RemotePwd));
|
||||||
Context.StateHelper = new WaitingConfigInfoState(Context);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException("pwd error!");
|
throw new NullReferenceException("Config is null!");
|
||||||
}
|
}
|
||||||
}
|
await Context.RemoteSocketLiten();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 1. 获取配置信息,它包含目标的服务器的配置信息
|
/// 1. 执行发布步骤
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
public class WaitingConfigInfoState(LocalSyncServer context) : StateHelpBase(context)
|
public class DeployHelper(LocalSyncServer context)
|
||||||
|
: StateHelpBase(context, SyncProcessStep.DeployProject)
|
||||||
{
|
{
|
||||||
public override void HandleRemoteMsg(SyncMsg? msg) { }
|
public void DeployProcess()
|
||||||
|
|
||||||
public override void HandleLocalMsg(SyncMsg? msg)
|
|
||||||
{
|
{
|
||||||
if (msg == null)
|
if (Context.NotNullSyncConfig.IsDeployProject == false)
|
||||||
{
|
{
|
||||||
return;
|
Context.LocalSocketSendMsg(CreateMsg("配置为不发布跳过此步骤")).Wait();
|
||||||
|
var h = new DiffFileAndPackHelper(Context);
|
||||||
|
Context.StateHelper = h;
|
||||||
|
h.DiffProcess();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string ConfigInfo = msg.Body;
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
Context.SyncConfig =
|
|
||||||
JsonSerializer.Deserialize<Config>(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)
|
ProcessStartInfo startInfo =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
FileName = "cmd.exe", // The command to execute (can be any command line tool)
|
||||||
|
Arguments =
|
||||||
|
$"msdeploy.exe -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)
|
||||||
{
|
{
|
||||||
var state = new RemoteAuthorityState(Context);
|
Context.LocalSocketSendMsg(CreateMsg("发布成功!")).Wait();
|
||||||
state.SendPwdToRemoteServer();
|
var h = new DiffFileAndPackHelper(Context);
|
||||||
Context.StateHelper = state;
|
Context.StateHelper = h;
|
||||||
|
h.DiffProcess();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new Exception("connect remote server failed!");
|
Context.LocalSocketSendMsg(CreateErrMsg(output)).Wait();
|
||||||
|
throw new Exception("执行发布错误,错误信息参考上一条消息!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new TimeoutException("connect remote server timeout");
|
throw new NotSupportedException("只支持windows!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void HandleRemoteMsg(SyncMsg msg) { }
|
||||||
|
|
||||||
|
protected override void HandleLocalMsg(SyncMsg msg) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public class DiffFileAndPackHelper(LocalSyncServer context)
|
||||||
/// 2. 目标服务器权限校验
|
: StateHelpBase(context, SyncProcessStep.DiffFileAndPack)
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
public class RemoteAuthorityState(LocalSyncServer context) : StateHelpBase(context)
|
|
||||||
{
|
{
|
||||||
public override void HandleRemoteMsg(SyncMsg? msg)
|
public void DiffProcess()
|
||||||
{
|
{
|
||||||
if (msg == null)
|
//提取本地文件的信息
|
||||||
|
Context.NotNullSyncConfig.DirFileConfigs.ForEach(e =>
|
||||||
{
|
{
|
||||||
return;
|
e.LocalDirInfo = new Dir(Context.NotNullSyncConfig.LocalRootPath + e.DirPath);
|
||||||
}
|
e.LocalDirInfo.ExtractInfo(e.CherryPicks, e.Excludes);
|
||||||
else { }
|
});
|
||||||
|
//将配置信息发送到remoteServer
|
||||||
|
Context
|
||||||
|
.RemoteSocketSendMsg(CreateMsg(JsonSerializer.Serialize(Context.NotNullSyncConfig)))
|
||||||
|
.Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void HandleLocalMsg(SyncMsg? msg) { }
|
protected override void HandleLocalMsg(SyncMsg msg) { }
|
||||||
|
|
||||||
|
protected override void HandleRemoteMsg(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// 3. 文件比较
|
// /// 0. 发布源验证密码
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
/// <param name="context"></param>
|
// /// <param name="context"></param>
|
||||||
public class DirFilesDiffState(LocalSyncServer context) : StateHelpBase(context)
|
// public class LocalAuthorityState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
{
|
// {
|
||||||
public override void HandleRemoteMsg(SyncMsg? msg)
|
// public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
{
|
// {
|
||||||
if (msg == null)
|
// throw new NotImplementedException("error usage!");
|
||||||
{
|
// }
|
||||||
return;
|
|
||||||
}
|
|
||||||
else { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleLocalMsg(SyncMsg? msg) { }
|
// 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!");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// 4. 本地打包并上传
|
// /// 1. 获取配置信息,它包含目标的服务器的配置信息
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
/// <param name="context"></param>
|
// /// <param name="context"></param>
|
||||||
public class LocalPackAndUploadState(LocalSyncServer context) : StateHelpBase(context)
|
// public class WaitingConfigInfoState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
{
|
// {
|
||||||
public override void HandleRemoteMsg(SyncMsg? msg)
|
// public override void HandleRemoteMsg(SyncMsg? msg) { }
|
||||||
{
|
|
||||||
if (msg == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleLocalMsg(SyncMsg? msg) { }
|
// public override void HandleLocalMsg(SyncMsg? msg)
|
||||||
}
|
// {
|
||||||
|
// if (msg == null)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// string ConfigInfo = msg.Body;
|
||||||
|
// Context.SyncConfig =
|
||||||
|
// JsonSerializer.Deserialize<Config>(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");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// 5. 目标服务器解包并发布
|
// /// 2. 目标服务器权限校验
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
/// <param name="context"></param>
|
// /// <param name="context"></param>
|
||||||
public class RemoteUnPackAndReleaseState(LocalSyncServer context) : StateHelpBase(context)
|
// public class RemoteAuthorityState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
{
|
// {
|
||||||
public override void HandleRemoteMsg(SyncMsg? msg)
|
// public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
{
|
// {
|
||||||
if (msg == null)
|
// if (msg == null)
|
||||||
{
|
// {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
else { }
|
// 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 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);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 3. 文件比较
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="context"></param>
|
||||||
|
// 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!")
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 4. 本地打包并上传
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="context"></param>
|
||||||
|
// public class LocalPackAndUploadState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
|
// {
|
||||||
|
// public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
|
// {
|
||||||
|
// if (msg == null)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// else { }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public override void HandleLocalMsg(SyncMsg? msg) { }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 打包文件
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="msg"></param>
|
||||||
|
// /// <exception cref="Exception"></exception>
|
||||||
|
// public void PackDiffDir(SyncMsg msg)
|
||||||
|
// {
|
||||||
|
// if (msg.IsSuccess)
|
||||||
|
// {
|
||||||
|
// var diff = JsonSerializer.Deserialize<Dir>(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 传递上传进度到前端。
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 5. 目标服务器解包并发布
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="context"></param>
|
||||||
|
// public class RemoteUnPackAndReleaseState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
|
// {
|
||||||
|
// public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
|
// {
|
||||||
|
// if (msg == null)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// else { }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public override void HandleLocalMsg(SyncMsg? msg) { }
|
||||||
|
// }
|
||||||
|
|
|
@ -5,5 +5,6 @@
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*",
|
||||||
|
"TempDir":"D:/TempPack"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,9 @@
|
||||||
|
|
||||||
namespace RemoteServer.Models;
|
namespace RemoteServer.Models;
|
||||||
|
|
||||||
public class SqliteDbContext : DbContext
|
public class SqliteDbContext(IConfiguration configuration) : DbContext
|
||||||
{
|
{
|
||||||
protected readonly IConfiguration Configuration;
|
protected readonly IConfiguration Configuration = configuration;
|
||||||
|
|
||||||
public SqliteDbContext(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
Configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using RemoteServer.Models;
|
using RemoteServer.Models;
|
||||||
|
|
||||||
using RemoteServer;
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
@ -22,8 +21,9 @@ if (app.Environment.IsDevelopment())
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
}
|
}
|
||||||
|
app.UseWebSockets();
|
||||||
app.Urls.Clear();
|
app.Urls.Clear();
|
||||||
app.Urls.Add("http://0.0.0.0:6888");
|
app.Urls.Add("http://0.0.0.0:6828");
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
157
Server/RemoteServer/RemoteSyncServer.cs
Normal file
157
Server/RemoteServer/RemoteSyncServer.cs
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Common;
|
||||||
|
|
||||||
|
namespace RemoteServer;
|
||||||
|
|
||||||
|
public class RemoteSyncServer
|
||||||
|
{
|
||||||
|
#pragma warning disable CA2211 // Non-constant fields should not be visible
|
||||||
|
public static string TempRootFile = "C:/TempPack";
|
||||||
|
#pragma warning restore CA2211 // Non-constant fields should not be visible
|
||||||
|
// public StateHelpBase StateHelper;
|
||||||
|
|
||||||
|
public Config? SyncConfig;
|
||||||
|
|
||||||
|
public Config NotNullSyncConfig {get {
|
||||||
|
if (SyncConfig == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("SyncConfig");
|
||||||
|
}
|
||||||
|
return SyncConfig;
|
||||||
|
}}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// remote server
|
||||||
|
/// </summary>
|
||||||
|
public readonly WebSocket RemoteSocket;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布源-缓冲区,存储数据 最大1MB
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Buffer = new byte[1024 * 1024];
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布开始时间
|
||||||
|
/// </summary>
|
||||||
|
private readonly DateTime StartTime = DateTime.Now;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发布名称
|
||||||
|
/// </summary>
|
||||||
|
public readonly string Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 父工程,用于释放资源
|
||||||
|
/// </summary>
|
||||||
|
public readonly RemoteSyncServerFactory Factory;
|
||||||
|
|
||||||
|
public RemoteSyncServer(WebSocket socket, string name, RemoteSyncServerFactory factory)
|
||||||
|
{
|
||||||
|
RemoteSocket = socket;
|
||||||
|
Name = name;
|
||||||
|
Factory = factory;
|
||||||
|
// StateHelper = new ConnectAuthorityHelper(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task RemoteSocketListen()
|
||||||
|
{
|
||||||
|
string CloseMsg = "任务结束关闭";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//最大1MB!=
|
||||||
|
var buffer = new byte[1024 * 1024];
|
||||||
|
|
||||||
|
while (RemoteSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
var receiveResult = await RemoteSocket.ReceiveAsync(
|
||||||
|
new ArraySegment<byte>(buffer),
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
|
||||||
|
if (receiveResult.MessageType == WebSocketMessageType.Close)
|
||||||
|
{
|
||||||
|
Close(receiveResult.CloseStatusDescription);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// StateHelper.ReceiveLocalMsg(
|
||||||
|
// Encoding.UTF8.GetString(buffer, 0, receiveResult.Count)
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
CloseMsg = e.Message;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Close(CloseMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// public async Task LocalSocketSendMsg(object msgOb)
|
||||||
|
// {
|
||||||
|
// string msg = JsonSerializer.Serialize(msgOb);
|
||||||
|
// await RemoteSocket.SendAsync(
|
||||||
|
// new ArraySegment<byte>(Encoding.UTF8.GetBytes(msg)),
|
||||||
|
// WebSocketMessageType.Text,
|
||||||
|
// true,
|
||||||
|
// CancellationToken.None
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
public async Task RemoteSocketSendMsg(object msgOb)
|
||||||
|
{
|
||||||
|
string msg = JsonSerializer.Serialize(msgOb);
|
||||||
|
var buffer = AESHelper.EncryptStringToBytes_Aes(msg);
|
||||||
|
await RemoteSocket.SendAsync(
|
||||||
|
buffer,
|
||||||
|
WebSocketMessageType.Text,
|
||||||
|
true,
|
||||||
|
CancellationToken.None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close(string? CloseReason)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (RemoteSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
RemoteSocket
|
||||||
|
.CloseAsync(
|
||||||
|
WebSocketCloseStatus.NormalClosure,
|
||||||
|
CloseReason,
|
||||||
|
CancellationToken.None
|
||||||
|
)
|
||||||
|
.Wait(60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RemoteSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
RemoteSocket
|
||||||
|
.CloseAsync(
|
||||||
|
WebSocketCloseStatus.NormalClosure,
|
||||||
|
CloseReason,
|
||||||
|
CancellationToken.None
|
||||||
|
)
|
||||||
|
.Wait(60 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//TODO 日志
|
||||||
|
Console.WriteLine(e.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Factory.RemoveLocalSyncServer(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
Server/RemoteServer/RemoteSyncServerFactory.cs
Normal file
35
Server/RemoteServer/RemoteSyncServerFactory.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
|
||||||
|
namespace RemoteServer;
|
||||||
|
|
||||||
|
public class RemoteSyncServerFactory
|
||||||
|
{
|
||||||
|
private readonly object Lock = new();
|
||||||
|
|
||||||
|
public void CreateLocalSyncServer(WebSocket socket, string name)
|
||||||
|
{
|
||||||
|
if (Servers.Select(x => x.Name == name).Any())
|
||||||
|
{
|
||||||
|
throw new Exception("RemoteServer:存在同名发布源!");
|
||||||
|
}
|
||||||
|
var server = new RemoteSyncServer(socket, name, this);
|
||||||
|
lock (Lock)
|
||||||
|
{
|
||||||
|
Servers.Add(server);
|
||||||
|
}
|
||||||
|
//脱离当前函数栈
|
||||||
|
Task.Run(async ()=>{
|
||||||
|
await server.RemoteSocketListen();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<RemoteSyncServer> Servers = [];
|
||||||
|
|
||||||
|
public void RemoveLocalSyncServer(RemoteSyncServer server)
|
||||||
|
{
|
||||||
|
lock (Lock)
|
||||||
|
{
|
||||||
|
Servers.Remove(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
415
Server/RemoteServer/StateHelper.cs
Normal file
415
Server/RemoteServer/StateHelper.cs
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Common;
|
||||||
|
|
||||||
|
namespace RemoteServer;
|
||||||
|
|
||||||
|
// enum StateWhenMsg
|
||||||
|
// {
|
||||||
|
// Authority = 0,
|
||||||
|
// ConfigInfo = 1,
|
||||||
|
// LocalPackAndUpload = 2,
|
||||||
|
// RemoteUnPackAndRelease = 3,
|
||||||
|
// }
|
||||||
|
|
||||||
|
public abstract class StateHelpBase(
|
||||||
|
LocalSyncServer context,
|
||||||
|
SyncProcessStep step = SyncProcessStep.Connect
|
||||||
|
)
|
||||||
|
{
|
||||||
|
protected readonly LocalSyncServer Context = context;
|
||||||
|
|
||||||
|
protected readonly SyncProcessStep Step = step;
|
||||||
|
|
||||||
|
public SyncMsg CreateErrMsg(string Body)
|
||||||
|
{
|
||||||
|
return new SyncMsg(SyncMsgType.Error, Step, Body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyncMsg CreateMsg(string body, SyncMsgType type = SyncMsgType.General)
|
||||||
|
{
|
||||||
|
return new SyncMsg(type, Step, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReceiveLocalMsg(string msg)
|
||||||
|
{
|
||||||
|
var syncMsg =
|
||||||
|
JsonSerializer.Deserialize<SyncMsg>(msg)
|
||||||
|
?? throw new NullReferenceException("msg is null");
|
||||||
|
if (syncMsg.Step != Step)
|
||||||
|
{
|
||||||
|
throw new Exception("Sync step error!");
|
||||||
|
}
|
||||||
|
HandleLocalMsg(syncMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReceiveRemoteMsg(byte[] bytes)
|
||||||
|
{
|
||||||
|
var msg = AESHelper.DecryptStringFromBytes_Aes(bytes);
|
||||||
|
|
||||||
|
var syncMsg =
|
||||||
|
JsonSerializer.Deserialize<SyncMsg>(msg)
|
||||||
|
?? throw new NullReferenceException("msg is null");
|
||||||
|
if (syncMsg.Step != Step)
|
||||||
|
{
|
||||||
|
throw new Exception("Sync step error!");
|
||||||
|
}
|
||||||
|
HandleLocalMsg(syncMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void HandleRemoteMsg(SyncMsg msg);
|
||||||
|
|
||||||
|
protected abstract void HandleLocalMsg(SyncMsg msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 0. 链接验证
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public class ConnectAuthorityHelper(LocalSyncServer context)
|
||||||
|
: StateHelpBase(context, SyncProcessStep.Connect)
|
||||||
|
{
|
||||||
|
// 如果密码错误,那么就直接关闭连接,不会进入这个方法
|
||||||
|
protected override void HandleRemoteMsg(SyncMsg msg)
|
||||||
|
{
|
||||||
|
//将remote的消息传递到前端界面
|
||||||
|
Context.LocalSocketSendMsg(msg).Wait();
|
||||||
|
//下一步
|
||||||
|
var deployHelper = new DeployHelper(Context);
|
||||||
|
Context.StateHelper = deployHelper;
|
||||||
|
deployHelper.DeployProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void HandleLocalMsg(SyncMsg msg)
|
||||||
|
{
|
||||||
|
//收到配置文件
|
||||||
|
var config = JsonSerializer.Deserialize<Config>(msg.Body);
|
||||||
|
Context.SyncConfig = config;
|
||||||
|
Context.RemoteSocketConnect().Wait(60 * 1000);
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
if (Context.SyncConfig != null)
|
||||||
|
{
|
||||||
|
await Context.RemoteSocketSendMsg(CreateMsg(Context.SyncConfig.RemotePwd));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NullReferenceException("Config is null!");
|
||||||
|
}
|
||||||
|
await Context.RemoteSocketLiten();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 1. 执行发布步骤
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
public class DeployHelper(LocalSyncServer context)
|
||||||
|
: StateHelpBase(context, SyncProcessStep.DeployProject)
|
||||||
|
{
|
||||||
|
public void DeployProcess()
|
||||||
|
{
|
||||||
|
if (Context.NotNullSyncConfig.IsDeployProject == false)
|
||||||
|
{
|
||||||
|
Context.LocalSocketSendMsg(CreateMsg("配置为不发布跳过此步骤")).Wait();
|
||||||
|
var h = new DiffFileAndPackHelper(Context);
|
||||||
|
Context.StateHelper = h;
|
||||||
|
h.DiffProcess();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
ProcessStartInfo startInfo =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
FileName = "cmd.exe", // The command to execute (can be any command line tool)
|
||||||
|
Arguments =
|
||||||
|
$"msdeploy.exe -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.LocalSocketSendMsg(CreateMsg("发布成功!")).Wait();
|
||||||
|
var h = new DiffFileAndPackHelper(Context);
|
||||||
|
Context.StateHelper = h;
|
||||||
|
h.DiffProcess();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Context.LocalSocketSendMsg(CreateErrMsg(output)).Wait();
|
||||||
|
throw new Exception("执行发布错误,错误信息参考上一条消息!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("只支持windows!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void HandleRemoteMsg(SyncMsg msg) { }
|
||||||
|
|
||||||
|
protected override void HandleLocalMsg(SyncMsg msg) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DiffFileAndPackHelper(LocalSyncServer context)
|
||||||
|
: StateHelpBase(context, SyncProcessStep.DiffFileAndPack)
|
||||||
|
{
|
||||||
|
public void DiffProcess()
|
||||||
|
{
|
||||||
|
//提取本地文件的信息
|
||||||
|
Context.NotNullSyncConfig.DirFileConfigs.ForEach(e =>
|
||||||
|
{
|
||||||
|
e.LocalDirInfo = new Dir(Context.NotNullSyncConfig.LocalRootPath + e.DirPath);
|
||||||
|
e.LocalDirInfo.ExtractInfo(e.CherryPicks, e.Excludes);
|
||||||
|
});
|
||||||
|
//将配置信息发送到remoteServer
|
||||||
|
Context
|
||||||
|
.RemoteSocketSendMsg(CreateMsg(JsonSerializer.Serialize(Context.NotNullSyncConfig)))
|
||||||
|
.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void HandleLocalMsg(SyncMsg msg) { }
|
||||||
|
|
||||||
|
protected override void HandleRemoteMsg(SyncMsg msg) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 0. 发布源验证密码
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="context"></param>
|
||||||
|
// 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!");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 1. 获取配置信息,它包含目标的服务器的配置信息
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="context"></param>
|
||||||
|
// 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<Config>(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");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 2. 目标服务器权限校验
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="context"></param>
|
||||||
|
// 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);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 3. 文件比较
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="context"></param>
|
||||||
|
// 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!")
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 4. 本地打包并上传
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="context"></param>
|
||||||
|
// public class LocalPackAndUploadState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
|
// {
|
||||||
|
// public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
|
// {
|
||||||
|
// if (msg == null)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// else { }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public override void HandleLocalMsg(SyncMsg? msg) { }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 打包文件
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="msg"></param>
|
||||||
|
// /// <exception cref="Exception"></exception>
|
||||||
|
// public void PackDiffDir(SyncMsg msg)
|
||||||
|
// {
|
||||||
|
// if (msg.IsSuccess)
|
||||||
|
// {
|
||||||
|
// var diff = JsonSerializer.Deserialize<Dir>(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 传递上传进度到前端。
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// <summary>
|
||||||
|
// /// 5. 目标服务器解包并发布
|
||||||
|
// /// </summary>
|
||||||
|
// /// <param name="context"></param>
|
||||||
|
// public class RemoteUnPackAndReleaseState(LocalSyncServer context) : StateHelpBase(context)
|
||||||
|
// {
|
||||||
|
// public override void HandleRemoteMsg(SyncMsg? msg)
|
||||||
|
// {
|
||||||
|
// if (msg == null)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// else { }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public override void HandleLocalMsg(SyncMsg? msg) { }
|
||||||
|
// }
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Text;
|
using Common;
|
||||||
using Common;
|
|
||||||
|
|
||||||
namespace ServerTest;
|
namespace ServerTest;
|
||||||
|
|
||||||
|
|
115
Tool/JsScript/release.js
Normal file
115
Tool/JsScript/release.js
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import chalk from "chalk";
|
||||||
|
import WebSocket from "ws";
|
||||||
|
|
||||||
|
|
||||||
|
//#region ############################## 配置文件 ###################################
|
||||||
|
|
||||||
|
//这是个例子,请在`config`中写你的配置
|
||||||
|
const example_config = {
|
||||||
|
//发布的项目名称,它的目的是为了防止有两个人同时发布一个项目,和便于排查发布历史记录
|
||||||
|
Name: "",
|
||||||
|
//发布服务器的地址
|
||||||
|
RemoteUrl: "http://192.168.1.100:8067",
|
||||||
|
//源SqlServer数据库的链接字符串,一般是开发或者测试数据库,**此数据库的ip是相对于运行此脚本的机器**
|
||||||
|
SrcDbConnection: "",
|
||||||
|
//目的SqlServer数据库的链接字符串,一般是正式环境数据库,**此数据库的ip是相对于运行RemoteServer的机器**
|
||||||
|
DstDbConnection: "",
|
||||||
|
|
||||||
|
//发布数据库时,只同步结构。此数组中的表,将会连数据也一起同步
|
||||||
|
SyncDataTables:[],
|
||||||
|
//源文件目录地址,是要发布的文件根目录,它是绝对路径,!执行发布时将发布到这个目录!
|
||||||
|
LocalRootPath: "",
|
||||||
|
//目标文件目录地址,也就是部署服务的机器上的项目文件根目录,它是绝对路径
|
||||||
|
RemoteRootPath: "",
|
||||||
|
|
||||||
|
//根目录下的子目录,分子目录是为了针对不同的目录有不同的发布策略,它是相对路径,即相对于LocalRootPath和RemoteRootPath,文件数据依此进行
|
||||||
|
DirFileConfigs: [
|
||||||
|
{
|
||||||
|
//子目录的相对路径
|
||||||
|
DirPath: "",
|
||||||
|
//排除的文件或目录,它是相对路径,相对于!!!LocalRootPath和RemoteRootPath!!!
|
||||||
|
Excludes: [],
|
||||||
|
//只追踪文件或目录,它是相对路径,相对于!!!LocalRootPath和RemoteRootPath!!!,它的优先级最高,如果你指定了它的值,Excludes将会失效
|
||||||
|
CherryPicks: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const config = {};
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region ############################## 打印函数 ###################################
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在新行打印错误信息
|
||||||
|
*/
|
||||||
|
function PrintErrInNewLine(str) {
|
||||||
|
process.stdout.write("\n");
|
||||||
|
var chunk = chalk["red"]("[错误]: ");
|
||||||
|
process.stdout.write(chunk);
|
||||||
|
process.stdout.write(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在新行打印成功信息
|
||||||
|
*/
|
||||||
|
function PrintSuccessInNewLine(str) {
|
||||||
|
process.stdout.write("\n");
|
||||||
|
var chunk = chalk["green"]("[成功]: ");
|
||||||
|
process.stdout.write(chunk);
|
||||||
|
process.stdout.write(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在新行打印一般信息
|
||||||
|
*/
|
||||||
|
function PrintGeneralInNewLine(str) {
|
||||||
|
process.stdout.write("\n");
|
||||||
|
process.stdout.write(str);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 在当前行打印一般信息,打印此行信息之前会清除当前行
|
||||||
|
*/
|
||||||
|
function PrintGeneralInCurrentLine(str) {
|
||||||
|
process.stdout.write(`\r${str}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region ############################### work #############################################
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1-n. localServer 工作,此处只展示信息
|
||||||
|
*/
|
||||||
|
|
||||||
|
//这个是固定死的
|
||||||
|
const wsUrl = `ws://127.0.0.1:4538/websoc?Name=${config.Name}`;
|
||||||
|
const ws = new WebSocket(wsUrl);
|
||||||
|
|
||||||
|
ws.on('open', () => {
|
||||||
|
//上传配置
|
||||||
|
ws.send(JSON.stringify(config),(err)=>{
|
||||||
|
console.log(err)
|
||||||
|
ws.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('message', (message) => {
|
||||||
|
var msg = message.toString('utf8')
|
||||||
|
DealMsg(msg)
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('close', () => {
|
||||||
|
});
|
||||||
|
|
||||||
|
function DealMsg(str) {
|
||||||
|
var msg = JSON.parse(str)
|
||||||
|
if(msg.IsSuccess) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
PrintErrInNewLine(msg.Body)
|
||||||
|
ws.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//#endregion
|
37
Tool/JsScript/test.js
Normal file
37
Tool/JsScript/test.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// import WebSocket from 'ws';
|
||||||
|
|
||||||
|
// const wsUrl = 'wss://toolin.cn/echo';
|
||||||
|
// const ws = new WebSocket(wsUrl);
|
||||||
|
|
||||||
|
// ws.on('open', () => {
|
||||||
|
// console.log('Connected to WebSocket server');
|
||||||
|
// ws.send("赵磊f",(err)=>{
|
||||||
|
// console.log(err)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// });
|
||||||
|
|
||||||
|
// ws.on('message', (message) => {
|
||||||
|
// var str = message.toString('utf8')
|
||||||
|
// if(str.includes("赵磊f")) {
|
||||||
|
// ws.close()
|
||||||
|
// }
|
||||||
|
// console.log('Received message:',str);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// ws.on('close', () => {
|
||||||
|
// console.log('Disconnected from WebSocket server');
|
||||||
|
// });
|
||||||
|
|
||||||
|
import chalk from 'chalk';
|
||||||
|
function logProgress(current, total) {
|
||||||
|
const progressPercentage = (current / total) * 100;
|
||||||
|
var str = `Progress: ${progressPercentage.toFixed(2)}%\r`
|
||||||
|
var x = chalk['red'](str);
|
||||||
|
process.stdout.write(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage:
|
||||||
|
setInterval(() => {
|
||||||
|
logProgress(Math.floor(Math.random() * 100), 100);
|
||||||
|
}, 100); // Update progress every 100 milliseconds with a random value
|
Loading…
Reference in a new issue