Skip to content

Commit

Permalink
Merge pull request #763 from EdiWang/master
Browse files Browse the repository at this point in the history
Release v14.0.1
  • Loading branch information
EdiWang authored Nov 21, 2023
2 parents 8cdba7f + 7ba0fe3 commit 0cb4cc6
Show file tree
Hide file tree
Showing 74 changed files with 336 additions and 773 deletions.
14 changes: 7 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@ FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
LABEL maintainer="edi.wang@outlook.com"
LABEL repo="https://github.com/EdiWang/Moonglade"

USER app

# If use aspnet:8.0-alpine, see https://github.com/dotnet/dotnet-docker/issues/1366
#RUN apk add --no-cache tzdata

# Captcha font
COPY ./build/OpenSans-Regular.ttf /usr/share/fonts/OpenSans-Regular.ttf

WORKDIR /app
#EXPOSE 80
# https://learn.microsoft.com/en-us/dotnet/core/compatibility/containers/8.0/aspnet-port
# Breaking changes: Default ASP.NET Core port changed from 80 to 8080
EXPOSE 8080
EXPOSE 443
EXPOSE 8081

ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
#ENV ASPNETCORE_URLS=http://+:80

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src

# Auto copy to prevent 996
Expand All @@ -28,10 +27,11 @@ RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.
RUN dotnet restore "Moonglade.Web/Moonglade.Web.csproj"
COPY ./src .
WORKDIR "/src/Moonglade.Web"
RUN dotnet build "Moonglade.Web.csproj" -c Release -o /app/build
RUN dotnet build "Moonglade.Web.csproj" -c $BUILD_CONFIGURATION -o /app/build

FROM build AS publish
RUN dotnet publish "Moonglade.Web.csproj" -c Release -o /app/publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "Moonglade.Web.csproj" -c $BUILD_CONFIGURATION -o /app/publish

FROM base AS final
WORKDIR /app
Expand Down
6 changes: 3 additions & 3 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<Authors>Edi Wang</Authors>
<Company>edi.wang</Company>
<Copyright>(C) 2023 edi.wang@outlook.com</Copyright>
<AssemblyVersion>14.0.0.0</AssemblyVersion>
<FileVersion>14.0.0.0</FileVersion>
<Version>14.0.0</Version>
<AssemblyVersion>14.0.1.0</AssemblyVersion>
<FileVersion>14.0.1.0</FileVersion>
<Version>14.0.1</Version>
</PropertyGroup>
</Project>
15 changes: 4 additions & 11 deletions src/Moonglade.Comments/CreateCommentCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,13 @@

namespace Moonglade.Comments;

public class CreateCommentCommand : IRequest<(int Status, CommentDetailedItem Item)>
public class CreateCommentCommand(Guid postId, CommentRequest payload, string ipAddress) : IRequest<(int Status, CommentDetailedItem Item)>
{
public CreateCommentCommand(Guid postId, CommentRequest payload, string ipAddress)
{
PostId = postId;
Payload = payload;
IpAddress = ipAddress;
}

public Guid PostId { get; set; }
public Guid PostId { get; set; } = postId;

public CommentRequest Payload { get; set; }
public CommentRequest Payload { get; set; } = payload;

public string IpAddress { get; set; }
public string IpAddress { get; set; } = ipAddress;
}

public class CreateCommentCommandHandler(IBlogConfig blogConfig, IRepository<PostEntity> postRepo, IModeratorService moderator, IRepository<CommentEntity> commentRepo) :
Expand Down
25 changes: 7 additions & 18 deletions src/Moonglade.Core/PostFeature/CountPostQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,35 @@ public enum CountType

public record CountPostQuery(CountType CountType, Guid? CatId = null, int? TagId = null) : IRequest<int>;

