🐛 Fixes for sticker & sticker packs
This commit is contained in:
parent
eab775e224
commit
3d5d4db3e3
@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 ?? [];
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user