新增rss功能
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
string Script,
|
||||
int PageSizeOfHomepage,
|
||||
int PageSizeOfListpage,
|
||||
int RssGenerationIntervalHours,
|
||||
string ArticleRoute,
|
||||
string PageRoute,
|
||||
string CategoryRoute,
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
|
||||
namespace Yes.Application.Blogs
|
||||
{
|
||||
|
||||
public record GetRssQuery() : IRequest<GetRssQueryResponse>;
|
||||
|
||||
public record GetRssQueryResponse(string Xml);
|
||||
|
||||
public class GetRssQueryHandler(
|
||||
IOptionsMonitor<BlogSettings> options,
|
||||
BlogDbContext db,
|
||||
IMapper mapper) : IRequestHandler<GetRssQuery, GetRssQueryResponse>
|
||||
{
|
||||
private readonly BlogSettings _settings = options.CurrentValue;
|
||||
private readonly BlogDbContext _db = db;
|
||||
public async Task<GetRssQueryResponse> Handle(GetRssQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var queryArticle = _db.Articles.Where(x => x.Type == ArticleTypeEnum.Article && x.Status == ArticleStatusEnum.Normal).AsQueryable();
|
||||
var pubDate = queryArticle.Any() ? _db.Articles.Min(x => x.PublishDate) : DateTime.MinValue;
|
||||
|
||||
var channel = new Channel
|
||||
{
|
||||
Title = _settings.Name,
|
||||
Link = _settings.Url,
|
||||
Description = _settings.Description,
|
||||
Language = "zh-CN",
|
||||
FeedUrl = $"{_settings.Url}/feed/",
|
||||
PubDate = pubDate
|
||||
};
|
||||
|
||||
// 添加扩展元素(示例)
|
||||
channel.Extensions.Add(new XElement("generator", _settings.Name));
|
||||
channel.Extensions.Add(new XElement("docs", "http://www.rssboard.org/rss-specification"));
|
||||
|
||||
var query = from article in queryArticle
|
||||
join categoryTemp in _db.Categories.AsNoTracking() on article.CategoryId equals categoryTemp.Id into tempCategory
|
||||
from category in tempCategory.DefaultIfEmpty()
|
||||
join userTemp in _db.Users.AsNoTracking() on article.UserId equals userTemp.Id into tempUser
|
||||
from user in tempUser.DefaultIfEmpty()
|
||||
orderby article.Id descending
|
||||
select new ArticleModel
|
||||
{
|
||||
Id = article.Id,
|
||||
CanComment = article.CanComment,
|
||||
CommentCount = article.CommentCount,
|
||||
CategoryId = article.CategoryId,
|
||||
ModifyDate = article.ModifyDate.ToSafeTime(),
|
||||
PublishDate = article.PublishDate.ToSafeTime(),
|
||||
CreateDate = article.CreateDate.ToSafeTime(),
|
||||
Content = article.Content,
|
||||
CoverUrl = article.CoverUrl,
|
||||
Slug = article.Slug,
|
||||
ReadCount = article.ReadCount,
|
||||
Title = article.Title,
|
||||
Summary = article.Summary,
|
||||
UserId = article.UserId,
|
||||
Category = new CategoryModel
|
||||
{
|
||||
CoverUrl = category.CoverUrl,
|
||||
Description = category.Description,
|
||||
Id = category.Id,
|
||||
Name = category.Name,
|
||||
Slug = category.Slug
|
||||
}
|
||||
};
|
||||
|
||||
var items = query.Select(x => new Item
|
||||
{
|
||||
Content = x.Content,
|
||||
Title = x.Title,
|
||||
Url = _settings.ArticleRoute.ToRoute(x.Category, x),
|
||||
Description = x.Summary,
|
||||
CommentsCount = x.CommentCount,
|
||||
Creator = x.UserId.ToString(),
|
||||
PublishDate = x.PublishDate,
|
||||
CommentsUrl = $"{_settings.ArticleRoute.ToRoute(x.Category, x)}#comments",
|
||||
CommentRssUrl = $"{_settings.Url}/feed/"
|
||||
});
|
||||
|
||||
|
||||
var generator = new RssGenerator();
|
||||
string xml = generator.GenerateRss(channel, items);
|
||||
|
||||
return new GetRssQueryResponse(xml);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
|
||||
namespace Yes.Application.Core.HostedServices
|
||||
{
|
||||
public class HangfireJobRegistrationService : BackgroundService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly BlogSettings _settings;
|
||||
|
||||
public HangfireJobRegistrationService(
|
||||
IServiceProvider serviceProvider,
|
||||
IOptionsMonitor<BlogSettings> options)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_settings = options.CurrentValue;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var recurringJobManager = scope.ServiceProvider.GetRequiredService<IRecurringJobManager>();
|
||||
|
||||
var intervalHours = _settings.RssGenerationIntervalHours;
|
||||
|
||||
//recurringJobManager.AddOrUpdate<RssGenerationJob>(
|
||||
// nameof(RssGenerationJob),
|
||||
// x => x.Execute(),
|
||||
// Cron.Hourly(intervalHours)
|
||||
//);
|
||||
|
||||
recurringJobManager.AddOrUpdate<RssGenerationJob>(
|
||||
nameof(RssGenerationJob),
|
||||
x => x.Execute(),
|
||||
Cron.Minutely
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
100
Yes.Application/Core/HostedServices/RssGenerationJob.cs
Normal file
100
Yes.Application/Core/HostedServices/RssGenerationJob.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
namespace Yes.Application.Core.HostedServices
|
||||
{
|
||||
public class RssGenerationJob
|
||||
{
|
||||
private readonly BlogSettings _settings;
|
||||
private readonly BlogDbContext _db;
|
||||
private readonly ILogger<RssGenerationJob> _logger;
|
||||
private readonly IWebHostEnvironment _env;
|
||||
public RssGenerationJob(
|
||||
IOptionsMonitor<BlogSettings> options,
|
||||
BlogDbContext db,
|
||||
IWebHostEnvironment env,
|
||||
ILogger<RssGenerationJob> logger)
|
||||
{
|
||||
_settings = options.CurrentValue;
|
||||
_db = db;
|
||||
_env = env;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Execute()
|
||||
{
|
||||
try
|
||||
{
|
||||
var queryArticle = _db.Articles.Where(x => x.Type == ArticleTypeEnum.Article && x.Status == ArticleStatusEnum.Normal).AsQueryable();
|
||||
var pubDate = queryArticle.Any() ? _db.Articles.Min(x => x.PublishDate) : DateTime.MinValue;
|
||||
|
||||
var channel = new Channel
|
||||
{
|
||||
Title = _settings.Name,
|
||||
Link = _settings.Url,
|
||||
Description = _settings.Description,
|
||||
Language = "zh-CN",
|
||||
FeedUrl = $"{_settings.Url}/feed/",
|
||||
PubDate = pubDate
|
||||
};
|
||||
|
||||
// 添加扩展元素(示例)
|
||||
channel.Extensions.Add(new XElement("generator", _settings.Name));
|
||||
channel.Extensions.Add(new XElement("docs", "http://www.rssboard.org/rss-specification"));
|
||||
|
||||
var query = from article in queryArticle
|
||||
join categoryTemp in _db.Categories.AsNoTracking() on article.CategoryId equals categoryTemp.Id into tempCategory
|
||||
from category in tempCategory.DefaultIfEmpty()
|
||||
join userTemp in _db.Users.AsNoTracking() on article.UserId equals userTemp.Id into tempUser
|
||||
from user in tempUser.DefaultIfEmpty()
|
||||
orderby article.Id descending
|
||||
select new ArticleModel
|
||||
{
|
||||
Id = article.Id,
|
||||
CanComment = article.CanComment,
|
||||
CommentCount = article.CommentCount,
|
||||
CategoryId = article.CategoryId,
|
||||
ModifyDate = article.ModifyDate.ToSafeTime(),
|
||||
PublishDate = article.PublishDate.ToSafeTime(),
|
||||
CreateDate = article.CreateDate.ToSafeTime(),
|
||||
Content = article.Content,
|
||||
CoverUrl = article.CoverUrl,
|
||||
Slug = article.Slug,
|
||||
ReadCount = article.ReadCount,
|
||||
Title = article.Title,
|
||||
Summary = article.Summary,
|
||||
UserId = article.UserId,
|
||||
Category = new CategoryModel
|
||||
{
|
||||
CoverUrl = category.CoverUrl,
|
||||
Description = category.Description,
|
||||
Id = category.Id,
|
||||
Name = category.Name,
|
||||
Slug = category.Slug
|
||||
}
|
||||
};
|
||||
|
||||
var items = query.Select(x => new Item
|
||||
{
|
||||
Content = x.Content,
|
||||
Title = x.Title,
|
||||
Url = $"{_settings.Url}/{_settings.ArticleRoute.ToRoute(x.Category, x)}",
|
||||
Description = x.Summary,
|
||||
CommentsCount = x.CommentCount,
|
||||
Creator = x.UserId.ToString(),
|
||||
PublishDate = x.PublishDate,
|
||||
CommentsUrl = $"{_settings.Url}/{_settings.ArticleRoute.ToRoute(x.Category, x)}#comments",
|
||||
CommentRssUrl = $"{_settings.Url}/feed/"
|
||||
});
|
||||
|
||||
|
||||
var generator = new RssGenerator();
|
||||
string xml = generator.GenerateRss(channel, items);
|
||||
|
||||
|
||||
File.WriteAllText(Path.Combine(_env.WebRootPath, "feed.xml"), xml);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "RSS generation failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,5 +39,6 @@
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,3 +40,8 @@ global using System.Text;
|
||||
global using Yes.Infrastructure.Authorizations.Identity.Authroizations;
|
||||
global using System.Xml.Linq;
|
||||
global using Yes.Infrastructure.Rss;
|
||||
|
||||
global using Microsoft.Extensions.Configuration;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
global using Hangfire;
|
||||
global using Microsoft.Extensions.Hosting;
|
||||
@@ -11,6 +11,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.18" />
|
||||
<PackageReference Include="Hangfire.Core" Version="1.8.18" />
|
||||
<PackageReference Include="Hangfire.MySqlStorage" Version="2.0.3" />
|
||||
<PackageReference Include="Hangfire.PostgreSql" Version="1.20.12" />
|
||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.18" />
|
||||
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Qiniu" Version="8.7.0" />
|
||||
|
||||
@@ -101,6 +101,11 @@
|
||||
<label>每页文章数目</label>
|
||||
<input type="text" class="form-control" x-model="config.pageSizeOfListpage">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>生成RSS频率(小时)</label>
|
||||
<input type="text" class="form-control" x-model="config.rssGenerationIntervalHours">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-9" x-show="tab=='link'">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
namespace Yes.Blog
|
||||
|
||||
namespace Yes.Blog
|
||||
{
|
||||
public static class DependencyInjection
|
||||
{
|
||||
@@ -30,6 +31,8 @@
|
||||
|
||||
services.AddEndpointsApiExplorer();
|
||||
|
||||
services.AddHangfire(configuration);
|
||||
|
||||
services.AddOpenApi(options =>
|
||||
{
|
||||
options.AddDocumentTransformer((document, context, cancellationToken) =>
|
||||
@@ -240,7 +243,7 @@
|
||||
|
||||
}
|
||||
|
||||
|
||||
app.MapHangfireDashboard();
|
||||
|
||||
return app;
|
||||
}
|
||||
@@ -273,5 +276,63 @@
|
||||
return app;
|
||||
}
|
||||
|
||||
|
||||
public static IServiceCollection AddHangfire(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
Enum.TryParse(configuration["DatabaseType"], out DatabaseTypeEnum databaseType);
|
||||
var connectionString = configuration["ConnectionString"] ?? "";
|
||||
var dbName = configuration["DatabaseName"] ?? "";
|
||||
|
||||
var builder = ConnectionStringBuilderFactory.CreateStringBuilder(databaseType, connectionString);
|
||||
builder.SetDatabaseName(dbName);
|
||||
|
||||
connectionString = builder.GetConnectionString();
|
||||
|
||||
// Add Hangfire services
|
||||
services.AddHangfire(config =>
|
||||
{
|
||||
switch (databaseType)
|
||||
{
|
||||
case DatabaseTypeEnum.SqlServer:
|
||||
config.UseSqlServerStorage(connectionString);
|
||||
break;
|
||||
case DatabaseTypeEnum.MySql:
|
||||
config.UseStorage(
|
||||
new MySqlStorage(
|
||||
connectionString,
|
||||
new MySqlStorageOptions
|
||||
{
|
||||
TransactionIsolationLevel = IsolationLevel.ReadCommitted,
|
||||
QueuePollInterval = TimeSpan.FromSeconds(15),
|
||||
JobExpirationCheckInterval = TimeSpan.FromHours(1),
|
||||
CountersAggregateInterval = TimeSpan.FromMinutes(5),
|
||||
PrepareSchemaIfNecessary = true,
|
||||
DashboardJobListLimit = 50000,
|
||||
TransactionTimeout = TimeSpan.FromMinutes(1),
|
||||
TablesPrefix = "Hangfire"
|
||||
}));
|
||||
break;
|
||||
case DatabaseTypeEnum.PostgreSQL:
|
||||
config.UsePostgreSqlStorage(c =>c.UseNpgsqlConnection(connectionString));
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Unsupported database type: {databaseType}");
|
||||
}
|
||||
|
||||
config.SetDataCompatibilityLevel(CompatibilityLevel.Version_180).UseSimpleAssemblyNameTypeSerializer().UseRecommendedSerializerSettings();
|
||||
});
|
||||
|
||||
|
||||
// Add the processing server as IHostedService
|
||||
services.AddHangfireServer();
|
||||
|
||||
|
||||
// var minute = 60;
|
||||
//int.TryParse(configuration["DatabaseName"], out minute);
|
||||
// RecurringJob.AddOrUpdate<RssGenerationJob>("dynamic-job", x => x.Execute(), Cron.Minutely);
|
||||
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
string Script,
|
||||
int PageSizeOfHomepage,
|
||||
int PageSizeOfListpage,
|
||||
string ArticleRoute,
|
||||
int RssGenerationIntervalHours,
|
||||
string ArticleRoute,
|
||||
string PageRoute,
|
||||
string CategoryRoute,
|
||||
string SearchRoute,
|
||||
@@ -33,6 +34,10 @@
|
||||
RuleFor(x => x.CategoryRoute).NotEmpty().WithMessage("分类页路径不能为空!");
|
||||
RuleFor(x => x.SearchRoute).NotEmpty().WithMessage("搜索页路径不能为空!");
|
||||
RuleFor(x => x.TagRoute).NotEmpty().WithMessage("标签页路径不能为空!");
|
||||
|
||||
RuleFor(x => x.PageSizeOfHomepage).GreaterThan(0).WithMessage("主页每页文章数必须大于0!");
|
||||
RuleFor(x => x.PageSizeOfListpage).GreaterThan(0).WithMessage("列表页每页文章数必须大于0!");
|
||||
RuleFor(x => x.RssGenerationIntervalHours).GreaterThan(0).WithMessage("RSS生成频率必须大于0!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
namespace Yes.Blog.Endpoints.Blogs
|
||||
{
|
||||
public class RssEndpoint : BlogEndpointScheme, IEndpoint
|
||||
{
|
||||
public void Map(IEndpointRouteBuilder app) => app.MapGet("feed", Handle).AllowAnonymous();
|
||||
|
||||
private async Task<Microsoft.AspNetCore.Http.IResult> Handle(
|
||||
IMediator mediator,
|
||||
IMapper mapper,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await mediator.Send(new GetRssQuery(), cancellationToken);
|
||||
|
||||
return Results.Text(response.Xml, "application/rss+xml; charset=utf-8");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -46,4 +46,9 @@ global using Microsoft.AspNetCore.Authorization;
|
||||
global using Yes.Application.Installs;
|
||||
global using Scalar.AspNetCore;
|
||||
global using Yes.Blog.Areas.Admin.Pages;
|
||||
global using Yes.Infrastructure.Authorizations.User;
|
||||
global using Yes.Infrastructure.Authorizations.User;
|
||||
global using System.Transactions;
|
||||
global using Hangfire;
|
||||
global using Hangfire.MySql;
|
||||
global using Hangfire.PostgreSql;
|
||||
global using Yes.Infrastructure.Data.ConnectionStringBuilders;
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
using Hangfire;
|
||||
|
||||
namespace Yes.Blog
|
||||
{
|
||||
public class Program
|
||||
@@ -22,6 +24,8 @@ namespace Yes.Blog
|
||||
|
||||
app.MapOpenApi();
|
||||
app.MapScalarApiReference();
|
||||
|
||||
app.UseHangfireDashboard();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +38,8 @@ namespace Yes.Blog
|
||||
|
||||
app.UseGlobalExceptionHandler();
|
||||
|
||||
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
|
||||
@@ -39,6 +39,11 @@
|
||||
<Content Remove="appsettings.json" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="files\themes\default\rss.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation" Version="12.0.0" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.0.0" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div id="comment">
|
||||
<div id="comment">
|
||||
<section class="">
|
||||
<div class="">
|
||||
|
||||
@@ -70,14 +70,6 @@
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% if Model.Comments.HasPreviousPage %}
|
||||
<a class="enabled current" href="?page={{ Model.Comments.PreviousPage }}">上一页</a>
|
||||
{% endif %}
|
||||
|
||||
{% if Model.Comments.HasNextPage %}
|
||||
<a class="enabled current" href="?page={{ Model.Comments.NextPage }}">下一页</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
@@ -91,31 +83,34 @@
|
||||
{% section scripts %}
|
||||
<script>
|
||||
document.getElementById("createComment").addEventListener("click", function(){
|
||||
var nickName = document.getElementsByName('nickName')[0].value;
|
||||
var email = document.getElementsByName('email')[0].value;
|
||||
var content = document.getElementsByName('content')[0].value;
|
||||
var url = document.getElementsByName('url')[0].value;
|
||||
var targetId = document.getElementsByName('targetId')[0].value;
|
||||
axios.post("/comments",{
|
||||
targetId:targetId,
|
||||
nickName:nickName,
|
||||
email: email,
|
||||
content: content,
|
||||
url: url
|
||||
}).catch(function (error) {
|
||||
var errorMessage = "";
|
||||
if (error.response) {
|
||||
if (error.response.status == 422) {
|
||||
const errors = Object.values(error.response.data.errors);
|
||||
const values = Array.from(new Set(errors.flat()));
|
||||
errorMessage = values.join(' ');
|
||||
} else {
|
||||
errorMessage = error.response.data.detail;
|
||||
}
|
||||
} else {
|
||||
errorMessage = "您的请求未能成功处理,可能是服务器开小差了!";
|
||||
}
|
||||
document.getElementById('comment-error').innerHTML = '<p>'+errorMessage+'</p>';
|
||||
var nickName = document.getElementsByName('nickName')[0].value;
|
||||
var email = document.getElementsByName('email').value;
|
||||
var content = document.getElementsByName('content').value;
|
||||
var url = document.getElementsByName('url').value;
|
||||
var targetId = document.getElementsByName('targetId').value;
|
||||
axios.post("/comments",{
|
||||
targetId:targetId,
|
||||
nickName:nickName,
|
||||
email: email,
|
||||
content: content,
|
||||
url: url
|
||||
}).catch(function (error) {
|
||||
var errorMessage = "";
|
||||
if (error.response) {
|
||||
if (error.response.status == 422) {
|
||||
const errors = Object.values(error.response.data.errors);
|
||||
console.log(errors);
|
||||
const values = Array.from(new Set(errors.flat()));
|
||||
console.log(values);
|
||||
errorMessage = values.join(' ');
|
||||
} else {
|
||||
errorMessage = error.response.data.detail;
|
||||
}
|
||||
} else {
|
||||
errorMessage = "您的请求未能成功处理,可能是服务器开小差了!";
|
||||
}
|
||||
document.getElementById('comment-error').innerHTML = '<p>'+errorMessage+'</p>';
|
||||
console.log(errorMessage);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<span class="footer-item">
|
||||
© {{ "now" | format_date: "yyyy" }}
|
||||
|
||||
<a href="{{ Model.Blog.Url }}" title="{{ Model.Blog.Name }}">{{ Model.Blog.Name }}</a> Powered by <a href="https://blog.yescent.com">Hyrule</a>
|
||||
<a href="{{ Model.Blog.Url }}" title="{{ Model.Blog.Name }}">{{ Model.Blog.Name }}</a> Powered by .NET 8.0
|
||||
</span>
|
||||
<span class="footer-item">
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
{% partial '_SiteCategories' %}
|
||||
{% partial '_Tags' %}
|
||||
{% partial '_Archive' %}
|
||||
{% partial '_Rss' %}
|
||||
|
||||
</div>
|
||||
</main>
|
||||
{% partial '_Footer' %}
|
||||
|
||||
5
Yes.Blog/wwwroot/defaulttheme/_Rss.liquid
Normal file
5
Yes.Blog/wwwroot/defaulttheme/_Rss.liquid
Normal file
@@ -0,0 +1,5 @@
|
||||
<div class="side-tags">
|
||||
<a href="/feed.xml" target="_blank">
|
||||
<i class="i-rss"></i>RSS 订阅
|
||||
</a>
|
||||
</div>
|
||||
@@ -842,7 +842,6 @@ pre .copyCodeButton:hover {
|
||||
#comment-error p{
|
||||
color:#ff0000;
|
||||
font-size:12px;
|
||||
padding:5px;
|
||||
}
|
||||
|
||||
|
||||
@@ -875,4 +874,12 @@ pre .copyCodeButton:hover {
|
||||
text-decoration: none;
|
||||
color: var(--color-fg-hyperlink);
|
||||
padding-left:10px;
|
||||
}
|
||||
.i-rss {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url("../rss.png") no-repeat;
|
||||
margin-right: 8px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
BIN
Yes.Blog/wwwroot/defaulttheme/rss.png
Normal file
BIN
Yes.Blog/wwwroot/defaulttheme/rss.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 324 B |
24
Yes.Blog/wwwroot/feed.xml
Normal file
24
Yes.Blog/wwwroot/feed.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-16"?>
|
||||
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
|
||||
<channel>
|
||||
<title>我的博客</title>
|
||||
<link>https://blog.yescent.com</link>
|
||||
<description>Hyrule 是一个简洁的开源博客系统</description>
|
||||
<language>zh-CN</language>
|
||||
<lastBuildDate>Thu, 15 May 2025 04:52:30 GMT</lastBuildDate>
|
||||
<pubDate>Wed, 07 May 2025 10:02:22 GMT</pubDate>
|
||||
<atom:link href="https://blog.yescent.com/feed/" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>欢迎使用 Hyrule</title>
|
||||
<link>https://blog.yescent.com//post/start.html</link>
|
||||
<guid isPermaLink="true">https://blog.yescent.com//post/start.html</guid>
|
||||
<pubDate>周六, 10 5月 2025 16:15:05 +08:00</pubDate>
|
||||
<dc:creator>1</dc:creator>
|
||||
<description><![CDATA[如果您看到这篇文章,表示您的 blog 已经安装成功.]]></description>
|
||||
<content:encoded xml:lang="zh-CN"><![CDATA[如果您看到这篇文章,表示您的 blog 已经安装成功.]]></content:encoded>
|
||||
<slash:comments>0</slash:comments>
|
||||
<comments>https://blog.yescent.com//post/start.html#comments</comments>
|
||||
<wfw:commentRss>https://blog.yescent.com/feed/</wfw:commentRss>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
@@ -59,6 +59,9 @@
|
||||
public string SecretKey { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
public int TokenLifetimeMinutes { get; set; } = 60 * 24 * 7;
|
||||
|
||||
|
||||
public int RssGenerationIntervalHours { get; set; } = 12;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{
|
||||
public static class AuthenticationExtensions
|
||||
{
|
||||
public static IServiceCollection AddAdminAuthentication(this IServiceCollection services,IConfiguration configuration)
|
||||
public static IServiceCollection AddAdminAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
var securityKeyBase64 = JwtHelper.GenerateKey(configuration["SecretKey"] ?? "");
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{
|
||||
builder = new MySql.Data.MySqlClient.MySqlConnectionStringBuilder
|
||||
{
|
||||
|
||||
AllowUserVariables = true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
{
|
||||
ConnectionString = connectionString
|
||||
};
|
||||
builder.AllowUserVariables = true;
|
||||
}
|
||||
|
||||
public MySqlConnectionStringBuilder(string server, string database, string userId, string password)
|
||||
@@ -27,7 +28,8 @@
|
||||
Server = server,
|
||||
Database = database,
|
||||
UserID = userId,
|
||||
Password = password
|
||||
Password = password,
|
||||
AllowUserVariables = true
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user