懒得勤快 2 gadi atpakaļ
vecāks
revīzija
43619bf1a7

+ 0 - 54
src/Masuit.MyBlogs.Core/Common/CommonHelper.cs

@@ -1,11 +1,7 @@
 using AngleSharp;
 using AngleSharp.Css.Dom;
 using AngleSharp.Dom;
-using AutoMapper;
 using Dispose.Scope;
-using FreeRedis;
-using Hangfire;
-using Masuit.MyBlogs.Core.Common.Mails;
 using Masuit.Tools.Media;
 using Masuit.Tools.Models;
 using MaxMind.GeoIP2;
@@ -127,31 +123,6 @@ namespace Masuit.MyBlogs.Core.Common
 		private static readonly DatabaseReader MaxmindAsnReader = new(Path.Combine(AppContext.BaseDirectory + "App_Data", "GeoLite2-ASN.mmdb"));
 		private static readonly DatabaseReader MaxmindCountryReader = new(Path.Combine(AppContext.BaseDirectory + "App_Data", "GeoLite2-Country.mmdb"));
 
-		/// <summary>
-		/// 是否是代理ip
-		/// </summary>
-		/// <param name="ip"></param>
-		/// <param name="cancellationToken"></param>
-		/// <returns></returns>
-		public static async Task<bool> IsProxy(this IPAddress ip, CancellationToken cancellationToken = default)
-		{
-			using var serviceScope = Startup.ServiceProvider.CreateScope();
-			var httpClient = serviceScope.ServiceProvider.GetRequiredService<IHttpClientFactory>().CreateClient();
-			httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.62");
-			return await httpClient.GetStringAsync("https://ipinfo.io/" + ip, cancellationToken).ContinueWith(t =>
-			{
-				if (t.IsCompletedSuccessfully)
-				{
-					var ctx = BrowsingContext.New(Configuration.Default);
-					var doc = ctx.OpenAsync(res => res.Content(t.Result)).Result;
-					var isAnycast = doc.DocumentElement.QuerySelectorAll(".title").Where(e => e.TextContent.Contains("Anycast")).Select(e => e.Parent).Any(n => n.TextContent.Contains("True"));
-					var isproxy = doc.DocumentElement.QuerySelectorAll("#block-privacy img").Any(e => e.OuterHtml.Contains("right"));
-					return isAnycast || isproxy;
-				}
-				return false;
-			});
-		}
-
 		public static AsnResponse GetIPAsn(this IPAddress ip)
 		{
 			if (ip.IsPrivateIP())
@@ -238,31 +209,6 @@ namespace Masuit.MyBlogs.Core.Common
 			return GetCityResp(ip).Location.TimeZone ?? "Asia/Shanghai";
 		}
 
-		/// <summary>
-		/// 类型映射
-		/// </summary>
-		/// <typeparam name="T"></typeparam>
-		/// <param name="source"></param>
-		/// <returns></returns>
-		public static T Mapper<T>(this object source) where T : class => Startup.ServiceProvider.GetRequiredService<IMapper>().Map<T>(source);
-
-		/// <summary>
-		/// 发送邮件
-		/// </summary>
-		/// <param name="title">标题</param>
-		/// <param name="content">内容</param>
-		/// <param name="tos">收件人</param>
-		/// <param name="clientip"></param>
-		[AutomaticRetry(Attempts = 1, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
-		public static void SendMail(string title, string content, string tos, string clientip)
-		{
-			using var serviceScope = Startup.ServiceProvider.CreateScope();
-			serviceScope.ServiceProvider.GetRequiredService<IMailSender>().Send(title, content, tos);
-			var redisClient = serviceScope.ServiceProvider.GetRequiredService<IRedisClient>();
-			redisClient.SAdd($"Email:{DateTime.Now:yyyyMMdd}", new { title, content, tos, time = DateTime.Now, clientip });
-			redisClient.Expire($"Email:{DateTime.Now:yyyyMMdd}", 86400);
-		}
-
 		/// <summary>
 		/// 清理html的img标签的除src之外的其他属性
 		/// </summary>

+ 2 - 2
src/Masuit.MyBlogs.Core/Common/Mails/IMailSender.cs

@@ -2,11 +2,11 @@
 
 public interface IMailSender
 {
-	void Send(string title, string content, string tos);
+	Task Send(string title, string content, string tos, string clientip);
 
 	List<string> GetBounces();
 
-	string AddRecipient(string email);
+	Task<string> AddRecipient(string email);
 
 	public bool HasBounced(string address);
 }

+ 12 - 5
src/Masuit.MyBlogs.Core/Common/Mails/MailgunSender.cs

@@ -1,4 +1,6 @@
 using CacheManager.Core;
+using FreeRedis;
+using Hangfire;
 using Masuit.Tools.Models;
 using Newtonsoft.Json.Linq;
 using System.Net.Http.Headers;
@@ -12,17 +14,20 @@ public sealed class MailgunSender : IMailSender
 	private readonly IConfiguration _configuration;
 	private readonly ICacheManager<List<string>> _cacheManager;
 	private readonly ICacheManager<bool> _bouncedCacheManager;
+	private readonly IRedisClient _redisClient;
 
-	public MailgunSender(HttpClient httpClient, IConfiguration configuration, ICacheManager<List<string>> cacheManager, ICacheManager<bool> bouncedCacheManager)
+	public MailgunSender(HttpClient httpClient, IConfiguration configuration, ICacheManager<List<string>> cacheManager, ICacheManager<bool> bouncedCacheManager, IRedisClient redisClient)
 	{
 		_configuration = configuration;
 		_cacheManager = cacheManager;
 		_bouncedCacheManager = bouncedCacheManager;
+		_redisClient = redisClient;
 		_httpClient = httpClient;
 		_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"api:{_configuration["MailgunConfig:apikey"]}")));
 	}
 
-	public void Send(string title, string content, string tos)
+	[AutomaticRetry(Attempts = 1, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
+	public async Task Send(string title, string content, string tos, string clientip)
 	{
 		EmailAddress email = _configuration["MailgunConfig:from"];
 		using var form = new MultipartFormDataContent
@@ -32,7 +37,9 @@ public sealed class MailgunSender : IMailSender
 			{ new StringContent(title,Encoding.UTF8), "subject" },
 			{ new StringContent(content,Encoding.UTF8), "html" }
 		};
-		_httpClient.PostAsync($"https://api.mailgun.net/v3/{email.Domain}/messages", form).Wait();
+		await _httpClient.PostAsync($"https://api.mailgun.net/v3/{email.Domain}/messages", form);
+		_redisClient.SAdd($"Email:{DateTime.Now:yyyyMMdd}", new { title, content, tos, time = DateTime.Now, clientip });
+		_redisClient.Expire($"Email:{DateTime.Now:yyyyMMdd}", 86400);
 	}
 
 	public List<string> GetBounces()
@@ -50,7 +57,7 @@ public sealed class MailgunSender : IMailSender
 		return _bouncedCacheManager.GetOrAdd("email-bounced", _ => _httpClient.GetStringAsync($"https://api.mailgun.net/v3/{email.Domain}/bounces/{address}").ContinueWith(t => t.IsCompletedSuccessfully && JObject.Parse(t.Result).ContainsKey("error")).Result);
 	}
 
-	public string AddRecipient(string email)
+	public Task<string> AddRecipient(string email)
 	{
 		EmailAddress mail = _configuration["MailgunConfig:from"];
 		return _httpClient.PostAsync($"https://api.mailgun.net/v3/{mail.Domain}/bounces", new MultipartFormDataContent
@@ -65,6 +72,6 @@ public sealed class MailgunSender : IMailSender
 				return (string)JObject.Parse(resp.Content.ReadAsStringAsync().Result)["message"];
 			}
 			return "添加失败";
-		}).Result;
+		});
 	}
 }

+ 17 - 4
src/Masuit.MyBlogs.Core/Common/Mails/SmtpSender.cs

@@ -1,11 +1,21 @@
-using Masuit.Tools.Models;
+using FreeRedis;
+using Hangfire;
+using Masuit.Tools.Models;
 using System.Text;
 
 namespace Masuit.MyBlogs.Core.Common.Mails;
 
 public sealed class SmtpSender : IMailSender
 {
-	public void Send(string title, string content, string tos)
+	private readonly IRedisClient _redisClient;
+
+	public SmtpSender(IRedisClient redisClient)
+	{
+		_redisClient = redisClient;
+	}
+
+	[AutomaticRetry(Attempts = 1, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
+	public Task Send(string title, string content, string tos, string clientip)
 	{
 		new Email()
 		{
@@ -18,6 +28,9 @@ public sealed class SmtpSender : IMailSender
 			Subject = title,
 			Tos = tos
 		}.Send();
+		_redisClient.SAdd($"Email:{DateTime.Now:yyyyMMdd}", new { title, content, tos, time = DateTime.Now, clientip });
+		_redisClient.Expire($"Email:{DateTime.Now:yyyyMMdd}", 86400);
+		return Task.CompletedTask;
 	}
 
 	public List<string> GetBounces()
@@ -25,11 +38,11 @@ public sealed class SmtpSender : IMailSender
 		return File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "email-bounces.txt"), Encoding.UTF8).Split(',').ToList();
 	}
 
-	public string AddRecipient(string email)
+	public async Task<string> AddRecipient(string email)
 	{
 		var bounces = GetBounces();
 		bounces.Add(email);
-		File.WriteAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "email-bounces.txt"), bounces.Join(","));
+		await File.WriteAllTextAsync(Path.Combine(AppContext.BaseDirectory + "App_Data", "email-bounces.txt"), bounces.Join(","));
 		return "添加成功";
 	}
 

+ 3 - 4
src/Masuit.MyBlogs.Core/Controllers/CategoryController.cs

@@ -1,5 +1,4 @@
-using Masuit.MyBlogs.Core.Common;
-using Masuit.MyBlogs.Core.Extensions;
+using Masuit.MyBlogs.Core.Extensions;
 using Masuit.Tools.AspNetCore.ModelBinder;
 using Masuit.Tools.Models;
 using Microsoft.AspNetCore.Mvc;
@@ -25,7 +24,7 @@ public sealed class CategoryController : BaseController
 	{
 		var categories = CategoryService.GetQueryNoTracking(c => c.Status == Status.Available, c => c.Name).ToList();
 		var list = categories.ToTree(c => c.Id, c => c.ParentId);
-		return ResultData(list.Mapper<List<CategoryDto>>());
+		return ResultData(Mapper.Map<List<CategoryDto>>(list));
 	}
 
 	/// <summary>
