ソースを参照

评论禁止挖坟

懒得勤快 4 年 前
コミット
2f047fa49f

+ 23 - 4
src/Masuit.MyBlogs.Core/Common/ImagebedClient.cs

@@ -5,6 +5,7 @@ using Masuit.Tools.Html;
 using Masuit.Tools.Logging;
 using Masuit.Tools.Systems;
 using Microsoft.Extensions.Configuration;
+using Newtonsoft.Json.Linq;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -70,7 +71,7 @@ namespace Masuit.MyBlogs.Core.Common
                 return UploadGitlab(gitlab, stream, file, cancellationToken);
             }
 
-            return Task.FromResult<(string url, bool success)>((null, false));
+            return UploadKieng(stream, cancellationToken);
         }
 
         /// <summary>
@@ -101,7 +102,7 @@ namespace Masuit.MyBlogs.Core.Common
                 }
 
                 LogManager.Info("图片上传到gitee失败。");
-                throw t.Exception ?? new Exception(t.Result.ReasonPhrase);
+                return UploadKieng(stream, cancellationToken).Result;
             });
         }
 
@@ -139,7 +140,7 @@ namespace Masuit.MyBlogs.Core.Common
                 }
 
                 LogManager.Info("图片上传到gitee失败。");
-                throw t.Exception ?? new Exception(t.Result.ReasonPhrase);
+                return UploadKieng(stream, cancellationToken).Result;
             });
         }
 
@@ -178,10 +179,28 @@ namespace Masuit.MyBlogs.Core.Common
 
                 LogManager.Info($"图片上传到gitlab({config.ApiUrl})失败。");
                 _failedList.Add(config.ApiUrl);
-                throw t.Exception ?? new Exception(t.Result.ReasonPhrase);
+                return UploadKieng(stream, cancellationToken).Result;
             });
         }
 
+        /// <summary>
+        /// 上传到聚合图床
+        /// </summary>
+        /// <param name="stream"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        private async Task<(string url, bool success)> UploadKieng(Stream stream, CancellationToken cancellationToken)
+        {
+            using var formData = new MultipartFormDataContent
+            {
+                { new StreamContent(stream), "image","1.jpg" }
+            };
+            var resp = await _httpClient.PostAsync("https://image.kieng.cn/upload.html?type=" + new[] { "tt", "jd", "c58", "sg", "sh", "wy" }.OrderByRandom().First(), formData, cancellationToken);
+            var json = await resp.Content.ReadAsStringAsync();
+            var result = JObject.Parse(json);
+            return ((string)result["data"]["url"], (int)result["code"] == 200);
+        }
+
         /// <summary>
         /// 替换img标签的src属性
         /// </summary>

+ 17 - 12
src/Masuit.MyBlogs.Core/Controllers/CommentController.cs

@@ -45,38 +45,43 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// </summary>
         /// <param name="messageService"></param>
         /// <param name="mailSender"></param>
-        /// <param name="dto"></param>
+        /// <param name="cmd"></param>
         /// <returns></returns>
         [HttpPost, ValidateAntiForgeryToken]
