refact: Fully refactor the conceptional structure and functional behaviour.

This commit is contained in:
jackfiled 2024-06-21 22:59:30 +08:00
parent 257f5d94ee
commit 2efe2fd5cd
64 changed files with 307 additions and 574 deletions

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -1,6 +0,0 @@
namespace YaeBlog.Core.Builder;
public class BlogApplicationOptions
{
public required string[] Args { get; init; }
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View File

@ -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>

View File

@ -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;

View File

@ -19,6 +19,4 @@ public class BlogHostedService(
logger.LogInformation("YaeBlog stopped!\nHave a nice day!");
return Task.CompletedTask;
}
}

View File

@ -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;
}
}
}

View File

@ -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>

View File

@ -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();

View File

@ -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"
}
}
}
}

View File

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

View File

@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -1 +0,0 @@
global using Xunit;

View File

@ -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>

View File

@ -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>();
}
}

View File

@ -1,3 +0,0 @@
# YaeBlog.Theme.FluentUI
A fluent design style theme for YaeBlog!

View File

@ -1,5 +0,0 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)"/>
</Found>
</Router>

View File

@ -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>

View File

@ -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

View File

@ -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>

View 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>

View 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);
}
}

View File

@ -1,4 +1,4 @@
@using YaeBlog.Core.Models
@using YaeBlog.Core.Models
@inherits LayoutComponentBase
@inject BlogOptions BlogOptionsInstance

36
YaeBlog/Pages/Error.razor Normal file
View 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
View 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();

View 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
View 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>

View File

@ -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
View 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"
}
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB