576 lines
17 KiB
C#
576 lines
17 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Data;
|
||
using System.Data.SqlClient;
|
||
using System.Drawing;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Net.NetworkInformation;
|
||
using System.Windows.Forms;
|
||
using System.Net;
|
||
using System.Security.Cryptography;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Linq;
|
||
using System.Diagnostics;
|
||
using Microsoft.VisualBasic.Logging;
|
||
using System.IO.Compression;
|
||
using SharpCompress.Archives.Rar;
|
||
using SharpCompress.Common;
|
||
using SharpCompress.Archives;
|
||
using System.Diagnostics.Eventing.Reader;
|
||
using System.ServiceProcess;
|
||
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
|
||
using System.Security.Policy;
|
||
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolBar;
|
||
using System.Data.SqlTypes;
|
||
|
||
namespace Start
|
||
{
|
||
public partial class Main : Form
|
||
{
|
||
|
||
private bool isCanDBConfig = true;
|
||
|
||
private bool isPortTestOk = false;
|
||
|
||
private bool isServiceTestOk = false;
|
||
|
||
private bool isDBNameTestOK = false;
|
||
|
||
public Main()
|
||
{
|
||
InitializeComponent();
|
||
|
||
var physicalAddressList = NetworkInterface.GetAllNetworkInterfaces().Select(t => t.GetPhysicalAddress().ToString());
|
||
|
||
this.machineTextBox.Text = physicalAddressList.FirstOrDefault()?.ToString();
|
||
|
||
this.KeySecreteTextBox.Text = Md5($"{this.machineTextBox.Text}_XINGCANG");
|
||
}
|
||
|
||
int apiPort = 7100;
|
||
int vuePort = 9527;
|
||
private void connectButton_Click(object sender, EventArgs e)
|
||
{
|
||
string connectionString = $"Server={serverTextBox.Text};User Id={usernameTextBox.Text};Password={passwordTextBox.Text};";
|
||
SqlConnection connection = new SqlConnection(connectionString);
|
||
|
||
try
|
||
{
|
||
connection.Open();
|
||
|
||
Log("数据库连接测试成功!", Color.Green);
|
||
|
||
isCanDBConfig = false;
|
||
serverTextBox.Enabled = isCanDBConfig;
|
||
usernameTextBox.Enabled = isCanDBConfig;
|
||
passwordTextBox.Enabled = isCanDBConfig;
|
||
connectButton.Enabled = false;
|
||
|
||
|
||
selectPathBtn.Enabled = true;
|
||
portBtn.Enabled = true;
|
||
testDBBtn.Enabled = true;
|
||
testServicebtn.Enabled = true;
|
||
confimDeployBtn.Enabled = true;
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
|
||
Log($"数据库连接测试失败:{ex.Message}", Color.Red);
|
||
|
||
}
|
||
finally
|
||
{
|
||
connection.Close();
|
||
}
|
||
}
|
||
|
||
private async void portBtn_Click(object sender, EventArgs e)
|
||
{
|
||
|
||
if (int.TryParse(nginxPortTBox.Text, out vuePort) == false || int.TryParse(apiPortTBox.Text, out apiPort) == false)
|
||
{
|
||
Log($"请输入合法的端口", Color.Red);
|
||
return;
|
||
}
|
||
|
||
if (IsPortInUse(vuePort))
|
||
{
|
||
Log($"服务设置的前端端口被占用,请选择其他端口", Color.Red);
|
||
|
||
return;
|
||
}
|
||
if (IsPortInUse(apiPort))
|
||
{
|
||
Log($"服务设置的后端端口被占用,请选择其他端口", Color.Red);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
Log("端口测试成功!", Color.Green);
|
||
|
||
isPortTestOk = true;
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
private void testServicebtn_Click(object sender, EventArgs e)
|
||
{
|
||
if (ServiceController.GetServices().Any(t => t.ServiceName == serviceNameTBox.Text))
|
||
{
|
||
Log($"当前服务名称已存在,请更换", Color.Red);
|
||
|
||
return;
|
||
}
|
||
Log($"测试服务名称成功", Color.Green);
|
||
isServiceTestOk = true;
|
||
}
|
||
|
||
private void testDBBtn_Click(object sender, EventArgs e)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(dbNameTBox.Text))
|
||
{
|
||
Log($"请输入部署的数据库名称", Color.Red);
|
||
|
||
return;
|
||
}
|
||
|
||
string connectionString = $"Server={serverTextBox.Text};User Id={usernameTextBox.Text};Password={passwordTextBox.Text};";
|
||
|
||
string sql = $"SELECT COUNT(*) FROM sys.databases WHERE name = '{dbNameTBox.Text.Trim()}';";
|
||
// 创建连接对象和命令对象
|
||
using (SqlConnection connection = new SqlConnection(connectionString))
|
||
using (SqlCommand command = new SqlCommand(sql, connection))
|
||
{
|
||
// 打开连接
|
||
connection.Open();
|
||
|
||
// 执行查询,返回结果行数
|
||
int rowCount = (int)command.ExecuteScalar();
|
||
|
||
// 判断结果是否大于0,即是否存在该数据库
|
||
if (rowCount > 0)
|
||
{
|
||
|
||
Log($"{dbNameTBox.Text.Trim()}该数据库已存在。", Color.Red);
|
||
|
||
return;
|
||
}
|
||
|
||
}
|
||
|
||
Log($"数据库名称测试成功", Color.Green);
|
||
isDBNameTestOK = true;
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
private void confimDeployBtn_Click(object sender, EventArgs e)
|
||
{
|
||
|
||
if (string.IsNullOrWhiteSpace(this.folderPathTbox.Text) || isDBNameTestOK == false || isPortTestOk == false || isServiceTestOk == false)
|
||
{
|
||
Log($"请选择部署路径,并分别测试端口,数据库名,服务名都成功才可以进行确认", Color.Red);
|
||
|
||
return;
|
||
}
|
||
|
||
Log($"部署配置确认成功", Color.Green);
|
||
|
||
activeBtn.Enabled = true;
|
||
|
||
confimDeployBtn.Enabled = false;
|
||
portBtn.Enabled = false;
|
||
selectPathBtn.Enabled = false;
|
||
testDBBtn.Enabled = false;
|
||
testServicebtn.Enabled = false;
|
||
|
||
|
||
nginxPortTBox.Enabled = false;
|
||
apiPortTBox.Enabled = false;
|
||
dbNameTBox.Enabled = false;
|
||
serviceNameTBox.Enabled = false;
|
||
serviceDisplayNameTBox.Enabled = false;
|
||
}
|
||
|
||
private static bool IsPortInUse(int port)
|
||
{
|
||
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
|
||
IPEndPoint[] tcpEndPoints = ipProperties.GetActiveTcpListeners();
|
||
foreach (IPEndPoint endPoint in tcpEndPoints)
|
||
{
|
||
if (endPoint.Port == port)
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public static string Md5(string target)
|
||
{
|
||
using (MD5 md5 = MD5.Create())
|
||
{ // MD5非线程安全
|
||
byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(target));
|
||
StringBuilder sb = new StringBuilder(32);
|
||
for (int i = 0; i < bytes.Length; ++i)
|
||
sb.Append(bytes[i].ToString("x2"));
|
||
return sb.ToString();
|
||
}
|
||
}
|
||
private void Log(string message, Color color)
|
||
{
|
||
if (InvokeRequired)
|
||
{
|
||
Invoke(Log, message, color);
|
||
return;
|
||
}
|
||
|
||
|
||
logTBox.SelectionColor = color;
|
||
logTBox.AppendText(message + Environment.NewLine);
|
||
}
|
||
|
||
private async Task ProcessStandardInputAsync(Process process, string cmd, string workDirectory = "")
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(workDirectory))
|
||
{
|
||
process.StartInfo.WorkingDirectory = workDirectory;
|
||
}
|
||
|
||
await process.StandardInput.WriteLineAsync(cmd);
|
||
await process.StandardInput.FlushAsync();
|
||
|
||
}
|
||
|
||
private async void activeBtn_Click(object sender, EventArgs e)
|
||
{
|
||
if (Md5($"{machineTextBox.Text}_XINGCANG") != KeySecreteTextBox.Text.Trim())
|
||
{
|
||
Log($"秘钥不正确,请联系供应商", Color.Red);
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
this.activeBtn.Enabled = false;
|
||
|
||
|
||
var serviceName = serviceNameTBox.Text;
|
||
var serviceDisplayName = serviceDisplayNameTBox.Text;
|
||
|
||
var deployFoder = this.folderPathTbox.Text;
|
||
|
||
string nginxExtractName = "nginx-1.20.1";
|
||
string apiExtractName = "APIPublish";
|
||
|
||
string nginxStartPath = Path.Combine(deployFoder, @$"{nginxExtractName}\");
|
||
var nginxConfigPath = Path.Combine(nginxStartPath, @$"conf\nginx.conf");
|
||
|
||
|
||
|
||
var apiBinPath = Path.Combine(deployFoder, "APIPublish/IRacIS.Core.API.exe");
|
||
|
||
string nginxRarFilePath = Path.Combine(AppContext.BaseDirectory, $@"deploy\nginx-1.20.1.rar"); // 要解压缩的RAR文件路径
|
||
string apiRarFilePath = Path.Combine(AppContext.BaseDirectory, "deploy/net6.0.rar"); // 要解压缩的RAR文件路径
|
||
|
||
|
||
|
||
#region 创建进程执行命令
|
||
|
||
var startInfo = new ProcessStartInfo();
|
||
startInfo.FileName = "cmd.exe"; // 指定要启动的应用程序
|
||
startInfo.RedirectStandardInput = true;
|
||
startInfo.UseShellExecute = false;
|
||
startInfo.CreateNoWindow = true;
|
||
|
||
// 创建新进程并启动
|
||
var process = new Process();
|
||
process.StartInfo = startInfo;
|
||
process.Start();
|
||
|
||
#endregion
|
||
|
||
|
||
|
||
#region 解压nginx 和前端部署的文件
|
||
|
||
Log($"开始解压 nginx 及前端部署 RAR文件", Color.Green);
|
||
|
||
|
||
await Task.Run(() =>
|
||
{
|
||
string extractPath = Path.Combine(deployFoder, nginxExtractName);
|
||
if (!Directory.Exists(extractPath))
|
||
{
|
||
Directory.CreateDirectory(extractPath);
|
||
}
|
||
|
||
using (var archive = RarArchive.Open(nginxRarFilePath))
|
||
{
|
||
foreach (var entry in archive.Entries)
|
||
{
|
||
if (!entry.IsDirectory)
|
||
{
|
||
entry.WriteToDirectory(extractPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true });
|
||
}
|
||
else
|
||
{
|
||
string directoryPath = Path.Combine(extractPath, entry.Key);
|
||
Directory.CreateDirectory(directoryPath);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
Log($"nginx 及前端部署 RAR文件已成功解压缩到: + {extractPath}", Color.Green);
|
||
|
||
|
||
|
||
|
||
});
|
||
|
||
|
||
#endregion
|
||
|
||
|
||
|
||
#region 解压后端部署的文件
|
||
|
||
Log($"开始解压 后端部署 RAR文件", Color.Green);
|
||
|
||
await Task.Run(() =>
|
||
{
|
||
string extractPath = Path.Combine(deployFoder, apiExtractName);
|
||
|
||
if (!Directory.Exists(extractPath))
|
||
{
|
||
Directory.CreateDirectory(extractPath);
|
||
}
|
||
|
||
|
||
using (var archive = RarArchive.Open(apiRarFilePath))
|
||
{
|
||
foreach (var entry in archive.Entries)
|
||
{
|
||
if (!entry.IsDirectory)
|
||
{
|
||
entry.WriteToDirectory(extractPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true });
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
Log($"后端部署 RAR文件已成功解压缩到: + {extractPath}", Color.Green);
|
||
|
||
|
||
|
||
});
|
||
|
||
|
||
#endregion
|
||
|
||
|
||
|
||
#region 修改 nginx 配置文件 启动nginx
|
||
|
||
|
||
|
||
if (!File.Exists(nginxConfigPath))
|
||
{
|
||
MessageBox.Show("预设路径不存在nginx");
|
||
}
|
||
|
||
var nginxConfig = File.ReadAllText(nginxConfigPath);
|
||
|
||
nginxConfig = nginxConfig.Replace("9520", vuePort.ToString());
|
||
nginxConfig = nginxConfig.Replace("7100", apiPort.ToString());
|
||
|
||
|
||
|
||
File.WriteAllText(nginxConfigPath, nginxConfig);
|
||
|
||
|
||
|
||
// 获取nginx.exe所在的目录
|
||
|
||
// 创建ProcessStartInfo对象,指定要启动的可执行文件及其参数
|
||
ProcessStartInfo psi = new ProcessStartInfo(nginxStartPath + "nginx.exe");
|
||
// 指定工作目录,即进入nginx.exe所在的目录
|
||
psi.WorkingDirectory = nginxStartPath;
|
||
|
||
Log(" 开始启动 nginx 服务...", Color.Green);
|
||
// 启动可执行文件
|
||
Process.Start(psi);
|
||
|
||
|
||
|
||
|
||
if (Process.GetProcesses().Any(t => t.ProcessName.Contains("nginx") && t.MainModule.FileName.Contains(nginxStartPath)))
|
||
{
|
||
Log(" nginx 服务启动成功", Color.Green);
|
||
}
|
||
else
|
||
{
|
||
Log(" nginx 服务启动失败", Color.Red);
|
||
return;
|
||
}
|
||
#endregion
|
||
|
||
var configObj = new
|
||
{
|
||
key = this.machineTextBox.Text,
|
||
value = this.KeySecreteTextBox.Text.Trim(),
|
||
user = usernameTextBox.Text,
|
||
server = serverTextBox.Text,
|
||
password = passwordTextBox.Text,
|
||
dbName = dbNameTBox.Text,
|
||
};
|
||
|
||
File.WriteAllText($@"C:\ProgramData\.xingcang\config.json", JsonConvert.SerializeObject(configObj));
|
||
|
||
Log("写入激活秘钥完成", Color.Green);
|
||
|
||
|
||
var createStr = $@"sc create {serviceName} binPath= ""{apiBinPath} --urls=""http://127.0.0.1:{apiPort}"" --env CertificateApply"" DisplayName= ""{serviceDisplayName}"" start= auto";
|
||
|
||
|
||
#region 创建服务
|
||
|
||
|
||
|
||
|
||
// 删除已存在的服务
|
||
|
||
|
||
//await ProcessStandardInputAsync(process, $"sc delete {serviceName}");
|
||
|
||
//Log($"删除服务{serviceName}...", Color.Green);
|
||
|
||
// 执行 sc create 命令来创建服务
|
||
await ProcessStandardInputAsync(process, createStr);
|
||
Log($"创建服务{serviceName}...", Color.Green);
|
||
|
||
|
||
Log($"执行初始化数据库脚本...", Color.Green);
|
||
//执行数据库脚本
|
||
|
||
//await ProcessStandardInputAsync(process, Path.Combine(AppContext.BaseDirectory, "deploy/sql/bin.bat"));
|
||
|
||
await ProcessStandardInputAsync(process, $@" SQLCMD -v dbName = ""{dbNameTBox.Text}"" -i {AppContext.BaseDirectory}deploy\sql\dbo.sql ");
|
||
|
||
await ProcessStandardInputAsync(process, $@" SQLCMD -v dbName = ""{dbNameTBox.Text}"" -i {AppContext.BaseDirectory}deploy\sql\data.sql ");
|
||
|
||
|
||
Log($"初始化数据库脚本执行结束", Color.Green);
|
||
|
||
// 执行 sc start 启动服务
|
||
|
||
Log($"启动部署的后端服务...", Color.Green);
|
||
await ProcessStandardInputAsync(process, $"sc start {serviceName}");
|
||
|
||
// 关闭进程流并等待进程退出
|
||
process.StandardInput.Close();
|
||
process.WaitForExit();
|
||
|
||
ServiceController sc = new ServiceController(serviceName);
|
||
if (sc.Status == ServiceControllerStatus.Running)
|
||
{
|
||
Log($"后端服务启动成功", Color.Green);
|
||
|
||
|
||
Log($"部署工作已完成", Color.Green);
|
||
|
||
try
|
||
{
|
||
Log($"现在打开浏览器...", Color.Green);
|
||
// Use ProcessStartInfo class
|
||
var start = new ProcessStartInfo($"http://127.0.0.1:{vuePort}")
|
||
{
|
||
UseShellExecute = true,
|
||
Verb = "open"
|
||
};
|
||
Process.Start(start);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// Handle exception
|
||
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Log($"后端服务启动失败", Color.Red);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#endregion
|
||
}
|
||
|
||
private async void selectPathBtn_Click(object sender, EventArgs e)
|
||
{
|
||
FolderBrowserDialog dialog = new FolderBrowserDialog();
|
||
if (dialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
folderPathTbox.Text = dialog.SelectedPath;
|
||
|
||
Log($"部署路径选择成功", Color.Green);
|
||
|
||
#region 测试 停止Nginx
|
||
|
||
var deployFoder = this.folderPathTbox.Text;
|
||
|
||
string nginxExtractName = "nginx-1.20.1";
|
||
|
||
string nginxStartPath = Path.Combine(deployFoder, @$"{nginxExtractName}\");
|
||
|
||
|
||
#region 创建进程执行命令
|
||
|
||
Process process = new Process();
|
||
process.StartInfo.FileName = "cmd.exe";
|
||
process.StartInfo.RedirectStandardInput = true;
|
||
process.StartInfo.UseShellExecute = false;
|
||
process.StartInfo.CreateNoWindow = true; // 不创建新窗口
|
||
process.Start();
|
||
|
||
var proList = Process.GetProcesses().Where(t => t.ProcessName.Contains("nginx") && t.MainModule.FileName.Contains(nginxStartPath));
|
||
|
||
foreach (var item in proList)
|
||
{
|
||
item.Kill();
|
||
}
|
||
|
||
|
||
await ProcessStandardInputAsync(process, $"sc stop {serviceNameTBox.Text}");
|
||
await ProcessStandardInputAsync(process, $"sc delete {serviceNameTBox.Text}");
|
||
|
||
process.StandardInput.Close();
|
||
process.WaitForExit();
|
||
|
||
#endregion
|
||
|
||
#endregion
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
}
|