Compare commits

..

2 Commits

Author SHA1 Message Date
dfb66b4301 refact: introduce YaeBlog.Abstractions layer.
All checks were successful
Build blog docker image / Build-Blog-Image (push) Successful in 1m0s
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-29 16:14:47 +08:00
05f99f4b79 feat: Use seperate application to parse and run command line.
Signed-off-by: jackfiled <xcrenchangjun@outlook.com>
2026-03-29 16:10:59 +08:00
47 changed files with 207 additions and 126 deletions

View File

@@ -12,6 +12,7 @@
<File Path="README.md" /> <File Path="README.md" />
</Folder> </Folder>
<Folder Name="/src/"> <Folder Name="/src/">
<Project Path="src/YaeBlog.Abstractions/YaeBlog.Abstractions.csproj" />
<Project Path="src/YaeBlog.Tests/YaeBlog.Tests.csproj" /> <Project Path="src/YaeBlog.Tests/YaeBlog.Tests.csproj" />
<Project Path="src/YaeBlog/YaeBlog.csproj" /> <Project Path="src/YaeBlog/YaeBlog.csproj" />
</Folder> </Folder>

View File

@@ -146,10 +146,6 @@ process {
dotnet run -- serve dotnet run -- serve
break break
} }
"list" {
dotnet run -- list
break
}
} }
} }

View File

