using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Management.Automation; using System.Management.Automation.Runspaces; using System.Security; using System.Diagnostics; using System.Net; using Renci.SshNet; using Serilog; using Newtonsoft.Json.Linq; using System.ServiceProcess; using Renci.SshNet.Messages; using System.Management; using System.IO; using static System.Windows.Forms.VisualStyles.VisualStyleElement.Window; using System.Management.Automation.Host; using SharpCompress.Common; using System.IO.Compression; namespace PublishSite { public partial class PublishForm : Form { public PublishForm() { InitializeComponent(); Serilog.Log.Logger = new LoggerConfiguration() .WriteTo.File("logs\\log.txt", rollingInterval: RollingInterval.Day) .CreateLogger(); } public string EnvName = ""; public JObject jObjectCofig; public string Host = ""; public string UserName = ""; public string Password = ""; private void WinformLog(string message, Color? color = null) { if (InvokeRequired) { Invoke(new Action(() => WinformLog(message, color))); return; } logTBox.SelectionColor = color == null ? Color.Green : (Color)color; logTBox.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}: {message}" + Environment.NewLine); Serilog.Log.Information(message); } #region 切换环境 配置回写 private void rb_Env_CheckedChanged(object sender, EventArgs e) { //本地 if (rb_local.Checked) { EnvName = "本地"; WinformLog($"当前操作环境切换为: 本地", Color.DarkRed); var configJson = File.ReadAllText("appsettings.local.json"); jObjectCofig = JObject.Parse(configJson); } //测试 if (rb_test.Checked) { EnvName = "测试"; WinformLog($"当前操作环境切换为: 测试", Color.DarkRed); var configJson = File.ReadAllText("appsettings.Development.json"); jObjectCofig = JObject.Parse(configJson); } //验证 if (rb_Verify.Checked) { EnvName = "验证"; WinformLog($"当前操作环境切换为: 验证", Color.DarkRed); var configJson = File.ReadAllText("appsettings.Verify.json"); jObjectCofig = JObject.Parse(configJson); } //生产 if (rb_production.Checked) { EnvName = "生产"; WinformLog($"当前操作环境切换为: 生产", Color.DarkRed); var configJson = File.ReadAllText("appsettings.Production.json"); jObjectCofig = JObject.Parse(configJson); } #region 界面控件赋值 Host = rb_local.Checked ? "" : jObjectCofig["Credential"]["Host"].ToString(); UserName = rb_local.Checked ? "" : jObjectCofig["Credential"]["UserName"].ToString(); Password = rb_local.Checked ? "" : jObjectCofig["Credential"]["Password"].ToString(); serviceNameTBox.Text = jObjectCofig["DefaultService"]["ServiceName"].ToString(); serviceDisplayNameTBox.Text = jObjectCofig["DefaultService"]["ServiceDisplayName"].ToString(); servicePortTBox.Text = jObjectCofig["DefaultService"]["ServicePort"].ToString(); exePathTbox.Text = jObjectCofig["DefaultService"]["BinPath"].ToString(); en_TBox.Text = jObjectCofig["DefaultService"]["Env"].ToString(); startOrStopServiceTbox.Text = jObjectCofig["DefaultService"]["ServiceName"].ToString(); bakDbName.Text = jObjectCofig["BackUp"]["DataBaseName"].ToString(); backPathTBox.Text = jObjectCofig["BackUp"]["BackPath"].ToString(); backExePath.Text = jObjectCofig["DefaultService"]["BinPath"].ToString(); var credentialNode = jObjectCofig["Credential"]; var defaultServiceNode = jObjectCofig["DefaultService"]; var BackUpNode = jObjectCofig["BackUp"]; #endregion } private void serviceConfigSaveBtn_Click(object sender, EventArgs e) { serviceConfigSaveBtn.Enabled = false; var serviceName = serviceNameTBox.Text; DialogResult dr = MessageBox.Show($"{EnvName}环境服务配置回写 ?", "保存提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (dr == DialogResult.OK) { var path = string.Empty; if (rb_local.Checked) { path = "appsettings.local.json"; } if (rb_test.Checked) { path = "appsettings.Development.json"; } if (rb_Verify.Checked) { path = "appsettings.Verify.json"; } if (rb_production.Checked) { path = "appsettings.Production.json"; } var configJson = File.ReadAllText(path); jObjectCofig = JObject.Parse(configJson); jObjectCofig["DefaultService"]["ServiceName"] = serviceNameTBox.Text.Trim(); jObjectCofig["DefaultService"]["ServiceDisplayName"] = serviceDisplayNameTBox.Text.Trim(); jObjectCofig["DefaultService"]["ServicePort"] = servicePortTBox.Text.Trim(); jObjectCofig["DefaultService"]["BinPath"] = exePathTbox.Text.Trim(); jObjectCofig["DefaultService"]["Env"] = en_TBox.Text.Trim(); File.WriteAllText(path, jObjectCofig.ToString()); WinformLog("服务配置回写成功!"); } serviceConfigSaveBtn.Enabled = true; } private void bakConfigSaveBtn_Click(object sender, EventArgs e) { bakConfigSaveBtn.Enabled = false; var serviceName = serviceNameTBox.Text; DialogResult dr = MessageBox.Show($"{EnvName}环境备份配置回写 ?", "保存提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (dr == DialogResult.OK) { var path = string.Empty; if (rb_local.Checked) { path = "appsettings.local.json"; } if (rb_test.Checked) { path = "appsettings.Development.json"; } if (rb_Verify.Checked) { path = "appsettings.Verify.json"; } if (rb_production.Checked) { path = "appsettings.Production.json"; } var configJson = File.ReadAllText(path); jObjectCofig = JObject.Parse(configJson); jObjectCofig["BackUp"]["DataBaseName"] = bakDbName.Text.Trim(); jObjectCofig["BackUp"]["BackPath"] = backPathTBox.Text.Trim(); File.WriteAllText(path, jObjectCofig.ToString()); WinformLog("备份配置回写成功!"); bakConfigSaveBtn.Enabled = true; } } #endregion #region 备份 private async void startBakBtn_Click(object sender, EventArgs e) { await Task.Run(() => { if (rb_local.Checked) { WinformLog("本地环境不支持备份操作,懒得开发"); return; } else { using (var client = new SshClient(Host, UserName, Password)) { // 连接到远程服务器 client.Connect(); try { var dbName = bakDbName.Text.Trim(); var backRootPath = backPathTBox.Text.Trim(); var serviceName = jObjectCofig["DefaultService"]["ServiceName"].ToString().Trim(); var programPath = backExePath.Text.Trim(); var programFolder = Path.GetDirectoryName(programPath); var dbUser = jObjectCofig["BackUp"]["DBUser"].ToString().Trim(); var dbPwd = jObjectCofig["BackUp"]["DBPwd"].ToString().Trim(); var backupFolderPath = Path.Combine(backRootPath, DateTime.Now.ToString("yyyyMMdd")); //创建文件夹 WinformLog("准备当日备份文件夹..."); var command = client.CreateCommand($"if not exist \"{backupFolderPath}\" mkdir \"{backupFolderPath}\""); command.Execute(); if (command.ExitStatus != 0) { throw new Exception($"Failed to create {backupFolderPath}."); } //停止服务 WinformLog($"停止后台服务{serviceName}..."); var stopServiceCommand = client.CreateCommand($"sc stop \"{serviceName}\""); stopServiceCommand.Execute(); if (stopServiceCommand.ExitStatus != 0) { throw new Exception($"Failed to stop {serviceName}."); } Task.Delay(1000); //备份后端文件 WinformLog($"备份后端文件..."); string archivePath = $"{backupFolderPath}\\NetCore.zip"; var compressCommand = client.CreateCommand($"powershell.exe Compress-Archive -Path \"{programFolder}\\*.*\" -DestinationPath \"{archivePath}\" -Force"); compressCommand.Execute(); if (compressCommand.ExitStatus != 0) { throw new Exception("Failed to compress and move folder contents."); } //启动服务 WinformLog($"启动后台服务{serviceName}..."); var startServiceCommand = client.CreateCommand($"sc start \"{serviceName}\""); startServiceCommand.Execute(); if (startServiceCommand.ExitStatus != 0) { throw new Exception($"Failed to start {serviceName}."); } //备份数据库 WinformLog($"开始备份数据库{dbName}..."); string databaseBackupPath = $"{backupFolderPath}\\db.bak"; var sqlcommand = client.CreateCommand($"sqlcmd -S . -U \"{dbUser}\" -P \"{dbPwd}\" -Q \"BACKUP DATABASE [{dbName}] TO DISK='{databaseBackupPath}' WITH INIT\""); sqlcommand.Execute(); if (sqlcommand.ExitStatus != 0) { throw new Exception("Failed to backup database."); } WinformLog($"备份操作执行结束..."); } catch (Exception ex) { WinformLog($"{ex.Message}", Color.Red); } } } }); } #endregion #region 服务的创建与删除 private void createService_Btn_Click(object sender, EventArgs e) { } private async void deleteService_Btn_Click(object sender, EventArgs e) { var serviceName = serviceNameTBox.Text; DialogResult dr = MessageBox.Show($"{EnvName}环境删除服务:{serviceName}?", "删除提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (dr == DialogResult.OK) { await Task.Run(() => { if (rb_local.Checked) { ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = "cmd.exe"; startInfo.Arguments = $"/c sc query {serviceName} | find \"STATE\" | find /i \"RUNNING\" && net stop {serviceName} & sc delete {serviceName}"; // 在后台运行命令行窗口 startInfo.WindowStyle = ProcessWindowStyle.Hidden; startInfo.CreateNoWindow = true; using (Process process = new Process()) { process.StartInfo = startInfo; process.Start(); process.WaitForExit(); if (process.ExitCode == 0) { WinformLog($"{serviceName} 服务已经成功删除!"); } else { WinformLog($"尝试停止并删除 {serviceName} 服务失败,请检查是否拥有管理员权限或服务是否存在。", Color.Red); } } } else { using (var client = new SshClient(Host, UserName, Password)) { // 连接到远程服务器 client.Connect(); // 停止服务 string stopCommand = $"sc stop {serviceName}"; using (var stopCmd = client.CreateCommand(stopCommand)) { WinformLog(stopCommand); var stopOutput = stopCmd.Execute(); WinformLog(stopOutput); } // 删除服务 string deleteCommand = $"sc delete {serviceName}"; using (var deleteCmd = client.CreateCommand(deleteCommand)) { WinformLog(deleteCommand); var deleteOutput = deleteCmd.Execute(); WinformLog(deleteOutput); } // 断开SSH连接 client.Disconnect(); } } }); } } private void selectPathBtn_Click(object sender, EventArgs e) { if (rb_local.Checked) { OpenFileDialog dialog = new OpenFileDialog(); if (dialog.ShowDialog() == DialogResult.OK) { exePathTbox.Text = dialog.FileName; WinformLog($"部署启动文件选择成功", Color.Green); } } else { WinformLog("远程环境不支持直接选择目录", Color.Red); } } #endregion #region 测试环境发布 private async void vuePublishBtn_Click(object sender, EventArgs e) { vuePublishBtn.Enabled = false; await Task.Run(() => { using (var client = new SshClient("123.56.94.154", "Administrator", "WHxckj2019")) { // 连接到远程服务器 client.Connect(); //&& ./IRaCIS_Vue_Web.bat string command = $@"cd C:\Users\Administrator\VueSourceCode && npm i && npm run build"; using (var cmd = client.CreateCommand(command)) { WinformLog("测试环境:开始执行前端发布"); WinformLog(command); var output = cmd.Execute(); WinformLog(output); var output3 = cmd.Execute($@"xcopy /Y /Q ""C:\Users\Administrator\VueSourceCode\dist\index.html"" ""D:\Develop\PublishSite\IRaCIS.Vue.Web\dist\"""); WinformLog(output); WinformLog("测试环境:前端发布成功"); } // 断开SSH连接 client.Disconnect(); } }); vuePublishBtn.Enabled = true; } private async void netCorePublishBtn_Click(object sender, EventArgs e) { netCorePublishBtn.Enabled = false; await Task.Run(() => { using (var client = new SshClient("123.56.94.154", "Administrator", "WHxckj2019")) { // 连接到远程服务器 client.Connect(); string command = $@"C:\Users\Administrator\bat\IRaCIS_Core_API.bat"; using (var cmd = client.CreateCommand(command)) { WinformLog("测试环境:开始执行后端发布"); WinformLog(command); var output = cmd.Execute(); WinformLog(output); WinformLog("测试环境:后端发布成功"); } // 断开SSH连接 client.Disconnect(); } }); netCorePublishBtn.Enabled = true; } #endregion #region 服务的启动与停止 private async void startServiceBtn_Click(object sender, EventArgs e) { startServiceBtn.Enabled = false; await Task.Run(() => { if (rb_local.Checked) { ServiceController controller = new ServiceController($"{startOrStopServiceTbox.Text}"); if (controller?.Status == ServiceControllerStatus.Running) { controller.Stop(); controller.WaitForStatus(ServiceControllerStatus.Stopped); } else { WinformLog("服务不是停止状态", Color.Red); } return; } using (var client = new SshClient(Host, UserName, Password)) { // 连接到远程服务器 client.Connect(); string command = $@"sc start {startOrStopServiceTbox.Text}"; using (var cmd = client.CreateCommand(command)) { WinformLog(command); var output = cmd.Execute(); WinformLog(output); } // 断开SSH连接 client.Disconnect(); } }); startServiceBtn.Enabled = true; } private async void stopServiceBtn_Click(object sender, EventArgs e) { startServiceBtn.Enabled = false; await Task.Run(() => { if (rb_local.Checked) { ServiceController controller = new ServiceController($"{startOrStopServiceTbox.Text}"); if (controller?.Status == ServiceControllerStatus.Running) { controller.Stop(); controller.WaitForStatus(ServiceControllerStatus.Stopped); } else { WinformLog("服务不是启动状态", Color.Red); } return; } using (var client = new SshClient(Host, UserName, Password)) { // 连接到远程服务器 client.Connect(); string command = $@"sc stop {startOrStopServiceTbox.Text}"; using (var cmd = client.CreateCommand(command)) { WinformLog(command); var output = cmd.Execute(); WinformLog(output); } // 断开SSH连接 client.Disconnect(); } }); startServiceBtn.Enabled = true; } #endregion #region 上传文件到远程服务器发布 private void selectPublishFilePathBtn_Click(object sender, EventArgs e) { OpenFileDialog dialog = new OpenFileDialog(); if (dialog.ShowDialog() == DialogResult.OK) { publishFileTBox.Text = dialog.FileName; WinformLog($"远程部署文件选择成功", Color.Green); } } private void selectPublishFolderPathBtn_Click(object sender, EventArgs e) { FolderBrowserDialog dialog = new FolderBrowserDialog(); dialog.Description = "选择目录"; if (dialog.ShowDialog() == DialogResult.OK) { publishFolderTBox.Text = dialog.SelectedPath; WinformLog($"远程部署后端发布文件夹选择成功", Color.Green); } } private async void sshVuePublishBtn_Click(object sender, EventArgs e) { var desTinationFoder = jObjectCofig["BackUp"]["VuePublishFolder"].ToString(); sshVuePublishBtn.Enabled = false; var filePath = publishFileTBox.Text; var realName = Path.GetFileName(filePath); var storeTempName = $"{Guid.NewGuid()}_{realName}"; if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) { WinformLog($"请选择存在的文件才能开始发布", Color.Red); return; } WinformLog($"开始上传前端部署文件{Path.GetFileName(filePath)}..."); await Task.Run(() => { using (var client = new SshClient(Host, UserName, Password)) { client.ConnectionInfo.Encoding = System.Text.Encoding.UTF8; // 连接到远程服务器 client.Connect(); using (var scpClient = new ScpClient(Host, UserName, Password)) { scpClient.Connect(); using (var fileStream = new FileStream(filePath, FileMode.Open)) { scpClient.Upload(fileStream, @$"/{storeTempName}"); } scpClient.Disconnect(); WinformLog($"上传{realName}结束"); } string command = $@"xcopy /Y /Q ""C:\{storeTempName}"" ""{desTinationFoder}"""; //拷贝文件到发布文件夹 using (var cmd = client.CreateCommand(command)) { WinformLog(command); var output = cmd.Execute(); WinformLog(output); } //删除临时文件 var delCommand = $"move /Y \"{Path.Combine(desTinationFoder, storeTempName)}\" \"{Path.Combine(desTinationFoder, realName)}\" && del C:\\{storeTempName}"; using (var cmd = client.CreateCommand(delCommand)) { WinformLog(delCommand); var output = cmd.Execute(); WinformLog(output); } WinformLog($"清除零时文件{storeTempName}完成"); // 断开SSH连接 client.Disconnect(); WinformLog($"前端发布成功"); } }); WinformLog($"前端你发布结束"); sshVuePublishBtn.Enabled = true; } private async void sshNetCorePublishBtn_Click(object sender, EventArgs e) { var binPath = jObjectCofig["DefaultService"]["BinPath"].ToString(); var desTinationFoder = Path.GetDirectoryName(binPath); sshNetCorePublishBtn.Enabled = false; var filePath = publishFileTBox.Text; var realName = Path.GetFileName(filePath); var storeTempName = $"{Guid.NewGuid()}_{realName}"; if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) { WinformLog($"请选择存在的文件才能开始发布", Color.Red); return; } if (!string.Equals(Path.GetExtension(realName), ".zip", StringComparison.OrdinalIgnoreCase)) { WinformLog($"后端远程发布的压缩包必须是zip格式,并且不带目录", Color.Red); return; } WinformLog($"开始上传后端部署文件{realName}..."); await NetCoreZipPublishAsync(filePath, desTinationFoder); WinformLog($"后端发布结束"); sshNetCorePublishBtn.Enabled = true; } private async Task NetCoreZipPublishAsync(string filePath, string desTinationFoder) { var realName = Path.GetFileName(filePath); var storeTempName = $"{Guid.NewGuid()}_{realName}"; await Task.Run(async () => { using (var client = new SshClient(Host, UserName, Password)) { client.ConnectionInfo.Encoding = System.Text.Encoding.UTF8; // 连接到远程服务器 client.Connect(); using (var scpClient = new ScpClient(Host, UserName, Password)) { scpClient.Connect(); using (var fileStream = new FileStream(filePath, FileMode.Open)) { scpClient.Upload(fileStream, @$"/{storeTempName}"); } scpClient.Disconnect(); WinformLog($"上传{realName}结束"); } //后端发布,处理压缩包流程 //停止服务 stopServiceBtn_Click(null, null); await Task.Delay(2000); //解压到发布的目录 string expandCommand = $"Expand-Archive -Path '{filePath}' -DestinationPath '{desTinationFoder}' -Force"; using (var cmd = client.CreateCommand($"powershell.exe {expandCommand}")) { WinformLog(expandCommand); var output = cmd.Execute(); WinformLog(output); } //启动服务 startServiceBtn_Click(null, null); //删除临时文件 var delCommand = $" del C:\\{storeTempName}"; using (var cmd = client.CreateCommand(delCommand)) { WinformLog(delCommand); var output = cmd.Execute(); WinformLog(output); } WinformLog($"清除零时文件{storeTempName}完成"); // 断开SSH连接 client.Disconnect(); } }); } private async void sshNetCorePublishBtn2_Click(object sender, EventArgs e) { var binPath = jObjectCofig["DefaultService"]["BinPath"].ToString(); var localSourceFolder = publishFolderTBox.Text; var desTinationFoder = Path.GetDirectoryName(binPath); sshNetCorePublishBtn2.Enabled = false; WinformLog($"开始打包该文件夹下的内容..."); var filePath = Path.Combine(localSourceFolder, "NetCorePublish.zip"); if (File.Exists(filePath)) { File.Delete(filePath); } try { //将文件夹里的内容打包成压缩包 ZipFile.CreateFromDirectory(localSourceFolder, filePath); } catch (Exception ex) { WinformLog("错误:" + ex.Message, Color.Red); } await NetCoreZipPublishAsync(filePath, desTinationFoder); WinformLog($"后端发布结束"); sshNetCorePublishBtn2.Enabled = true; } #endregion } }