refact: Fully refactor the conceptional structure and functional behaviour.
This commit is contained in:
parent
257f5d94ee
commit
2efe2fd5cd
|
@ -1,41 +0,0 @@
|
|||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace YaeBlog.Core.Builder;
|
||||
|
||||
public class BlogApplication : IHost
|
||||
{
|
||||
private readonly IHost _host;
|
||||
|
||||
internal BlogApplication(IHost host)
|
||||
{
|
||||
_host = host;
|
||||
}
|
||||
|
||||
public static BlogApplicationBuilder Create(string[] args)
|
||||
{
|
||||
BlogApplicationOptions options = new() { Args = args };
|
||||
return new BlogApplicationBuilder(options);
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken = new())
|
||||
{
|
||||
return _host.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken = new())
|
||||
{
|
||||
return _host.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public IServiceProvider Services => _host.Services;
|
||||
|
||||
public Task RunAsync() => _host.RunAsync();
|
||||
|
||||
public void Run() => _host.Run();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_host.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
using Markdig;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Diagnostics.Metrics;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using YaeBlog.Core.Extensions;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace YaeBlog.Core.Builder;
|
||||
|
||||
public sealed class BlogApplicationBuilder : IHostApplicationBuilder
|
||||
{
|
||||
private readonly HostApplicationBuilder _hostApplicationBuilder;
|
||||
|
||||
internal List<Action<WebApplicationBuilder>> WebApplicationBuilderConfigurations { get; } = [];
|
||||
|
||||
internal List<Action<WebApplication>> WebApplicationConfigurations { get; } = [];
|
||||
|
||||
public MarkdownPipelineBuilder MarkdigPipelineBuilder { get; }
|
||||
|
||||
public DeserializerBuilder YamlDeserializerBuilder { get; }
|
||||
|
||||
internal BlogApplicationBuilder(BlogApplicationOptions options)
|
||||
{
|
||||
ConfigurationManager configuration = new();
|
||||
MarkdigPipelineBuilder = new MarkdownPipelineBuilder();
|
||||
YamlDeserializerBuilder = new DeserializerBuilder();
|
||||
|
||||
_hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings
|
||||
{
|
||||
Args = options.Args, Configuration = configuration
|
||||
});
|
||||
}
|
||||
|
||||
public BlogApplication Build()
|
||||
{
|
||||
this.ConfigureDefaultBlogApplicationBuilder();
|
||||
BlogApplication application = new(_hostApplicationBuilder.Build());
|
||||
application.ConfigureDefaultBlogApplication();
|
||||
return application;
|
||||
}
|
||||
|
||||
public void ConfigureContainer<TContainerBuilder>(
|
||||
IServiceProviderFactory<TContainerBuilder> factory, Action<TContainerBuilder>? configure = null)
|
||||
where TContainerBuilder : notnull
|
||||
=> _hostApplicationBuilder.ConfigureContainer(factory, configure);
|
||||
|
||||
public IDictionary<object, object> Properties
|
||||
=> (_hostApplicationBuilder as IHostApplicationBuilder).Properties;
|
||||
|
||||
public IHostEnvironment Environment => _hostApplicationBuilder.Environment;
|
||||
|
||||
public IConfigurationManager Configuration => _hostApplicationBuilder.Configuration;
|
||||
|
||||
public ILoggingBuilder Logging => _hostApplicationBuilder.Logging;
|
||||
|
||||
public IMetricsBuilder Metrics => _hostApplicationBuilder.Metrics;
|
||||
|
||||
public IServiceCollection Services => _hostApplicationBuilder.Services;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
namespace YaeBlog.Core.Builder;
|
||||
|
||||
public class BlogApplicationOptions
|
||||
{
|
||||
public required string[] Args { get; init; }
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
using Markdig;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using YaeBlog.Core.Builder;
|
||||
using YaeBlog.Core.Models;
|
||||
using YaeBlog.Core.Processors;
|
||||
using YaeBlog.Core.Services;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace YaeBlog.Core.Extensions;
|
||||
|
||||
public static class BlogApplicationBuilderExtension
|
||||
{
|
||||
internal static void ConfigureDefaultBlogApplicationBuilder(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.YamlDeserializerBuilder.WithNamingConvention(CamelCaseNamingConvention.Instance);
|
||||
builder.YamlDeserializerBuilder.IgnoreUnmatchedProperties();
|
||||
|
||||
builder.Services.AddSingleton<MarkdownPipeline>(
|
||||
_ => builder.MarkdigPipelineBuilder.Build());
|
||||
builder.Services.AddSingleton<IDeserializer>(
|
||||
_ => builder.YamlDeserializerBuilder.Build());
|
||||
|
||||
builder.Services.AddHostedService<BlogHostedService>();
|
||||
builder.Services.AddSingleton<EssayScanService>();
|
||||
builder.Services.AddSingleton<RendererService>();
|
||||
builder.Services.AddSingleton<EssayContentService>();
|
||||
|
||||
// 设置图像处理器
|
||||
builder.Services.AddSingleton<ImagePostRenderProcessor>();
|
||||
ImagePostRenderProcessor.AddImageApiEndpoint(builder);
|
||||
|
||||
builder.Services.AddHostedService<WebApplicationHostedService>((provider)
|
||||
=> new WebApplicationHostedService(builder.WebApplicationBuilderConfigurations,
|
||||
builder.WebApplicationConfigurations, provider));
|
||||
}
|
||||
|
||||
public static void ConfigureWebApplicationBuilder(this BlogApplicationBuilder builder,
|
||||
Action<WebApplicationBuilder> configureWebApplicationBuilder)
|
||||
{
|
||||
builder.WebApplicationBuilderConfigurations.Add(configureWebApplicationBuilder);
|
||||
}
|
||||
|
||||
public static void ConfigureWebApplication(this BlogApplicationBuilder builder,
|
||||
Action<WebApplication> configureWebApplication)
|
||||
{
|
||||
builder.WebApplicationConfigurations.Add(configureWebApplication);
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using YaeBlog.Core.Abstractions;
|
||||
using YaeBlog.Core.Builder;
|
||||
using YaeBlog.Core.Processors;
|
||||
using YaeBlog.Core.Services;
|
||||
|
||||
namespace YaeBlog.Core.Extensions;
|
||||
|
||||
public static class BlogApplicationExtension
|
||||
{
|
||||
internal static void ConfigureDefaultBlogApplication(this BlogApplication application)
|
||||
{
|
||||
application.UsePostRenderProcessor<ImagePostRenderProcessor>();
|
||||
}
|
||||
|
||||
public static void UsePreRenderProcessor<T>(this BlogApplication application)
|
||||
where T : IPreRenderProcessor
|
||||
{
|
||||
RendererService rendererService =
|
||||
application.Services.GetRequiredService<RendererService>();
|
||||
T preRenderProcessor =
|
||||
application.Services.GetRequiredService<T>();
|
||||
rendererService.AddPreRenderProcessor(preRenderProcessor);
|
||||
}
|
||||
|
||||
public static void UsePostRenderProcessor<T>(this BlogApplication application)
|
||||
where T : IPostRenderProcessor
|
||||
{
|
||||
RendererService rendererService =
|
||||
application.Services.GetRequiredService<RendererService>();
|
||||
T postRenderProcessor =
|
||||
application.Services.GetRequiredService<T>();
|
||||
rendererService.AddPostRenderProcessor(postRenderProcessor);
|
||||
}
|
||||
}
|
30
YaeBlog.Core/Extensions/ServiceCollectionExtensions.cs
Normal file
30
YaeBlog.Core/Extensions/ServiceCollectionExtensions.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using Markdig;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace YaeBlog.Core.Extensions;
|
||||
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddMarkdig(this IServiceCollection collection)
|
||||
{
|
||||
MarkdownPipelineBuilder builder = new();
|
||||
|
||||
collection.AddSingleton<MarkdownPipeline>(_ => builder.Build());
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddYamlParser(this IServiceCollection collection)
|
||||
{
|
||||
DeserializerBuilder builder = new();
|
||||
|
||||
builder.WithNamingConvention(CamelCaseNamingConvention.Instance);
|
||||
builder.IgnoreUnmatchedProperties();
|
||||
|
||||
collection.AddSingleton<IDeserializer>(_ => builder.Build());
|
||||
|
||||
return collection;
|
||||
}
|
||||
}
|
33
YaeBlog.Core/Extensions/WebApplicationBuilderExtensions.cs
Normal file
33
YaeBlog.Core/Extensions/WebApplicationBuilderExtensions.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using YaeBlog.Core.Models;
|
||||
using YaeBlog.Core.Processors;
|
||||
using YaeBlog.Core.Services;
|
||||
|
||||
namespace YaeBlog.Core.Extensions;
|
||||
|
||||
public static class WebApplicationBuilderExtensions
|
||||
{
|
||||
public static WebApplicationBuilder AddYaeBlog(this WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.Configure<BlogOptions>(builder.Configuration.GetSection(BlogOptions.OptionName));
|
||||
|
||||
builder.Services.AddHttpClient();
|
||||
builder.Services.AddFluentUIComponents();
|
||||
|
||||
builder.Services.AddMarkdig();
|
||||
builder.Services.AddYamlParser();
|
||||
builder.Services.AddSingleton<EssayScanService>();
|
||||
builder.Services.AddSingleton<RendererService>();
|
||||
builder.Services.AddSingleton<EssayContentService>();
|
||||
builder.Services.AddTransient<ImagePostRenderProcessor>();
|
||||
builder.Services.AddTransient<BlogOptions>(provider =>
|
||||
provider.GetRequiredService<IOptions<BlogOptions>>().Value);
|
||||
|
||||
builder.Services.AddHostedService<BlogHostedService>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
33
YaeBlog.Core/Extensions/WebApplicationExtensions.cs
Normal file
33
YaeBlog.Core/Extensions/WebApplicationExtensions.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using YaeBlog.Core.Abstractions;
|
||||
using YaeBlog.Core.Processors;
|
||||
using YaeBlog.Core.Services;
|
||||
|
||||
namespace YaeBlog.Core.Extensions;
|
||||
|
||||
public static class WebApplicationExtensions
|
||||
{
|
||||
public static WebApplication UseMiddleRenderProcessors(this WebApplication application)
|
||||
{
|
||||
application.UsePostRenderProcessor<ImagePostRenderProcessor>();
|
||||
|
||||
return application;
|
||||
}
|
||||
|
||||
private static void UsePreRenderProcessor<T>(this WebApplication application) where T : IPreRenderProcessor
|
||||
{
|
||||
RendererService rendererService = application.Services.GetRequiredService<RendererService>();
|
||||
T preRenderProcessor = application.Services.GetRequiredService<T>();
|
||||
|
||||
rendererService.AddPreRenderProcessor(preRenderProcessor);
|
||||
}
|
||||
|
||||
private static void UsePostRenderProcessor<T>(this WebApplication application) where T : IPostRenderProcessor
|
||||
{
|
||||
RendererService rendererService = application.Services.GetRequiredService<RendererService>();
|
||||
T postRenderProcessor = application.Services.GetRequiredService<T>();
|
||||
|
||||
rendererService.AddPostRenderProcessor(postRenderProcessor);
|
||||
}
|
||||
}
|
|
@ -9,11 +9,6 @@ public class BlogOptions
|
|||
/// </summary>
|
||||
public required string Root { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 博客挂载的子路径
|
||||
/// </summary>
|
||||
public required string SubPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 博客作者
|
||||
/// </summary>
|
||||
|
@ -26,11 +21,6 @@ public class BlogOptions
|
|||
/// </summary>
|
||||
public required int StartYear { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 博客项目的名称
|
||||
/// </summary>
|
||||
public required string ProjectName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 博客起始页面的背景图片
|
||||
/// </summary>
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
using AngleSharp;
|
||||
using AngleSharp.Dom;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using YaeBlog.Core.Abstractions;
|
||||
using YaeBlog.Core.Builder;
|
||||
using YaeBlog.Core.Extensions;
|
||||
using YaeBlog.Core.Models;
|
||||
|
||||
namespace YaeBlog.Core.Processors;
|
||||
|
@ -45,31 +40,6 @@ public class ImagePostRenderProcessor(ILogger<ImagePostRenderProcessor> logger,
|
|||
|
||||
public string Name => "ImagePostRenderProcessor";
|
||||
|
||||
public static void AddImageApiEndpoint(BlogApplicationBuilder builder)
|
||||
{
|
||||
builder.ConfigureWebApplication((application) =>
|
||||
{
|
||||
application.MapGet("/api/files/{*filename}", ImageHandler);
|
||||
});
|
||||
}
|
||||
|
||||
private static Results<FileStreamHttpResult, NotFound> ImageHandler(string filename)
|
||||
{
|
||||
string contentType = "image/png";
|
||||
if (filename.EndsWith("jpg") || filename.EndsWith("jpeg"))
|
||||
{
|
||||
contentType = "image/jpeg";
|
||||
}
|
||||
|
||||
if (!Path.Exists(filename))
|
||||
{
|
||||
return TypedResults.NotFound();
|
||||
}
|
||||
|
||||
Stream imageStream = File.OpenRead(filename);
|
||||
return TypedResults.Stream(imageStream, contentType);
|
||||
}
|
||||
|
||||
private string GenerateImageLink(string filename, string essayFilename)
|
||||
{
|
||||
if (!filename.Contains(essayFilename))
|
||||
|
@ -81,8 +51,8 @@ public class ImagePostRenderProcessor(ILogger<ImagePostRenderProcessor> logger,
|
|||
|
||||
if (!Path.Exists(filename))
|
||||
{
|
||||
logger.LogWarning("Failed to found image: {}.", filename);
|
||||
return _options.BannerImage;
|
||||
logger.LogError("Failed to found image: {}.", filename);
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
string imageLink = "api/files/" + filename;
|
||||
|
|
|
@ -19,6 +19,4 @@ public class BlogHostedService(
|
|||
logger.LogInformation("YaeBlog stopped!\nHave a nice day!");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
using YaeBlog.Core.Models;
|
||||
|
||||
namespace YaeBlog.Core.Services;
|
||||
|
||||
public class WebApplicationHostedService : IHostedService
|
||||
{
|
||||
private readonly WebApplicationBuilder _websiteBuilder = WebApplication.CreateBuilder();
|
||||
|
||||
private readonly List<Action<WebApplication>> _webApplicationConfigurations;
|
||||
|
||||
private readonly IOptions<BlogOptions> _options;
|
||||
|
||||
private Website? _currentWebsite;
|
||||
|
||||
public WebApplicationHostedService(List<Action<WebApplicationBuilder>> webApplicationBuilderConfigurations,
|
||||
List<Action<WebApplication>> webApplicationConfigurations,
|
||||
IServiceProvider hostServiceProvider)
|
||||
{
|
||||
_webApplicationConfigurations = webApplicationConfigurations;
|
||||
_options = hostServiceProvider.GetRequiredService<IOptions<BlogOptions>>();
|
||||
|
||||
foreach (Action<WebApplicationBuilder> configure in webApplicationBuilderConfigurations)
|
||||
{
|
||||
configure(_websiteBuilder);
|
||||
}
|
||||
|
||||
AddHostServices(hostServiceProvider);
|
||||
}
|
||||
|
||||
public async Task BuildWebsite()
|
||||
{
|
||||
if (_currentWebsite is not null)
|
||||
{
|
||||
await _currentWebsite.ShutdownAsync(new CancellationToken());
|
||||
}
|
||||
|
||||
WebApplication application = _websiteBuilder.Build();
|
||||
application.UsePathBase("/" + _options.Value.SubPath);
|
||||
foreach (Action<WebApplication> configure in _webApplicationConfigurations)
|
||||
{
|
||||
configure(application);
|
||||
}
|
||||
IHostLifetime websiteLifetime = application.Services.GetRequiredService<IHostLifetime>();
|
||||
_currentWebsite = new Website(application, websiteLifetime);
|
||||
}
|
||||
|
||||
public Task RunAsync()
|
||||
{
|
||||
if (_currentWebsite is not null)
|
||||
{
|
||||
return _currentWebsite.RunAsync();
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Website has not been built.");
|
||||
}
|
||||
|
||||
public Task ShutdownAsync()
|
||||
{
|
||||
if (_currentWebsite is { Running: true })
|
||||
{
|
||||
return _currentWebsite.ShutdownAsync(new CancellationToken());
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Website is not running.");
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await BuildWebsite();
|
||||
_ = RunAsync();
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_currentWebsite is { Running: true })
|
||||
{
|
||||
return _currentWebsite.ShutdownAsync(cancellationToken);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void AddHostServices(IServiceProvider provider)
|
||||
{
|
||||
_websiteBuilder.Services.AddSingleton<EssayContentService>(_ =>
|
||||
provider.GetRequiredService<EssayContentService>());
|
||||
_websiteBuilder.Services.AddTransient<BlogOptions>(_ =>
|
||||
provider.GetRequiredService<IOptions<BlogOptions>>().Value);
|
||||
}
|
||||
|
||||
|
||||
private class Website(WebApplication application, IHostLifetime websiteLifetime)
|
||||
{
|
||||
public bool Running { get; private set; }
|
||||
|
||||
public Task RunAsync()
|
||||
{
|
||||
Running = true;
|
||||
return application.RunAsync();
|
||||
}
|
||||
|
||||
public async Task ShutdownAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!Running)
|
||||
{
|
||||
await websiteLifetime.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
Running = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,9 +27,11 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="1.1.0" />
|
||||
<PackageReference Include="Markdig" Version="0.34.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="13.7.1" />
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.8.0" />
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
using YaeBlog.Example.Components;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseAntiforgery();
|
||||
|
||||
app.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode();
|
||||
|
||||
app.Run();
|
|
@ -1,29 +0,0 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:60424",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5015",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
global using Xunit;
|
|
@ -1,30 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
<PackageReference Include="moq" Version="4.20.70" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\YaeBlog.Core\YaeBlog.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,30 +0,0 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using YaeBlog.Core.Builder;
|
||||
using YaeBlog.Core.Extensions;
|
||||
|
||||
namespace YaeBlog.Theme.FluentUI;
|
||||
|
||||
public static class BlogApplicationBuilderExtensions
|
||||
{
|
||||
public static void UseFluentTheme(this BlogApplicationBuilder builder)
|
||||
{
|
||||
builder.ConfigureWebApplicationBuilder(ConfigureWebApplicationBuilder);
|
||||
builder.ConfigureWebApplication(ConfigureWebApplication);
|
||||
}
|
||||
|
||||
private static void ConfigureWebApplicationBuilder(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddRazorComponents();
|
||||
builder.Services.AddFluentUIComponents();
|
||||
}
|
||||
|
||||
private static void ConfigureWebApplication(WebApplication application)
|
||||
{
|
||||
application.UseStaticFiles();
|
||||
application.UseAntiforgery();
|
||||
application.UseStatusCodePagesWithRedirects("~/NotFound");
|
||||
application.MapRazorComponents<App>();
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
# YaeBlog.Theme.FluentUI
|
||||
|
||||
A fluent design style theme for YaeBlog!
|
|
@ -1,5 +0,0 @@
|
|||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)"/>
|
||||
</Found>
|
||||
</Router>
|
|
@ -1,36 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>YaeBlog.Theme.FluentUI</PackageId>
|
||||
<Version>0.1.1</Version>
|
||||
<Authors>Ricardo Ren</Authors>
|
||||
<Company>Ricardo Ren</Company>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<SupportedPlatform Include="browser" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.3.1" />
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.3.1" />
|
||||
<PackageReference Include="YaeBlog.Core" Version="0.1.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="**/.gitattributes"/>
|
||||
<None Remove="**/.gitattributes"/>
|
||||
<None Include="README.md" Pack="true" PackagePath="/"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
22
YaeBlog.sln
22
YaeBlog.sln
|
@ -5,11 +5,7 @@ VisualStudioVersion = 17.0.31903.59
|
|||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YaeBlog.Core", "YaeBlog.Core\YaeBlog.Core.csproj", "{1671A8AE-78F6-4641-B97D-D8ABA5E9CBEF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YaeBlog.Theme.FluentUI", "YaeBlog.Theme.FluentUI\YaeBlog.Theme.FluentUI.csproj", "{E3EB73E2-0267-416A-BA0A-9D0FC93AAFA0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YaeBlog.Example", "YaeBlog.Example\YaeBlog.Example.csproj", "{3A768948-D4E8-4111-A1FE-DF98EBFC4991}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YaeBlog.Tests", "YaeBlog.Tests\YaeBlog.Tests.csproj", "{5866CB58-6FE2-4DB8-AE20-8439D8C6C3B9}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YaeBlog", "YaeBlog\YaeBlog.csproj", "{20438EFD-8DDE-43AF-92E2-76495C29233C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -21,18 +17,10 @@ Global
|
|||
{1671A8AE-78F6-4641-B97D-D8ABA5E9CBEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1671A8AE-78F6-4641-B97D-D8ABA5E9CBEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1671A8AE-78F6-4641-B97D-D8ABA5E9CBEF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E3EB73E2-0267-416A-BA0A-9D0FC93AAFA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E3EB73E2-0267-416A-BA0A-9D0FC93AAFA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E3EB73E2-0267-416A-BA0A-9D0FC93AAFA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E3EB73E2-0267-416A-BA0A-9D0FC93AAFA0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3A768948-D4E8-4111-A1FE-DF98EBFC4991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3A768948-D4E8-4111-A1FE-DF98EBFC4991}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3A768948-D4E8-4111-A1FE-DF98EBFC4991}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3A768948-D4E8-4111-A1FE-DF98EBFC4991}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5866CB58-6FE2-4DB8-AE20-8439D8C6C3B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5866CB58-6FE2-4DB8-AE20-8439D8C6C3B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5866CB58-6FE2-4DB8-AE20-8439D8C6C3B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5866CB58-6FE2-4DB8-AE20-8439D8C6C3B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{20438EFD-8DDE-43AF-92E2-76495C29233C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{20438EFD-8DDE-43AF-92E2-76495C29233C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{20438EFD-8DDE-43AF-92E2-76495C29233C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{20438EFD-8DDE-43AF-92E2-76495C29233C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
@using YaeBlog.Core.Models
|
||||
@inject BlogOptions BlogOptionsInstance
|
||||
|
||||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<base href="/@(BlogOptionsInstance.SubPath)/"/>
|
||||
<link href="@($"{BlogOptionsInstance.ProjectName}.styles.css")" rel="stylesheet"/>
|
||||
<base href="/"/>
|
||||
<link rel="stylesheet" href="YaeBlog.styles.css"/>
|
||||
<link href="_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css" rel="stylesheet" />
|
||||
<link href="_content/YaeBlog.Theme.FluentUI/globals.css" rel="stylesheet"/>
|
||||
<link href="favicon.ico" rel="icon"/>
|
||||
<link href="globals.css" rel="stylesheet"/>
|
||||
<HeadOutlet/>
|
||||
</head>
|
||||
|
6
YaeBlog/Components/Routes.razor
Normal file
6
YaeBlog/Components/Routes.razor
Normal file
|
@ -0,0 +1,6 @@
|
|||
<Router AppAssembly="typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"/>
|
||||
<FocusOnNavigate RouteData="routeData" Selector="h1"/>
|
||||
</Found>
|
||||
</Router>
|
28
YaeBlog/Controllers/FilesController.cs
Normal file
28
YaeBlog/Controllers/FilesController.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace YaeBlog.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/files")]
|
||||
public class FilesController : ControllerBase
|
||||
{
|
||||
[HttpGet("{*filename}")]
|
||||
public IActionResult Images(string filename)
|
||||
{
|
||||
string contentType = "image/png";
|
||||
if (filename.EndsWith("jpg") || filename.EndsWith("jpeg"))
|
||||
{
|
||||
contentType = "image/jpeg";
|
||||
}
|
||||
|
||||
FileInfo imageFile = new(filename);
|
||||
|
||||
if (!imageFile.Exists)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
Stream imageStream = imageFile.OpenRead();
|
||||
return File(imageStream, contentType);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
@using YaeBlog.Core.Models
|
||||
@using YaeBlog.Core.Models
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
@inject BlogOptions BlogOptionsInstance
|
36
YaeBlog/Pages/Error.razor
Normal file
36
YaeBlog/Pages/Error.razor
Normal file
|
@ -0,0 +1,36 @@
|
|||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
|
||||
@code{
|
||||
[CascadingParameter] private HttpContext? HttpContext { get; set; }
|
||||
|
||||
private string? RequestId { get; set; }
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
protected override void OnInitialized() =>
|
||||
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
|
||||
}
|
21
YaeBlog/Program.cs
Normal file
21
YaeBlog/Program.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using YaeBlog.Components;
|
||||
using YaeBlog.Core.Extensions;
|
||||
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents();
|
||||
builder.Services.AddControllers();
|
||||
builder.AddYaeBlog();
|
||||
|
||||
WebApplication application = builder.Build();
|
||||
|
||||
application.UseStaticFiles();
|
||||
application.UseAntiforgery();
|
||||
application.UseMiddleRenderProcessors();
|
||||
|
||||
application.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode();
|
||||
application.MapControllers();
|
||||
|
||||
await application.RunAsync();
|
22
YaeBlog/Properties/launchSettings.json
Normal file
22
YaeBlog/Properties/launchSettings.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:14405",
|
||||
"sslPort": 44378
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5275",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
YaeBlog/YaeBlog.csproj
Normal file
37
YaeBlog/YaeBlog.csproj
Normal file
|
@ -0,0 +1,37 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\YaeBlog.Core\YaeBlog.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Layout\MainLayout.razor" />
|
||||
<AdditionalFiles Include="Pages\About.razor" />
|
||||
<AdditionalFiles Include="Pages\Archives.razor" />
|
||||
<AdditionalFiles Include="Pages\Error.razor" />
|
||||
<AdditionalFiles Include="Pages\Essay.razor" />
|
||||
<AdditionalFiles Include="Pages\Home.razor" />
|
||||
<AdditionalFiles Include="Pages\Links.razor" />
|
||||
<AdditionalFiles Include="Pages\NotFound.razor" />
|
||||
<AdditionalFiles Include="Pages\Tags.razor" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_ContentIncludedByDefault Remove="Components\Pages\About.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Pages\Archives.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Pages\Error.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Pages\Essay.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Pages\Home.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Pages\Links.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Pages\NotFound.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Pages\Tags.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Layout\MainLayout.razor" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -7,4 +7,5 @@
|
|||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.JSInterop
|
||||
@using Microsoft.FluentUI.AspNetCore.Components
|
||||
@using YaeBlog.Theme.FluentUI.Components
|
||||
@using YaeBlog
|
||||
@using YaeBlog.Components
|
44
YaeBlog/appsettings.json
Normal file
44
YaeBlog/appsettings.json
Normal file
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Blog": {
|
||||
"Root": "source",
|
||||
"Author": "Ricardo Ren",
|
||||
"Announcement": "堂!堂!开!始!",
|
||||
"StartYear": 2021,
|
||||
"ProjectName": "Blog",
|
||||
"BannerImage": "images/banner.png",
|
||||
"EssayImage": "images/banner.png",
|
||||
"RegisterInformation": "蜀ICP备2022004429号-1",
|
||||
"About": {
|
||||
"Introduction": "A CS Student",
|
||||
"Description": "还太菜了,没有做出太多的贡献。",
|
||||
"AvatarImage": "images/avatar.png"
|
||||
},
|
||||
"Links": [
|
||||
{
|
||||
"Name": "Ichirinko",
|
||||
"Description": "这是个大哥",
|
||||
"Link": "https://ichirinko.top",
|
||||
"AvatarImage": "https://ichirinko-blog-img-1.oss-cn-shenzhen.aliyuncs.com/Pic_res/img/202209122110798.png"
|
||||
},
|
||||
{
|
||||
"Name": "志田千陽",
|
||||
"Description": "日出多期待",
|
||||
"Link": "https://zzachary.top/",
|
||||
"AvatarImage": "https://zzachary.top/img/ztqy_hub928259802d192ff5718c06370f0f2c4_48203_300x0_resize_q75_box.jpg"
|
||||
},
|
||||
{
|
||||
"Name": "Chenxu",
|
||||
"Description": "一个普通大学生",
|
||||
"Link": "https://chenxutalk.top",
|
||||
"AvatarImage": "https://www.chenxutalk.top/img/photo.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
BIN
YaeBlog/wwwroot/images/avatar.png
Normal file
BIN
YaeBlog/wwwroot/images/avatar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 440 KiB |
BIN
YaeBlog/wwwroot/images/banner.png
Normal file
BIN
YaeBlog/wwwroot/images/banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
BIN
YaeBlog/wwwroot/images/favicon.ico
Normal file
BIN
YaeBlog/wwwroot/images/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Loading…
Reference in New Issue
Block a user