🐛 Fixes for sticker & sticker packs

This commit is contained in:
LittleSheep 2025-05-11 22:13:13 +08:00
parent eab775e224
commit 3d5d4db3e3
8 changed files with 93 additions and 34 deletions

View File

@ -17,13 +17,13 @@ public class AccountService(
{
public async Task PurgeAccountCache(Account account)
{
cache.Remove($"dyn_user_friends_{account.Id}");
cache.Remove($"UserFriends_{account.Id}");
var sessions = await db.AuthSessions.Where(e => e.Account.Id == account.Id).Select(e => e.Id)
.ToListAsync();
foreach (var session in sessions)
{
cache.Remove($"dyn_auth_{session}");
cache.Remove($"Auth_{session}");
}
}

View File

@ -51,8 +51,8 @@ public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCa
await db.SaveChangesAsync();
await ApplyRelationshipPermissions(relationship);
cache.Remove($"dyn_user_friends_{relationship.AccountId}");
cache.Remove($"dyn_user_friends_{relationship.RelatedId}");
cache.Remove($"UserFriends_{relationship.AccountId}");
cache.Remove($"UserFriends_{relationship.RelatedId}");
return relationship;
}
@ -105,8 +105,8 @@ public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCa
ApplyRelationshipPermissions(relationshipBackward)
);
cache.Remove($"dyn_user_friends_{relationship.AccountId}");
cache.Remove($"dyn_user_friends_{relationship.RelatedId}");
cache.Remove($"UserFriends_{relationship.AccountId}");
cache.Remove($"UserFriends_{relationship.RelatedId}");
return relationshipBackward;
}
@ -120,20 +120,20 @@ public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCa
db.Update(relationship);
await db.SaveChangesAsync();
await ApplyRelationshipPermissions(relationship);
cache.Remove($"dyn_user_friends_{related.Id}");
cache.Remove($"UserFriends_{related.Id}");
return relationship;
}
public async Task<List<long>> ListAccountFriends(Account account)
{
if (!cache.TryGetValue($"dyn_user_friends_{account.Id}", out List<long>? friends))
if (!cache.TryGetValue($"UserFriends_{account.Id}", out List<long>? friends))
{
friends = await db.AccountRelationships
.Where(r => r.RelatedId == account.Id)
.Where(r => r.Status == RelationshipStatus.Friends)
.Select(r => r.AccountId)
.ToListAsync();
cache.Set($"dyn_user_friends_{account.Id}", friends, TimeSpan.FromHours(1));
cache.Set($"UserFriends_{account.Id}", friends, TimeSpan.FromHours(1));
}
return friends ?? [];

View File

@ -10,7 +10,7 @@ public class UserInfoMiddleware(RequestDelegate next, IMemoryCache cache)
var sessionIdClaim = context.User.FindFirst("session_id")?.Value;
if (sessionIdClaim is not null && Guid.TryParse(sessionIdClaim, out var sessionId))
{
if (!cache.TryGetValue($"dyn_auth_{sessionId}", out Session? session))
if (!cache.TryGetValue($"Auth_{sessionId}", out Session? session))
{
session = await db.AuthSessions
.Include(e => e.Challenge)
@ -21,7 +21,7 @@ public class UserInfoMiddleware(RequestDelegate next, IMemoryCache cache)
if (session is not null)
{
cache.Set($"dyn_auth_{sessionId}", session, TimeSpan.FromHours(1));
cache.Set($"Auth_{sessionId}", session, TimeSpan.FromHours(1));
}
}

View File

@ -230,6 +230,8 @@ public class PostController(AppDatabase db, PostService ps, RelationshipService
.FirstOrDefaultAsync();
if (post is null) return NotFound();
var isSelfReact = post.Publisher.AccountId is not null && post.Publisher.AccountId == currentUser.Id;
var isExistingReaction = await db.PostReactions
.AnyAsync(r => r.PostId == post.Id &&
r.Symbol == request.Symbol &&
@ -241,7 +243,7 @@ public class PostController(AppDatabase db, PostService ps, RelationshipService
PostId = post.Id,
AccountId = currentUser.Id
};
var isRemoving = await ps.ModifyPostVotes(post, reaction, isExistingReaction);
var isRemoving = await ps.ModifyPostVotes(post, reaction, isExistingReaction, isSelfReact);
if (isRemoving) return NoContent();
return Ok(reaction);

View File

@ -174,7 +174,7 @@ public class PostService(AppDatabase db, FileService fs, ActivityService act)
/// <param name="post">Post that modifying</param>
/// <param name="reaction">The new / target reaction adding / removing</param>
/// <param name="isRemoving">Indicate this operation is adding / removing</param>
public async Task<bool> ModifyPostVotes(Post post, PostReaction reaction, bool isRemoving)
public async Task<bool> ModifyPostVotes(Post post, PostReaction reaction, bool isRemoving, bool isSelfReact)
{
var isExistingReaction = await db.Set<PostReaction>()
.AnyAsync(r => r.PostId == post.Id && r.AccountId == reaction.AccountId);
@ -193,6 +193,12 @@ public class PostService(AppDatabase db, FileService fs, ActivityService act)
return isRemoving;
}
if (isSelfReact)
{
await db.SaveChangesAsync();
return isRemoving;
}
switch (reaction.Attitude)
{
case PostReactionAttitude.Positive:

View File

@ -26,6 +26,14 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
return Ok(publisher);
}
[HttpGet("{name}/stats")]
public async Task<ActionResult<PublisherService.PublisherStats>> GetPublisherStats(string name)
{
var stats = await ps.GetPublisherStats(name);
if (stats is null) return NotFound();
return Ok(stats);
}
[HttpGet]
[Authorize]
public async Task<ActionResult<List<Publisher>>> ListManagedPublishers()

View File

@ -1,9 +1,11 @@
using DysonNetwork.Sphere.Storage;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using NodaTime;
namespace DysonNetwork.Sphere.Post;
public class PublisherService(AppDatabase db, FileService fs)
public class PublisherService(AppDatabase db, FileService fs, IMemoryCache cache)
{
public async Task<Publisher> CreateIndividualPublisher(
Account.Account account,
@ -44,4 +46,50 @@ public class PublisherService(AppDatabase db, FileService fs)
}
// TODO Able to create organizational publisher when the realm system is completed
public class PublisherStats
{
public int PostsCreated { get; set; }
public int StickerPacksCreated { get; set; }
public int StickersCreated { get; set; }
public int UpvoteReceived { get; set; }
public int DownvoteReceived { get; set; }
}
private const string PublisherStatsCacheKey = "PublisherStats_{0}";
public async Task<PublisherStats?> GetPublisherStats(string name)
{
var cacheKey = string.Format(PublisherStatsCacheKey, name);
if (cache.TryGetValue(cacheKey, out PublisherStats? stats))
return stats;
var publisher = await db.Publishers.FirstOrDefaultAsync(e => e.Name == name);
if (publisher is null) return null;
var postsCount = await db.Posts.Where(e => e.Publisher.Id == publisher.Id).CountAsync();
var postsUpvotes = await db.PostReactions
.Where(r => r.Post.Publisher.Id == publisher.Id && r.Attitude == PostReactionAttitude.Positive)
.CountAsync();
var postsDownvotes = await db.PostReactions
.Where(r => r.Post.Publisher.Id == publisher.Id && r.Attitude == PostReactionAttitude.Negative)
.CountAsync();
var stickerPacksId = await db.StickerPacks.Where(e => e.Publisher.Id == publisher.Id).Select(e => e.Id).ToListAsync();
var stickerPacksCount = stickerPacksId.Count;
var stickersCount = await db.Stickers.Where(e => stickerPacksId.Contains(e.PackId)).CountAsync();
stats = new PublisherStats
{
PostsCreated = postsCount,
StickerPacksCreated = stickerPacksCount,
StickersCreated = stickersCount,
UpvoteReceived = postsUpvotes,
DownvoteReceived = postsDownvotes
};
cache.Set(cacheKey, stats, TimeSpan.FromMinutes(5));
return stats;
}
}

View File

@ -63,7 +63,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
}
[HttpPost]
[RequiredPermission("global", "sticker.packs.create")]
[RequiredPermission("global", "stickers.packs.create")]
public async Task<ActionResult<StickerPack>> CreateStickerPack([FromBody] StickerPackRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
@ -104,16 +104,13 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
var pack = await db.StickerPacks
.Include(p => p.Publisher)
.FirstOrDefaultAsync(p => p.Id == id);
if (pack is null)
return NotFound();
var member = await db.PublisherMembers
.FirstOrDefaultAsync(m => m.AccountId == currentUser.Id && m.PublisherId == pack.PublisherId);
if (member is null)
return StatusCode(403, "You are not a member of this publisher");
if (member.Role < PublisherMemberRole.Editor)
return StatusCode(403, "You need to be at least an editor to update sticker packs");
@ -151,26 +148,17 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
await st.DeleteStickerPackAsync(pack);
return NoContent();
}
[HttpGet("{packId:guid}/stickers")]
public async Task<ActionResult<List<Sticker>>> ListStickers(Guid packId, [FromQuery] int offset = 0,
[FromQuery] int take = 20)
[HttpGet("{packId:guid}/content")]
public async Task<ActionResult<List<Sticker>>> ListStickers(Guid packId)
{
var totalCount = await db.Stickers
.Where(s => s.Pack.Id == packId)
.CountAsync();
var stickers = await db.Stickers
.Where(s => s.Pack.Id == packId)
.Include(e => e.Pack)
.Include(e => e.Image)
.OrderByDescending(e => e.CreatedAt)
.Skip(offset)
.Take(take)
.ToListAsync();
Response.Headers["X-Total"] = totalCount.ToString();
return Ok(stickers);
}
@ -189,7 +177,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
return Ok(sticker);
}
[HttpGet("{packId:guid}/stickers/{id:guid}")]
[HttpGet("{packId:guid}/content/{id:guid}")]
public async Task<ActionResult<Sticker>> GetSticker(Guid packId, Guid id)
{
var sticker = await db.Stickers
@ -208,7 +196,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
public string? ImageId { get; set; }
}
[HttpPatch("{packId:guid}/stickers/{id:guid}")]
[HttpPatch("{packId:guid}/content/{id:guid}")]
public async Task<IActionResult> UpdateSticker(Guid packId, Guid id, [FromBody] StickerRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser)
@ -219,6 +207,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
return permissionCheck;
var sticker = await db.Stickers
.Include(s => s.Image)
.Include(s => s.Pack)
.ThenInclude(p => p.Publisher)
.FirstOrDefaultAsync(e => e.Id == id && e.Pack.Id == packId);
@ -242,7 +231,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
return Ok(sticker);
}
[HttpDelete("{packId:guid}/stickers/{id:guid}")]
[HttpDelete("{packId:guid}/content/{id:guid}")]
public async Task<IActionResult> DeleteSticker(Guid packId, Guid id)
{
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser)
@ -253,6 +242,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
return permissionCheck;
var sticker = await db.Stickers
.Include(s => s.Image)
.Include(s => s.Pack)
.ThenInclude(p => p.Publisher)
.FirstOrDefaultAsync(e => e.Id == id && e.Pack.Id == packId);
@ -264,7 +254,9 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
return NoContent();
}
[HttpPost("{packId:guid}/stickers")]
public const int MaxStickersPerPack = 24;
[HttpPost("{packId:guid}/content")]
[RequiredPermission("global", "stickers.create")]
public async Task<IActionResult> CreateSticker(Guid packId, [FromBody] StickerRequest request)
{
@ -283,9 +275,12 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
var pack = await db.StickerPacks
.Include(p => p.Publisher)
.FirstOrDefaultAsync(e => e.Id == packId);
if (pack is null)
return BadRequest("Sticker pack was not found.");
var stickersCount = await db.Stickers.CountAsync(s => s.PackId == packId);
if (stickersCount >= MaxStickersPerPack)
return BadRequest($"Sticker pack has reached maximum capacity of {MaxStickersPerPack} stickers.");
var image = await db.Files.FirstOrDefaultAsync(e => e.Id == request.ImageId);
if (image is null)