@@ -36,7 +35,7 @@ public sealed class CategoryController : BaseController
 	public async Task<ActionResult> Get(int id)
 	{
 		var model = await CategoryService.GetByIdAsync(id) ?? throw new NotFoundException("分类不存在!");
-		return ResultData(model.Mapper<CategoryDto>());
+		return ResultData(Mapper.Map<CategoryDto>(model));
 	}
 
 	/// <summary>

+ 7 - 7
src/Masuit.MyBlogs.Core/Controllers/CommentController.cs

@@ -71,7 +71,7 @@ public sealed class CommentController : BaseController
 			return ResultData(null, false, "您的发言频率过快,请稍后再发表吧!");
 		}
 
-		var comment = cmd.Mapper<Comment>();
+		var comment = Mapper.Map<Comment>(cmd);
 		if (cmd.ParentId > 0)
 		{
 			comment.GroupTag = CommentService.GetQuery(c => c.Id == cmd.ParentId).Select(c => c.GroupTag).FirstOrDefault();
@@ -152,7 +152,7 @@ public sealed class CommentController : BaseController
 				//新评论,只通知博主和楼主
 				foreach (var s in emails)
 				{
-					BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "|博客文章新评论:", content.Set("link", Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment").Render(false), s, comment.IP));
+					BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(Request.Host + "|博客文章新评论:", content.Set("link", Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment").Render(false), s, comment.IP));
 				}
 			}
 			else
@@ -164,7 +164,7 @@ public sealed class CommentController : BaseController
 				string link = Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment";
 				foreach (var s in emails)
 				{
-					BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{Request.Host}{CommonHelper.SystemSettings["Title"]}文章评论回复:", content.Set("link", link).Render(false), s, comment.IP));
+					BackgroundJob.Enqueue<IMailSender>(sender => sender.Send($"{Request.Host}{CommonHelper.SystemSettings["Title"]}文章评论回复:", content.Set("link", link).Render(false), s, comment.IP));
 				}
 			}
 			return ResultData(null, true, "评论发表成功,服务器正在后台处理中,这会有一定的延迟,稍后将显示到评论列表中");
@@ -172,7 +172,7 @@ public sealed class CommentController : BaseController
 
 		foreach (var s in emails)
 		{
-			BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "|博客文章新评论(待审核):", content.Set("link", Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment").Render(false) + "<p style='color:red;'>(待审核)</p>", s, comment.IP));
+			BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(Request.Host + "|博客文章新评论(待审核):", content.Set("link", Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment").Render(false) + "<p style='color:red;'>(待审核)</p>", s, comment.IP));
 		}
 
 		return ResultData(null, true, "评论成功,待站长审核通过以后将显示");
@@ -234,7 +234,7 @@ public sealed class CommentController : BaseController
 				parentTotal = 1,
 				page,
 				size,
-				rows = layer.ToTree(c => c.Id, c => c.ParentId).Mapper<IList<CommentViewModel>>()
+				rows = Mapper.Map<IList<CommentViewModel>>(layer.ToTree(c => c.Id, c => c.ParentId))
 			});
 		}
 
@@ -265,7 +265,7 @@ public sealed class CommentController : BaseController
 				parentTotal = total,
 				page,
 				size,
-				rows = comments.OrderByDescending(c => c.CommentDate).ToTree(c => c.Id, c => c.ParentId).Mapper<IList<CommentViewModel>>()
+				rows = Mapper.Map<IList<CommentViewModel>>(comments.OrderByDescending(c => c.CommentDate).ToTree(c => c.Id, c => c.ParentId))
 			});
 		}
 
@@ -299,7 +299,7 @@ public sealed class CommentController : BaseController
 			}, Request.Scheme) + "#comment";
 			foreach (var email in emails)
 			{
-				BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{Request.Host}{CommonHelper.SystemSettings["Title"]}文章评论回复:", content.Set("link", link).Render(false), email, ClientIP.ToString()));
+				BackgroundJob.Enqueue<IMailSender>(sender => sender.Send($"{Request.Host}{CommonHelper.SystemSettings["Title"]}文章评论回复:", content.Set("link", link).Render(false), email, ClientIP.ToString()));
 			}
 
 			return ResultData(null, true, "审核通过!");

+ 2 - 1
src/Masuit.MyBlogs.Core/Controllers/ErrorController.cs

@@ -13,6 +13,7 @@ using Microsoft.Net.Http.Headers;
 using System.Diagnostics;
 using System.Text;
 using System.Web;
+using Masuit.MyBlogs.Core.Common.Mails;
 using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
 
 namespace Masuit.MyBlogs.Core.Controllers;
@@ -202,7 +203,7 @@ public sealed class ErrorController : Controller
 
 		var token = SnowFlake.GetInstance().GetUniqueShortId(6);
 		RedisClient.Set("token:" + email, token, 86400);
-		BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "博客访问验证码", $"{Request.Host}本次验证码是:<span style='color:red'>{token}</span>,有效期为24h,请按时使用!", email, HttpContext.Connection.RemoteIpAddress.ToString()));
+		BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(Request.Host + "博客访问验证码", $"{Request.Host}本次验证码是:<span style='color:red'>{token}</span>,有效期为24h,请按时使用!", email, HttpContext.Connection.RemoteIpAddress.ToString()));
 		RedisClient.Set("get:" + email, token, 120);
 		return ResultData(null);
 	}

+ 2 - 3
src/Masuit.MyBlogs.Core/Controllers/MenuController.cs

