🐛 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)
|
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)
|
var sessions = await db.AuthSessions.Where(e => e.Account.Id == account.Id).Select(e => e.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
foreach (var session in sessions)
|
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 db.SaveChangesAsync();
|
||||||
await ApplyRelationshipPermissions(relationship);
|
await ApplyRelationshipPermissions(relationship);
|
||||||
|
|
||||||
cache.Remove($"dyn_user_friends_{relationship.AccountId}");
|
cache.Remove($"UserFriends_{relationship.AccountId}");
|
||||||
cache.Remove($"dyn_user_friends_{relationship.RelatedId}");
|
cache.Remove($"UserFriends_{relationship.RelatedId}");
|
||||||
|
|
||||||
return relationship;
|
return relationship;
|
||||||
}
|
}
|
||||||
@ -105,8 +105,8 @@ public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCa
|
|||||||
ApplyRelationshipPermissions(relationshipBackward)
|
ApplyRelationshipPermissions(relationshipBackward)
|
||||||
);
|
);
|
||||||
|
|
||||||
cache.Remove($"dyn_user_friends_{relationship.AccountId}");
|
cache.Remove($"UserFriends_{relationship.AccountId}");
|
||||||
cache.Remove($"dyn_user_friends_{relationship.RelatedId}");
|
cache.Remove($"UserFriends_{relationship.RelatedId}");
|
||||||
|
|
||||||
return relationshipBackward;
|
return relationshipBackward;
|
||||||
}
|
}
|
||||||
@ -120,20 +120,20 @@ public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCa
|
|||||||
db.Update(relationship);
|
db.Update(relationship);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
await ApplyRelationshipPermissions(relationship);
|
await ApplyRelationshipPermissions(relationship);
|
||||||
cache.Remove($"dyn_user_friends_{related.Id}");
|
cache.Remove($"UserFriends_{related.Id}");
|
||||||
return relationship;
|
return relationship;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<long>> ListAccountFriends(Account account)
|
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
|
friends = await db.AccountRelationships
|
||||||
.Where(r => r.RelatedId == account.Id)
|
.Where(r => r.RelatedId == account.Id)
|
||||||
.Where(r => r.Status == RelationshipStatus.Friends)
|
.Where(r => r.Status == RelationshipStatus.Friends)
|
||||||
.Select(r => r.AccountId)
|
.Select(r => r.AccountId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
cache.Set($"dyn_user_friends_{account.Id}", friends, TimeSpan.FromHours(1));
|
cache.Set($"UserFriends_{account.Id}", friends, TimeSpan.FromHours(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return friends ?? [];
|
return friends ?? [];
|
||||||
|
@ -10,7 +10,7 @@ public class UserInfoMiddleware(RequestDelegate next, IMemoryCache cache)
|
|||||||
var sessionIdClaim = context.User.FindFirst("session_id")?.Value;
|
var sessionIdClaim = context.User.FindFirst("session_id")?.Value;
|
||||||
if (sessionIdClaim is not null && Guid.TryParse(sessionIdClaim, out var sessionId))
|
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
|
session = await db.AuthSessions
|
||||||
.Include(e => e.Challenge)
|
.Include(e => e.Challenge)
|
||||||
@ -21,7 +21,7 @@ public class UserInfoMiddleware(RequestDelegate next, IMemoryCache cache)
|
|||||||
|
|
||||||
if (session is not null)
|
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();
|
.FirstOrDefaultAsync();
|
||||||
if (post is null) return NotFound();
|
if (post is null) return NotFound();
|
||||||
|
|
||||||
|
var isSelfReact = post.Publisher.AccountId is not null && post.Publisher.AccountId == currentUser.Id;
|
||||||
|
|
||||||
var isExistingReaction = await db.PostReactions
|
var isExistingReaction = await db.PostReactions
|
||||||
.AnyAsync(r => r.PostId == post.Id &&
|
.AnyAsync(r => r.PostId == post.Id &&
|
||||||
r.Symbol == request.Symbol &&
|
r.Symbol == request.Symbol &&
|
||||||
@ -241,7 +243,7 @@ public class PostController(AppDatabase db, PostService ps, RelationshipService
|
|||||||
PostId = post.Id,
|
PostId = post.Id,
|
||||||
AccountId = currentUser.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();
|
if (isRemoving) return NoContent();
|
||||||
return Ok(reaction);
|
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="post">Post that modifying</param>
|
||||||
/// <param name="reaction">The new / target reaction adding / removing</param>
|
/// <param name="reaction">The new / target reaction adding / removing</param>
|
||||||
/// <param name="isRemoving">Indicate this operation is 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>()
|
var isExistingReaction = await db.Set<PostReaction>()
|
||||||
.AnyAsync(r => r.PostId == post.Id && r.AccountId == reaction.AccountId);
|
.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;
|
return isRemoving;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isSelfReact)
|
||||||
|
{
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
return isRemoving;
|
||||||
|
}
|
||||||
|
|
||||||
switch (reaction.Attitude)
|
switch (reaction.Attitude)
|
||||||
{
|
{
|
||||||
case PostReactionAttitude.Positive:
|
case PostReactionAttitude.Positive:
|
||||||
|
@ -26,6 +26,14 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
|
|||||||
return Ok(publisher);
|
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]
|
[HttpGet]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ActionResult<List<Publisher>>> ListManagedPublishers()
|
public async Task<ActionResult<List<Publisher>>> ListManagedPublishers()
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
using DysonNetwork.Sphere.Storage;
|
using DysonNetwork.Sphere.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Post;
|
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(
|
public async Task<Publisher> CreateIndividualPublisher(
|
||||||
Account.Account account,
|
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
|
// 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]
|
[HttpPost]
|
||||||
[RequiredPermission("global", "sticker.packs.create")]
|
[RequiredPermission("global", "stickers.packs.create")]
|
||||||
public async Task<ActionResult<StickerPack>> CreateStickerPack([FromBody] StickerPackRequest request)
|
public async Task<ActionResult<StickerPack>> CreateStickerPack([FromBody] StickerPackRequest request)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
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
|
var pack = await db.StickerPacks
|
||||||
.Include(p => p.Publisher)
|
.Include(p => p.Publisher)
|
||||||
.FirstOrDefaultAsync(p => p.Id == id);
|
.FirstOrDefaultAsync(p => p.Id == id);
|
||||||
|
|
||||||
if (pack is null)
|
if (pack is null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
var member = await db.PublisherMembers
|
var member = await db.PublisherMembers
|
||||||
.FirstOrDefaultAsync(m => m.AccountId == currentUser.Id && m.PublisherId == pack.PublisherId);
|
.FirstOrDefaultAsync(m => m.AccountId == currentUser.Id && m.PublisherId == pack.PublisherId);
|
||||||
|
|
||||||
if (member is null)
|
if (member is null)
|
||||||
return StatusCode(403, "You are not a member of this publisher");
|
return StatusCode(403, "You are not a member of this publisher");
|
||||||
|
|
||||||
if (member.Role < PublisherMemberRole.Editor)
|
if (member.Role < PublisherMemberRole.Editor)
|
||||||
return StatusCode(403, "You need to be at least an editor to update sticker packs");
|
return StatusCode(403, "You need to be at least an editor to update sticker packs");
|
||||||
|
|
||||||
@ -152,25 +149,16 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
|
|||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("{packId:guid}/content")]
|
||||||
[HttpGet("{packId:guid}/stickers")]
|
public async Task<ActionResult<List<Sticker>>> ListStickers(Guid packId)
|
||||||
public async Task<ActionResult<List<Sticker>>> ListStickers(Guid packId, [FromQuery] int offset = 0,
|
|
||||||
[FromQuery] int take = 20)
|
|
||||||
{
|
{
|
||||||
var totalCount = await db.Stickers
|
|
||||||
.Where(s => s.Pack.Id == packId)
|
|
||||||
.CountAsync();
|
|
||||||
|
|
||||||
var stickers = await db.Stickers
|
var stickers = await db.Stickers
|
||||||
.Where(s => s.Pack.Id == packId)
|
.Where(s => s.Pack.Id == packId)
|
||||||
.Include(e => e.Pack)
|
.Include(e => e.Pack)
|
||||||
.Include(e => e.Image)
|
.Include(e => e.Image)
|
||||||
.OrderByDescending(e => e.CreatedAt)
|
.OrderByDescending(e => e.CreatedAt)
|
||||||
.Skip(offset)
|
|
||||||
.Take(take)
|
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
Response.Headers["X-Total"] = totalCount.ToString();
|
|
||||||
return Ok(stickers);
|
return Ok(stickers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +177,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
|
|||||||
return Ok(sticker);
|
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)
|
public async Task<ActionResult<Sticker>> GetSticker(Guid packId, Guid id)
|
||||||
{
|
{
|
||||||
var sticker = await db.Stickers
|
var sticker = await db.Stickers
|
||||||
@ -208,7 +196,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
|
|||||||
public string? ImageId { get; set; }
|
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)
|
public async Task<IActionResult> UpdateSticker(Guid packId, Guid id, [FromBody] StickerRequest request)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser)
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser)
|
||||||
@ -219,6 +207,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
|
|||||||
return permissionCheck;
|
return permissionCheck;
|
||||||
|
|
||||||
var sticker = await db.Stickers
|
var sticker = await db.Stickers
|
||||||
|
.Include(s => s.Image)
|
||||||
.Include(s => s.Pack)
|
.Include(s => s.Pack)
|
||||||
.ThenInclude(p => p.Publisher)
|
.ThenInclude(p => p.Publisher)
|
||||||
.FirstOrDefaultAsync(e => e.Id == id && e.Pack.Id == packId);
|
.FirstOrDefaultAsync(e => e.Id == id && e.Pack.Id == packId);
|
||||||
@ -242,7 +231,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
|
|||||||
return Ok(sticker);
|
return Ok(sticker);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{packId:guid}/stickers/{id:guid}")]
|
[HttpDelete("{packId:guid}/content/{id:guid}")]
|
||||||
public async Task<IActionResult> DeleteSticker(Guid packId, Guid id)
|
public async Task<IActionResult> DeleteSticker(Guid packId, Guid id)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser)
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser)
|
||||||
@ -253,6 +242,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
|
|||||||
return permissionCheck;
|
return permissionCheck;
|
||||||
|
|
||||||
var sticker = await db.Stickers
|
var sticker = await db.Stickers
|
||||||
|
.Include(s => s.Image)
|
||||||
.Include(s => s.Pack)
|
.Include(s => s.Pack)
|
||||||
.ThenInclude(p => p.Publisher)
|
.ThenInclude(p => p.Publisher)
|
||||||
.FirstOrDefaultAsync(e => e.Id == id && e.Pack.Id == packId);
|
.FirstOrDefaultAsync(e => e.Id == id && e.Pack.Id == packId);
|
||||||
@ -264,7 +254,9 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
|
|||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{packId:guid}/stickers")]
|
public const int MaxStickersPerPack = 24;
|
||||||
|
|
||||||
|
[HttpPost("{packId:guid}/content")]
|
||||||
[RequiredPermission("global", "stickers.create")]
|
[RequiredPermission("global", "stickers.create")]
|
||||||
public async Task<IActionResult> CreateSticker(Guid packId, [FromBody] StickerRequest request)
|
public async Task<IActionResult> CreateSticker(Guid packId, [FromBody] StickerRequest request)
|
||||||
{
|
{
|
||||||
@ -283,10 +275,13 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa
|
|||||||
var pack = await db.StickerPacks
|
var pack = await db.StickerPacks
|
||||||
.Include(p => p.Publisher)
|
.Include(p => p.Publisher)
|
||||||
.FirstOrDefaultAsync(e => e.Id == packId);
|
.FirstOrDefaultAsync(e => e.Id == packId);
|
||||||
|
|
||||||
if (pack is null)
|
if (pack is null)
|
||||||
return BadRequest("Sticker pack was not found.");
|
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);
|
var image = await db.Files.FirstOrDefaultAsync(e => e.Id == request.ImageId);
|
||||||
if (image is null)
|
if (image is null)
|
||||||
return BadRequest("Image was not found.");
|
return BadRequest("Image was not found.");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user