325 lines
14 KiB
C#
325 lines
14 KiB
C#
using Microsoft.Web.Administration;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Linq;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Configuration;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Security.Cryptography.X509Certificates;
|
||
using System.ServiceProcess;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Xml.Linq;
|
||
|
||
namespace OneClickPublishWeb
|
||
{
|
||
/// <summary>
|
||
/// 网站发布帮助类
|
||
/// </summary>
|
||
public class PublishWebHelper
|
||
{
|
||
/// <summary>
|
||
/// 执行一键部署(单个网站部署)
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public static void Execute()
|
||
{
|
||
//IIS 配置信息
|
||
var configs = GetIISConfigs();
|
||
foreach (var config in configs.MainApplications)
|
||
{
|
||
var vDir = config.VDir;
|
||
var app = config.Applications;
|
||
//程序池配置信息
|
||
var poolsConfig = config.ApplicationPools;
|
||
var iismanager = CreateServerManager(config);
|
||
|
||
//创建虚拟目录
|
||
if (vDir != null && vDir.Count() > 0)
|
||
{
|
||
foreach (var dir in vDir)
|
||
{
|
||
CreateVDir(config.WebName, dir.DirName, dir.PhysicalPath, iismanager);
|
||
}
|
||
}
|
||
if (app!=null)
|
||
{
|
||
//创建子程序虚拟目录
|
||
foreach (var item in app)
|
||
{
|
||
if (item.VDir!=null)
|
||
{
|
||
foreach (var dir in item.VDir)
|
||
{
|
||
CreateSubitemVDir(config.WebName, item.Path, dir.DirName, dir.PhysicalPath, iismanager);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
Console.WriteLine("---------------- 程序池 Start ----------------");
|
||
|
||
//创建程序池
|
||
foreach (var item in poolsConfig)
|
||
{
|
||
CreateApplicationPool(item, iismanager);
|
||
}
|
||
Console.WriteLine("---------------- 程序池 End ----------------");
|
||
//提交保存
|
||
CommitChanges(iismanager);
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建应用程序
|
||
/// </summary>
|
||
/// <param name="config"></param>
|
||
/// <returns></returns>
|
||
private static ServerManager CreateServerManager(MainApplication config)
|
||
{
|
||
var ApplicationsConfig = config;
|
||
|
||
ServiceController service = ServiceController.GetServices("127.0.0.1").FirstOrDefault(x => x.ServiceName == "W3SVC");
|
||
|
||
if (service is null)
|
||
{
|
||
Console.WriteLine("服务器尚未安装 IIS 服务模块!");
|
||
return null;
|
||
}
|
||
|
||
if (!System.IO.Directory.Exists(config.WebsiteDirectory))
|
||
{
|
||
Console.WriteLine("指定目录不存在!");
|
||
return null;
|
||
}
|
||
ServerManager iismanager = new ServerManager();
|
||
//判断web应用程序是否存在
|
||
if (iismanager.Sites[config.WebName] != null)
|
||
{
|
||
///移除应用程序
|
||
iismanager.Sites.Remove(iismanager.Sites[config.WebName]);
|
||
}
|
||
//建立web应用程序(第二个参数为安装文件的地址)
|
||
|
||
Site site = default;
|
||
if (config.IsCertificate)
|
||
{
|
||
site = AddHostHeaderSSL(iismanager, config.WebName, config.Port, "", config.PhysicalPath);
|
||
}
|
||
else
|
||
{
|
||
site = iismanager.Sites.Add(config.WebName, config.WebsiteDirectory, config.Port);
|
||
}
|
||
|
||
Console.WriteLine("---------------- 主应用程序 Start ----------------");
|
||
Console.WriteLine($"网站名称:{config.ServerDomainName}");
|
||
Console.WriteLine($"端口:{config.Port}");
|
||
Console.WriteLine($"服务器域名:{config.ServerDomainName}");
|
||
Console.WriteLine($"网站目录:{config.WebsiteDirectory}");
|
||
Console.WriteLine($"程序池名称:{config.ApplicationPoolName}");
|
||
Console.WriteLine("---------------- 主应用程序 End ----------------");
|
||
|
||
Console.WriteLine("---------------- 子程序 Start ----------------");
|
||
//设置子程序 - 应用程序池
|
||
if (ApplicationsConfig.Applications!=null)
|
||
{
|
||
foreach (var item in ApplicationsConfig.Applications)
|
||
{
|
||
var application = site.Applications.Add("/" + item.Path, item.PhysicalPath);
|
||
application.ApplicationPoolName = item.ApplicationPoolName;
|
||
Console.WriteLine("****************************** ↓");
|
||
Console.WriteLine($"子程序路径名称:/{item.Path}");
|
||
Console.WriteLine($"物理路径:{item.PhysicalPath}");
|
||
}
|
||
Console.WriteLine("---------------- 子程序 End ----------------");
|
||
}
|
||
|
||
//设置web网站的应用程序池
|
||
var website = iismanager.Sites[config.WebName];
|
||
website.Applications["/"].ApplicationPoolName = config.ApplicationPoolName;
|
||
if (!string.IsNullOrEmpty(config.ServerDomainName))
|
||
{
|
||
string str = website.Bindings[0].Host.Split(new char[] { '.' })[0];
|
||
string bindingInformation = $"*:{config.Port}:{str}{config.ServerDomainName}";
|
||
website.Bindings.Add(bindingInformation, "http");
|
||
}
|
||
return iismanager;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 提交更改
|
||
/// </summary>
|
||
/// <param name="iismanager"></param>
|
||
private static void CommitChanges(ServerManager iismanager)
|
||
{
|
||
//提交更改
|
||
iismanager.CommitChanges();
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 创建程序池
|
||
/// </summary>
|
||
/// <param name="pool"></param>
|
||
private static void CreateApplicationPool(ApplicationPool poolConfig, ServerManager iismanager)
|
||
{
|
||
//判断应用程序池是否存在
|
||
if (iismanager.ApplicationPools[poolConfig.Name] != null)
|
||
{
|
||
//移除应用程序池
|
||
iismanager.ApplicationPools.Remove(iismanager.ApplicationPools[poolConfig.Name]);
|
||
}
|
||
|
||
//cpu
|
||
var cpuConfig = poolConfig.Cpu;
|
||
//回收
|
||
var recyclingConfig = poolConfig.Recycling;
|
||
//定期重启
|
||
var periodicRestartConfig = poolConfig.Recycling.PeriodicRestart;
|
||
//进程孤立
|
||
var failureConfig = poolConfig.Failure;
|
||
//进程模型
|
||
var processModelConfig = poolConfig.ProcessModel;
|
||
|
||
Microsoft.Web.Administration.ApplicationPool pool = iismanager.ApplicationPools.Add(poolConfig.Name);
|
||
pool.Name = poolConfig.Name; // 程序池名字
|
||
pool.StartMode = poolConfig.StartMode;//启动模式
|
||
pool.QueueLength = poolConfig.QueueLength;//队列长度
|
||
pool.ManagedRuntimeVersion = poolConfig.ManagedRuntimeVersion;
|
||
//pool.Enable32BitAppOnWin64 = pool.Enable32BitAppOnWin64;
|
||
pool.ManagedPipelineMode = ManagedPipelineMode.Integrated; //托管管道模式
|
||
pool.Cpu.Limit = cpuConfig.Limit;//限制最大CPU 50%
|
||
pool.Cpu.Action = cpuConfig.Action;//竞争cpu时限制使用最大cpu 百分比
|
||
pool.Cpu.ResetInterval = new TimeSpan(00, cpuConfig.ResetInterval, 00); //时隔5分钟
|
||
pool.Cpu.SmpAffinitized = cpuConfig.SmpAffinitized ?? false;
|
||
//回收
|
||
pool.Recycling.DisallowRotationOnConfigChange = recyclingConfig.DisallowRotationOnConfigChange ?? true; //发生配置更改时禁止回收
|
||
pool.Recycling.DisallowOverlappingRotation = recyclingConfig.DisallowOverlappingRotation ?? true;//禁用重叠回收
|
||
RecyclingLogEventOnRecycle logEventOnRecycle = RecyclingLogEventOnRecycle.None;
|
||
foreach (var item in recyclingConfig.LogEventOnRecycle)
|
||
{
|
||
logEventOnRecycle = logEventOnRecycle | item;
|
||
}
|
||
if (recyclingConfig.LogEventOnRecycle != null && recyclingConfig.LogEventOnRecycle.Count() > 0)
|
||
pool.Recycling.LogEventOnRecycle = logEventOnRecycle;
|
||
foreach (var item in periodicRestartConfig.Schedule)
|
||
{
|
||
pool.Recycling.PeriodicRestart.Schedule.Add(item);//定时回收资源
|
||
}
|
||
pool.Recycling.PeriodicRestart.PrivateMemory = periodicRestartConfig.PrivateMemory;
|
||
pool.Recycling.PeriodicRestart.Time = new TimeSpan(00, periodicRestartConfig.Time, 00);
|
||
pool.Recycling.PeriodicRestart.Requests = periodicRestartConfig.Requests;
|
||
pool.Recycling.PeriodicRestart.Memory = periodicRestartConfig.Memory;
|
||
//进程孤立
|
||
pool.Failure.OrphanActionParams = failureConfig.OrphanActionParams;
|
||
pool.Failure.OrphanActionExe = failureConfig.OrphanActionExe;
|
||
pool.Failure.OrphanWorkerProcess = failureConfig.OrphanWorkerProcess ?? false;
|
||
//模型
|
||
pool.ProcessModel.PingInterval = new TimeSpan(00, 00, processModelConfig.PingInterval);
|
||
pool.ProcessModel.PingResponseTime = new TimeSpan(00, 00, processModelConfig.PingResponseTime);
|
||
pool.ProcessModel.IdentityType = processModelConfig.IdentityType;
|
||
pool.ProcessModel.UserName = processModelConfig.UserName;
|
||
pool.ProcessModel.Password = processModelConfig.Password;
|
||
pool.ProcessModel.ShutdownTimeLimit = new TimeSpan(00, 00, processModelConfig.ShutdownTimeLimit);
|
||
pool.ProcessModel.LoadUserProfile = processModelConfig.LoadUserProfile ?? false;
|
||
pool.ProcessModel.IdleTimeoutAction = IdleTimeoutAction.Terminate;
|
||
pool.ProcessModel.StartupTimeLimit = new TimeSpan(00, 00, processModelConfig.StartupTimeLimit);
|
||
pool.ProcessModel.PingingEnabled = processModelConfig.PingingEnabled ?? false;
|
||
pool.ProcessModel.LogEventOnProcessModel = processModelConfig.LogEventOnProcessModel;
|
||
pool.ProcessModel.IdleTimeout = new TimeSpan(00, processModelConfig.IdleTimeout, 00);
|
||
pool.ProcessModel.MaxProcesses = processModelConfig.MaxProcesses;
|
||
Console.WriteLine("****************************** ↓");
|
||
Console.WriteLine($"程序池名称:{poolConfig.Name}");
|
||
Console.WriteLine($"队列长度:{poolConfig.QueueLength}");
|
||
Console.WriteLine($"启动模式:{poolConfig.StartMode}");
|
||
//Console.WriteLine($"启用32位应用程序:{poolConfig.Enable32BitAppOnWin64}");
|
||
Console.WriteLine($"托管管道模式:{poolConfig.ManagedPipelineMode}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取IIS配置
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private static IISConfig GetIISConfigs()
|
||
{
|
||
var path = Path.GetFullPath("./cfg/config.json");
|
||
using (System.IO.StreamReader file = System.IO.File.OpenText(path))
|
||
{
|
||
using (JsonTextReader reader = new JsonTextReader(file))
|
||
{
|
||
var o = (JObject)JToken.ReadFrom(reader);
|
||
var iis = o.ToObject<IISConfig>();
|
||
|
||
return iis;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 添加虚拟目录
|
||
/// </summary>
|
||
/// <param name="siteName">网站名</param>
|
||
/// <param name="vDirName">目录名</param>
|
||
/// <param name="physicalPath">对应的文件夹路径</param>
|
||
/// <param name="iismanager"></param>
|
||
private static void CreateVDir(string siteName, string vDirName, string physicalPath, ServerManager iismanager)
|
||
{
|
||
|
||
Site site = iismanager.Sites[siteName];
|
||
if (site == null)
|
||
{
|
||
return;
|
||
}
|
||
site.Applications["/"].VirtualDirectories.Add("/" + vDirName, physicalPath);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加虚拟目录
|
||
/// </summary>
|
||
/// <param name="siteName">网站名</param>
|
||
/// <param name="vDirName">目录名</param>
|
||
/// <param name="physicalPath">对应的文件夹路径</param>
|
||
/// <param name="iismanager"></param>
|
||
private static void CreateSubitemVDir(string siteName, string subitemSiteName, string vDirName, string physicalPath, ServerManager iismanager)
|
||
{
|
||
|
||
var app = iismanager.Sites[siteName].Applications["/" + subitemSiteName];
|
||
if (app == null)
|
||
{
|
||
return;
|
||
}
|
||
app.VirtualDirectories.Add("/" + vDirName, physicalPath);
|
||
}
|
||
|
||
|
||
// <summary>
|
||
/// 增加绑定域名(ssl)
|
||
/// </summary>
|
||
/// <param name="webName">站点名称</param>
|
||
/// <param name="port">端口</param>
|
||
/// <param name="bindingDomainName">绑定域名</param>
|
||
public static Site AddHostHeaderSSL(ServerManager iismanager, string webName, int port, string bindingDomainName, string physicalPath)
|
||
{
|
||
//**pfxPath**是指提供给你的.pfx文件的路径,购买ssl之后他会提供给你相关的文件,里面有多个文件
|
||
//一般包含apache文件夹,iis文件夹,nginx文件夹,我使用的是iis部署,所以.pfx文件在iis文件夹里面
|
||
//**certPwd**是指购买ssl之后提供给你的秘钥
|
||
string pfx = "./cfg/isas.pfx";
|
||
var store = new X509Store(StoreName.AuthRoot, StoreLocation.LocalMachine);
|
||
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
|
||
var certificate = new X509Certificate2(pfx, "123456", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
|
||
store.Add(certificate);
|
||
store.Close();
|
||
var certificateStoreName = store.Name; //绑定的证书名称
|
||
var certificateHash = certificate.GetCertHash(); //证书内容
|
||
string bindingInformation = "*:" + port + ":" + bindingDomainName;
|
||
|
||
Site site = iismanager.Sites.Add(webName,bindingInformation, physicalPath, certificateHash, certificateStoreName);
|
||
return site;
|
||
}
|
||
}
|
||
}
|