🐛 Bug fixes on posts and publishers
This commit is contained in:
parent
fb1de3da9e
commit
c43ff6be7b
@ -26,6 +26,8 @@ public class UserInfoMiddleware(RequestDelegate next, IMemoryCache cache)
|
|||||||
if (user is not null)
|
if (user is not null)
|
||||||
{
|
{
|
||||||
context.Items["CurrentUser"] = user;
|
context.Items["CurrentUser"] = user;
|
||||||
|
var prefix = user.IsSuperuser ? "super:" : "";
|
||||||
|
context.Items["CurrentIdentity"] = $"{prefix}{userId}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,4 +11,4 @@ g = _, _, _
|
|||||||
e = some(where (p.eft == allow))
|
e = some(where (p.eft == allow))
|
||||||
|
|
||||||
[matchers]
|
[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)
|
m = regexMatch(r.sub, "^super:") || (g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act)
|
@ -1,12 +1,14 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Casbin;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Post;
|
namespace DysonNetwork.Sphere.Post;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("/posts")]
|
[Route("/posts")]
|
||||||
public class PostController(AppDatabase db, PostService ps) : ControllerBase
|
public class PostController(AppDatabase db, PostService ps, IEnforcer enforcer) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<List<Post>>> ListPosts([FromQuery] int offset = 0, [FromQuery] int take = 20)
|
public async Task<ActionResult<List<Post>>> 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<string>? Categories { get; set; }
|
[MaxLength(8)] public List<string>? Categories { get; set; }
|
||||||
[MaxLength(32)] public List<string>? Attachments { get; set; }
|
[MaxLength(32)] public List<string>? Attachments { get; set; }
|
||||||
public Dictionary<string, object>? Meta { get; set; }
|
public Dictionary<string, object>? Meta { get; set; }
|
||||||
|
public Instant? PublishedAt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[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 (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;
|
Publisher? publisher;
|
||||||
if (publisherName is null)
|
if (publisherName is null)
|
||||||
@ -136,8 +141,10 @@ public class PostController(AppDatabase db, PostService ps) : ControllerBase
|
|||||||
Description = request.Description,
|
Description = request.Description,
|
||||||
Content = request.Content,
|
Content = request.Content,
|
||||||
Visibility = request.Visibility ?? PostVisibility.Public,
|
Visibility = request.Visibility ?? PostVisibility.Public,
|
||||||
|
PublishedAt = request.PublishedAt,
|
||||||
Type = request.Type ?? PostType.Moment,
|
Type = request.Type ?? PostType.Moment,
|
||||||
Meta = request.Meta,
|
Meta = request.Meta,
|
||||||
|
Publisher = publisher,
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -190,7 +197,8 @@ public class PostController(AppDatabase db, PostService ps) : ControllerBase
|
|||||||
post,
|
post,
|
||||||
attachments: request.Attachments,
|
attachments: request.Attachments,
|
||||||
tags: request.Tags,
|
tags: request.Tags,
|
||||||
categories: request.Categories
|
categories: request.Categories,
|
||||||
|
publishedAt: request.PublishedAt
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException err)
|
catch (InvalidOperationException err)
|
||||||
|
@ -13,6 +13,12 @@ public class PostService(AppDatabase db, FileService fs)
|
|||||||
List<string>? categories = null
|
List<string>? 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)
|
if (attachments is not null)
|
||||||
{
|
{
|
||||||
post.Attachments = await db.Files.Where(e => attachments.Contains(e.Id)).ToListAsync();
|
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())
|
if (post.Categories.Count != categories.Distinct().Count())
|
||||||
throw new InvalidOperationException("Categories contains one or more categories that wasn't exists.");
|
throw new InvalidOperationException("Categories contains one or more categories that wasn't exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (post.Empty)
|
if (post.Empty)
|
||||||
throw new InvalidOperationException("Cannot create a post with barely no content.");
|
throw new InvalidOperationException("Cannot create a post with barely no content.");
|
||||||
|
|
||||||
@ -63,11 +69,21 @@ public class PostService(AppDatabase db, FileService fs)
|
|||||||
Post post,
|
Post post,
|
||||||
List<string>? attachments = null,
|
List<string>? attachments = null,
|
||||||
List<string>? tags = null,
|
List<string>? tags = null,
|
||||||
List<string>? categories = null
|
List<string>? categories = null,
|
||||||
|
Instant? publishedAt = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
post.EditedAt = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
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)
|
if (attachments is not null)
|
||||||
{
|
{
|
||||||
var records = await db.Files.Where(e => attachments.Contains(e.Id)).ToListAsync();
|
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(added, 1);
|
||||||
await fs.MarkUsageRangeAsync(removed, -1);
|
await fs.MarkUsageRangeAsync(removed, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tags is not null)
|
if (tags is not null)
|
||||||
{
|
{
|
||||||
var existingTags = await db.PostTags.Where(e => tags.Contains(e.Slug)).ToListAsync();
|
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();
|
post.Categories = await db.PostCategories.Where(e => categories.Contains(e.Slug)).ToListAsync();
|
||||||
if (post.Categories.Count != categories.Distinct().Count())
|
if (post.Categories.Count != categories.Distinct().Count())
|
||||||
throw new InvalidOperationException("Categories contains one or more categories that wasn't exists.");
|
throw new InvalidOperationException("Categories contains one or more categories that wasn't exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (post.Empty)
|
if (post.Empty)
|
||||||
throw new InvalidOperationException("Cannot edit a post to barely no content.");
|
throw new InvalidOperationException("Cannot edit a post to barely no content.");
|
||||||
|
|
||||||
db.Update(post);
|
db.Update(post);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
return post;
|
return post;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
|
|||||||
|
|
||||||
var publisher = await db.Publishers
|
var publisher = await db.Publishers
|
||||||
.Where(e => e.Name == name)
|
.Where(e => e.Name == name)
|
||||||
|
.Include(e => e.Picture)
|
||||||
|
.Include(e => e.Background)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (publisher is null) return NotFound();
|
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.AccountId == userId)
|
||||||
.Where(m => m.JoinedAt != null)
|
.Where(m => m.JoinedAt != null)
|
||||||
.Include(e => e.Publisher)
|
.Include(e => e.Publisher)
|
||||||
|
.Include(e => e.Publisher.Picture)
|
||||||
|
.Include(e => e.Publisher.Background)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return members.Select(m => m.Publisher).ToList();
|
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.AccountId == userId)
|
||||||
.Where(m => m.JoinedAt == null)
|
.Where(m => m.JoinedAt == null)
|
||||||
.Include(e => e.Publisher)
|
.Include(e => e.Publisher)
|
||||||
|
.Include(e => e.Publisher.Picture)
|
||||||
|
.Include(e => e.Publisher.Background)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return members.ToList();
|
return members.ToList();
|
||||||
@ -164,7 +170,8 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
|
|||||||
public async Task<ActionResult<Publisher>> CreatePublisherIndividual(PublisherRequest request)
|
public async Task<ActionResult<Publisher>> CreatePublisherIndividual(PublisherRequest request)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
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);
|
return StatusCode(403);
|
||||||
|
|
||||||
var takenName = request.Name ?? currentUser.Name;
|
var takenName = request.Name ?? currentUser.Name;
|
||||||
|
@ -51,6 +51,7 @@ var casbinDbContext = new CasbinDbContext<int>(
|
|||||||
var casbinEfcore = new EFCoreAdapter<int>(casbinDbContext);
|
var casbinEfcore = new EFCoreAdapter<int>(casbinDbContext);
|
||||||
casbinDbContext.Database.EnsureCreated();
|
casbinDbContext.Database.EnsureCreated();
|
||||||
var casbinEncofcer = new Enforcer("Casbin.conf", casbinEfcore);
|
var casbinEncofcer = new Enforcer("Casbin.conf", casbinEfcore);
|
||||||
|
casbinEncofcer.EnableCache(true);
|
||||||
casbinEncofcer.LoadPolicy();
|
casbinEncofcer.LoadPolicy();
|
||||||
|
|
||||||
builder.Services.AddSingleton<IEnforcer>(casbinEncofcer);
|
builder.Services.AddSingleton<IEnforcer>(casbinEncofcer);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADirectory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb6f0571a6bc744b0b551fd4578292582e54c00_003Fde_003F94973e27_003FDirectory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADirectory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb6f0571a6bc744b0b551fd4578292582e54c00_003Fde_003F94973e27_003FDirectory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEndpointConventionBuilderExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003F8a_003F101938e3_003FEndpointConventionBuilderExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEndpointConventionBuilderExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003F8a_003F101938e3_003FEndpointConventionBuilderExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnforcerExtension_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbb4a120e56464fc6abd8c30969ef70864ba00_003Fb5_003F180850e0_003FEnforcerExtension_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnforcerExtension_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbb4a120e56464fc6abd8c30969ef70864ba00_003Fb5_003F180850e0_003FEnforcerExtension_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnforcer_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbb4a120e56464fc6abd8c30969ef70864ba00_003F47_003F3a6b6c4b_003FEnforcer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fe096e6f12c5d6b49356bc34ff1ea08738f910c0929c9d717c9cba7f44288_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fe096e6f12c5d6b49356bc34ff1ea08738f910c0929c9d717c9cba7f44288_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcb0587797ea44bd6915ede69888c6766291038_003F55_003F277f2d4c_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcb0587797ea44bd6915ede69888c6766291038_003F55_003F277f2d4c_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a28847852ee9ba45fd3107526c0a749a733bd4f4ebf33aa3c9a59737a3f758_003FEntityFrameworkServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a28847852ee9ba45fd3107526c0a749a733bd4f4ebf33aa3c9a59737a3f758_003FEntityFrameworkServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user