diff --git a/Hua.DDNS.Test/Start/DIConfig.cs b/Hua.DDNS.Test/Start/DIConfig.cs index 0059226..132e4dd 100644 --- a/Hua.DDNS.Test/Start/DIConfig.cs +++ b/Hua.DDNS.Test/Start/DIConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -41,7 +41,7 @@ namespace Hua.DDNS.Test.Start services.AddSingleton(); services.AddSingleton(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); return services.BuildServiceProvider(); } } diff --git a/Hua.DDNS/Common/Config/Options/AliCloudOption.cs b/Hua.DDNS/Common/Config/Options/AliCloudOption.cs new file mode 100644 index 0000000..4282f6e --- /dev/null +++ b/Hua.DDNS/Common/Config/Options/AliCloudOption.cs @@ -0,0 +1,10 @@ +namespace Hua.DDNS.Common.Config.Options +{ + public class AliCloudOption + { + public string AccessKeyId { get; set; } + public string AccessKeySecret { get; set; } + public string RegionId { get; set; } + public string Endpoint { get; set; } + } +} diff --git a/Hua.DDNS/Common/Config/Options/SslDownloadOption.cs b/Hua.DDNS/Common/Config/Options/SslDownloadOption.cs new file mode 100644 index 0000000..816ac64 --- /dev/null +++ b/Hua.DDNS/Common/Config/Options/SslDownloadOption.cs @@ -0,0 +1,24 @@ +namespace Hua.DDNS.Common.Config.Options +{ + public enum SslPlatformEnum + { + Ali = 1, + Tencent = 2 + } + + public class SslDownloadOption + { + public bool Enabled { get; set; } + public string Corn { get; set; } + public SslPlatformEnum Platform { get; set; } + public string SavePath { get; set; } + public int ExpireDays { get; set; } + public List DownloadItems { get; set; } + } + + public class SslDownloadItem + { + public string Domain { get; set; } + public string FileName { get; set; } + } +} diff --git a/Hua.DDNS/Common/Config/Options/TencentCloudOption.cs b/Hua.DDNS/Common/Config/Options/TencentCloudOption.cs new file mode 100644 index 0000000..6ba4ec8 --- /dev/null +++ b/Hua.DDNS/Common/Config/Options/TencentCloudOption.cs @@ -0,0 +1,12 @@ +using Hua.DDNS.DDNSProviders.Dnspod; + +namespace Hua.DDNS.Common.Config.Options +{ + public class TencentCloudOption + { + public string SecretId { get; set; } + public string SecretKey { get; set; } + public string Region { get; set; } + public DnspodOption Dnspod { get; set; } + } +} diff --git a/Hua.DDNS/Common/Http/HttpHelper.cs b/Hua.DDNS/Common/Http/HttpHelper.cs index 7df79aa..11854e2 100644 --- a/Hua.DDNS/Common/Http/HttpHelper.cs +++ b/Hua.DDNS/Common/Http/HttpHelper.cs @@ -8,10 +8,12 @@ namespace Hua.DDNS.Common.Http { private static ILogger _logger; private static HttpClientHandler _handler; + private IConfiguration _configuration; - public HttpHelper(ILogger logger) + public HttpHelper(ILogger logger, IConfiguration configuration) { _logger = logger; + _configuration = configuration; _handler = new HttpClientHandler(); } @@ -85,31 +87,45 @@ namespace Hua.DDNS.Common.Http #region 下载文件 /// - /// http下载文件 (仅支持小文件) + /// http下载文件 /// /// 下载文件地址 /// 文件存放地址,包含文件名 /// public bool DownloadFile(string url, string localPath) { - ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true; - - var request = WebRequest.Create(url) as HttpWebRequest; - Stream stream = new FileStream(localPath, FileMode.CreateNew); try { - // 设置参数 - //发送请求并获取相应回应数据 + ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true; + + var request = WebRequest.Create(url) as HttpWebRequest; var response = request?.GetResponse() as HttpWebResponse; - //直到request.GetResponse()程序才开始向目标网页发送Post请求 var responseStream = response?.GetResponseStream(); - //创建本地文件写入流 - stream.Close(); - responseStream?.Close(); + + if (responseStream == null) + { + _logger.LogError($"下载文件失败: {url}, 无法获取响应流"); + return false; + } + + var directory = Path.GetDirectoryName(localPath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + using var fileStream = new FileStream(localPath, FileMode.Create); + responseStream.CopyTo(fileStream); + fileStream.Close(); + responseStream.Close(); + response?.Close(); + + _logger.LogInformation($"文件下载成功: {url} -> {localPath}"); return true; } catch (Exception ex) { + _logger.LogError($"下载文件失败: {url}, 错误: {ex.Message}"); return false; } } @@ -123,7 +139,7 @@ namespace Hua.DDNS.Common.Http public async Task GetCurrentPublicIpv4() { using var client = new HttpClient(); - using var request = new HttpRequestMessage(HttpMethod.Get, "http://175.24.175.136:8008/WebUtil/GetIp"); + using var request = new HttpRequestMessage(HttpMethod.Get, _configuration["App:GetIpv4Url"]); using var response = await client.SendAsync(request); return await response.Content.ReadAsStringAsync(); } diff --git a/Hua.DDNS/DDNSProviders/Ali/AliDDNSOption.cs b/Hua.DDNS/DDNSProviders/Ali/AliDDNSOption.cs index b8f7c70..7f5fd55 100644 --- a/Hua.DDNS/DDNSProviders/Ali/AliDDNSOption.cs +++ b/Hua.DDNS/DDNSProviders/Ali/AliDDNSOption.cs @@ -1,25 +1,9 @@ -namespace Hua.DDNS.DDNSProviders.Ali +namespace Hua.DDNS.DDNSProviders.Ali { /// /// domain configuration Ali /// public class AliDdnsOption { - - /// - /// Id, the id and key from DnsPod - /// - public string Id { get; set; } - - /// - /// Key - /// - public string Key { get; set; } - - /// - /// Endpoint - /// - public string Endpoint { get; set; } } - } diff --git a/Hua.DDNS/DDNSProviders/Ali/AliDDNSProvider.cs b/Hua.DDNS/DDNSProviders/Ali/AliDDNSProvider.cs index 70d1348..5641335 100644 --- a/Hua.DDNS/DDNSProviders/Ali/AliDDNSProvider.cs +++ b/Hua.DDNS/DDNSProviders/Ali/AliDDNSProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -21,24 +21,22 @@ namespace Hua.DDNS.DDNSProviders.Ali public class AliDdnsProvider : IDdnsProvider { private readonly Client _client; - private readonly AliDdnsOption _aliDDNSOption; + private readonly AliCloudOption _aliCloudOption; private readonly DdnsOption _ddnsOption; private readonly IMapper _mapper; - public AliDdnsProvider(IOptions aliDDNSOption, IMapper mapper,IOptions ddnsOption) + public AliDdnsProvider(IOptions aliCloudOption, IMapper mapper,IOptions ddnsOption) { - _aliDDNSOption = aliDDNSOption.Value; + _aliCloudOption = aliCloudOption.Value; _ddnsOption = ddnsOption.Value; _mapper = mapper; _client = new Client(new Config() { - // 您的 AccessKey ID - AccessKeyId = _aliDDNSOption.Id, - // 您的 AccessKey Secret - AccessKeySecret = _aliDDNSOption.Key, - Endpoint = _aliDDNSOption.Endpoint,//alidns.cn-beijing.aliyuncs.com + AccessKeyId = _aliCloudOption.AccessKeyId, + AccessKeySecret = _aliCloudOption.AccessKeySecret, + Endpoint = _aliCloudOption.Endpoint, }); } diff --git a/Hua.DDNS/DDNSProviders/Dnspod/DnspodDDNSProvider.cs b/Hua.DDNS/DDNSProviders/Dnspod/DnspodDDNSProvider.cs index b7ad0f2..caba936 100644 --- a/Hua.DDNS/DDNSProviders/Dnspod/DnspodDDNSProvider.cs +++ b/Hua.DDNS/DDNSProviders/Dnspod/DnspodDDNSProvider.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using AutoMapper; -using Hua.DDNS.DDNSProviders.Ali; +using Hua.DDNS.Common.Config.Options; using Hua.DDNS.Models; using Microsoft.Extensions.Options; using TencentCloud.Common.Profile; @@ -25,26 +25,22 @@ namespace Hua.DDNS.DDNSProviders.Dnspod { private readonly DnspodClient _client; - private readonly DnspodOption _dnspodOption; + private readonly TencentCloudOption _tencentCloudOption; private readonly DdnsOption _ddnsOption; private readonly IMapper _mapper; - public DnspodDdnsProvider(IMapper mapper, IOptions dnspodOption, IOptions ddnsOption) + public DnspodDdnsProvider(IMapper mapper, IOptions tencentCloudOption, IOptions ddnsOption) { _mapper = mapper; - _dnspodOption = dnspodOption.Value; + _tencentCloudOption = tencentCloudOption.Value; _ddnsOption = ddnsOption.Value; _client = new DnspodClient( - // 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密 - // 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取 - new Credential { SecretId = _dnspodOption.Id, SecretKey = _dnspodOption.Key }, + new Credential { SecretId = _tencentCloudOption.SecretId, SecretKey = _tencentCloudOption.SecretKey }, "", - // 实例化一个client选项,可选的,没有特殊需求可以跳过 new ClientProfile() { - // 实例化一个http选项,可选的,没有特殊需求可以跳过 - HttpProfile = new HttpProfile { Endpoint = (_dnspodOption.Endpoint) }//"dnspod.tencentcloudapi.com" + HttpProfile = new HttpProfile { Endpoint = _tencentCloudOption.Dnspod.Endpoint } }); } diff --git a/Hua.DDNS/DDNSProviders/Dnspod/DnspodOption.cs b/Hua.DDNS/DDNSProviders/Dnspod/DnspodOption.cs index 1b38bdf..29ea299 100644 --- a/Hua.DDNS/DDNSProviders/Dnspod/DnspodOption.cs +++ b/Hua.DDNS/DDNSProviders/Dnspod/DnspodOption.cs @@ -1,25 +1,13 @@ -namespace Hua.DDNS.DDNSProviders.Dnspod +namespace Hua.DDNS.DDNSProviders.Dnspod { /// /// domain configuration Dnspod /// public class DnspodOption { - - /// - /// Id, the id and key from AliCould or DnsPod - /// - public string Id { get; set; } - - /// - /// Key - /// - public string Key { get; set; } - /// /// Endpoint dnspod.tencentcloudapi.com /// public string Endpoint { get; set; } } - } \ No newline at end of file diff --git a/Hua.DDNS/Dockerfile b/Hua.DDNS/Dockerfile index dd2f5f1..a0dd74e 100644 --- a/Hua.DDNS/Dockerfile +++ b/Hua.DDNS/Dockerfile @@ -1,22 +1,21 @@ -#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS base -USER app +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +#Depending on the operating system of the host machines(s) that will build or run the containers, the image specified in the FROM statement may need to be changed. +#For more information, please see https://aka.ms/containercompat + +FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base WORKDIR /app -#EXPOSE 8080 -#EXPOSE 8081 FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build -ARG BUILD_CONFIGURATION=Release WORKDIR /src -COPY ["Hua.DDNS/Hua.DDNS.csproj", "Hua.DDNS/"] -RUN dotnet restore "./Hua.DDNS/Hua.DDNS.csproj" +COPY ["/Hua.DDNS.csproj", "Hua.DDNS/"] +RUN dotnet restore "Hua.DDNS/Hua.DDNS.csproj" COPY . . -WORKDIR "/src/Hua.DDNS" -RUN dotnet build "./Hua.DDNS.csproj" -c $BUILD_CONFIGURATION -o /app/build +WORKDIR "/Hua.DDNS" +RUN dotnet build "Hua.DDNS.csproj" -c Release -o /app/build FROM build AS publish -ARG BUILD_CONFIGURATION=Release -RUN dotnet publish "./Hua.DDNS.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=true +RUN dotnet publish "Hua.DDNS.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app diff --git a/Hua.DDNS/Hua.DDNS.csproj b/Hua.DDNS/Hua.DDNS.csproj index c54ce7e..a899728 100644 --- a/Hua.DDNS/Hua.DDNS.csproj +++ b/Hua.DDNS/Hua.DDNS.csproj @@ -10,6 +10,7 @@ + @@ -28,6 +29,7 @@ + diff --git a/Hua.DDNS/Jobs/SslDownloadJob.cs b/Hua.DDNS/Jobs/SslDownloadJob.cs new file mode 100644 index 0000000..e079051 --- /dev/null +++ b/Hua.DDNS/Jobs/SslDownloadJob.cs @@ -0,0 +1,133 @@ +using Hua.DDNS.Common.Config.Options; +using Hua.DDNS.SslProviders; +using Hua.DDNS.SslProviders.Ali; +using Hua.DDNS.SslProviders.Tencent; +using Quartz; +using Microsoft.Extensions.Options; + +namespace Hua.DDNS.Jobs +{ + [DisallowConcurrentExecution] + public class SslDownloadJob : IJob, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceProvider _serviceProvider; + private readonly SslDownloadOption _sslDownloadOption; + + public SslDownloadJob( + ILogger logger, + IServiceProvider serviceProvider, + IOptions sslDownloadOption) + { + _logger = logger; + _serviceProvider = serviceProvider; + _sslDownloadOption = sslDownloadOption.Value; + } + + public async Task Execute(IJobExecutionContext context) + { + if (!_sslDownloadOption.Enabled) + { + _logger.LogInformation("SSL下载任务已禁用,跳过执行"); + return; + } + + _logger.LogInformation("开始SSL文件下载任务"); + + try + { + ISslDownloadProvider? sslProvider = _sslDownloadOption.Platform switch + { + SslPlatformEnum.Ali => _serviceProvider.GetService(typeof(AliSslProvider)) as ISslDownloadProvider, + SslPlatformEnum.Tencent => _serviceProvider.GetService(typeof(TencentSslProvider)) as ISslDownloadProvider, + _ => null + }; + + if (sslProvider == null) + { + _logger.LogError($"未找到 SSL 下载提供者: {_sslDownloadOption.Platform}"); + return; + } +; + if (!Directory.Exists(_sslDownloadOption.SavePath)) + { + Directory.CreateDirectory(_sslDownloadOption.SavePath); + _logger.LogInformation($"创建目录: {_sslDownloadOption.SavePath}"); + } + + var certificates = await sslProvider.GetCertificatesAsync(); + var downloadTasks = new List(); + + foreach (var item in _sslDownloadOption.DownloadItems) + { + var matchingCertificates = certificates.Where(c => c.Domain == item.Domain).ToList(); + + if (matchingCertificates.Count == 0) + { + _logger.LogWarning($"未找到域名 {item.Domain} 的证书,跳过下载"); + continue; + } + + var certificate = matchingCertificates.OrderByDescending(c => c.CertEndTime).First(); + + var daysUntilExpiry = (certificate.CertEndTime - DateTime.Now).Days; + + if (daysUntilExpiry <= _sslDownloadOption.ExpireDays) + { + _logger.LogInformation($"证书 {certificate.CertificateId} ({certificate.Domain}) 将在 {daysUntilExpiry} 天后过期,开始下载"); + downloadTasks.Add(DownloadFileAsync(sslProvider, item, certificate.CertificateId, certificate.Domain)); + } + else + { + _logger.LogInformation($"证书 {certificate.CertificateId} ({certificate.Domain}) 还有 {daysUntilExpiry} 天过期,跳过下载"); + } + } + + await Task.WhenAll(downloadTasks); + + _logger.LogInformation($"SSL文件下载任务完成,共下载 {downloadTasks.Count} 个文件"); + } + catch (Exception ex) + { + _logger.LogError(ex, "SSL文件下载任务执行失败"); + } + } + + private async Task DownloadFileAsync(ISslDownloadProvider provider, SslDownloadItem item, string certificateId, string domain) + { + try + { + var domainPath = Path.Combine(_sslDownloadOption.SavePath, domain); + + if (!Directory.Exists(domainPath)) + { + Directory.CreateDirectory(domainPath); + _logger.LogInformation($"创建域名目录: {domainPath}"); + } + + var localPath = Path.Combine(domainPath, item.FileName); + _logger.LogInformation($"开始下载证书: {certificateId} -> {localPath}"); + + var success = await provider.DownloadCertificateAsync(certificateId, domainPath, item.FileName); + + if (success) + { + _logger.LogInformation($"证书下载成功: {domain}/{item.FileName}"); + } + else + { + _logger.LogError($"证书下载失败: {domain}/{item.FileName}"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"下载证书时发生错误: {item.FileName}"); + } + } + + public void Dispose() + { + _logger.LogInformation("SslDownloadJob已销毁"); + } + } +} diff --git a/Hua.DDNS/SslProviders/Ali/AliSslOption.cs b/Hua.DDNS/SslProviders/Ali/AliSslOption.cs new file mode 100644 index 0000000..3f173ba --- /dev/null +++ b/Hua.DDNS/SslProviders/Ali/AliSslOption.cs @@ -0,0 +1,9 @@ +namespace Hua.DDNS.SslProviders.Ali +{ + public class AliSslOption + { + public string AccessKeyId { get; set; } + public string AccessKeySecret { get; set; } + public string Endpoint { get; set; } + } +} diff --git a/Hua.DDNS/SslProviders/Ali/AliSslProvider.cs b/Hua.DDNS/SslProviders/Ali/AliSslProvider.cs new file mode 100644 index 0000000..2a820e6 --- /dev/null +++ b/Hua.DDNS/SslProviders/Ali/AliSslProvider.cs @@ -0,0 +1,62 @@ +using AlibabaCloud.SDK.Cas20200407; +using AlibabaCloud.SDK.Cas20200407.Models; +using AlibabaCloud.OpenApiClient.Models; +using Hua.DDNS.Common.Config.Options; +using Hua.DDNS.SslProviders; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Hua.DDNS.SslProviders.Ali +{ + public class AliSslProvider : ISslDownloadProvider + { + private readonly Client _client; + private readonly AliCloudOption _aliCloudOption; + private readonly ILogger _logger; + + public AliSslProvider( + ILogger logger, + IOptions aliCloudOption) + { + _logger = logger; + _aliCloudOption = aliCloudOption.Value; + + var config = new Config + { + AccessKeyId = _aliCloudOption.AccessKeyId, + AccessKeySecret = _aliCloudOption.AccessKeySecret, + Endpoint = _aliCloudOption.Endpoint + }; + + _client = new Client(config); + } + + public async Task> GetCertificatesAsync() + { + try + { + _logger.LogWarning("阿里云 SSL 证书列表功能待实现"); + return new List(); + } + catch (Exception ex) + { + _logger.LogError(ex, "获取阿里云 SSL 证书列表失败"); + throw; + } + } + + public async Task DownloadCertificateAsync(string certificateId, string savePath, string fileName) + { + try + { + _logger.LogWarning($"阿里云 SSL 证书下载功能待实现: {certificateId}"); + return false; + } + catch (Exception ex) + { + _logger.LogError(ex, $"下载证书失败: {certificateId}"); + return false; + } + } + } +} diff --git a/Hua.DDNS/SslProviders/ISslDownloadProvider.cs b/Hua.DDNS/SslProviders/ISslDownloadProvider.cs new file mode 100644 index 0000000..e7c11d2 --- /dev/null +++ b/Hua.DDNS/SslProviders/ISslDownloadProvider.cs @@ -0,0 +1,19 @@ +namespace Hua.DDNS.SslProviders +{ + public interface ISslDownloadProvider + { + Task> GetCertificatesAsync(); + Task DownloadCertificateAsync(string certificateId, string savePath, string fileName); + } + + public class SslCertificate + { + public string CertificateId { get; set; } + public string Domain { get; set; } + public string Alias { get; set; } + public DateTime CertBeginTime { get; set; } + public DateTime CertEndTime { get; set; } + public int Status { get; set; } + public string StatusMsg { get; set; } + } +} diff --git a/Hua.DDNS/SslProviders/Tencent/TencentSslOption.cs b/Hua.DDNS/SslProviders/Tencent/TencentSslOption.cs new file mode 100644 index 0000000..d1cd84a --- /dev/null +++ b/Hua.DDNS/SslProviders/Tencent/TencentSslOption.cs @@ -0,0 +1,11 @@ +using Hua.DDNS.Common.Config.Options; + +namespace Hua.DDNS.SslProviders.Tencent +{ + public class TencentSslOption + { + public string SecretId { get; set; } + public string SecretKey { get; set; } + public string Region { get; set; } + } +} diff --git a/Hua.DDNS/SslProviders/Tencent/TencentSslProvider.cs b/Hua.DDNS/SslProviders/Tencent/TencentSslProvider.cs new file mode 100644 index 0000000..2708b7f --- /dev/null +++ b/Hua.DDNS/SslProviders/Tencent/TencentSslProvider.cs @@ -0,0 +1,176 @@ +using Hua.DDNS.Common.Config.Options; +using Hua.DDNS.SslProviders; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using TencentCloud.Common; +using TencentCloud.Common.Profile; +using TencentCloud.Ssl.V20191205; +using TencentCloud.Ssl.V20191205.Models; +using System.IO.Compression; + +namespace Hua.DDNS.SslProviders.Tencent +{ + public class TencentSslProvider : ISslDownloadProvider + { + private readonly SslClient _client; + private readonly TencentCloudOption _tencentCloudOption; + private readonly ILogger _logger; + + public TencentSslProvider( + ILogger logger, + IOptions tencentCloudOption) + { + _logger = logger; + _tencentCloudOption = tencentCloudOption.Value; + + _client = new SslClient( + new Credential { SecretId = _tencentCloudOption.SecretId, SecretKey = _tencentCloudOption.SecretKey }, + _tencentCloudOption.Region, + new ClientProfile() + { + HttpProfile = new HttpProfile { Endpoint = "ssl.tencentcloudapi.com" } + }); + } + + public async Task> GetCertificatesAsync() + { + try + { + var request = new DescribeCertificatesRequest + { + Limit = 100, + Offset = 0 + }; + + var response = await _client.DescribeCertificates(request); + var certificates = new List(); + + if (response.Certificates != null) + { + foreach (var cert in response.Certificates) + { + certificates.Add(new SslCertificate + { + CertificateId = cert.CertificateId, + Domain = cert.Domain, + Alias = cert.Alias, + CertBeginTime = DateTime.Parse(cert.CertBeginTime), + CertEndTime = DateTime.Parse(cert.CertEndTime), + Status = (int)(cert.Status ?? 0), + StatusMsg = cert.StatusMsg + }); + } + } + + _logger.LogInformation($"获取到 {certificates.Count} 个腾讯云 SSL 证书"); + return certificates; + } + catch (Exception ex) + { + _logger.LogError(ex, "获取腾讯云 SSL 证书列表失败"); + throw; + } + } + + public async Task DownloadCertificateAsync(string certificateId, string savePath, string fileName) + { + string tempZipPath = null; + string tempExtractPath = null; + + try + { + var request = new DownloadCertificateRequest + { + CertificateId = certificateId + }; + + var response = await _client.DownloadCertificate(request); + + if (response == null || string.IsNullOrEmpty(response.Content)) + { + _logger.LogError($"证书内容为空: {certificateId}"); + return false; + } + + var zipBytes = Convert.FromBase64String(response.Content); + + if (!Directory.Exists(savePath)) + { + Directory.CreateDirectory(savePath); + } + + tempZipPath = Path.Combine(Path.GetTempPath(), $"ssl_{Guid.NewGuid()}.zip"); + await File.WriteAllBytesAsync(tempZipPath, zipBytes); + _logger.LogInformation($"证书ZIP文件临时保存成功: {tempZipPath}"); + + tempExtractPath = Path.Combine(Path.GetTempPath(), $"ssl_extract_{Guid.NewGuid()}"); + ZipFile.ExtractToDirectory(tempZipPath, tempExtractPath); + _logger.LogInformation($"证书ZIP文件解压成功: {tempExtractPath}"); + + var nginxPath = Path.Combine(tempExtractPath, "Nginx"); + if (!Directory.Exists(nginxPath)) + { + _logger.LogError($"ZIP文件中未找到Nginx目录"); + return false; + } + + var certFiles = Directory.GetFiles(nginxPath, "*.crt"); + var keyFiles = Directory.GetFiles(nginxPath, "*.key"); + + if (certFiles.Length == 0 || keyFiles.Length == 0) + { + _logger.LogError($"Nginx目录中未找到证书文件或私钥文件"); + return false; + } + + var certFile = certFiles[0]; + var keyFile = keyFiles[0]; + + var certFileName = Path.GetFileNameWithoutExtension(fileName); + var certSavePath = Path.Combine(savePath, $"{certFileName}_bundle.crt"); + var keySavePath = Path.Combine(savePath, $"{certFileName}.key"); + + File.Copy(certFile, certSavePath, true); + File.Copy(keyFile, keySavePath, true); + + _logger.LogInformation($"证书文件保存成功: {certSavePath}"); + _logger.LogInformation($"私钥文件保存成功: {keySavePath}"); + + return true; + } + catch (Exception ex) + { + _logger.LogError(ex, $"下载证书失败: {certificateId}"); + return false; + } + finally + { + try + { + if (tempExtractPath != null && Directory.Exists(tempExtractPath)) + { + Directory.Delete(tempExtractPath, true); + _logger.LogInformation($"清理临时解压目录: {tempExtractPath}"); + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, $"清理临时解压目录失败: {tempExtractPath}"); + } + + try + { + if (tempZipPath != null && File.Exists(tempZipPath)) + { + File.Delete(tempZipPath); + _logger.LogInformation($"清理临时ZIP文件: {tempZipPath}"); + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, $"清理临时ZIP文件失败: {tempZipPath}"); + } + } + } + } +} diff --git a/Hua.DDNS/Start/Program.cs b/Hua.DDNS/Start/Program.cs index 9a2660e..3dfb85d 100644 --- a/Hua.DDNS/Start/Program.cs +++ b/Hua.DDNS/Start/Program.cs @@ -2,12 +2,16 @@ using System.Configuration; using System.Reflection; using Hua.DDNS.Common; using Hua.DDNS.Common.Config; +using Hua.DDNS.Common.Config.Options; using Hua.DDNS.Common.Http; using Hua.DDNS.DDNSProviders; using Hua.DDNS.DDNSProviders.Ali; using Hua.DDNS.DDNSProviders.Dnspod; using Hua.DDNS.DDNSProviders.Namesilo; using Hua.DDNS.Jobs; +using Hua.DDNS.SslProviders; +using Hua.DDNS.SslProviders.Ali; +using Hua.DDNS.SslProviders.Tencent; using Quartz; using Serilog; using Serilog.Extensions.Logging; @@ -47,8 +51,9 @@ namespace Hua.DDNS.Start services.AddAutoMapper(Assembly.GetExecutingAssembly()); services.Configure(hostContext.Configuration.GetSection("DDNS")); services.Configure(hostContext.Configuration.GetSection("Namesilo")); - services.Configure(hostContext.Configuration.GetSection("Dnspod")); - services.Configure(hostContext.Configuration.GetSection("Ali")); + services.Configure(hostContext.Configuration.GetSection("TencentCloud")); + services.Configure(hostContext.Configuration.GetSection("AliCloud")); + services.Configure(hostContext.Configuration.GetSection("SslDownload")); ConfigDi(hostContext, services); ConfigQuartz(hostContext, services); }); @@ -62,6 +67,8 @@ namespace Hua.DDNS.Start services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); return services.BuildServiceProvider(); } @@ -102,6 +109,27 @@ namespace Hua.DDNS.Start .WithDescription("NewJob trigger") .StartNow() ); + + var sslDownloadJobKey = new JobKey("SslDownloadJob", "SslDownloadJobGroup"); + q.AddJob(j => j + .StoreDurably() + .WithIdentity(sslDownloadJobKey) + .WithDescription("SslDownloadJob") + ); + + var sslCorn = hostContext.Configuration.GetSection("SslDownload:Corn").Value; + var sslEnabled = hostContext.Configuration.GetSection("SslDownload:Enabled").Get(); + + if (sslEnabled && !string.IsNullOrEmpty(sslCorn)) + { + q.AddTrigger(t => t + .WithIdentity("SslDownloadJob Trigger") + .ForJob(sslDownloadJobKey) + .WithCronSchedule(sslCorn) + .WithDescription("SslDownloadJob trigger") + .StartNow() + ); + } }); // Quartz.Extensions.Hosting hosting diff --git a/Hua.DDNS/appsettings.json b/Hua.DDNS/appsettings.json index c02604b..799873c 100644 --- a/Hua.DDNS/appsettings.json +++ b/Hua.DDNS/appsettings.json @@ -8,15 +8,16 @@ "Microsoft.Hosting.Lifetime": "Information" } }, - "App": { - "AppJob": { - "Corn": "* * * * * ?" //https://cron.qqe2.com/ - } - }, + "App": { + "GetIpv4Url": "http://47.108.74.59:8001/api/NetWork/GetIp", + "AppJob": { + "Corn": "0/5 * * * * ?" //https://cron.qqe2.com/ + } + }, "DDNS": { - "Platform": 3, //1 Ali 2 Tencent 3 Namesilo + "Platform": 2, //1 Ali 2 Tencent 3 Namesilo // 主域名 - "Domain": "we965.com", + "Domain": "we965.cn", // 子域名前缀 "SubDomainArray": [ "git", "webutil", "dev" ], // 记录类型 @@ -24,17 +25,42 @@ //间隔时间 秒 "time": "30" }, + "SslDownload": { + "Enabled": true, + "Corn": "0/5 * * * * ?", + "Platform": 2, + "SavePath": "D:\\Paths\\ssl", + "ExpireDays": 10000, + "DownloadItems": [ + { + "Domain": "git.we965.cn", + "FileName": "git.we965.cn.pem" + }, + { + "Domain": "webutil.we965.cn", + "FileName": "webutil.we965.cn.pem" + }, + { + "Domain": "dev.we965.cn", + "FileName": "dev.we965.cn.pem" + } + ] + }, + "TencentCloud": { + "SecretId": "1111", + "SecretKey": "11111", + "Region": "ap-guangzhou", + "Dnspod": { + "Endpoint": "dnspod.tencentcloudapi.com" + } + }, + "AliCloud": { + "AccessKeyId": "1111", + "AccessKeySecret": "1111", + "RegionId": "cn-hangzhou", + "Endpoint": "1111" + }, "Namesilo": { "ApiKey": "1111" - }, - "Dnspod": { - "Id": "1111", - "Key": "1111", - "Endpoint": "1111" - }, - "Ali": { - "Id": "1111", - "Key": "1111", - "Endpoint": "1111" } } \ No newline at end of file