-        public async Task<ActionResult> Submit([FromServices] IInternalMessageService messageService, [FromServices] IMailSender mailSender, CommentCommand dto)
+        public async Task<ActionResult> Submit([FromServices] IInternalMessageService messageService, [FromServices] IMailSender mailSender, CommentCommand cmd)
         {
-            var match = Regex.Match(dto.NickName + dto.Content.RemoveHtmlTag(), CommonHelper.BanRegex);
+            var match = Regex.Match(cmd.NickName + cmd.Content.RemoveHtmlTag(), CommonHelper.BanRegex);
             if (match.Success)
             {
-                LogManager.Info($"提交内容:{dto.NickName}/{dto.Content},敏感词:{match.Value}");
+                LogManager.Info($"提交内容:{cmd.NickName}/{cmd.Content},敏感词:{match.Value}");
                 return ResultData(null, false, "您提交的内容包含敏感词,被禁止发表,请检查您的内容后尝试重新提交!");
             }
-            var error = await ValidateEmailCode(mailSender, dto.Email, dto.Code);
+            var error = await ValidateEmailCode(mailSender, cmd.Email, cmd.Code);
             if (!string.IsNullOrEmpty(error))
             {
                 return ResultData(null, false, error);
             }
 
-            Post post = await PostService.GetByIdAsync(dto.PostId) ?? throw new NotFoundException("评论失败,文章未找到");
+            if (cmd.ParentId > 0 && DateTime.Now - CommentService[cmd.ParentId, c => c.CommentDate] > TimeSpan.FromDays(180))
+            {
+                return ResultData(null, false, "当前评论过于久远,不再允许回复!");
+            }
+
+            Post post = await PostService.GetByIdAsync(cmd.PostId) ?? throw new NotFoundException("评论失败,文章未找到");
             if (post.DisableComment)
             {
                 return ResultData(null, false, "本文已禁用评论功能,不允许任何人回复!");
             }
 
-            dto.Content = dto.Content.Trim().Replace("<p><br></p>", string.Empty);
+            cmd.Content = cmd.Content.Trim().Replace("<p><br></p>", string.Empty);
             if (CommentFeq.GetOrAdd("Comments:" + ClientIP, 1) > 2)
             {
                 CommentFeq.Expire("Comments:" + ClientIP, TimeSpan.FromMinutes(1));
                 return ResultData(null, false, "您的发言频率过快,请稍后再发表吧!");
             }
 
-            var comment = dto.Mapper<Comment>();
-            if (dto.Email == post.Email || dto.Email == post.ModifierEmail || Regex.Match(dto.NickName + dto.Content, CommonHelper.ModRegex).Length <= 0)
+            var comment = cmd.Mapper<Comment>();
+            if (cmd.Email == post.Email || cmd.Email == post.ModifierEmail || Regex.Match(cmd.NickName + cmd.Content, CommonHelper.ModRegex).Length <= 0)
             {
                 comment.Status = Status.Published;
             }
@@ -93,8 +98,8 @@ namespace Masuit.MyBlogs.Core.Controllers
                     comment.IsMaster = true;
                 }
             }
-            comment.Content = await dto.Content.HtmlSantinizerStandard().ClearImgAttributes();
-            comment.Browser = dto.Browser ?? Request.Headers[HeaderNames.UserAgent];
+            comment.Content = await cmd.Content.HtmlSantinizerStandard().ClearImgAttributes();
+            comment.Browser = cmd.Browser ?? Request.Headers[HeaderNames.UserAgent];
             comment.IP = ClientIP;
             comment.Location = Request.Location();
             comment = CommentService.AddEntitySaved(comment);
@@ -108,7 +113,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 Expires = DateTimeOffset.Now.AddYears(1),
                 SameSite = SameSiteMode.Lax
             });
-            WriteEmailKeyCookie(dto.Email);
+            WriteEmailKeyCookie(cmd.Email);
             CommentFeq.AddOrUpdate("Comments:" + ClientIP, 1, i => i + 1, 5);
             CommentFeq.Expire("Comments:" + ClientIP, TimeSpan.FromMinutes(1));
             var emails = new HashSet<string>();

+ 9 - 3
src/Masuit.MyBlogs.Core/Controllers/ErrorController.cs

@@ -61,6 +61,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         public async Task<ActionResult> ServiceUnavailable()
         {
             var feature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
+            string accept = Request.Headers[HeaderNames.Accept] + "";
             if (feature != null)
             {
                 string err;
@@ -104,7 +105,12 @@ namespace Masuit.MyBlogs.Core.Controllers
                         return View("AccessDeny", tips);
                     case TempDenyException:
                         Response.StatusCode = 403;
-                        return View("TempDeny");
+                        return accept.StartsWith("application/json") ? Json(new
+                        {
+                            StatusCode = 404,
+                            Success = false,
+                            Message = $"检测到您的IP({ip})访问过于频繁,已被本站暂时禁止访问,请稍后再试!"
+                        }) : View("TempDeny");
                     default:
                         LogManager.Error($"异常源:{feature.Error.Source},异常类型:{feature.Error.GetType().Name},请求路径:{req.Scheme}://{req.Host}{HttpUtility.UrlDecode(feature.Path)}{req.QueryString} ,客户端用户代理:{req.Headers[HeaderNames.UserAgent]},客户端IP:{ip},请求参数:\n{body}\n堆栈信息:", feature.Error);
                         break;
@@ -112,12 +118,12 @@ namespace Masuit.MyBlogs.Core.Controllers
             }
 
             Response.StatusCode = 503;
-            return Request.Method.Equals(HttpMethods.Get) ? View() : Json(new
+            return accept.StartsWith("application/json") ? Json(new
             {
                 StatusCode = 503,
                 Success = false,
                 Message = "服务器发生错误!"
-            });
+            }) : View();
         }
 
         /// <summary>

