feat: add update time.
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 3m57s
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 3m57s
Make the link in blog blue.
This commit is contained in:
@@ -6,5 +6,11 @@ public interface IEssayScanService
|
|||||||
{
|
{
|
||||||
public Task<BlogContents> ScanContents();
|
public Task<BlogContents> ScanContents();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将对应的博客文章保存在磁盘上
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="content"></param>
|
||||||
|
/// <param name="isDraft">指定对应博客文章是否为草稿。因为BlogContent是不可变对象,因此提供该参数以方便publish的实现。</param>
|
||||||
|
/// <returns></returns>
|
||||||
public Task SaveBlogContent(BlogContent content, bool isDraft = true);
|
public Task SaveBlogContent(BlogContent content, bool isDraft = true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public sealed class YaeBlogCommand
|
|||||||
AddWatchCommand(_rootCommand);
|
AddWatchCommand(_rootCommand);
|
||||||
AddListCommand(_rootCommand);
|
AddListCommand(_rootCommand);
|
||||||
AddNewCommand(_rootCommand);
|
AddNewCommand(_rootCommand);
|
||||||
|
AddUpdateCommand(_rootCommand);
|
||||||
AddPublishCommand(_rootCommand);
|
AddPublishCommand(_rootCommand);
|
||||||
AddScanCommand(_rootCommand);
|
AddScanCommand(_rootCommand);
|
||||||
AddCompressCommand(_rootCommand);
|
AddCompressCommand(_rootCommand);
|
||||||
@@ -46,7 +47,7 @@ public sealed class YaeBlogCommand
|
|||||||
|
|
||||||
WebApplication application = builder.Build();
|
WebApplication application = builder.Build();
|
||||||
|
|
||||||
application.UseStaticFiles();
|
application.MapStaticAssets();
|
||||||
application.UseAntiforgery();
|
application.UseAntiforgery();
|
||||||
application.UseYaeBlog();
|
application.UseYaeBlog();
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ public sealed class YaeBlogCommand
|
|||||||
|
|
||||||
WebApplication application = builder.Build();
|
WebApplication application = builder.Build();
|
||||||
|
|
||||||
application.UseStaticFiles();
|
application.MapStaticAssets();
|
||||||
application.UseAntiforgery();
|
application.UseAntiforgery();
|
||||||
application.UseYaeBlog();
|
application.UseYaeBlog();
|
||||||
|
|
||||||
@@ -109,7 +110,7 @@ public sealed class YaeBlogCommand
|
|||||||
|
|
||||||
await essayScanService.SaveBlogContent(new BlogContent(
|
await essayScanService.SaveBlogContent(new BlogContent(
|
||||||
new FileInfo(Path.Combine(blogOption.Value.Root, "drafts", file + ".md")),
|
new FileInfo(Path.Combine(blogOption.Value.Root, "drafts", file + ".md")),
|
||||||
new MarkdownMetadata { Title = file, Date = DateTime.Now },
|
new MarkdownMetadata { Title = file, Date = DateTimeOffset.Now, UpdateTime = DateTimeOffset.Now },
|
||||||
string.Empty, true, [], []));
|
string.Empty, true, [], []));
|
||||||
|
|
||||||
Console.WriteLine($"Created new blog '{file}.");
|
Console.WriteLine($"Created new blog '{file}.");
|
||||||
@@ -117,6 +118,33 @@ public sealed class YaeBlogCommand
|
|||||||
new EssayScanServiceBinder());
|
new EssayScanServiceBinder());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AddUpdateCommand(RootCommand rootCommand)
|
||||||
|
{
|
||||||
|
Command newCommand = new("update", "Update the blog essay.");
|
||||||
|
rootCommand.AddCommand(newCommand);
|
||||||
|
|
||||||
|
Argument<string> filenameArgument = new(name: "blog name", description: "The blog filename to update.");
|
||||||
|
newCommand.AddArgument(filenameArgument);
|
||||||
|
|
||||||
|
newCommand.SetHandler(async (file, _, _, essayScanService) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine("HINT: The update command only consider published blogs.");
|
||||||
|
BlogContents contents = await essayScanService.ScanContents();
|
||||||
|
|
||||||
|
BlogContent? content = contents.Posts.FirstOrDefault(c => c.BlogName == file);
|
||||||
|
if (content is null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Target essay {file} is not exist.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
content.Metadata.UpdateTime = DateTimeOffset.Now;
|
||||||
|
await essayScanService.SaveBlogContent(content, content.IsDraft);
|
||||||
|
|
||||||
|
}, filenameArgument,
|
||||||
|
new BlogOptionsBinder(), new LoggerBinder<EssayScanService>(), new EssayScanServiceBinder());
|
||||||
|
}
|
||||||
|
|
||||||
private static void AddListCommand(RootCommand rootCommand)
|
private static void AddListCommand(RootCommand rootCommand)
|
||||||
{
|
{
|
||||||
Command command = new("list", "List all blogs");
|
Command command = new("list", "List all blogs");
|
||||||
|
|||||||
@@ -19,10 +19,6 @@
|
|||||||
|
|
||||||
<div class="px-6 pt-4 pb-2">
|
<div class="px-6 pt-4 pb-2">
|
||||||
<div class="flex flex-row gap-4">
|
<div class="flex flex-row gap-4">
|
||||||
<div class="font-light">
|
|
||||||
@(_essay!.PublishTime.ToString("yyyy-MM-dd"))
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@foreach (string tag in _essay!.Tags)
|
@foreach (string tag in _essay!.Tags)
|
||||||
{
|
{
|
||||||
<div class="text-sky-500">
|
<div class="text-sky-500">
|
||||||
@@ -32,10 +28,19 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="px-6 pt-2 pb-4">
|
<div class="font-light py-1">
|
||||||
<div class="font-light">
|
发布于: @(_essay.PublishTime.ToString("yyyy年MM月dd日 hh:mm:ss"))
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (_essay.UpdateTime != _essay.PublishTime)
|
||||||
|
{
|
||||||
|
<div class="font-light pb-1">
|
||||||
|
更新于: @(_essay.UpdateTime.ToString("yyyy年MM月dd日 hh:mm:ss"))
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="font-light pb-1">
|
||||||
总字数:@(_essay!.WordCount)字,预计阅读时间 @(_essay!.ReadTime)。
|
总字数:@(_essay!.WordCount)字,预计阅读时间 @(_essay!.ReadTime)。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ public class BlogEssay : IComparable<BlogEssay>
|
|||||||
|
|
||||||
public required bool IsDraft { get; init; }
|
public required bool IsDraft { get; init; }
|
||||||
|
|
||||||
public required DateTime PublishTime { get; init; }
|
public required DateTimeOffset PublishTime { get; init; }
|
||||||
|
|
||||||
|
public required DateTimeOffset UpdateTime { get; init; }
|
||||||
|
|
||||||
public required string Description { get; init; }
|
public required string Description { get; init; }
|
||||||
|
|
||||||
@@ -28,6 +30,7 @@ public class BlogEssay : IComparable<BlogEssay>
|
|||||||
FileName = FileName,
|
FileName = FileName,
|
||||||
IsDraft = IsDraft,
|
IsDraft = IsDraft,
|
||||||
PublishTime = PublishTime,
|
PublishTime = PublishTime,
|
||||||
|
UpdateTime = UpdateTime,
|
||||||
Description = Description,
|
Description = Description,
|
||||||
WordCount = WordCount,
|
WordCount = WordCount,
|
||||||
ReadTime = ReadTime,
|
ReadTime = ReadTime,
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ public class MarkdownMetadata
|
|||||||
{
|
{
|
||||||
public string? Title { get; set; }
|
public string? Title { get; set; }
|
||||||
|
|
||||||
public DateTime? Date { get; set; }
|
public DateTimeOffset Date { get; set; }
|
||||||
|
|
||||||
|
public DateTimeOffset UpdateTime { get; set; }
|
||||||
|
|
||||||
public List<string>? Tags { get; set; }
|
public List<string>? Tags { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor
|
|||||||
public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
|
public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
|
||||||
{
|
{
|
||||||
BrowsingContext context = new(Configuration.Default);
|
BrowsingContext context = new(Configuration.Default);
|
||||||
IDocument document = await context.OpenAsync(
|
IDocument document = await context.OpenAsync(req => req.Content(essay.HtmlContent));
|
||||||
req => req.Content(essay.HtmlContent));
|
|
||||||
|
|
||||||
ApplyGlobalCssStyles(document);
|
ApplyGlobalCssStyles(document);
|
||||||
BeatifyTable(document);
|
BeatifyTable(document);
|
||||||
@@ -36,6 +35,7 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor
|
|||||||
{ "h5", "text-lg font-bold py-1" },
|
{ "h5", "text-lg font-bold py-1" },
|
||||||
{ "p", "p-2" },
|
{ "p", "p-2" },
|
||||||
{ "img", "w-11/12 block mx-auto my-2 rounded-md shadow-md" },
|
{ "img", "w-11/12 block mx-auto my-2 rounded-md shadow-md" },
|
||||||
|
{ "a", "text-blue-600" }
|
||||||
};
|
};
|
||||||
|
|
||||||
private void ApplyGlobalCssStyles(IDocument document)
|
private void ApplyGlobalCssStyles(IDocument document)
|
||||||
|
|||||||
@@ -47,7 +47,10 @@ public partial class RendererService(
|
|||||||
Description = GetDescription(content),
|
Description = GetDescription(content),
|
||||||
WordCount = wordCount,
|
WordCount = wordCount,
|
||||||
ReadTime = CalculateReadTime(wordCount),
|
ReadTime = CalculateReadTime(wordCount),
|
||||||
PublishTime = content.Metadata.Date ?? DateTime.Now,
|
PublishTime = content.Metadata.Date == default ? DateTimeOffset.Now : content.Metadata.Date,
|
||||||
|
// 如果不存在最后的更新时间,就把更新时间设置为发布时间
|
||||||
|
UpdateTime =
|
||||||
|
content.Metadata.UpdateTime == default ? content.Metadata.Date : content.Metadata.UpdateTime,
|
||||||
HtmlContent = content.Content
|
HtmlContent = content.Content
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -182,7 +185,7 @@ public partial class RendererService(
|
|||||||
|
|
||||||
string description = builder.ToString();
|
string description = builder.ToString();
|
||||||
|
|
||||||
logger.LogDebug("Description of {} is {}.", content.BlogName,
|
logger.LogDebug("Description of {name} is {desc}.", content.BlogName,
|
||||||
description);
|
description);
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
@@ -193,7 +196,7 @@ public partial class RendererService(
|
|||||||
where char.IsLetterOrDigit(c)
|
where char.IsLetterOrDigit(c)
|
||||||
select c).Count();
|
select c).Count();
|
||||||
|
|
||||||
logger.LogDebug("Word count of {} is {}", content.BlogName,
|
logger.LogDebug("Word count of {blog} is {count}", content.BlogName,
|
||||||
count);
|
count);
|
||||||
return (uint)count;
|
return (uint)count;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user