SubscribeController.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. using Masuit.MyBlogs.Core.Common;
  2. using Masuit.MyBlogs.Core.Extensions;
  3. using Masuit.MyBlogs.Core.Extensions.Firewall;
  4. using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
  5. using Masuit.MyBlogs.Core.Models.Entity;
  6. using Masuit.MyBlogs.Core.Models.Enum;
  7. using Masuit.MyBlogs.Core.Models.ViewModel;
  8. using Masuit.Tools;
  9. using Masuit.Tools.AspNetCore.Mime;
  10. using Masuit.Tools.Core.Net;
  11. using Microsoft.AspNetCore.Mvc;
  12. using Microsoft.Net.Http.Headers;
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Linq;
  16. using System.Text;
  17. using System.Threading.Tasks;
  18. using WilderMinds.RssSyndication;
  19. namespace Masuit.MyBlogs.Core.Controllers
  20. {
  21. /// <summary>
  22. /// 订阅服务
  23. /// </summary>
  24. public class SubscribeController : Controller
  25. {
  26. public IPostService PostService { get; set; }
  27. public IAdvertisementService AdvertisementService { get; set; }
  28. /// <summary>
  29. /// RSS订阅
  30. /// </summary>
  31. /// <returns></returns>
  32. [Route("/rss"), ResponseCache(Duration = 3600)]
  33. public async Task<IActionResult> Rss()
  34. {
  35. var time = DateTime.Today.AddDays(-1);
  36. string scheme = Request.Scheme;
  37. var host = Request.Host;
  38. var raw = PostService.GetQueryFromCache(p => p.Rss && p.Status == Status.Published && p.ModifyDate >= time, p => p.ModifyDate, false).ToList();
  39. CheckPermission(raw);
  40. var data = await raw.SelectAsync(async p =>
  41. {
  42. var summary = await p.Content.GetSummary(300, 50);
  43. return new Item()
  44. {
  45. Author = new Author
  46. {
  47. Name = p.Modifier
  48. },
  49. Body = summary,
  50. Categories = new List<string>
  51. {
  52. p.Category.Name
  53. },
  54. Link = new Uri(scheme + "://" + host + "/" + p.Id),
  55. PublishDate = p.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone)),
  56. Title = p.Title,
  57. Permalink = scheme + "://" + host + "/" + p.Id,
  58. Guid = p.Id.ToString(),
  59. FullHtmlContent = summary
  60. };
  61. });
  62. var posts = data.ToList();
  63. InsertAdvertisement(posts);
  64. var feed = new Feed()
  65. {
  66. Title = CommonHelper.SystemSettings["Title"],
  67. Description = CommonHelper.SystemSettings["Description"],
  68. Link = new Uri(scheme + "://" + host + "/rss"),
  69. Copyright = CommonHelper.SystemSettings["Title"],
  70. Language = "zh-cn",
  71. Items = posts
  72. };
  73. var rss = feed.Serialize(new SerializeOption()
  74. {
  75. Encoding = Encoding.UTF8
  76. });
  77. return Content(rss, ContentType.Xml);
  78. }
  79. private void InsertAdvertisement(List<Item> posts, int? cid = null)
  80. {
  81. if (posts.Count > 2)
  82. {
  83. var ad = AdvertisementService.GetByWeightedPrice((AdvertiseType)(DateTime.Now.Second % 4 + 1), Request.Location(), cid);
  84. if (ad is not null)
  85. {
  86. posts.Insert(new Random().Next(1, posts.Count), new Item()
  87. {
  88. Author = new Author()
  89. {
  90. Name = ad.Title
  91. },
  92. Body = ad.Description,
  93. Title = ad.Title,
  94. FullHtmlContent = ad.Description,
  95. Guid = ad.IndexId,
  96. PublishDate = DateTime.UtcNow,
  97. Link = new Uri(Url.ActionLink("Redirect", "Advertisement", new { id = ad.Id })),
  98. Permalink = Url.ActionLink("Redirect", "Advertisement", new { id = ad.Id })
  99. });
  100. }
  101. }
  102. }
  103. /// <summary>
  104. /// RSS分类订阅
  105. /// </summary>
  106. /// <returns></returns>
  107. [Route("/cat/{id}/rss"), ResponseCache(Duration = 3600)]
  108. public async Task<IActionResult> CategoryRss([FromServices] ICategoryService categoryService, int id)
  109. {
  110. var time = DateTime.Today.AddDays(-1);
  111. string scheme = Request.Scheme;
  112. var host = Request.Host;
  113. var category = await categoryService.GetByIdAsync(id) ?? throw new NotFoundException("分类未找到");
  114. var raw = PostService.GetQueryFromCache(p => p.Rss && p.CategoryId == id && p.Status == Status.Published && p.ModifyDate >= time, p => p.ModifyDate, false).ToList();
  115. CheckPermission(raw);
  116. var data = await raw.SelectAsync(async p =>
  117. {
  118. var summary = await p.Content.GetSummary(300, 50);
  119. return new Item()
  120. {
  121. Author = new Author
  122. {
  123. Name = p.Modifier
  124. },
  125. Body = summary,
  126. Categories = new List<string>
  127. {
  128. p.Category.Name
  129. },
  130. Link = new Uri(scheme + "://" + host + "/" + p.Id),
  131. PublishDate = p.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone)),
  132. Title = p.Title,
  133. Permalink = scheme + "://" + host + "/" + p.Id,
  134. Guid = p.Id.ToString(),
  135. FullHtmlContent = summary
  136. };
  137. });
  138. var posts = data.ToList();
  139. InsertAdvertisement(posts, id);
  140. var feed = new Feed()
  141. {
  142. Title = Request.Host + $":分类{category.Name}文章订阅",
  143. Description = category.Description,
  144. Link = new Uri(scheme + "://" + host + "/rss"),
  145. Copyright = CommonHelper.SystemSettings["Title"],
  146. Language = "zh-cn",
  147. Items = posts
  148. };
  149. var rss = feed.Serialize(new SerializeOption()
  150. {
  151. Encoding = Encoding.UTF8
  152. });
  153. return Content(rss, ContentType.Xml);
  154. }
  155. /// <summary>
  156. /// RSS文章订阅
  157. /// </summary>
  158. /// <returns></returns>
  159. [Route("/{id}/rss"), ResponseCache(Duration = 3600)]
  160. public async Task<IActionResult> PostRss(int id)
  161. {
  162. string scheme = Request.Scheme;
  163. var host = Request.Host;
  164. var post = await PostService.GetAsync(p => p.Rss && p.Status == Status.Published && p.Id == id) ?? throw new NotFoundException("文章未找到");
  165. CheckPermission(post);
  166. var summary = await post.Content.GetSummary(300, 50);
  167. var item = new Item()
  168. {
  169. Author = new Author
  170. {
  171. Name = post.Modifier
  172. },
  173. Body = summary,
  174. Categories = new List<string>
  175. {
  176. post.Category.Name
  177. },
  178. Link = new Uri(scheme + "://" + host + "/" + post.Id),
  179. PublishDate = post.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone)),
  180. Title = post.Title,
  181. Permalink = scheme + "://" + host + "/" + post.Id,
  182. Guid = post.Id.ToString(),
  183. FullHtmlContent = summary
  184. };
  185. var feed = new Feed()
  186. {
  187. Title = Request.Host + $":文章【{post.Title}】更新订阅",
  188. Description = summary,
  189. Link = new Uri(scheme + "://" + host + "/rss/" + id),
  190. Copyright = CommonHelper.SystemSettings["Title"],
  191. Language = "zh-cn",
  192. Items = new List<Item>() { item }
  193. };
  194. var rss = feed.Serialize(new SerializeOption()
  195. {
  196. Encoding = Encoding.UTF8
  197. });
  198. return Content(rss, ContentType.Xml);
  199. }
  200. private void CheckPermission(List<Post> posts)
  201. {
  202. var location = Request.Location() + "|" + Request.Headers[HeaderNames.UserAgent];
  203. posts.RemoveAll(p =>
  204. {
  205. switch (p.LimitMode)
  206. {
  207. case RegionLimitMode.AllowRegion:
  208. return !location.Contains(p.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !Request.IsRobot();
  209. case RegionLimitMode.ForbidRegion:
  210. return location.Contains(p.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !Request.IsRobot();
  211. case RegionLimitMode.AllowRegionExceptForbidRegion:
  212. if (location.Contains(p.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)))
  213. {
  214. return true;
  215. }
  216. goto case RegionLimitMode.AllowRegion;
  217. case RegionLimitMode.ForbidRegionExceptAllowRegion:
  218. if (location.Contains(p.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)))
  219. {
  220. return false;
  221. }
  222. goto case RegionLimitMode.ForbidRegion;
  223. default:
  224. return false;
  225. }
  226. });
  227. foreach (var item in posts)
  228. {
  229. item.PostDate = item.PostDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
  230. item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
  231. }
  232. }
  233. private void CheckPermission(Post post)
  234. {
  235. var location = Request.Location() + "|" + Request.Headers[HeaderNames.UserAgent];
  236. switch (post.LimitMode)
  237. {
  238. case RegionLimitMode.AllowRegion:
  239. if (!location.Contains(post.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !Request.IsRobot())
  240. {
  241. Disallow(post);
  242. }
  243. break;
  244. case RegionLimitMode.ForbidRegion:
  245. if (location.Contains(post.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !Request.IsRobot())
  246. {
  247. Disallow(post);
  248. }
  249. break;
  250. case RegionLimitMode.AllowRegionExceptForbidRegion:
  251. if (location.Contains(post.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)))
  252. {
  253. Disallow(post);
  254. }
  255. goto case RegionLimitMode.AllowRegion;
  256. case RegionLimitMode.ForbidRegionExceptAllowRegion:
  257. if (location.Contains(post.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)))
  258. {
  259. break;
  260. }
  261. goto case RegionLimitMode.ForbidRegion;
  262. }
  263. }
  264. private void Disallow(Post post)
  265. {
  266. RedisHelper.IncrBy("interceptCount");
  267. RedisHelper.LPush("intercept", new IpIntercepter()
  268. {
  269. IP = HttpContext.Connection.RemoteIpAddress.ToString(),
  270. RequestUrl = $"//{Request.Host}/{post.Id}",
  271. Referer = Request.Headers[HeaderNames.Referer],
  272. Time = DateTime.Now,
  273. UserAgent = Request.Headers[HeaderNames.UserAgent],
  274. Remark = "无权限查看该文章",
  275. Address = Request.Location()
  276. });
  277. throw new NotFoundException("文章未找到");
  278. }
  279. }
  280. }