+ 16 - 11
src/Masuit.MyBlogs.Core/Controllers/MsgController.cs

@@ -119,33 +119,38 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// 发表留言
         /// </summary>
         /// <param name="mailSender"></param>
-        /// <param name="dto"></param>
+        /// <param name="cmd"></param>
         /// <returns></returns>
         [HttpPost, ValidateAntiForgeryToken]
-        public async Task<ActionResult> Submit([FromServices] IMailSender mailSender, LeaveMessageCommand dto)
+        public async Task<ActionResult> Submit([FromServices] IMailSender mailSender, LeaveMessageCommand cmd)
         {
-            var match = Regex.Match(dto.NickName + dto.Content.RemoveHtmlTag(), CommonHelper.BanRegex);
+            var match = Regex.Match(cmd.NickName + cmd.Content.RemoveHtmlTag(), CommonHelper.BanRegex);
             if (match.Success)
             {
-                LogManager.Info($"提交内容:{dto.NickName}/{dto.Content},敏感词:{match.Value}");
+                LogManager.Info($"提交内容:{cmd.NickName}/{cmd.Content},敏感词:{match.Value}");
                 return ResultData(null, false, "您提交的内容包含敏感词,被禁止发表,请检查您的内容后尝试重新提交!");
             }
 
-            var error = await ValidateEmailCode(mailSender, dto.Email, dto.Code);
+            var error = await ValidateEmailCode(mailSender, cmd.Email, cmd.Code);
             if (!string.IsNullOrEmpty(error))
             {
                 return ResultData(null, false, error);
             }
 
-            dto.Content = dto.Content.Trim().Replace("<p><br></p>", string.Empty);
+            if (cmd.ParentId > 0 && DateTime.Now - LeaveMessageService[cmd.ParentId, m => m.PostDate] > TimeSpan.FromDays(180))
+            {
+                return ResultData(null, false, "当前留言过于久远,不再允许回复!");
+            }
+
+            cmd.Content = cmd.Content.Trim().Replace("<p><br></p>", string.Empty);
             if (MsgFeq.GetOrAdd("Comments:" + ClientIP, 1) > 2)
             {
                 MsgFeq.Expire("Comments:" + ClientIP, TimeSpan.FromMinutes(1));
                 return ResultData(null, false, "您的发言频率过快,请稍后再发表吧!");
             }
 
-            var msg = dto.Mapper<LeaveMessage>();
-            if (Regex.Match(dto.NickName + dto.Content, CommonHelper.ModRegex).Length <= 0)
+            var msg = cmd.Mapper<LeaveMessage>();
+            if (Regex.Match(cmd.NickName + cmd.Content, CommonHelper.ModRegex).Length <= 0)
             {
                 msg.Status = Status.Published;
             }
@@ -163,8 +168,8 @@ namespace Masuit.MyBlogs.Core.Controllers
                 }
             }
 
-            msg.Content = await dto.Content.HtmlSantinizerStandard().ClearImgAttributes();
-            msg.Browser = dto.Browser ?? Request.Headers[HeaderNames.UserAgent];
+            msg.Content = await cmd.Content.HtmlSantinizerStandard().ClearImgAttributes();
+            msg.Browser = cmd.Browser ?? Request.Headers[HeaderNames.UserAgent];
             msg.IP = ClientIP;
             msg.Location = Request.Location();
             msg = LeaveMessageService.AddEntitySaved(msg);
@@ -178,7 +183,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 Expires = DateTimeOffset.Now.AddYears(1),
                 SameSite = SameSiteMode.Lax
             });
-            WriteEmailKeyCookie(dto.Email);
+            WriteEmailKeyCookie(cmd.Email);
             MsgFeq.AddOrUpdate("Comments:" + ClientIP, 1, i => i + 1, 5);
             MsgFeq.Expire("Comments:" + ClientIP, TimeSpan.FromMinutes(1));
             var email = CommonHelper.SystemSettings["ReceiveEmail"];

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

@@ -60,7 +60,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 CityInfo = cityInfo,
                 Address = $"{location}(UTC{TZConvert.GetTimeZoneInfo(cityInfo.Location.TimeZone ?? "Asia/Shanghai").BaseUtcOffset.Hours:+#;-#;0})",
                 Asn = ip.GetIPAsn(),