public class CountPostQueryHandler : IRequestHandler<CountPostQuery, int>
{
private readonly IRepository<PostEntity> _postRepo;
private readonly IRepository<PostTagEntity> _postTagRepo;
private readonly IRepository<PostCategoryEntity> _postCatRepo;

public CountPostQueryHandler(
IRepository<PostEntity> postRepo,
public class CountPostQueryHandler(IRepository<PostEntity> postRepo,
IRepository<PostTagEntity> postTagRepo,
IRepository<PostCategoryEntity> postCatRepo)
{
_postRepo = postRepo;
_postTagRepo = postTagRepo;
_postCatRepo = postCatRepo;
}

: IRequestHandler<CountPostQuery, int>
{
public async Task<int> Handle(CountPostQuery request, CancellationToken ct)
{
int count = 0;

switch (request.CountType)
{
case CountType.Public:
count = await _postRepo.CountAsync(p => p.IsPublished && !p.IsDeleted, ct);
count = await postRepo.CountAsync(p => p.IsPublished && !p.IsDeleted, ct);
break;

case CountType.Category:
if (request.CatId == null) throw new ArgumentNullException(nameof(request.CatId));
count = await _postCatRepo.CountAsync(c => c.CategoryId == request.CatId.Value
count = await postCatRepo.CountAsync(c => c.CategoryId == request.CatId.Value
&& c.Post.IsPublished
&& !c.Post.IsDeleted, ct);
break;

case CountType.Tag:
if (request.TagId == null) throw new ArgumentNullException(nameof(request.TagId));
count = await _postTagRepo.CountAsync(p => p.TagId == request.TagId.Value && p.Post.IsPublished && !p.Post.IsDeleted, ct);
count = await postTagRepo.CountAsync(p => p.TagId == request.TagId.Value && p.Post.IsPublished && !p.Post.IsDeleted, ct);
break;

case CountType.Featured:
count = await _postRepo.CountAsync(p => p.IsFeatured && p.IsPublished && !p.IsDeleted, ct);
count = await postRepo.CountAsync(p => p.IsFeatured && p.IsPublished && !p.IsDeleted, ct);
break;
}

Expand Down
35 changes: 10 additions & 25 deletions src/Moonglade.Core/PostFeature/CreatePostCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,19 @@ namespace Moonglade.Core.PostFeature;

public record CreatePostCommand(PostEditModel Payload) : IRequest<PostEntity>;

public class CreatePostCommandHandler : IRequestHandler<CreatePostCommand, PostEntity>
{
private readonly IRepository<PostEntity> _postRepo;
private readonly ILogger<CreatePostCommandHandler> _logger;
private readonly IRepository<TagEntity> _tagRepo;
private readonly IBlogConfig _blogConfig;
private readonly IConfiguration _configuration;

public CreatePostCommandHandler(
IRepository<PostEntity> postRepo,
public class CreatePostCommandHandler(IRepository<PostEntity> postRepo,
ILogger<CreatePostCommandHandler> logger,
IRepository<TagEntity> tagRepo,
IConfiguration configuration,
IBlogConfig blogConfig)
{
_postRepo = postRepo;
_logger = logger;
_tagRepo = tagRepo;
_configuration = configuration;
_blogConfig = blogConfig;
}

: IRequestHandler<CreatePostCommand, PostEntity>
{
public async Task<PostEntity> Handle(CreatePostCommand request, CancellationToken ct)
{
var abs = ContentProcessor.GetPostAbstract(
string.IsNullOrEmpty(request.Payload.Abstract) ? request.Payload.EditorContent : request.Payload.Abstract.Trim(),
_blogConfig.ContentSettings.PostAbstractWords,
_configuration.GetSection("Editor").Get<EditorChoice>() == EditorChoice.Markdown);
blogConfig.ContentSettings.PostAbstractWords,
configuration.GetSection("Editor").Get<EditorChoice>() == EditorChoice.Markdown);

var post = new PostEntity
{
Expand All @@ -63,11 +48,11 @@ public async Task<PostEntity> Handle(CreatePostCommand request, CancellationToke

// check if exist same slug under the same day
var todayUtc = DateTime.UtcNow.Date;
if (await _postRepo.AnyAsync(new PostSpec(post.Slug, todayUtc), ct))
if (await postRepo.AnyAsync(new PostSpec(post.Slug, todayUtc), ct))
{
var uid = Guid.NewGuid();
post.Slug += $"-{uid.ToString().ToLower()[..8]}";
_logger.LogInformation($"Found conflict for post slug, generated new slug: {post.Slug}");
logger.LogInformation($"Found conflict for post slug, generated new slug: {post.Slug}");
}

// compute hash
Expand Down Expand Up @@ -99,12 +84,12 @@ public async Task<PostEntity> Handle(CreatePostCommand request, CancellationToke
{
if (!Tag.ValidateName(item)) continue;

var tag = await _tagRepo.GetAsync(q => q.DisplayName == item) ?? await CreateTag(item);
var tag = await tagRepo.GetAsync(q => q.DisplayName == item) ?? await CreateTag(item);
post.Tags.Add(tag);
}
}

await _postRepo.AddAsync(post, ct);
await postRepo.AddAsync(post, ct);

return post;
}
Expand All @@ -117,7 +102,7 @@ private async Task<TagEntity> CreateTag(string item)
NormalizedName = Tag.NormalizeName(item, Helper.TagNormalizationDictionary)
};

var tag = await _tagRepo.AddAsync(newTag);
var tag = await tagRepo.AddAsync(newTag);
return tag;
}
}
19 changes: 5 additions & 14 deletions src/Moonglade.Core/PostFeature/DeletePostCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,24 @@ namespace Moonglade.Core.PostFeature;

public record DeletePostCommand(Guid Id, bool SoftDelete = false) : IRequest;

public class DeletePostCommandHandler : IRequestHandler<DeletePostCommand>
public class DeletePostCommandHandler(IRepository<PostEntity> repo, ICacheAside cache) : IRequestHandler<DeletePostCommand>
{
private readonly IRepository<PostEntity> _repo;
private readonly ICacheAside _cache;

public DeletePostCommandHandler(IRepository<PostEntity> repo, ICacheAside cache)
{
_repo = repo;
_cache = cache;
}

public async Task Handle(DeletePostCommand request, CancellationToken ct)
{
var (guid, softDelete) = request;
var post = await _repo.GetAsync(guid, ct);
var post = await repo.GetAsync(guid, ct);
if (null == post) return;

if (softDelete)
{
post.IsDeleted = true;
await _repo.UpdateAsync(post, ct);
await repo.UpdateAsync(post, ct);
}
else
{
await _repo.DeleteAsync(post, ct);
await repo.DeleteAsync(post, ct);
}

_cache.Remove(BlogCachePartition.Post.ToString(), guid.ToString());
cache.Remove(BlogCachePartition.Post.ToString(), guid.ToString());
}
}
9 changes: 3 additions & 6 deletions src/Moonglade.Core/PostFeature/GetArchiveQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,20 @@ namespace Moonglade.Core.PostFeature;
public record struct Archive(int Year, int Month, int Count);
public record GetArchiveQuery : IRequest<IReadOnlyList<Archive>>;

public class GetArchiveQueryHandler : IRequestHandler<GetArchiveQuery, IReadOnlyList<Archive>>
public class GetArchiveQueryHandler(IRepository<PostEntity> repo) : IRequestHandler<GetArchiveQuery, IReadOnlyList<Archive>>
{
private readonly IRepository<PostEntity> _repo;
private readonly Expression<Func<IGrouping<(int Year, int Month), PostEntity>, Archive>> _archiveSelector =
p => new(p.Key.Year, p.Key.Month, p.Count());

public GetArchiveQueryHandler(IRepository<PostEntity> repo) => _repo = repo;

public async Task<IReadOnlyList<Archive>> Handle(GetArchiveQuery request, CancellationToken ct)
{
if (!await _repo.AnyAsync(p => p.IsPublished && !p.IsDeleted, ct))
if (!await repo.AnyAsync(p => p.IsPublished && !p.IsDeleted, ct))
{
return new List<Archive>();
}

var spec = new PostSpec(PostStatus.Published);
var list = await _repo.SelectAsync(
var list = await repo.SelectAsync(
post => new(post.PubDateUtc.Value.Year, post.PubDateUtc.Value.Month),
_archiveSelector, spec);

Expand Down
8 changes: 2 additions & 6 deletions src/Moonglade.Core/PostFeature/GetDraftQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@ namespace Moonglade.Core.PostFeature;

public record GetDraftQuery(Guid Id) : IRequest<Post>;

public class GetDraftQueryHandler : IRequestHandler<GetDraftQuery, Post>
public class GetDraftQueryHandler(IRepository<PostEntity> repo) : IRequestHandler<GetDraftQuery, Post>
{
private readonly IRepository<PostEntity> _repo;

public GetDraftQueryHandler(IRepository<PostEntity> repo) => _repo = repo;

public Task<Post> Handle(GetDraftQuery request, CancellationToken ct)
{
var spec = new PostSpec(request.Id);
var post = _repo.FirstOrDefaultAsync(spec, Post.EntitySelector);
var post = repo.FirstOrDefaultAsync(spec, Post.EntitySelector);
return post;
}
}
8 changes: 2 additions & 6 deletions src/Moonglade.Core/PostFeature/GetPostByIdQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@ namespace Moonglade.Core.PostFeature;

public record GetPostByIdQuery(Guid Id) : IRequest<Post>;

public class GetPostByIdQueryHandler : IRequestHandler<GetPostByIdQuery, Post>
public class GetPostByIdQueryHandler(IRepository<PostEntity> repo) : IRequestHandler<GetPostByIdQuery, Post>
{
private readonly IRepository<PostEntity> _repo;

public GetPostByIdQueryHandler(IRepository<PostEntity> repo) => _repo = repo;

public Task<Post> Handle(GetPostByIdQuery request, CancellationToken ct)
{
var spec = new PostSpec(request.Id);
var post = _repo.FirstOrDefaultAsync(spec, Post.EntitySelector);
var post = repo.FirstOrDefaultAsync(spec, Post.EntitySelector);
return post;
}
}
28 changes: 9 additions & 19 deletions src/Moonglade.Core/PostFeature/GetPostBySlugQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,9 @@ namespace Moonglade.Core.PostFeature;

public record GetPostBySlugQuery(PostSlug Slug) : IRequest<Post>;

public class GetPostBySlugQueryHandler : IRequestHandler<GetPostBySlugQuery, Post>
public class GetPostBySlugQueryHandler(IRepository<PostEntity> repo, ICacheAside cache, IConfiguration configuration)
: IRequestHandler<GetPostBySlugQuery, Post>
{
private readonly IRepository<PostEntity> _repo;
private readonly ICacheAside _cache;
private readonly IConfiguration _configuration;

public GetPostBySlugQueryHandler(IRepository<PostEntity> repo, ICacheAside cache, IConfiguration configuration)
{
_repo = repo;
_cache = cache;
_configuration = configuration;
}

public async Task<Post> Handle(GetPostBySlugQuery request, CancellationToken ct)
{
var date = new DateTime(request.Slug.Year, request.Slug.Month, request.Slug.Day);
Expand All @@ -28,27 +18,27 @@ public async Task<Post> Handle(GetPostBySlugQuery request, CancellationToken ct)
var slugCheckSum = Helper.ComputeCheckSum($"{request.Slug.Slug}#{date:yyyyMMdd}");
ISpecification<PostEntity> spec = new PostSpec(slugCheckSum);

var pid = await _repo.FirstOrDefaultAsync(spec, p => p.Id);
var pid = await repo.FirstOrDefaultAsync(spec, p => p.Id);
if (pid == Guid.Empty)
{
// Post does not have a checksum, fall back to old method
spec = new PostSpec(date, request.Slug.Slug);
pid = await _repo.FirstOrDefaultAsync(spec, x => x.Id);
pid = await repo.FirstOrDefaultAsync(spec, x => x.Id);

if (pid == Guid.Empty) return null;

// Post is found, fill it's checksum so that next time the query can be run against checksum
var p = await _repo.GetAsync(pid, ct);
var p = await repo.GetAsync(pid, ct);
p.HashCheckSum = slugCheckSum;

await _repo.UpdateAsync(p, ct);
await repo.UpdateAsync(p, ct);
}

var psm = await _cache.GetOrCreateAsync(BlogCachePartition.Post.ToString(), $"{pid}", async entry =>
var psm = await cache.GetOrCreateAsync(BlogCachePartition.Post.ToString(), $"{pid}", async entry =>
{
entry.SlidingExpiration = TimeSpan.FromMinutes(int.Parse(_configuration["CacheSlidingExpirationMinutes:Post"]!));
entry.SlidingExpiration = TimeSpan.FromMinutes(int.Parse(configuration["CacheSlidingExpirationMinutes:Post"]!));
var post = await _repo.FirstOrDefaultAsync(spec, Post.EntitySelector);
var post = await repo.FirstOrDefaultAsync(spec, Post.EntitySelector);
return post;
});

Expand Down
7 changes: 2 additions & 5 deletions src/Moonglade.Core/PostFeature/ListArchiveQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ namespace Moonglade.Core.PostFeature;

public record ListArchiveQuery(int Year, int? Month = null) : IRequest<IReadOnlyList<PostDigest>>;

public class ListArchiveQueryHandler : IRequestHandler<ListArchiveQuery, IReadOnlyList<PostDigest>>
public class ListArchiveQueryHandler(IRepository<PostEntity> repo) : IRequestHandler<ListArchiveQuery, IReadOnlyList<PostDigest>>
{
private readonly IRepository<PostEntity> _repo;
public ListArchiveQueryHandler(IRepository<PostEntity> repo) => _repo = repo;

public Task<IReadOnlyList<PostDigest>> Handle(ListArchiveQuery request, CancellationToken ct)
{
var spec = new PostSpec(request.Year, request.Month.GetValueOrDefault());
var list = _repo.SelectAsync(spec, PostDigest.EntitySelector, ct);
var list = repo.SelectAsync(spec, PostDigest.EntitySelector, ct);
return list;
}
}
Loading

0 comments on commit 0cb4cc6

Please sign in to comment.