@@ -1,5 +1,4 @@
-using Masuit.MyBlogs.Core.Common;
-using Masuit.Tools.AspNetCore.ModelBinder;
+using Masuit.Tools.AspNetCore.ModelBinder;
 using Masuit.Tools.Models;
 using Microsoft.AspNetCore.Mvc;
 using Z.EntityFramework.Plus;
@@ -73,7 +72,7 @@ public sealed class MenuController : AdminController
 		var m = await MenuService.GetByIdAsync(model.Id);
 		if (m == null)
 		{
-			var menu = model.Mapper<Menu>();
+			var menu = Mapper.Map<Menu>(model);
 			menu.Path = model.ParentId > 0 ? (MenuService[model.ParentId.Value].Path + "," + model.ParentId).Trim(',') : SnowFlake.NewId;
 			return await MenuService.AddEntitySavedAsync(menu) > 0 ? ResultData(model, true, "添加成功") : ResultData(null, false, "添加失败");
 		}

+ 5 - 4
src/Masuit.MyBlogs.Core/Controllers/MergeController.cs

@@ -1,6 +1,7 @@
 using AutoMapper;
 using Hangfire;
 using Masuit.MyBlogs.Core.Common;
+using Masuit.MyBlogs.Core.Common.Mails;
 using Masuit.MyBlogs.Core.Extensions;
 using Masuit.Tools.AspNetCore.ModelBinder;
 using Microsoft.AspNetCore.Mvc;
@@ -75,7 +76,7 @@ public sealed class MergeController : AdminController
 		string diffOutput = diffHelper.Build();
 		old.Content = Regex.Replace(Regex.Replace(diffOutput, "<ins.+?</ins>", string.Empty), @"<\w+></\w+>", string.Empty);
 		newer.Content = Regex.Replace(Regex.Replace(diffOutput, "<del.+?</del>", string.Empty), @"<\w+></\w+>", string.Empty);
-		return ResultData(new { old = old.Mapper<PostMergeRequestDto>(), newer = newer.Mapper<PostMergeRequestDto>() });
+		return ResultData(new { old = Mapper.Map<PostMergeRequestDto>(old), newer = Mapper.Map<PostMergeRequestDto>(newer) });
 	}
 
 	/// <summary>
@@ -87,7 +88,7 @@ public sealed class MergeController : AdminController
 	public async Task<IActionResult> Merge(int id)
 	{
 		var merge = await PostMergeRequestService.GetByIdAsync(id) ?? throw new NotFoundException("待合并文章未找到");
-		var history = merge.Post.Mapper<PostHistoryVersion>();
+		var history = Mapper.Map<PostHistoryVersion>(merge.Post);
 		history.Id = 0;
 		merge.Post = Mapper.Map(merge, merge.Post);
 		merge.Post.PostHistoryVersion.Add(history);
@@ -101,7 +102,7 @@ public sealed class MergeController : AdminController
 
 		string link = Request.Scheme + "://" + Request.Host + "/" + merge.Post.Id;
 		string content = new Template(await new FileInfo(HostEnvironment.WebRootPath + "/template/merge-pass.html").ShareReadWrite().ReadAllTextAsync(Encoding.UTF8)).Set("link", link).Set("title", merge.Post.Title).Render();
-		BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "博客你提交的修改已通过", content, merge.ModifierEmail, "127.0.0.1"));
+		BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(CommonHelper.SystemSettings["Title"] + "博客你提交的修改已通过", content, merge.ModifierEmail, "127.0.0.1"));
 		return ResultData(null, true, "文章合并完成!");
 	}
 
@@ -138,7 +139,7 @@ public sealed class MergeController : AdminController
 
 		var link = Request.Scheme + "://" + Request.Host + "/" + merge.Post.Id + "/merge/" + id;
 		var content = new Template(await new FileInfo(HostEnvironment.WebRootPath + "/template/merge-reject.html").ShareReadWrite().ReadAllTextAsync(Encoding.UTF8)).Set("link", link).Set("title", merge.Post.Title).Set("reason", reason).Render();
-		BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "博客你提交的修改已被拒绝", content, merge.ModifierEmail, "127.0.0.1"));
+		BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(CommonHelper.SystemSettings["Title"] + "博客你提交的修改已被拒绝", content, merge.ModifierEmail, "127.0.0.1"));
 		return ResultData(null, true, "合并已拒绝!");
 	}
 

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/MiscController.cs

@@ -189,6 +189,6 @@ public sealed class MiscController : BaseController
 			misc.PostDate = misc.PostDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
 		}
 
-		return ResultData(misc.Mapper<MiscDto>());
+		return ResultData(Mapper.Map<MiscDto>(misc));
 	}
 }

+ 7 - 7
src/Masuit.MyBlogs.Core/Controllers/MsgController.cs

@@ -78,7 +78,7 @@ public sealed class MsgController : BaseController
 				parentTotal = 1,
 				page,
 				size,
-				rows = layer.ToTree(e => e.Id, e => e.ParentId).Mapper<IList<LeaveMessageViewModel>>()
+				rows = Mapper.Map<IList<LeaveMessageViewModel>>(layer.ToTree(e => e.Id, e => e.ParentId))
 			});
 		}
 
@@ -108,7 +108,7 @@ public sealed class MsgController : BaseController
 				parentTotal = total,
 				page,
 				size,
-				rows = messages.OrderByDescending(c => c.PostDate).ToTree(c => c.Id, c => c.ParentId).Mapper<IList<LeaveMessageViewModel>>()
+				rows = Mapper.Map<IList<LeaveMessageViewModel>>(messages.OrderByDescending(c => c.PostDate).ToTree(c => c.Id, c => c.ParentId))
 			});
 		}
 
@@ -150,7 +150,7 @@ public sealed class MsgController : BaseController
 			return ResultData(null, false, "您的发言频率过快,请稍后再发表吧!");
 		}
 
-		var msg = cmd.Mapper<LeaveMessage>();
+		var msg = Mapper.Map<LeaveMessage>(cmd);
 		if (cmd.ParentId > 0)
 		{
 			msg.GroupTag = LeaveMessageService.GetQuery(c => c.Id == cmd.ParentId).Select(c => c.GroupTag).FirstOrDefault();
@@ -214,7 +214,7 @@ public sealed class MsgController : BaseController
 			if (msg.ParentId == null)
 			{
 				//新评论,只通知博主
-				BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "|博客新留言:", content.Set("link", Url.Action("Index", "Msg", new { cid = msg.Id }, Request.Scheme)).Render(false), email, ip));
+				BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(Request.Host + "|博客新留言:", content.Set("link", Url.Action("Index", "Msg", new { cid = msg.Id }, Request.Scheme)).Render(false), email, ip));
 			}
 			else
 			{
@@ -223,13 +223,13 @@ public sealed class MsgController : BaseController
 				string link = Url.Action("Index", "Msg", new { cid = msg.Id }, Request.Scheme);
 				foreach (var s in emails)
 				{
-					BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{Request.Host}{CommonHelper.SystemSettings["Title"]} 留言回复:", content.Set("link", link).Render(false), s, ip));
+					BackgroundJob.Enqueue<IMailSender>(sender => sender.Send($"{Request.Host}{CommonHelper.SystemSettings["Title"]} 留言回复:", content.Set("link", link).Render(false), s, ip));
 				}
 			}
 			return ResultData(null, true, "留言发表成功,服务器正在后台处理中,这会有一定的延迟,稍后将会显示到列表中!");
 		}
 
-		BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "|博客新留言(待审核):", content.Set("link", Url.Action("Index", "Msg", new
+		BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(Request.Host + "|博客新留言(待审核):", content.Set("link", Url.Action("Index", "Msg", new
 		{
 			cid = msg.Id
 		}, Request.Scheme)).Render(false) + "<p style='color:red;'>(待审核)</p>", email, ip));
@@ -254,7 +254,7 @@ public sealed class MsgController : BaseController
 			var link = Url.Action("Index", "Msg", new { cid = id }, Request.Scheme);
 			foreach (var s in emails)
 			{
-				BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{Request.Host}{CommonHelper.SystemSettings["Title"]} 留言回复:", content.Set("link", link).Render(false), s, ClientIP.ToString()));
+				BackgroundJob.Enqueue<IMailSender>(sender => sender.Send($"{Request.Host}{CommonHelper.SystemSettings["Title"]} 留言回复:", content.Set("link", link).Render(false), s, ClientIP.ToString()));
 			}
 		}
 

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/NoticeController.cs

@@ -206,7 +206,7 @@ public sealed class NoticeController : BaseController
 
 		notice.ViewCount += 1;
 		await NoticeService.SaveChangesAsync();
-		var dto = notice.Mapper<NoticeDto>();
+		var dto = Mapper.Map<NoticeDto>(notice);
 		Response.Cookies.Append("last-notice", dto.Id.ToString(), new CookieOptions()
 		{
 			Expires = DateTime.Now.AddYears(1),

+ 13 - 12
src/Masuit.MyBlogs.Core/Controllers/PostController.cs

@@ -25,6 +25,7 @@ using System.Linq.Dynamic.Core;
 using System.Net;
 using System.Text;
 using System.Text.RegularExpressions;
+using Masuit.MyBlogs.Core.Common.Mails;
 using Z.EntityFramework.Plus;
 using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
 
@@ -196,7 +197,7 @@ public sealed class PostController : BaseController
 	public async Task<ActionResult> CompareVersion(int id, int v1, int v2)
 	{
 		var post = await PostService.GetAsync(p => p.Id == id && (p.Status == Status.Published || CurrentUser.IsAdmin));
-		var main = post.Mapper<PostHistoryVersion>() ?? throw new NotFoundException("文章未找到");
+		var main = Mapper.Map<PostHistoryVersion>(post) ?? throw new NotFoundException("文章未找到");
 		CheckPermission(post);
 		var left = v1 <= 0 ? main : await PostHistoryVersionService.GetAsync(v => v.Id == v1) ?? throw new NotFoundException("文章未找到");
 		var right = v2 <= 0 ? main : await PostHistoryVersionService.GetAsync(v => v.Id == v2) ?? throw new NotFoundException("文章未找到");
@@ -298,7 +299,7 @@ public sealed class PostController : BaseController
 		post.Label = string.IsNullOrEmpty(post.Label?.Trim()) ? null : post.Label.Replace(",", ",");
 		post.Status = Status.Pending;
 		post.Content = await ImagebedClient.ReplaceImgSrc(await post.Content.HtmlSantinizerStandard().ClearImgAttributes(), cancellationToken);
-		Post p = post.Mapper<Post>();
+		Post p = Mapper.Map<Post>(post);
 		p.IP = ClientIP.ToString();
 		p.Modifier = p.Author;
 		p.ModifierEmail = p.Email;
@@ -320,8 +321,8 @@ public sealed class PostController : BaseController
 			.Set("link", Url.Action("Details", "Post", new { id = p.Id }, Request.Scheme))
 			.Set("time", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
 			.Set("title", p.Title).Render();
-		BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "有访客投稿:", content, CommonHelper.SystemSettings["ReceiveEmail"], p.IP));
-		return ResultData(p.Mapper<PostDto>(), message: "文章发表成功,待站长审核通过以后将显示到列表中!");
+		BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(CommonHelper.SystemSettings["Title"] + "有访客投稿:", content, CommonHelper.SystemSettings["ReceiveEmail"], p.IP));
+		return ResultData(Mapper.Map<PostDto>(p), message: "文章发表成功,待站长审核通过以后将显示到列表中!");
 	}
 
 	/// <summary>
@@ -408,7 +409,7 @@ public sealed class PostController : BaseController
 
 		var token = SnowFlake.GetInstance().GetUniqueShortId(6);
 		RedisHelper.Set("token:" + email, token, 86400);
-		BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "博客访问验证码", $"{Request.Host}本次验证码是:<span style='color:red'>{token}</span>,有效期为24h,请按时使用!", email, ClientIP.ToString()));
+		BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(Request.Host + "博客访问验证码", $"{Request.Host}本次验证码是:<span style='color:red'>{token}</span>,有效期为24h,请按时使用!", email, ClientIP.ToString()));
 		RedisHelper.Set("get:" + email, token, 120);
 		return ResultData(null);
 	}
@@ -480,7 +481,7 @@ public sealed class PostController : BaseController
 
 		if (post.Email.Equals(dto.ModifierEmail))
 		{
-			var history = post.Mapper<PostHistoryVersion>();
+			var history = Mapper.Map<PostHistoryVersion>(post);
 			Mapper.Map(dto, post);
 			post.PostHistoryVersion.Add(history);
 			post.ModifyDate = DateTime.Now;
@@ -526,7 +527,7 @@ public sealed class PostController : BaseController
 			.Set("host", "//" + Request.Host)
 			.Set("id", merge.Id.ToString())
 			.Render();
-		BackgroundJob.Enqueue(() => CommonHelper.SendMail("博客文章修改请求:", content, CommonHelper.SystemSettings["ReceiveEmail"], merge.IP));
+		BackgroundJob.Enqueue<IMailSender>(sender => sender.Send("博客文章修改请求:", content, CommonHelper.SystemSettings["ReceiveEmail"], merge.IP));
 		return ResultData(null, true, "您的修改请求已提交,已进入审核状态,感谢您的参与!");
 	}
 
@@ -620,7 +621,7 @@ public sealed class PostController : BaseController
 	public ActionResult Get(int id)
 	{
 		var post = PostService.GetQuery(e => e.Id == id).Include(e => e.Seminar).FirstOrDefault() ?? throw new NotFoundException("文章未找到");
-		var model = post.Mapper<PostDto>();
+		var model = Mapper.Map<PostDto>(post);
 		model.Seminars = post.Seminar.Select(s => s.Id).Join(",");
 		return ResultData(model);
 	}
@@ -705,7 +706,7 @@ public sealed class PostController : BaseController
 		{
 			if (p.Content.HammingDistance(post.Content) > 0)
 			{
-				var history = p.Mapper<PostHistoryVersion>();
+				var history = Mapper.Map<PostHistoryVersion>(p);
 				history.PostId = p.Id;
 				PostHistoryVersionService.AddEntity(history);
 			}
@@ -761,7 +762,7 @@ public sealed class PostController : BaseController
 		{
 			SearchEngine.LuceneIndexer.Delete(p);
 		}
-		return ResultData(p.Mapper<PostDto>(), message: "文章修改成功!");
+		return ResultData(Mapper.Map<PostDto>(p), message: "文章修改成功!");
 	}
 
 	/// <summary>
@@ -782,7 +783,7 @@ public sealed class PostController : BaseController
 		}
 
 		post.Status = Status.Published;
-		Post p = post.Mapper<Post>();
+		Post p = Mapper.Map<Post>(post);
 		p.Modifier = p.Author;
 		p.ModifierEmail = p.Email;
 		p.IP = ClientIP.ToString();
@@ -804,7 +805,7 @@ public sealed class PostController : BaseController
 			p.PostDate = timespan.Value.ToUniversalTime();
 			p.ModifyDate = timespan.Value.ToUniversalTime();
 			BackgroundJob.Enqueue<IHangfireBackJob>(job => job.PublishPost(p));
-			return ResultData(p.Mapper<PostDto>(), message: $"文章于{timespan.Value:yyyy-MM-dd HH:mm:ss}将会自动发表!");
+			return ResultData(Mapper.Map<PostDto>(p), message: $"文章于{timespan.Value:yyyy-MM-dd HH:mm:ss}将会自动发表!");
 		}
 
 		PostService.AddEntity(p);

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/SeminarController.cs

@@ -112,7 +112,7 @@ public sealed class SeminarController : BaseController
 	public async Task<ActionResult> Get(int id)
 	{
 		Seminar seminar = await SeminarService.GetByIdAsync(id);
-		return ResultData(seminar.Mapper<SeminarDto>());
+		return ResultData(Mapper.Map<SeminarDto>(seminar));
 	}
 
 	/// <summary>

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/SystemController.cs