-                IsProxy = location.Contains(new[] { "cloud", "Compute", "Serv", "Tech", "Solution", "Host", "云", "Data Services" }) || await ip.IsProxy()
+                IsProxy = location.Contains(new[] { "cloud", "Compute", "Serv", "Tech", "Solution", "Host", "云", "Datacenter", "Data Center", "Business" }) || await ip.IsProxy()
             };
             if (Request.Method.Equals(HttpMethods.Get))
             {

+ 3 - 3
src/Masuit.MyBlogs.Core/Extensions/Firewall/FirewallAttribute.cs

@@ -37,7 +37,7 @@ namespace Masuit.MyBlogs.Core.Extensions.Firewall
                 return;
             }
 
-            if (CommonHelper.SystemSettings.GetOrAdd("FirewallEnabled", "true") == "false" || context.Filters.Any(m => m.ToString().Contains(nameof(AllowAccessFirewallAttribute))) || tokenValid)
+            if (CommonHelper.SystemSettings.GetOrAdd("FirewallEnabled", "true") == "false" || context.Filters.Any(m => m.ToString().Contains(new[] { nameof(AllowAccessFirewallAttribute), nameof(MyAuthorizeAttribute) })) || tokenValid)
             {
                 return;
             }
@@ -69,7 +69,7 @@ namespace Masuit.MyBlogs.Core.Extensions.Firewall
             }
 
             var times = CacheManager.AddOrUpdate("Frequency:" + ip, 1, i => i + 1, 5);
-            CacheManager.Expire("Frequency:" + ip, ExpirationMode.Sliding, TimeSpan.FromSeconds(CommonHelper.SystemSettings.GetOrAdd("LimitIPFrequency", "60").ToInt32()));
+            CacheManager.Expire("Frequency:" + ip, ExpirationMode.Absolute, TimeSpan.FromSeconds(CommonHelper.SystemSettings.GetOrAdd("LimitIPFrequency", "60").ToInt32()));
             var limit = CommonHelper.SystemSettings.GetOrAdd("LimitIPRequestTimes", "90").ToInt32();
             if (times <= limit)
             {
@@ -78,7 +78,7 @@ namespace Masuit.MyBlogs.Core.Extensions.Firewall
 
             if (times > limit * 1.2)
             {
-                CacheManager.Expire("Frequency:" + ip, ExpirationMode.Sliding, TimeSpan.FromMinutes(CommonHelper.SystemSettings.GetOrAdd("BanIPTimespan", "10").ToInt32()));
+                CacheManager.Expire("Frequency:" + ip, TimeSpan.FromMinutes(CommonHelper.SystemSettings.GetOrAdd("BanIPTimespan", "10").ToInt32()));
                 AccessDeny(ip, request, "访问频次限制");
             }
 

+ 7 - 0
src/Masuit.MyBlogs.Core/Infrastructure/Services/BaseService.cs

@@ -1037,6 +1037,13 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
             get => GetById(id);
             set => AddEntity(value);
         }
+
+        public virtual string this[int id, Expression<Func<T, string>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+        public virtual int this[int id, Expression<Func<T, int>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+        public virtual DateTime this[int id, Expression<Func<T, DateTime>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+        public virtual long this[int id, Expression<Func<T, long>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+        public virtual decimal this[int id, Expression<Func<T, decimal>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+
         public static T operator +(BaseService<T> left, T right) => left.AddEntity(right);
         public static bool operator -(BaseService<T> left, T right) => left.DeleteEntity(right);
         public static bool operator -(BaseService<T> left, int id) => left.DeleteById(id);

+ 6 - 0
src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IBaseService.cs

@@ -738,6 +738,12 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
         Task<IEnumerable<T>> AddEntitiesAsync(IList<T> list);
 
         T this[int id] => GetById(id);
+        string this[int id, Expression<Func<T, string>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+        int this[int id, Expression<Func<T, int>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+        DateTime this[int id, Expression<Func<T, DateTime>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+        long this[int id, Expression<Func<T, long>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+        decimal this[int id, Expression<Func<T, decimal>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+
         List<T> this[Expression<Func<T, bool>> where] => GetQuery(where).ToList();
         public static T operator +(IBaseService<T> left, T right) => left.AddEntitySaved(right);
         public static bool operator -(IBaseService<T> left, T right) => left.DeleteEntitySaved(right);