using Masuit.MyBlogs.Core.Common; using Masuit.MyBlogs.Core.Extensions; using Masuit.MyBlogs.Core.Extensions.Firewall; using Masuit.MyBlogs.Core.Infrastructure.Services.Interface; using Masuit.MyBlogs.Core.Models.Entity; using Masuit.MyBlogs.Core.Models.Enum; using Masuit.MyBlogs.Core.Models.ViewModel; using Masuit.Tools; using Masuit.Tools.AspNetCore.Mime; using Masuit.Tools.Core.Net; using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WilderMinds.RssSyndication; namespace Masuit.MyBlogs.Core.Controllers { /// /// 订阅服务 /// public class SubscribeController : Controller { public IPostService PostService { get; set; } public IAdvertisementService AdvertisementService { get; set; } /// /// RSS订阅 /// /// [Route("/rss"), ResponseCache(Duration = 3600)] public async Task Rss() { var time = DateTime.Today.AddDays(-1); string scheme = Request.Scheme; var host = Request.Host; var raw = PostService.GetQueryFromCache(p => p.Rss && p.Status == Status.Published && p.ModifyDate >= time, p => p.ModifyDate, false).ToList(); CheckPermission(raw); var data = await raw.SelectAsync(async p => { var summary = await p.Content.GetSummary(300, 50); return new Item() { Author = new Author { Name = p.Modifier }, Body = summary, Categories = new List { p.Category.Name }, Link = new Uri(scheme + "://" + host + "/" + p.Id), PublishDate = p.ModifyDate.ToTimeZone(HttpContext.Session.Get(SessionKey.TimeZone)), Title = p.Title, Permalink = scheme + "://" + host + "/" + p.Id, Guid = p.Id.ToString(), FullHtmlContent = summary }; }); var posts = data.ToList(); InsertAdvertisement(posts); var feed = new Feed() { Title = CommonHelper.SystemSettings["Title"], Description = CommonHelper.SystemSettings["Description"], Link = new Uri(scheme + "://" + host + "/rss"), Copyright = CommonHelper.SystemSettings["Title"], Language = "zh-cn", Items = posts }; var rss = feed.Serialize(new SerializeOption() { Encoding = Encoding.UTF8 }); return Content(rss, ContentType.Xml); } private void InsertAdvertisement(List posts, int? cid = null) { if (posts.Count > 2) { var ad = AdvertisementService.GetByWeightedPrice((AdvertiseType)(DateTime.Now.Second % 4 + 1), Request.Location(), cid); if (ad is not null) { posts.Insert(new Random().Next(1, posts.Count), new Item() { Author = new Author() { Name = ad.Title }, Body = ad.Description, Title = ad.Title, FullHtmlContent = ad.Description, Guid = ad.IndexId, PublishDate = DateTime.UtcNow, Link = new Uri(Url.ActionLink("Redirect", "Advertisement", new { id = ad.Id })), Permalink = Url.ActionLink("Redirect", "Advertisement", new { id = ad.Id }) }); } } } /// /// RSS分类订阅 /// /// [Route("/cat/{id}/rss"), ResponseCache(Duration = 3600)] public async Task CategoryRss([FromServices] ICategoryService categoryService, int id) { var time = DateTime.Today.AddDays(-1); string scheme = Request.Scheme; var host = Request.Host; var category = await categoryService.GetByIdAsync(id) ?? throw new NotFoundException("分类未找到"); var raw = PostService.GetQueryFromCache(p => p.Rss && p.CategoryId == id && p.Status == Status.Published && p.ModifyDate >= time, p => p.ModifyDate, false).ToList(); CheckPermission(raw); var data = await raw.SelectAsync(async p => { var summary = await p.Content.GetSummary(300, 50); return new Item() { Author = new Author { Name = p.Modifier }, Body = summary, Categories = new List { p.Category.Name }, Link = new Uri(scheme + "://" + host + "/" + p.Id), PublishDate = p.ModifyDate.ToTimeZone(HttpContext.Session.Get(SessionKey.TimeZone)), Title = p.Title, Permalink = scheme + "://" + host + "/" + p.Id, Guid = p.Id.ToString(), FullHtmlContent = summary }; }); var posts = data.ToList(); InsertAdvertisement(posts, id); var feed = new Feed() { Title = Request.Host + $":分类{category.Name}文章订阅", Description = category.Description, Link = new Uri(scheme + "://" + host + "/rss"), Copyright = CommonHelper.SystemSettings["Title"], Language = "zh-cn", Items = posts }; var rss = feed.Serialize(new SerializeOption() { Encoding = Encoding.UTF8 }); return Content(rss, ContentType.Xml); } /// /// RSS文章订阅 /// /// [Route("/{id}/rss"), ResponseCache(Duration = 3600)] public async Task PostRss(int id) { string scheme = Request.Scheme; var host = Request.Host; var post = await PostService.GetAsync(p => p.Rss && p.Status == Status.Published && p.Id == id) ?? throw new NotFoundException("文章未找到"); CheckPermission(post); var summary = await post.Content.GetSummary(300, 50); var item = new Item() { Author = new Author { Name = post.Modifier }, Body = summary, Categories = new List { post.Category.Name }, Link = new Uri(scheme + "://" + host + "/" + post.Id), PublishDate = post.ModifyDate.ToTimeZone(HttpContext.Session.Get(SessionKey.TimeZone)), Title = post.Title, Permalink = scheme + "://" + host + "/" + post.Id, Guid = post.Id.ToString(), FullHtmlContent = summary }; var feed = new Feed() { Title = Request.Host + $":文章【{post.Title}】更新订阅", Description = summary, Link = new Uri(scheme + "://" + host + "/rss/" + id), Copyright = CommonHelper.SystemSettings["Title"], Language = "zh-cn", Items = new List() { item } }; var rss = feed.Serialize(new SerializeOption() { Encoding = Encoding.UTF8 }); return Content(rss, ContentType.Xml); } private void CheckPermission(List posts) { var location = Request.Location() + "|" + Request.Headers[HeaderNames.UserAgent]; posts.RemoveAll(p => { switch (p.LimitMode) { case RegionLimitMode.AllowRegion: return !location.Contains(p.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !Request.IsRobot(); case RegionLimitMode.ForbidRegion: return location.Contains(p.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !Request.IsRobot(); case RegionLimitMode.AllowRegionExceptForbidRegion: if (location.Contains(p.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries))) { return true; } goto case RegionLimitMode.AllowRegion; case RegionLimitMode.ForbidRegionExceptAllowRegion: if (location.Contains(p.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries))) { return false; } goto case RegionLimitMode.ForbidRegion; default: return false; } }); foreach (var item in posts) { item.PostDate = item.PostDate.ToTimeZone(HttpContext.Session.Get(SessionKey.TimeZone)); item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get(SessionKey.TimeZone)); } } private void CheckPermission(Post post) { var location = Request.Location() + "|" + Request.Headers[HeaderNames.UserAgent]; switch (post.LimitMode) { case RegionLimitMode.AllowRegion: if (!location.Contains(post.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !Request.IsRobot()) { Disallow(post); } break; case RegionLimitMode.ForbidRegion: if (location.Contains(post.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !Request.IsRobot()) { Disallow(post); } break; case RegionLimitMode.AllowRegionExceptForbidRegion: if (location.Contains(post.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries))) { Disallow(post); } goto case RegionLimitMode.AllowRegion; case RegionLimitMode.ForbidRegionExceptAllowRegion: if (location.Contains(post.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries))) { break; } goto case RegionLimitMode.ForbidRegion; } } private void Disallow(Post post) { RedisHelper.IncrBy("interceptCount"); RedisHelper.LPush("intercept", new IpIntercepter() { IP = HttpContext.Connection.RemoteIpAddress.ToString(), RequestUrl = $"//{Request.Host}/{post.Id}", Referer = Request.Headers[HeaderNames.Referer], Time = DateTime.Now, UserAgent = Request.Headers[HeaderNames.UserAgent], Remark = "无权限查看该文章", Address = Request.Location() }); throw new NotFoundException("文章未找到"); } } }