@@ -196,7 +196,7 @@ public sealed class SystemController : AdminController
 	/// <returns></returns>
 	public ActionResult SendMail([Required(ErrorMessage = "收件人不能为空"), FromBodyOrDefault] string tos, [Required(ErrorMessage = "邮件标题不能为空"), FromBodyOrDefault] string title, [Required(ErrorMessage = "邮件内容不能为空"), FromBodyOrDefault] string content)
 	{
-		BackgroundJob.Enqueue(() => CommonHelper.SendMail(title, content + "<p style=\"color: red\">本邮件由系统自动发出,请勿回复本邮件!</p>", tos, "127.0.0.1"));
+		BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(title, content + "<p style=\"color: red\">本邮件由系统自动发出,请勿回复本邮件!</p>", tos, "127.0.0.1"));
 		return Ok();
 	}
 

+ 26 - 2
src/Masuit.MyBlogs.Core/Controllers/ToolsController.cs

@@ -1,4 +1,5 @@
-using DnsClient;
+using AngleSharp;
+using DnsClient;
 using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Configs;
 using Masuit.Tools.AspNetCore.Mime;
@@ -64,7 +65,7 @@ public sealed class ToolsController : BaseController
 				Organization = loc.ISP
 			},
 			TimeZone = loc.Coodinate.TimeZone + $"  UTC{TZConvert.GetTimeZoneInfo(loc.Coodinate.TimeZone ?? "Asia/Shanghai").BaseUtcOffset.Hours:+#;-#;0}",
-			IsProxy = loc.Network.Contains(new[] { "cloud", "Compute", "Serv", "Tech", "Solution", "Host", "云", "Datacenter", "Data Center", "Business", "ASN" }) || domain.Length > 1 || await ipAddress.IsProxy(cts.Token),
+			IsProxy = loc.Network.Contains(new[] { "cloud", "Compute", "Serv", "Tech", "Solution", "Host", "云", "Datacenter", "Data Center", "Business", "ASN" }) || domain.Length > 1 || await IsProxy(ipAddress, cts.Token),
 			Domain = domain
 		};
 		if (Request.Method.Equals(HttpMethods.Get) || (Request.Headers[HeaderNames.Accept] + "").StartsWith(ContentType.Json))
@@ -75,6 +76,29 @@ public sealed class ToolsController : BaseController
 		return Json(address);
 	}
 
+	/// <summary>
+	/// 是否是代理ip
+	/// </summary>
+	/// <param name="ip"></param>
+	/// <param name="cancellationToken"></param>
+	/// <returns></returns>
+	public async Task<bool> IsProxy(IPAddress ip, CancellationToken cancellationToken = default)
+	{
+		_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.62");
+		return await _httpClient.GetStringAsync("https://ipinfo.io/" + ip, cancellationToken).ContinueWith(t =>
+		{
+			if (t.IsCompletedSuccessfully)
+			{
+				var ctx = BrowsingContext.New(Configuration.Default);
+				var doc = ctx.OpenAsync(res => res.Content(t.Result)).Result;
+				var isAnycast = doc.DocumentElement.QuerySelectorAll(".title").Where(e => e.TextContent.Contains("Anycast")).Select(e => e.Parent).Any(n => n.TextContent.Contains("True"));
+				var isproxy = doc.DocumentElement.QuerySelectorAll("#block-privacy img").Any(e => e.OuterHtml.Contains("right"));
+				return isAnycast || isproxy;
+			}
+			return false;
+		});
+	}
+
 	/// <summary>
 	/// 根据经纬度获取详细地理信息
 	/// </summary>

+ 2 - 2
src/Masuit.MyBlogs.Core/Controllers/ValidateController.cs

@@ -1,5 +1,5 @@
 using Hangfire;
-using Masuit.MyBlogs.Core.Common;
+using Masuit.MyBlogs.Core.Common.Mails;
 using Masuit.Tools.Core.Validator;
 using Microsoft.AspNetCore.Mvc;
 
@@ -29,7 +29,7 @@ public sealed class ValidateController : BaseController
 
 		string code = SnowFlake.GetInstance().GetUniqueShortId(6);
 		RedisHelper.Set("code:" + email, code, 86400);
-		BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "博客验证码", $"{Request.Host}本次验证码是:<span style='color:red'>{code}</span>,有效期为24h,请按时使用!", email, ClientIP.ToString()));
+		BackgroundJob.Enqueue<IMailSender>(sender => sender.Send(Request.Host + "博客验证码", $"{Request.Host}本次验证码是:<span style='color:red'>{code}</span>,有效期为24h,请按时使用!", email, ClientIP.ToString()));
 		RedisHelper.Set("get:" + email, code, 120);
 #if !DEBUG
 		return ResultData(null, true, "验证码发送成功!");

+ 5 - 2
src/Masuit.MyBlogs.Core/Extensions/Hangfire/HangfireBackJob.cs

@@ -4,6 +4,7 @@ using Masuit.MyBlogs.Core.Common;
 using Masuit.Tools.Logging;
 using Microsoft.EntityFrameworkCore;
 using System.Net;
+using Masuit.MyBlogs.Core.Common.Mails;
 
 namespace Masuit.MyBlogs.Core.Extensions.Hangfire;
 
@@ -17,16 +18,18 @@ public sealed class HangfireBackJob : Disposable, IHangfireBackJob
 	private readonly IServiceScope _serviceScope;
 	private readonly IRedisClient _redisClient;
 	private readonly IConfiguration _configuration;
+	private readonly IMailSender _mailSender;
 
 	/// <summary>
 	/// hangfire后台任务
 	/// </summary>
-	public HangfireBackJob(IServiceProvider serviceProvider, IHttpClientFactory httpClientFactory, IWebHostEnvironment hostEnvironment, IRedisClient redisClient, IConfiguration configuration)
+	public HangfireBackJob(IServiceProvider serviceProvider, IHttpClientFactory httpClientFactory, IWebHostEnvironment hostEnvironment, IRedisClient redisClient, IConfiguration configuration, IMailSender mailSender)
 	{
 		_httpClientFactory = httpClientFactory;
 		_hostEnvironment = hostEnvironment;
 		_redisClient = redisClient;
 		_configuration = configuration;
+		_mailSender = mailSender;
 		_serviceScope = serviceProvider.CreateScope();
 	}
 
@@ -55,7 +58,7 @@ public sealed class HangfireBackJob : Disposable, IHangfireBackJob
 			.Set("time", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
 			.Set("ip", record.IP)
 			.Set("address", record.PhysicAddress).Render();
-		CommonHelper.SendMail(settingService.Get(s => s.Name.Equals("Title")).Value + "账号登录通知", content, settingService.Get(s => s.Name.Equals("ReceiveEmail")).Value, "127.0.0.1");
+		_mailSender.Send(settingService.Get(s => s.Name.Equals("Title")).Value + "账号登录通知", content, settingService.Get(s => s.Name.Equals("ReceiveEmail")).Value, "127.0.0.1");
 	}
 
 	/// <summary>

+ 1 - 2
src/Masuit.MyBlogs.Core/Extensions/MyAuthorizeAttribute.cs

@@ -31,8 +31,7 @@ public sealed class MyAuthorizeAttribute : ActionFilterAttribute
 		{
 			string name = filterContext.HttpContext.Request.Cookies["username"] ?? "";
 			string pwd = filterContext.HttpContext.Request.Cookies["password"]?.DesDecrypt(AppConfig.BaiduAK) ?? "";
-
-			var userInfo = (Startup.ServiceProvider.GetRequiredService<IUserInfoService>()).Login(name, pwd);
+			var userInfo = filterContext.HttpContext.RequestServices.GetRequiredService<IUserInfoService>().Login(name, pwd);
 			if (userInfo != null)
 			{
 				filterContext.HttpContext.Response.Cookies.Append("username", name, new CookieOptions()

+ 7 - 4
src/Masuit.MyBlogs.Core/Infrastructure/Services/UserInfoService.cs

@@ -1,12 +1,14 @@
-using Masuit.LuceneEFCore.SearchEngine.Interfaces;
-using Masuit.MyBlogs.Core.Common;
+using AutoMapper;
+using Masuit.LuceneEFCore.SearchEngine.Interfaces;
 using Masuit.MyBlogs.Core.Infrastructure.Repository.Interface;
 using Masuit.Tools.DateTimeExt;
 
 namespace Masuit.MyBlogs.Core.Infrastructure.Services;
 
-public sealed partial class UserInfoService : BaseService<UserInfo>, IUserInfoService
+public sealed class UserInfoService : BaseService<UserInfo>, IUserInfoService
 {
+	public IMapper Mapper { get; set; }
+
 	/// <summary>
 	/// 根据用户名获取
 	/// </summary>
@@ -28,7 +30,7 @@ public sealed partial class UserInfoService : BaseService<UserInfo>, IUserInfoSe
 		UserInfo userInfo = GetByUsername(username);
 		if (userInfo != null)
 		{
-			UserInfoDto user = userInfo.Mapper<UserInfoDto>();
+			UserInfoDto user = Mapper.Map<UserInfoDto>(userInfo);
 			string key = userInfo.SaltKey;
 			string pwd = userInfo.Password;
 			password = password.MDString3(key);
@@ -37,6 +39,7 @@ public sealed partial class UserInfoService : BaseService<UserInfo>, IUserInfoSe
 				return user;
 			}
 		}
+
 		return null;
 	}
 

+ 0 - 7
src/Masuit.MyBlogs.Core/Startup.cs

@@ -17,7 +17,6 @@ using Masuit.Tools.AspNetCore.Mime;
 using Masuit.Tools.Config;
 using Masuit.Tools.Core.AspNetCore;
 using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Primitives;
 using Newtonsoft.Json;
 using SixLabors.ImageSharp.Web.DependencyInjection;
@@ -30,11 +29,6 @@ namespace Masuit.MyBlogs.Core
 	/// </summary>
 	public class Startup
 	{
-		/// <summary>
-		/// 依赖注入容器
-		/// </summary>
-		public static IServiceProvider ServiceProvider { get; private set; }
-
 		/// <summary>
 		/// 配置中心
 		/// </summary>
@@ -129,7 +123,6 @@ namespace Masuit.MyBlogs.Core
 		/// <param name="loggerdb"></param>
 		public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHangfireBackJob hangfire, LuceneIndexerOptions luceneIndexerOptions, DataContext maindb, LoggerDbContext loggerdb)
 		{
-			ServiceProvider = app.ApplicationServices;
 			maindb.Database.EnsureCreated();
 			loggerdb.Database.EnsureCreated();
 			app.InitSettings();