This commit is contained in:
@@ -30,6 +30,7 @@ robots = read("src/TaxBaik.Web/wwwroot/robots.txt")
|
|||||||
require("Layout = null" in sitemap_view, "sitemap.xml must render without the HTML layout")
|
require("Layout = null" in sitemap_view, "sitemap.xml must render without the HTML layout")
|
||||||
require("Layout = null" in rss_view, "rss.xml must render without the HTML layout")
|
require("Layout = null" in rss_view, "rss.xml must render without the HTML layout")
|
||||||
require("Html.Raw(rssContent.ToString())" in rss_view, "RSS XML must not be HTML-encoded")
|
require("Html.Raw(rssContent.ToString())" in rss_view, "RSS XML must not be HTML-encoded")
|
||||||
|
require("Model.Items" in rss_view, "RSS view must render from the combined feed items")
|
||||||
require("StatusCodes.Status308PermanentRedirect" in feed_view, "feed.xml must permanently redirect to rss.xml")
|
require("StatusCodes.Status308PermanentRedirect" in feed_view, "feed.xml must permanently redirect to rss.xml")
|
||||||
require("DateTime.UtcNow:yyyy-MM-dd" not in sitemap_view, "sitemap lastmod must not use the current request date")
|
require("DateTime.UtcNow:yyyy-MM-dd" not in sitemap_view, "sitemap lastmod must not use the current request date")
|
||||||
require("GetAllPublishedPostsAsync" in sitemap_model, "sitemap must page through all published blog posts")
|
require("GetAllPublishedPostsAsync" in sitemap_model, "sitemap must page through all published blog posts")
|
||||||
|
|||||||
@@ -14,20 +14,20 @@
|
|||||||
rssContent.AppendLine($" <lastBuildDate>{Model.LastBuildDate}</lastBuildDate>");
|
rssContent.AppendLine($" <lastBuildDate>{Model.LastBuildDate}</lastBuildDate>");
|
||||||
rssContent.AppendLine(" <ttl>60</ttl>");
|
rssContent.AppendLine(" <ttl>60</ttl>");
|
||||||
|
|
||||||
foreach (var post in Model.Posts)
|
foreach (var item in Model.Items)
|
||||||
{
|
{
|
||||||
var postUrl = $"https://www.taxbaik.com/blog/{Uri.EscapeDataString(post.Slug)}";
|
var itemUrl = item.Link;
|
||||||
var fullDescription = post.Content ?? string.Empty;
|
var fullDescription = item.Description ?? string.Empty;
|
||||||
var safeDescription = fullDescription.Replace("]]>", "]]]]><![CDATA[>");
|
var safeDescription = fullDescription.Replace("]]>", "]]]]><![CDATA[>");
|
||||||
|
|
||||||
rssContent.AppendLine(" <item>");
|
rssContent.AppendLine(" <item>");
|
||||||
rssContent.AppendLine($" <title>{System.Net.WebUtility.HtmlEncode(post.Title)}</title>");
|
rssContent.AppendLine($" <title>{System.Net.WebUtility.HtmlEncode(item.Title)}</title>");
|
||||||
rssContent.AppendLine($" <link>{postUrl}</link>");
|
rssContent.AppendLine($" <link>{itemUrl}</link>");
|
||||||
rssContent.AppendLine($" <guid isPermaLink=\"true\">{postUrl}</guid>");
|
rssContent.AppendLine($" <guid isPermaLink=\"true\">{item.Guid}</guid>");
|
||||||
|
|
||||||
if (post.PublishedAt is not null)
|
if (item.PublishedAt is not null)
|
||||||
{
|
{
|
||||||
rssContent.AppendLine($" <pubDate>{post.PublishedAt.Value.ToUniversalTime():R}</pubDate>");
|
rssContent.AppendLine($" <pubDate>{item.PublishedAt.Value.ToUniversalTime():R}</pubDate>");
|
||||||
}
|
}
|
||||||
|
|
||||||
rssContent.AppendLine($" <description><![CDATA[{safeDescription}]]></description>");
|
rssContent.AppendLine($" <description><![CDATA[{safeDescription}]]></description>");
|
||||||
|
|||||||
@@ -7,30 +7,83 @@ namespace TaxBaik.Web.Pages;
|
|||||||
public class RssModel : PageModel
|
public class RssModel : PageModel
|
||||||
{
|
{
|
||||||
private readonly BlogService _blogService;
|
private readonly BlogService _blogService;
|
||||||
|
private readonly FaqService _faqService;
|
||||||
|
private readonly AnnouncementService _announcementService;
|
||||||
|
|
||||||
public List<BlogPost> Posts { get; set; } = [];
|
public List<RssItem> Items { get; set; } = [];
|
||||||
public string LastBuildDate { get; set; } = DateTime.UtcNow.ToString("R");
|
public string LastBuildDate { get; set; } = DateTime.UtcNow.ToString("R");
|
||||||
|
|
||||||
public RssModel(BlogService blogService)
|
public RssModel(
|
||||||
|
BlogService blogService,
|
||||||
|
FaqService faqService,
|
||||||
|
AnnouncementService announcementService)
|
||||||
{
|
{
|
||||||
_blogService = blogService;
|
_blogService = blogService;
|
||||||
|
_faqService = faqService;
|
||||||
|
_announcementService = announcementService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnGetAsync()
|
public async Task OnGetAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 최근 50개 블로그 포스트 (RSS는 일반적으로 최신 기사만 포함)
|
var blogTask = _blogService.GetPublishedPagedAsync(1, 50, categoryId: null, ct: default);
|
||||||
var (posts, _) = await _blogService.GetPublishedPagedAsync(1, 50, categoryId: null, ct: default);
|
var faqTask = _faqService.GetActiveAsync(HttpContext.RequestAborted);
|
||||||
Posts = posts.OrderByDescending(p => p.PublishedAt).ToList();
|
var announcementTask = _announcementService.GetActiveAsync(HttpContext.RequestAborted);
|
||||||
|
|
||||||
|
await Task.WhenAll(blogTask, faqTask, announcementTask);
|
||||||
|
|
||||||
|
var blogPosts = (await blogTask).Item1
|
||||||
|
.OrderByDescending(p => p.PublishedAt)
|
||||||
|
.Select(post => new RssItem(
|
||||||
|
Title: post.Title,
|
||||||
|
Link: $"https://www.taxbaik.com/blog/{Uri.EscapeDataString(post.Slug)}",
|
||||||
|
Description: post.Content ?? string.Empty,
|
||||||
|
PublishedAt: post.PublishedAt,
|
||||||
|
Guid: $"https://www.taxbaik.com/blog/{Uri.EscapeDataString(post.Slug)}"))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var faqs = (await faqTask)
|
||||||
|
.OrderByDescending(f => f.UpdatedAt)
|
||||||
|
.Select(faq => new RssItem(
|
||||||
|
Title: $"FAQ: {faq.Question}",
|
||||||
|
Link: "https://www.taxbaik.com/#faq",
|
||||||
|
Description: faq.Answer,
|
||||||
|
PublishedAt: faq.UpdatedAt,
|
||||||
|
Guid: $"https://www.taxbaik.com/rss/faq/{faq.Id}"))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var announcements = (await announcementTask)
|
||||||
|
.OrderByDescending(a => a.UpdatedAt)
|
||||||
|
.Select(announcement => new RssItem(
|
||||||
|
Title: $"공지: {announcement.Title}",
|
||||||
|
Link: "https://www.taxbaik.com/#top",
|
||||||
|
Description: announcement.Content ?? announcement.Title,
|
||||||
|
PublishedAt: announcement.UpdatedAt,
|
||||||
|
Guid: $"https://www.taxbaik.com/rss/announcement/{announcement.Id}"))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
Items = blogPosts
|
||||||
|
.Concat(faqs)
|
||||||
|
.Concat(announcements)
|
||||||
|
.OrderByDescending(item => item.PublishedAt ?? DateTime.MinValue)
|
||||||
|
.Take(50)
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// DB 연결 실패: 빈 포스트 목록 반환 (정적 피드 구조는 유지)
|
// DB 연결 실패: 빈 피드가 되지 않도록 로컬 서버 시간 기준 단일 항목을 유지하지는 않는다.
|
||||||
Posts = [];
|
Items = [];
|
||||||
System.Diagnostics.Debug.WriteLine($"RSS Feed: Blog posts load failed: {ex.Message}");
|
System.Diagnostics.Debug.WriteLine($"RSS Feed: Blog posts load failed: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
LastBuildDate = Posts.FirstOrDefault()?.PublishedAt?.ToString("R") ?? DateTime.UtcNow.ToString("R");
|
LastBuildDate = Items.FirstOrDefault()?.PublishedAt?.ToString("R") ?? DateTime.UtcNow.ToString("R");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed record RssItem(
|
||||||
|
string Title,
|
||||||
|
string Link,
|
||||||
|
string Description,
|
||||||
|
DateTime? PublishedAt,
|
||||||
|
string Guid);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user