From c43ff6be7bb6f579291420693987ba1522e2fa6a Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 19 Apr 2025 23:51:27 +0800 Subject: [PATCH] :bug: Bug fixes on posts and publishers --- .../Auth/UserInfoMiddleware.cs | 2 ++ DysonNetwork.Sphere/Casbin.conf | 2 +- DysonNetwork.Sphere/Post/PostController.cs | 12 ++++++-- DysonNetwork.Sphere/Post/PostService.cs | 30 ++++++++++++++----- .../Post/PublisherController.cs | 9 +++++- DysonNetwork.Sphere/Program.cs | 1 + DysonNetwork.sln.DotSettings.user | 1 + 7 files changed, 46 insertions(+), 11 deletions(-) diff --git a/DysonNetwork.Sphere/Auth/UserInfoMiddleware.cs b/DysonNetwork.Sphere/Auth/UserInfoMiddleware.cs index e09a201..d49618b 100644 --- a/DysonNetwork.Sphere/Auth/UserInfoMiddleware.cs +++ b/DysonNetwork.Sphere/Auth/UserInfoMiddleware.cs @@ -26,6 +26,8 @@ public class UserInfoMiddleware(RequestDelegate next, IMemoryCache cache) if (user is not null) { context.Items["CurrentUser"] = user; + var prefix = user.IsSuperuser ? "super:" : ""; + context.Items["CurrentIdentity"] = $"{prefix}{userId}"; } } diff --git a/DysonNetwork.Sphere/Casbin.conf b/DysonNetwork.Sphere/Casbin.conf index b3b09b6..6767047 100644 --- a/DysonNetwork.Sphere/Casbin.conf +++ b/DysonNetwork.Sphere/Casbin.conf @@ -11,4 +11,4 @@ g = _, _, _ e = some(where (p.eft == allow)) [matchers] -m = r.sub.StartsWith("super:") || (g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act) \ No newline at end of file +m = regexMatch(r.sub, "^super:") || (g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act) \ No newline at end of file diff --git a/DysonNetwork.Sphere/Post/PostController.cs b/DysonNetwork.Sphere/Post/PostController.cs index 407c6b9..eca8181 100644 --- a/DysonNetwork.Sphere/Post/PostController.cs +++ b/DysonNetwork.Sphere/Post/PostController.cs @@ -1,12 +1,14 @@ using System.ComponentModel.DataAnnotations; +using Casbin; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using NodaTime; namespace DysonNetwork.Sphere.Post; [ApiController] [Route("/posts")] -public class PostController(AppDatabase db, PostService ps) : ControllerBase +public class PostController(AppDatabase db, PostService ps, IEnforcer enforcer) : ControllerBase { [HttpGet] public async Task>> ListPosts([FromQuery] int offset = 0, [FromQuery] int take = 20) @@ -99,6 +101,7 @@ public class PostController(AppDatabase db, PostService ps) : ControllerBase [MaxLength(8)] public List? Categories { get; set; } [MaxLength(32)] public List? Attachments { get; set; } public Dictionary? Meta { get; set; } + public Instant? PublishedAt { get; set; } } [HttpPost] @@ -108,6 +111,8 @@ public class PostController(AppDatabase db, PostService ps) : ControllerBase ) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); + if (!await enforcer.EnforceAsync((string)HttpContext.Items["CurrentIdentity"]!, "global", "posts", "create")) + return StatusCode(403); Publisher? publisher; if (publisherName is null) @@ -136,8 +141,10 @@ public class PostController(AppDatabase db, PostService ps) : ControllerBase Description = request.Description, Content = request.Content, Visibility = request.Visibility ?? PostVisibility.Public, + PublishedAt = request.PublishedAt, Type = request.Type ?? PostType.Moment, Meta = request.Meta, + Publisher = publisher, }; try @@ -190,7 +197,8 @@ public class PostController(AppDatabase db, PostService ps) : ControllerBase post, attachments: request.Attachments, tags: request.Tags, - categories: request.Categories + categories: request.Categories, + publishedAt: request.PublishedAt ); } catch (InvalidOperationException err) diff --git a/DysonNetwork.Sphere/Post/PostService.cs b/DysonNetwork.Sphere/Post/PostService.cs index cafa0dd..3e1002b 100644 --- a/DysonNetwork.Sphere/Post/PostService.cs +++ b/DysonNetwork.Sphere/Post/PostService.cs @@ -13,6 +13,12 @@ public class PostService(AppDatabase db, FileService fs) List? categories = null ) { + if (post.PublishedAt is not null) + { + if (post.PublishedAt.Value.ToDateTimeUtc() < DateTime.UtcNow) + throw new InvalidOperationException("Cannot create the post which published in the past."); + } + if (attachments is not null) { post.Attachments = await db.Files.Where(e => attachments.Contains(e.Id)).ToListAsync(); @@ -46,7 +52,7 @@ public class PostService(AppDatabase db, FileService fs) if (post.Categories.Count != categories.Distinct().Count()) throw new InvalidOperationException("Categories contains one or more categories that wasn't exists."); } - + if (post.Empty) throw new InvalidOperationException("Cannot create a post with barely no content."); @@ -63,11 +69,21 @@ public class PostService(AppDatabase db, FileService fs) Post post, List? attachments = null, List? tags = null, - List? categories = null + List? categories = null, + Instant? publishedAt = null ) { post.EditedAt = Instant.FromDateTimeUtc(DateTime.UtcNow); + if (publishedAt is not null) + { + // User cannot set the published at to the past to prevent scam, + // But we can just let the controller set the published at, because when no changes to + // the published at will blocked the update operation + if (publishedAt.Value.ToDateTimeUtc() < DateTime.UtcNow) + throw new InvalidOperationException("Cannot set the published at to the past."); + } + if (attachments is not null) { var records = await db.Files.Where(e => attachments.Contains(e.Id)).ToListAsync(); @@ -87,7 +103,7 @@ public class PostService(AppDatabase db, FileService fs) await fs.MarkUsageRangeAsync(added, 1); await fs.MarkUsageRangeAsync(removed, -1); } - + if (tags is not null) { var existingTags = await db.PostTags.Where(e => tags.Contains(e.Slug)).ToListAsync(); @@ -111,14 +127,14 @@ public class PostService(AppDatabase db, FileService fs) post.Categories = await db.PostCategories.Where(e => categories.Contains(e.Slug)).ToListAsync(); if (post.Categories.Count != categories.Distinct().Count()) throw new InvalidOperationException("Categories contains one or more categories that wasn't exists."); - } - + } + if (post.Empty) throw new InvalidOperationException("Cannot edit a post to barely no content."); - + db.Update(post); await db.SaveChangesAsync(); - + return post; } diff --git a/DysonNetwork.Sphere/Post/PublisherController.cs b/DysonNetwork.Sphere/Post/PublisherController.cs index 785d966..567c27a 100644 --- a/DysonNetwork.Sphere/Post/PublisherController.cs +++ b/DysonNetwork.Sphere/Post/PublisherController.cs @@ -20,6 +20,8 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic var publisher = await db.Publishers .Where(e => e.Name == name) + .Include(e => e.Picture) + .Include(e => e.Background) .FirstOrDefaultAsync(); if (publisher is null) return NotFound(); @@ -37,6 +39,8 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic .Where(m => m.AccountId == userId) .Where(m => m.JoinedAt != null) .Include(e => e.Publisher) + .Include(e => e.Publisher.Picture) + .Include(e => e.Publisher.Background) .ToListAsync(); return members.Select(m => m.Publisher).ToList(); @@ -53,6 +57,8 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic .Where(m => m.AccountId == userId) .Where(m => m.JoinedAt == null) .Include(e => e.Publisher) + .Include(e => e.Publisher.Picture) + .Include(e => e.Publisher.Background) .ToListAsync(); return members.ToList(); @@ -164,7 +170,8 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic public async Task> CreatePublisherIndividual(PublisherRequest request) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); - if (!await enforcer.EnforceAsync(currentUser.Id.ToString(), "global", "publishers", "create")) + if (!await enforcer.EnforceAsync((string)HttpContext.Items["CurrentIdentity"]!, "global", "publishers", + "create")) return StatusCode(403); var takenName = request.Name ?? currentUser.Name; diff --git a/DysonNetwork.Sphere/Program.cs b/DysonNetwork.Sphere/Program.cs index 9efae50..d17da85 100644 --- a/DysonNetwork.Sphere/Program.cs +++ b/DysonNetwork.Sphere/Program.cs @@ -51,6 +51,7 @@ var casbinDbContext = new CasbinDbContext( var casbinEfcore = new EFCoreAdapter(casbinDbContext); casbinDbContext.Database.EnsureCreated(); var casbinEncofcer = new Enforcer("Casbin.conf", casbinEfcore); +casbinEncofcer.EnableCache(true); casbinEncofcer.LoadPolicy(); builder.Services.AddSingleton(casbinEncofcer); diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index 266e9af..3a5abba 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -11,6 +11,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded