dev: 渲染服务和文件内容服务
This commit is contained in:
parent
3d811770c4
commit
2d75c5c9a7
19
YaeBlog.Core/Exceptions/BlogFileException.cs
Normal file
19
YaeBlog.Core/Exceptions/BlogFileException.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace YaeBlog.Core.Exceptions;
|
||||
|
||||
public class BlogFileException : Exception
|
||||
{
|
||||
public BlogFileException() : base()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BlogFileException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BlogFileException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
31
YaeBlog.Core/Extensions/BlogApplicationExtension.cs
Normal file
31
YaeBlog.Core/Extensions/BlogApplicationExtension.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using Markdig;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using YaeBlog.Core.Builder;
|
||||
using YaeBlog.Core.Models;
|
||||
using YaeBlog.Core.Services;
|
||||
|
||||
namespace YaeBlog.Core.Extensions;
|
||||
|
||||
internal static class BlogApplicationExtension
|
||||
{
|
||||
public static BlogApplicationBuilder ConfigureBlogApplication(this BlogApplicationBuilder builder)
|
||||
{
|
||||
builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
|
||||
builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json",
|
||||
optional: true, reloadOnChange: true);
|
||||
builder.Configuration.AddEnvironmentVariables();
|
||||
|
||||
builder.Services.Configure<BlogOptions>(
|
||||
builder.Configuration.GetSection(BlogOptions.OptionName));
|
||||
|
||||
builder.Services.AddHostedService<BlogHostedService>();
|
||||
builder.Services.AddSingleton<EssayScanService>();
|
||||
builder.Services.AddSingleton<RendererService>();
|
||||
builder.Services.AddSingleton<MarkdownPipeline>(
|
||||
_ => builder.MarkdigPipelineBuilder.Build());
|
||||
builder.Services.AddSingleton<EssayContentService>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
8
YaeBlog.Core/Models/BlogContent.cs
Normal file
8
YaeBlog.Core/Models/BlogContent.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace YaeBlog.Core.Models;
|
||||
|
||||
public class BlogContent
|
||||
{
|
||||
public required string FileName { get; init; }
|
||||
|
||||
public required string FileContent { get; init; }
|
||||
}
|
10
YaeBlog.Core/Models/BlogEssay.cs
Normal file
10
YaeBlog.Core/Models/BlogEssay.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace YaeBlog.Core.Models;
|
||||
|
||||
public class BlogEssay
|
||||
{
|
||||
public required string Title { get; init; }
|
||||
|
||||
public required DateTime PublishTime { get; init; }
|
||||
|
||||
public required string HtmlContent { get; init; }
|
||||
}
|
10
YaeBlog.Core/Models/BlogOptions.cs
Normal file
10
YaeBlog.Core/Models/BlogOptions.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace YaeBlog.Core.Models;
|
||||
|
||||
public class BlogOptions
|
||||
{
|
||||
public const string OptionName = "Blog";
|
||||
|
||||
public required string Root { get; set; }
|
||||
}
|
24
YaeBlog.Core/Services/BlogHostedService.cs
Normal file
24
YaeBlog.Core/Services/BlogHostedService.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace YaeBlog.Core.Services;
|
||||
|
||||
public class BlogHostedService(
|
||||
ILogger<BlogHostedService> logger,
|
||||
RendererService rendererService) : IHostedService
|
||||
{
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogInformation("Welcome to YaeBlog!");
|
||||
|
||||
await rendererService.RenderAsync();
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogInformation("YaeBlog stopped!\nHave a nice day!");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
}
|
18
YaeBlog.Core/Services/EssayContentService.cs
Normal file
18
YaeBlog.Core/Services/EssayContentService.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Concurrent;
|
||||
using YaeBlog.Core.Models;
|
||||
|
||||
namespace YaeBlog.Core.Services;
|
||||
|
||||
public class EssayContentService
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, BlogEssay> _essays = new();
|
||||
|
||||
public bool TryGet(string key, out BlogEssay? essay)
|
||||
=> _essays.TryGetValue(key, out essay);
|
||||
|
||||
public bool TryAdd(string key, BlogEssay essay) => _essays.TryAdd(key, essay);
|
||||
|
||||
public IEnumerable<KeyValuePair<string, BlogEssay>> Essays => _essays;
|
||||
|
||||
public int Count => _essays.Count;
|
||||
}
|
57
YaeBlog.Core/Services/EssayScanService.cs
Normal file
57
YaeBlog.Core/Services/EssayScanService.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System.Collections.Concurrent;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using YaeBlog.Core.Exceptions;
|
||||
using YaeBlog.Core.Models;
|
||||
|
||||
namespace YaeBlog.Core.Services;
|
||||
|
||||
public class EssayScanService(
|
||||
IOptions<BlogOptions> blogOptions,
|
||||
ILogger<EssayContentService> logger)
|
||||
{
|
||||
private readonly BlogOptions _blogOptions = blogOptions.Value;
|
||||
|
||||
public async Task<List<BlogContent>> ScanAsync()
|
||||
{
|
||||
string root = Path.Combine(Environment.CurrentDirectory, _blogOptions.Root);
|
||||
DirectoryInfo rootDirectory = new(root);
|
||||
|
||||
if (!rootDirectory.Exists)
|
||||
{
|
||||
throw new BlogFileException($"'{root}' is not a directory.");
|
||||
}
|
||||
|
||||
List<FileInfo> markdownFiles = [];
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
foreach (FileInfo fileInfo in rootDirectory.EnumerateFiles())
|
||||
{
|
||||
if (fileInfo.Extension != ".md")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.LogDebug("Scan markdown file: {}.", fileInfo.Name);
|
||||
markdownFiles.Add(fileInfo);
|
||||
}
|
||||
});
|
||||
|
||||
ConcurrentBag<BlogContent> contents = [];
|
||||
|
||||
await Parallel.ForEachAsync(markdownFiles, async (info, token) =>
|
||||
{
|
||||
StreamReader reader = new(info.OpenRead());
|
||||
|
||||
BlogContent content = new()
|
||||
{
|
||||
FileName = info.Name, FileContent = await reader.ReadToEndAsync(token)
|
||||
};
|
||||
|
||||
contents.Add(content);
|
||||
});
|
||||
|
||||
return contents.ToList();
|
||||
}
|
||||
}
|
35
YaeBlog.Core/Services/RendererService.cs
Normal file
35
YaeBlog.Core/Services/RendererService.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.Collections.Concurrent;
|
||||
using Markdig;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using YaeBlog.Core.Exceptions;
|
||||
using YaeBlog.Core.Models;
|
||||
|
||||
namespace YaeBlog.Core.Services;
|
||||
|
||||
public class RendererService(ILogger<RendererService> logger,
|
||||
EssayScanService essayScanService,
|
||||
MarkdownPipeline markdownPipeline,
|
||||
EssayContentService essayContentService)
|
||||
{
|
||||
public async Task RenderAsync()
|
||||
{
|
||||
List<BlogContent> contents = await essayScanService.ScanAsync();
|
||||
|
||||
Parallel.ForEach(contents, content =>
|
||||
{
|
||||
logger.LogDebug("Render markdown file {}.", content.FileName);
|
||||
BlogEssay essay = new()
|
||||
{
|
||||
Title = content.FileName,
|
||||
PublishTime = DateTime.Now,
|
||||
HtmlContent = Markdown.ToHtml(content.FileContent, markdownPipeline)
|
||||
};
|
||||
|
||||
if (!essayContentService.TryAdd(essay.Title, essay))
|
||||
{
|
||||
throw new BlogFileException(
|
||||
$"There are two essays with the same name: '{content.FileName}'.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user