@@ -1,7 +1,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Abstraction; namespace YaeBlog.Abstractions;
public interface IEssayContentService public interface IEssayContentService
{ {

View File

@@ -1,6 +1,6 @@
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Abstraction; namespace YaeBlog.Abstractions;
public interface IEssayScanService public interface IEssayScanService
{ {

View File

@@ -1,6 +1,6 @@
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Abstraction; namespace YaeBlog.Abstractions;
public interface IPostRenderProcessor public interface IPostRenderProcessor
{ {

View File

@@ -1,6 +1,6 @@
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Abstraction; namespace YaeBlog.Abstractions;
public interface IPreRenderProcessor public interface IPreRenderProcessor
{ {

View File

@@ -1,4 +1,4 @@
namespace YaeBlog.Models; namespace YaeBlog.Abstractions.Models;
/// <summary> /// <summary>
/// 单个博客文件的所有数据和元数据 /// 单个博客文件的所有数据和元数据

View File

@@ -1,7 +1,7 @@
using System.Collections; using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace YaeBlog.Models; namespace YaeBlog.Abstractions.Models;
public record BlogContents(ConcurrentBag<BlogContent> Drafts, ConcurrentBag<BlogContent> Posts) public record BlogContents(ConcurrentBag<BlogContent> Drafts, ConcurrentBag<BlogContent> Posts)
: IEnumerable<BlogContent> : IEnumerable<BlogContent>

View File

@@ -1,4 +1,4 @@
namespace YaeBlog.Models; namespace YaeBlog.Abstractions.Models;
public record BlogEssay( public record BlogEssay(
string Title, string Title,

View File

@@ -1,4 +1,4 @@
namespace YaeBlog.Models; namespace YaeBlog.Abstractions.Models;
public class BlogHeadline(string title, string selectorId) public class BlogHeadline(string title, string selectorId)
{ {

View File

@@ -1,6 +1,6 @@
using System.Text; using System.Text;
namespace YaeBlog.Models; namespace YaeBlog.Abstractions.Models;
public record BlogImageInfo(FileInfo File, long Width, long Height, string MineType, byte[] Content, bool IsUsed) public record BlogImageInfo(FileInfo File, long Width, long Height, string MineType, byte[] Content, bool IsUsed)
: IComparable<BlogImageInfo> : IComparable<BlogImageInfo>

View File

@@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace YaeBlog.Models; namespace YaeBlog.Abstractions.Models;
/// <summary> /// <summary>
/// 友链模型类 /// 友链模型类

View File

@@ -1,6 +1,6 @@
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
namespace YaeBlog.Models; namespace YaeBlog.Abstractions.Models;
public class EssayTag(string tagName) : IEquatable<EssayTag> public class EssayTag(string tagName) : IEquatable<EssayTag>
{ {

View File

@@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace YaeBlog.Models; namespace YaeBlog.Abstractions.Models;
public class GiteaOptions public class GiteaOptions
{ {

View File

@@ -1,4 +1,4 @@
namespace YaeBlog.Models; namespace YaeBlog.Abstractions.Models;
public record GitContributionItem(DateOnly Time, long ContributionCount) public record GitContributionItem(DateOnly Time, long ContributionCount)
{ {

View File

@@ -1,4 +1,4 @@
namespace YaeBlog.Models; namespace YaeBlog.Abstractions.Models;
public class MarkdownMetadata public class MarkdownMetadata
{ {

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -2,7 +2,7 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
using YaeBlog.Services; using YaeBlog.Services;
namespace YaeBlog.Tests; namespace YaeBlog.Tests;

View File

@@ -1,6 +1,6 @@
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Tests; namespace YaeBlog.Tests;

View File

@@ -1,6 +1,6 @@
@using Microsoft.Extensions.Options @using Microsoft.Extensions.Options
@using YaeBlog.Abstraction @using YaeBlog.Abstractions
@using YaeBlog.Models @using YaeBlog.Abstractions.Models
@inject IEssayContentService Contents @inject IEssayContentService Contents
@inject IOptions<BlogOptions> Options @inject IOptions<BlogOptions> Options

View File

@@ -1,5 +1,5 @@
@using System.Text.Encodings.Web @using System.Text.Encodings.Web
@using YaeBlog.Models @using YaeBlog.Abstractions.Models
<div class="flex flex-col p-3"> <div class="flex flex-col p-3">
<div class="text-3xl font-bold py-2"> <div class="text-3xl font-bold py-2">

View File

@@ -1,4 +1,4 @@
@using YaeBlog.Models @using YaeBlog.Abstractions.Models
@using YaeBlog.Services @using YaeBlog.Services
@inject GitHeapMapService GitHeapMapInstance @inject GitHeapMapService GitHeapMapInstance

View File

@@ -1,6 +1,6 @@
@page "/blog/archives" @page "/blog/archives"
@using YaeBlog.Abstraction @using YaeBlog.Abstractions
@using YaeBlog.Models @using YaeBlog.Abstractions.Models
@inject IEssayContentService Contents @inject IEssayContentService Contents

View File

@@ -1,6 +1,6 @@
@page "/blog" @page "/blog"
@using YaeBlog.Abstraction @using YaeBlog.Abstractions
@using YaeBlog.Models @using YaeBlog.Abstractions.Models
@inject IEssayContentService Contents @inject IEssayContentService Contents
@inject NavigationManager NavigationInstance @inject NavigationManager NavigationInstance

View File

@@ -1,7 +1,7 @@
@page "/blog/essays/{BlogKey}" @page "/blog/essays/{BlogKey}"
@using System.Text.Encodings.Web @using System.Text.Encodings.Web
@using YaeBlog.Abstraction @using YaeBlog.Abstractions
@using YaeBlog.Models @using YaeBlog.Abstractions.Models
@inject IEssayContentService Contents @inject IEssayContentService Contents
@inject NavigationManager NavigationInstance @inject NavigationManager NavigationInstance

View File

@@ -1,6 +1,6 @@
@page "/friends" @page "/friends"
@using Microsoft.Extensions.Options @using Microsoft.Extensions.Options
@using YaeBlog.Models @using YaeBlog.Abstractions.Models
@inject IOptions<BlogOptions> BlogOptionInstance @inject IOptions<BlogOptions> BlogOptionInstance
<PageTitle> <PageTitle>

View File

@@ -1,6 +1,6 @@
@page "/" @page "/"
@using YaeBlog.Abstraction @using YaeBlog.Abstractions
@using YaeBlog.Models @using YaeBlog.Abstractions.Models
@inject IEssayContentService EssayContentInstance @inject IEssayContentService EssayContentInstance
<PageTitle> <PageTitle>

View File

@@ -1,7 +1,7 @@
@page "/blog/tags/" @page "/blog/tags/"
@using System.Text.Encodings.Web @using System.Text.Encodings.Web
@using YaeBlog.Abstraction @using YaeBlog.Abstractions
@using YaeBlog.Models @using YaeBlog.Abstractions.Models
@inject IEssayContentService Contents @inject IEssayContentService Contents
@inject NavigationManager NavigationInstance @inject NavigationManager NavigationInstance

View File

@@ -1,26 +1,73 @@
using AngleSharp; using AngleSharp;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
using YaeBlog.Services; using YaeBlog.Services;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
using YaeBlog.Processors; using YaeBlog.Processors;
namespace YaeBlog.Extensions; namespace YaeBlog.Extensions;
public static class WebApplicationBuilderExtensions public static class HostApplicationBuilderExtensions
{ {
extension(WebApplicationBuilder builder) extension(IHostApplicationBuilder builder)
{ {
public WebApplicationBuilder AddYaeBlog() public ConsoleInfoService AddYaeCommand(string[] arguments)
{ {
builder.ConfigureOptions<BlogOptions>(BlogOptions.OptionName) builder.AddCommonServices();
.ConfigureOptions<GiteaOptions>(GiteaOptions.OptionName);
builder.Logging.AddFilter("Microsoft.Hosting.Lifetime", LogLevel.Warning);
builder.Services.AddTransient<ImageCompressService>();
builder.Services.AddHostedService<YaeCommandService>(provider =>
{
IEssayScanService essayScanService = provider.GetRequiredService<IEssayScanService>();
ImageCompressService imageCompressService = provider.GetRequiredService<ImageCompressService>();
ConsoleInfoService consoleInfoService = provider.GetRequiredService<ConsoleInfoService>();
IOptions<BlogOptions> blogOptions = provider.GetRequiredService<IOptions<BlogOptions>>();
ILogger<YaeCommandService> logger = provider.GetRequiredService<ILogger<YaeCommandService>>();
IHostApplicationLifetime hostApplicationLifetime =
provider.GetRequiredService<IHostApplicationLifetime>();
return new YaeCommandService(arguments, essayScanService, imageCompressService, consoleInfoService,
hostApplicationLifetime, blogOptions, logger);
});
ConsoleInfoService infoService = new();
builder.Services.AddSingleton<ConsoleInfoService>(_ => infoService);
return infoService;
}
private void AddCommonServices()
{
builder.Services.AddHttpClient() builder.Services.AddHttpClient()
.AddMarkdig() .AddMarkdig()
.AddYamlParser(); .AddYamlParser();
builder.ConfigureOptions<BlogOptions>(BlogOptions.OptionName)
.ConfigureOptions<GiteaOptions>(GiteaOptions.OptionName);
builder.Services.AddSingleton<IEssayScanService, EssayScanService>();
}
private IHostApplicationBuilder ConfigureOptions<T>(string optionSectionName) where T : class
{
builder.Services
.AddOptions<T>()
.Bind(builder.Configuration.GetSection(optionSectionName))
.ValidateDataAnnotations();
return builder;
}
}
extension(WebApplicationBuilder builder)
{
public WebApplicationBuilder AddYaeServer(ConsoleInfoService consoleInfoService)
{
builder.AddCommonServices();
builder.Services.AddSingleton<AngleSharp.IConfiguration>(_ => Configuration.Default) builder.Services.AddSingleton<AngleSharp.IConfiguration>(_ => Configuration.Default)
.AddSingleton<ConsoleInfoService>(_ => consoleInfoService)
.AddSingleton<IEssayScanService, EssayScanService>() .AddSingleton<IEssayScanService, EssayScanService>()
.AddSingleton<RendererService>() .AddSingleton<RendererService>()
.AddSingleton<IEssayContentService, EssayContentService>() .AddSingleton<IEssayContentService, EssayContentService>()
@@ -32,32 +79,9 @@ public static class WebApplicationBuilderExtensions
.AddTransient<BlogHotReloadService>() .AddTransient<BlogHotReloadService>()
.AddSingleton<GitHeapMapService>(); .AddSingleton<GitHeapMapService>();
return builder; builder.Services.AddHostedService<StartServerService>();
}
public WebApplicationBuilder AddYaeCommand(string[] arguments)
{
builder.Services.AddHostedService<YaeCommandService>(provider =>
{
IEssayScanService essayScanService = provider.GetRequiredService<IEssayScanService>();
IOptions<BlogOptions> blogOptions = provider.GetRequiredService<IOptions<BlogOptions>>();
ILogger<YaeCommandService> logger = provider.GetRequiredService<ILogger<YaeCommandService>>();
IHostApplicationLifetime applicationLifetime = provider.GetRequiredService<IHostApplicationLifetime>();
return new YaeCommandService(arguments, essayScanService, provider, blogOptions, logger,
applicationLifetime);
});
return builder; return builder;
} }
private WebApplicationBuilder ConfigureOptions<T>(string optionSectionName) where T : class
{
builder.Services
.AddOptions<T>()
.Bind(builder.Configuration.GetSection(optionSectionName))
.ValidateDataAnnotations();
return builder;
}
} }
} }

View File

@@ -1,4 +1,4 @@
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
using YaeBlog.Processors; using YaeBlog.Processors;
using YaeBlog.Services; using YaeBlog.Services;

View File

@@ -1,8 +1,8 @@
using AngleSharp; using AngleSharp;
using AngleSharp.Dom; using AngleSharp.Dom;
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
using YaeBlog.Extensions; using YaeBlog.Extensions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Processors; namespace YaeBlog.Processors;

View File

@@ -1,7 +1,7 @@
using AngleSharp; using AngleSharp;
using AngleSharp.Dom; using AngleSharp.Dom;
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Processors; namespace YaeBlog.Processors;

View File

@@ -1,9 +1,9 @@
using AngleSharp; using AngleSharp;
using AngleSharp.Dom; using AngleSharp.Dom;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
using YaeBlog.Core.Exceptions; using YaeBlog.Core.Exceptions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Processors; namespace YaeBlog.Processors;

View File

@@ -1,13 +1,25 @@
using YaeBlog.Components; using YaeBlog.Components;
using YaeBlog.Extensions; using YaeBlog.Extensions;
using YaeBlog.Services;
HostApplicationBuilder consoleBuilder = Host.CreateApplicationBuilder(args);
ConsoleInfoService consoleInfoService = consoleBuilder.AddYaeCommand(args);
IHost consoleApp = consoleBuilder.Build();
await consoleApp.RunAsync();
if (consoleInfoService.IsOneShotCommand)
{
return;
}
WebApplicationBuilder builder = WebApplication.CreateBuilder(args); WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents() builder.Services.AddRazorComponents()
.AddInteractiveServerComponents(); .AddInteractiveServerComponents();
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.AddYaeBlog(); builder.AddYaeServer(consoleInfoService);
builder.AddYaeCommand(args);
WebApplication application = builder.Build(); WebApplication application = builder.Build();

View File

@@ -1,5 +1,5 @@
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Services; namespace YaeBlog.Services;

View File

@@ -1,4 +1,4 @@
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
namespace YaeBlog.Services; namespace YaeBlog.Services;

View File

@@ -0,0 +1,14 @@
namespace YaeBlog.Services;
public enum ServerCommand
{
Serve,
Watch
}
public sealed class ConsoleInfoService
{
public bool IsOneShotCommand { get; set; }
public ServerCommand Command { get; set; }
}

View File

@@ -1,7 +1,7 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Services; namespace YaeBlog.Services;

View File

@@ -3,9 +3,9 @@ using System.Text.RegularExpressions;
using Imageflow.Bindings; using Imageflow.Bindings;
using Imageflow.Fluent; using Imageflow.Fluent;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
using YaeBlog.Core.Exceptions; using YaeBlog.Core.Exceptions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
using YamlDotNet.Core; using YamlDotNet.Core;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;

View File

@@ -1,7 +1,7 @@
using DotNext; using DotNext;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using YaeBlog.Extensions; using YaeBlog.Extensions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Services; namespace YaeBlog.Services;

View File

@@ -3,7 +3,7 @@ using System.Text.Json;
using DotNext; using DotNext;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using YaeBlog.Core.Exceptions; using YaeBlog.Core.Exceptions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Services; namespace YaeBlog.Services;

View File

@@ -1,7 +1,7 @@
using Imageflow.Fluent; using Imageflow.Fluent;
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
using YaeBlog.Core.Exceptions; using YaeBlog.Core.Exceptions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Services; namespace YaeBlog.Services;
@@ -34,6 +34,7 @@ public sealed class ImageCompressService(IEssayScanService essayScanService, ILo
if (needCompressContents.Count == 0) if (needCompressContents.Count == 0)
{ {
logger.LogInformation("No candidates found to be compressed.");
return; return;
} }
@@ -51,7 +52,7 @@ public sealed class ImageCompressService(IEssayScanService essayScanService, ILo
foreach (BlogImageInfo image in uncompressedImages) foreach (BlogImageInfo image in uncompressedImages)
{ {
logger.LogInformation("Uncompressed image: {} belonging to blog {}.", image.File.Name, logger.LogInformation("Uncompressed image: {filename} belonging to blog {blog}.", image.File.Name,
content.BlogName); content.BlogName);
} }
@@ -82,7 +83,7 @@ public sealed class ImageCompressService(IEssayScanService essayScanService, ILo
logger.LogInformation("Compression ratio: {}%.", (double)compressedSize / uncompressedSize * 100.0); logger.LogInformation("Compression ratio: {}%.", (double)compressedSize / uncompressedSize * 100.0);
if (dryRun is false) if (!dryRun)
{ {
await Task.WhenAll(from content in compressedContent await Task.WhenAll(from content in compressedContent
select essayScanService.SaveBlogContent(content, content.IsDraft)); select essayScanService.SaveBlogContent(content, content.IsDraft));

View File

@@ -1,5 +1,5 @@
using YaeBlog.Extensions; using YaeBlog.Extensions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Services namespace YaeBlog.Services
{ {

View File

@@ -3,9 +3,9 @@ using System.Diagnostics;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Markdig; using Markdig;
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
using YaeBlog.Core.Exceptions; using YaeBlog.Core.Exceptions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Services; namespace YaeBlog.Services;

View File

@@ -0,0 +1,27 @@
namespace YaeBlog.Services;
public sealed class StartServerService(ConsoleInfoService consoleInfoService,
RendererService rendererService,
BlogHotReloadService blogHotReloadService) : IHostedService
{
public async Task StartAsync(CancellationToken cancellationToken)
{
switch (consoleInfoService.Command)
{
case ServerCommand.Serve:
{
await rendererService.RenderAsync();
break;
}
case ServerCommand.Watch:
{
await blogHotReloadService.StartAsync(cancellationToken);
break;
}
default:
throw new InvalidOperationException();
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

View File

@@ -2,30 +2,31 @@
using System.CommandLine.Invocation; using System.CommandLine.Invocation;
using System.Text; using System.Text;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using YaeBlog.Abstraction; using YaeBlog.Abstractions;
using YaeBlog.Core.Exceptions; using YaeBlog.Core.Exceptions;
using YaeBlog.Models; using YaeBlog.Abstractions.Models;
namespace YaeBlog.Services; namespace YaeBlog.Services;
public class YaeCommandService( public sealed class YaeCommandService(
string[] arguments, string[] arguments,
IEssayScanService essayScanService, IEssayScanService essayScanService,
IServiceProvider serviceProvider, ImageCompressService imageCompressService,
ConsoleInfoService consoleInfoService,
IHostApplicationLifetime hostApplicationLifetime,
IOptions<BlogOptions> blogOptions, IOptions<BlogOptions> blogOptions,
ILogger<YaeCommandService> logger, ILogger<YaeCommandService> logger)
IHostApplicationLifetime applicationLifetime) : BackgroundService
: IHostedService
{ {
private readonly BlogOptions _blogOptions = blogOptions.Value; private readonly BlogOptions _blogOptions = blogOptions.Value;
private bool _oneShotCommandFlag = true; private bool _oneShotCommandFlag = true;
public async Task StartAsync(CancellationToken cancellationToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
RootCommand rootCommand = new("YaeBlog CLI"); RootCommand rootCommand = new("YaeBlog CLI");
RegisterServeCommand(rootCommand); RegisterServeCommand(rootCommand);
RegisterWatchCommand(rootCommand, cancellationToken); RegisterWatchCommand(rootCommand);
RegisterNewCommand(rootCommand); RegisterNewCommand(rootCommand);
RegisterUpdateCommand(rootCommand); RegisterUpdateCommand(rootCommand);
@@ -33,6 +34,10 @@ public class YaeCommandService(
RegisterPublishCommand(rootCommand); RegisterPublishCommand(rootCommand);
RegisterCompressCommand(rootCommand); RegisterCompressCommand(rootCommand);
// Shit code: wait for the application starting.
// If the command service finished early before the application starting, there will be an ugly exception.
await Task.Delay(500, stoppingToken);
logger.LogInformation("Running YaeBlog Command.");
int exitCode = await rootCommand.InvokeAsync(arguments); int exitCode = await rootCommand.InvokeAsync(arguments);
if (exitCode != 0) if (exitCode != 0)
@@ -40,13 +45,14 @@ public class YaeCommandService(
throw new BlogCommandException($"YaeBlog command exited with no-zero code {exitCode}"); throw new BlogCommandException($"YaeBlog command exited with no-zero code {exitCode}");
} }
if (_oneShotCommandFlag) consoleInfoService.IsOneShotCommand = _oneShotCommandFlag;
{
applicationLifetime.StopApplication();
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; if (!consoleInfoService.IsOneShotCommand)
{
logger.LogInformation("Start YaeBlog command: {}", consoleInfoService.Command);
}
hostApplicationLifetime.StopApplication();
}
private void RegisterServeCommand(RootCommand rootCommand) private void RegisterServeCommand(RootCommand rootCommand)
{ {
@@ -59,27 +65,23 @@ public class YaeCommandService(
rootCommand.SetHandler(HandleServeCommand); rootCommand.SetHandler(HandleServeCommand);
} }
private async Task HandleServeCommand(InvocationContext context) private Task HandleServeCommand(InvocationContext context)
{ {
_oneShotCommandFlag = false; _oneShotCommandFlag = false;
consoleInfoService.Command = ServerCommand.Serve;
logger.LogInformation("Failed to load cache, re-render essays."); return Task.CompletedTask;
RendererService rendererService = serviceProvider.GetRequiredService<RendererService>();
await rendererService.RenderAsync();
} }
private void RegisterWatchCommand(RootCommand rootCommand, CancellationToken cancellationToken) private void RegisterWatchCommand(RootCommand rootCommand)
{ {
Command command = new("watch", "Start a blog watcher that re-render when file changes."); Command command = new("watch", "Start a blog watcher that re-render when file changes.");
rootCommand.AddCommand(command); rootCommand.AddCommand(command);
command.SetHandler(async _ => command.SetHandler(_ =>
{ {
_oneShotCommandFlag = false; _oneShotCommandFlag = false;
consoleInfoService.Command = ServerCommand.Watch;
// BlogHotReloadService is derived from BackgroundService, but we do not let framework trigger it.
BlogHotReloadService blogHotReloadService = serviceProvider.GetRequiredService<BlogHotReloadService>();
await blogHotReloadService.StartAsync(cancellationToken);
}); });
} }
@@ -270,12 +272,6 @@ public class YaeCommandService(
getDefaultValue: () => false); getDefaultValue: () => false);
command.AddOption(dryRunOption); command.AddOption(dryRunOption);
command.SetHandler(HandleCompressCommand, dryRunOption); command.SetHandler(async dryRun => { await imageCompressService.Compress(dryRun); }, dryRunOption);
}
private async Task HandleCompressCommand(bool dryRun)
{
ImageCompressService imageCompressService = serviceProvider.GetRequiredService<ImageCompressService>();
await imageCompressService.Compress(dryRun);
} }
} }

View File

@@ -13,6 +13,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../third-party/BlazorSvgComponents/src/BlazorSvgComponents/BlazorSvgComponents.csproj" /> <ProjectReference Include="../../third-party/BlazorSvgComponents/src/BlazorSvgComponents/BlazorSvgComponents.csproj" />
<ProjectReference Include="..\YaeBlog.Abstractions\YaeBlog.Abstractions.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>