🐛 Bug fixes 💄 Optimizations
This commit is contained in:
parent
5b9b28d77a
commit
cf9084b8c0
@ -32,6 +32,7 @@ public class ActionLogType
|
|||||||
public const string RealmJoin = "realms.join";
|
public const string RealmJoin = "realms.join";
|
||||||
public const string RealmLeave = "realms.leave";
|
public const string RealmLeave = "realms.leave";
|
||||||
public const string RealmKick = "realms.kick";
|
public const string RealmKick = "realms.kick";
|
||||||
|
public const string RealmAdjustRole = "realms.role.edit";
|
||||||
public const string ChatroomCreate = "chatrooms.create";
|
public const string ChatroomCreate = "chatrooms.create";
|
||||||
public const string ChatroomUpdate = "chatrooms.update";
|
public const string ChatroomUpdate = "chatrooms.update";
|
||||||
public const string ChatroomDelete = "chatrooms.delete";
|
public const string ChatroomDelete = "chatrooms.delete";
|
||||||
@ -39,6 +40,7 @@ public class ActionLogType
|
|||||||
public const string ChatroomJoin = "chatrooms.join";
|
public const string ChatroomJoin = "chatrooms.join";
|
||||||
public const string ChatroomLeave = "chatrooms.leave";
|
public const string ChatroomLeave = "chatrooms.leave";
|
||||||
public const string ChatroomKick = "chatrooms.kick";
|
public const string ChatroomKick = "chatrooms.kick";
|
||||||
|
public const string ChatroomAdjustRole = "chatrooms.role.edit";
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ActionLog : ModelBase
|
public class ActionLog : ModelBase
|
||||||
|
@ -115,7 +115,7 @@ public class AppDatabase(
|
|||||||
"chat.realtime.create",
|
"chat.realtime.create",
|
||||||
"accounts.statuses.create",
|
"accounts.statuses.create",
|
||||||
"accounts.statuses.update",
|
"accounts.statuses.update",
|
||||||
"stickers.pack.create",
|
"stickers.packs.create",
|
||||||
"stickers.create"
|
"stickers.create"
|
||||||
}.Select(permission =>
|
}.Select(permission =>
|
||||||
PermissionService.NewPermissionNode("group:default", "global", permission, true))
|
PermissionService.NewPermissionNode("group:default", "global", permission, true))
|
||||||
|
@ -18,6 +18,7 @@ public class ChatRoom : ModelBase
|
|||||||
[MaxLength(1024)] public string? Name { get; set; }
|
[MaxLength(1024)] public string? Name { get; set; }
|
||||||
[MaxLength(4096)] public string? Description { get; set; }
|
[MaxLength(4096)] public string? Description { get; set; }
|
||||||
public ChatRoomType Type { get; set; }
|
public ChatRoomType Type { get; set; }
|
||||||
|
public bool IsCommunity { get; set; }
|
||||||
public bool IsPublic { get; set; }
|
public bool IsPublic { get; set; }
|
||||||
|
|
||||||
[MaxLength(32)] public string? PictureId { get; set; }
|
[MaxLength(32)] public string? PictureId { get; set; }
|
||||||
|
@ -127,6 +127,8 @@ public class ChatRoomController(
|
|||||||
[MaxLength(32)] public string? PictureId { get; set; }
|
[MaxLength(32)] public string? PictureId { get; set; }
|
||||||
[MaxLength(32)] public string? BackgroundId { get; set; }
|
[MaxLength(32)] public string? BackgroundId { get; set; }
|
||||||
public Guid? RealmId { get; set; }
|
public Guid? RealmId { get; set; }
|
||||||
|
public bool? IsCommunity { get; set; }
|
||||||
|
public bool? IsPublic { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
@ -141,6 +143,8 @@ public class ChatRoomController(
|
|||||||
{
|
{
|
||||||
Name = request.Name,
|
Name = request.Name,
|
||||||
Description = request.Description ?? string.Empty,
|
Description = request.Description ?? string.Empty,
|
||||||
|
IsCommunity = request.IsCommunity ?? false,
|
||||||
|
IsPublic = request.IsPublic ?? false,
|
||||||
Type = ChatRoomType.Group,
|
Type = ChatRoomType.Group,
|
||||||
Members = new List<ChatMember>
|
Members = new List<ChatMember>
|
||||||
{
|
{
|
||||||
@ -243,6 +247,10 @@ public class ChatRoomController(
|
|||||||
chatRoom.Name = request.Name;
|
chatRoom.Name = request.Name;
|
||||||
if (request.Description is not null)
|
if (request.Description is not null)
|
||||||
chatRoom.Description = request.Description;
|
chatRoom.Description = request.Description;
|
||||||
|
if (request.IsCommunity is not null)
|
||||||
|
chatRoom.IsCommunity = request.IsCommunity.Value;
|
||||||
|
if (request.IsPublic is not null)
|
||||||
|
chatRoom.IsPublic = request.IsPublic.Value;
|
||||||
|
|
||||||
db.ChatRooms.Update(chatRoom);
|
db.ChatRooms.Update(chatRoom);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
@ -507,29 +515,34 @@ public class ChatRoomController(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Check if the current user has permission to change roles
|
|
||||||
var currentMember = await db.ChatMembers
|
|
||||||
.Where(m => m.AccountId == currentUser.Id && m.ChatRoomId == roomId)
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
if (currentMember is null || currentMember.Role < ChatMemberRole.Moderator)
|
|
||||||
return StatusCode(403, "You need at least be a moderator to change member roles.");
|
|
||||||
|
|
||||||
// Find the target member
|
|
||||||
var targetMember = await db.ChatMembers
|
var targetMember = await db.ChatMembers
|
||||||
.Where(m => m.AccountId == memberId && m.ChatRoomId == roomId)
|
.Where(m => m.AccountId == memberId && m.ChatRoomId == roomId)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (targetMember is null) return NotFound();
|
if (targetMember is null) return NotFound();
|
||||||
|
|
||||||
// Check if the current user has sufficient permissions
|
// Check if the current user has permission to change roles
|
||||||
if (currentMember.Role <= targetMember.Role)
|
if (
|
||||||
return StatusCode(403, "You cannot modify the role of members with equal or higher roles.");
|
!await crs.IsMemberWithRole(
|
||||||
if (currentMember.Role <= newRole)
|
chatRoom.Id,
|
||||||
return StatusCode(403, "You cannot assign a role equal to or higher than your own.");
|
currentUser.Id,
|
||||||
|
ChatMemberRole.Moderator,
|
||||||
|
targetMember.Role,
|
||||||
|
newRole
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return StatusCode(403, "You don't have enough permission to edit the roles of members.");
|
||||||
|
|
||||||
targetMember.Role = newRole;
|
targetMember.Role = newRole;
|
||||||
db.ChatMembers.Update(targetMember);
|
db.ChatMembers.Update(targetMember);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
als.CreateActionLogFromRequest(
|
||||||
|
ActionLogType.RealmAdjustRole,
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{ { "chatroom_id", roomId }, { "account_id", memberId }, { "new_role", newRole } },
|
||||||
|
Request
|
||||||
|
);
|
||||||
|
|
||||||
return Ok(targetMember);
|
return Ok(targetMember);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,20 +563,12 @@ public class ChatRoomController(
|
|||||||
// Check if the chat room is owned by a realm
|
// Check if the chat room is owned by a realm
|
||||||
if (chatRoom.RealmId is not null)
|
if (chatRoom.RealmId is not null)
|
||||||
{
|
{
|
||||||
var realmMember = await db.RealmMembers
|
if (!await rs.IsMemberWithRole(chatRoom.RealmId.Value, currentUser.Id, RealmMemberRole.Moderator))
|
||||||
.Where(m => m.AccountId == currentUser.Id)
|
|
||||||
.Where(m => m.RealmId == chatRoom.RealmId)
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
if (realmMember is null || realmMember.Role < RealmMemberRole.Moderator)
|
|
||||||
return StatusCode(403, "You need at least be a realm moderator to remove members.");
|
return StatusCode(403, "You need at least be a realm moderator to remove members.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Check if the current user has permission to remove members
|
if (!await crs.IsMemberWithRole(chatRoom.Id, currentUser.Id, ChatMemberRole.Moderator))
|
||||||
var currentMember = await db.ChatMembers
|
|
||||||
.Where(m => m.AccountId == currentUser.Id && m.ChatRoomId == roomId)
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
if (currentMember is null || currentMember.Role < ChatMemberRole.Moderator)
|
|
||||||
return StatusCode(403, "You need at least be a moderator to remove members.");
|
return StatusCode(403, "You need at least be a moderator to remove members.");
|
||||||
|
|
||||||
// Find the target member
|
// Find the target member
|
||||||
@ -572,8 +577,8 @@ public class ChatRoomController(
|
|||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (targetMember is null) return NotFound();
|
if (targetMember is null) return NotFound();
|
||||||
|
|
||||||
// Check if current user has sufficient permissions
|
// Check if the current user has sufficient permissions
|
||||||
if (currentMember.Role <= targetMember.Role)
|
if (!await crs.IsMemberWithRole(chatRoom.Id, memberId, targetMember.Role))
|
||||||
return StatusCode(403, "You cannot remove members with equal or higher roles.");
|
return StatusCode(403, "You cannot remove members with equal or higher roles.");
|
||||||
|
|
||||||
db.ChatMembers.Remove(targetMember);
|
db.ChatMembers.Remove(targetMember);
|
||||||
@ -602,8 +607,8 @@ public class ChatRoomController(
|
|||||||
.Where(r => r.Id == roomId)
|
.Where(r => r.Id == roomId)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (chatRoom is null) return NotFound();
|
if (chatRoom is null) return NotFound();
|
||||||
if (!chatRoom.IsPublic)
|
if (!chatRoom.IsCommunity)
|
||||||
return StatusCode(403, "This chat room is private. You need an invitation to join.");
|
return StatusCode(403, "This chat room isn't a community. You need an invitation to join.");
|
||||||
|
|
||||||
var existingMember = await db.ChatMembers
|
var existingMember = await db.ChatMembers
|
||||||
.FirstOrDefaultAsync(m => m.AccountId == currentUser.Id && m.ChatRoomId == roomId);
|
.FirstOrDefaultAsync(m => m.AccountId == currentUser.Id && m.ChatRoomId == roomId);
|
||||||
|
@ -72,10 +72,14 @@ public class ChatRoomService(AppDatabase db, IMemoryCache cache)
|
|||||||
return room;
|
return room;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsMemberWithRole(Guid roomId, Guid accountId, ChatMemberRole requiredRole)
|
public async Task<bool> IsMemberWithRole(Guid roomId, Guid accountId, params ChatMemberRole[] requiredRoles)
|
||||||
{
|
{
|
||||||
|
if (requiredRoles.Length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var maxRequiredRole = requiredRoles.Max();
|
||||||
var member = await db.ChatMembers
|
var member = await db.ChatMembers
|
||||||
.FirstOrDefaultAsync(m => m.ChatRoomId == roomId && m.AccountId == accountId);
|
.FirstOrDefaultAsync(m => m.ChatRoomId == roomId && m.AccountId == accountId);
|
||||||
return member?.Role >= requiredRole;
|
return member?.Role >= maxRequiredRole;
|
||||||
}
|
}
|
||||||
}
|
}
|
3406
DysonNetwork.Sphere/Migrations/20250518092939_OptimizeFileStorage.Designer.cs
generated
Normal file
3406
DysonNetwork.Sphere/Migrations/20250518092939_OptimizeFileStorage.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,244 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using DysonNetwork.Sphere.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DysonNetwork.Sphere.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class OptimizeFileStorage : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "image_id",
|
||||||
|
table: "stickers",
|
||||||
|
type: "character varying(32)",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(128)");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "picture_id",
|
||||||
|
table: "realms",
|
||||||
|
type: "character varying(32)",
|
||||||
|
maxLength: 32,
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(128)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "background_id",
|
||||||
|
table: "realms",
|
||||||
|
type: "character varying(32)",
|
||||||
|
maxLength: 32,
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(128)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "picture_id",
|
||||||
|
table: "publishers",
|
||||||
|
type: "character varying(32)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(128)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "background_id",
|
||||||
|
table: "publishers",
|
||||||
|
type: "character varying(32)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(128)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "id",
|
||||||
|
table: "files",
|
||||||
|
type: "character varying(32)",
|
||||||
|
maxLength: 32,
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(128)",
|
||||||
|
oldMaxLength: 128);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<List<CloudFileSensitiveMark>>(
|
||||||
|
name: "sensitive_marks",
|
||||||
|
table: "files",
|
||||||
|
type: "jsonb",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "storage_id",
|
||||||
|
table: "files",
|
||||||
|
type: "character varying(32)",
|
||||||
|
maxLength: 32,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "storage_url",
|
||||||
|
table: "files",
|
||||||
|
type: "character varying(4096)",
|
||||||
|
maxLength: 4096,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "picture_id",
|
||||||
|
table: "chat_rooms",
|
||||||
|
type: "character varying(32)",
|
||||||
|
maxLength: 32,
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(128)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "background_id",
|
||||||
|
table: "chat_rooms",
|
||||||
|
type: "character varying(32)",
|
||||||
|
maxLength: 32,
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(128)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "picture_id",
|
||||||
|
table: "account_profiles",
|
||||||
|
type: "character varying(32)",
|
||||||
|
maxLength: 32,
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(128)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "background_id",
|
||||||
|
table: "account_profiles",
|
||||||
|
type: "character varying(32)",
|
||||||
|
maxLength: 32,
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(128)",
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "sensitive_marks",
|
||||||
|
table: "files");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "storage_id",
|
||||||
|
table: "files");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "storage_url",
|
||||||
|
table: "files");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "image_id",
|
||||||
|
table: "stickers",
|
||||||
|
type: "character varying(128)",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(32)");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "picture_id",
|
||||||
|
table: "realms",
|
||||||
|
type: "character varying(128)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(32)",
|
||||||
|
oldMaxLength: 32,
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "background_id",
|
||||||
|
table: "realms",
|
||||||
|
type: "character varying(128)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(32)",
|
||||||
|
oldMaxLength: 32,
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "picture_id",
|
||||||
|
table: "publishers",
|
||||||
|
type: "character varying(128)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(32)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "background_id",
|
||||||
|
table: "publishers",
|
||||||
|
type: "character varying(128)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(32)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "id",
|
||||||
|
table: "files",
|
||||||
|
type: "character varying(128)",
|
||||||
|
maxLength: 128,
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(32)",
|
||||||
|
oldMaxLength: 32);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "picture_id",
|
||||||
|
table: "chat_rooms",
|
||||||
|
type: "character varying(128)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(32)",
|
||||||
|
oldMaxLength: 32,
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "background_id",
|
||||||
|
table: "chat_rooms",
|
||||||
|
type: "character varying(128)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(32)",
|
||||||
|
oldMaxLength: 32,
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "picture_id",
|
||||||
|
table: "account_profiles",
|
||||||
|
type: "character varying(128)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(32)",
|
||||||
|
oldMaxLength: 32,
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "background_id",
|
||||||
|
table: "account_profiles",
|
||||||
|
type: "character varying(128)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(32)",
|
||||||
|
oldMaxLength: 32,
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3414
DysonNetwork.Sphere/Migrations/20250518113150_DontKnowHowToNameThing.Designer.cs
generated
Normal file
3414
DysonNetwork.Sphere/Migrations/20250518113150_DontKnowHowToNameThing.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,37 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DysonNetwork.Sphere.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class DontKnowHowToNameThing : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_stickers_slug",
|
||||||
|
table: "stickers",
|
||||||
|
column: "slug");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_sticker_packs_prefix",
|
||||||
|
table: "sticker_packs",
|
||||||
|
column: "prefix",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "ix_stickers_slug",
|
||||||
|
table: "stickers");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "ix_sticker_packs_prefix",
|
||||||
|
table: "sticker_packs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using DysonNetwork.Sphere;
|
using DysonNetwork.Sphere;
|
||||||
using DysonNetwork.Sphere.Account;
|
using DysonNetwork.Sphere.Account;
|
||||||
|
using DysonNetwork.Sphere.Storage;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
@ -11,7 +12,6 @@ using NetTopologySuite.Geometries;
|
|||||||
using NodaTime;
|
using NodaTime;
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
using NpgsqlTypes;
|
using NpgsqlTypes;
|
||||||
using Point = NetTopologySuite.Geometries.Point;
|
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
@ -537,7 +537,8 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnName("account_id");
|
.HasColumnName("account_id");
|
||||||
|
|
||||||
b.Property<string>("BackgroundId")
|
b.Property<string>("BackgroundId")
|
||||||
.HasColumnType("character varying(128)")
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
.HasColumnName("background_id");
|
.HasColumnName("background_id");
|
||||||
|
|
||||||
b.Property<string>("Bio")
|
b.Property<string>("Bio")
|
||||||
@ -573,7 +574,8 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnName("middle_name");
|
.HasColumnName("middle_name");
|
||||||
|
|
||||||
b.Property<string>("PictureId")
|
b.Property<string>("PictureId")
|
||||||
.HasColumnType("character varying(128)")
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
.HasColumnName("picture_id");
|
.HasColumnName("picture_id");
|
||||||
|
|
||||||
b.Property<Instant>("UpdatedAt")
|
b.Property<Instant>("UpdatedAt")
|
||||||
@ -957,7 +959,8 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnName("id");
|
.HasColumnName("id");
|
||||||
|
|
||||||
b.Property<string>("BackgroundId")
|
b.Property<string>("BackgroundId")
|
||||||
.HasColumnType("character varying(128)")
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
.HasColumnName("background_id");
|
.HasColumnName("background_id");
|
||||||
|
|
||||||
b.Property<Instant>("CreatedAt")
|
b.Property<Instant>("CreatedAt")
|
||||||
@ -983,7 +986,8 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<string>("PictureId")
|
b.Property<string>("PictureId")
|
||||||
.HasColumnType("character varying(128)")
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
.HasColumnName("picture_id");
|
.HasColumnName("picture_id");
|
||||||
|
|
||||||
b.Property<Guid?>("RealmId")
|
b.Property<Guid?>("RealmId")
|
||||||
@ -1766,7 +1770,7 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnName("account_id");
|
.HasColumnName("account_id");
|
||||||
|
|
||||||
b.Property<string>("BackgroundId")
|
b.Property<string>("BackgroundId")
|
||||||
.HasColumnType("character varying(128)")
|
.HasColumnType("character varying(32)")
|
||||||
.HasColumnName("background_id");
|
.HasColumnName("background_id");
|
||||||
|
|
||||||
b.Property<string>("Bio")
|
b.Property<string>("Bio")
|
||||||
@ -1795,7 +1799,7 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnName("nick");
|
.HasColumnName("nick");
|
||||||
|
|
||||||
b.Property<string>("PictureId")
|
b.Property<string>("PictureId")
|
||||||
.HasColumnType("character varying(128)")
|
.HasColumnType("character varying(32)")
|
||||||
.HasColumnName("picture_id");
|
.HasColumnName("picture_id");
|
||||||
|
|
||||||
b.Property<Guid?>("RealmId")
|
b.Property<Guid?>("RealmId")
|
||||||
@ -1972,7 +1976,8 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnName("account_id");
|
.HasColumnName("account_id");
|
||||||
|
|
||||||
b.Property<string>("BackgroundId")
|
b.Property<string>("BackgroundId")
|
||||||
.HasColumnType("character varying(128)")
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
.HasColumnName("background_id");
|
.HasColumnName("background_id");
|
||||||
|
|
||||||
b.Property<Instant>("CreatedAt")
|
b.Property<Instant>("CreatedAt")
|
||||||
@ -2004,7 +2009,8 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<string>("PictureId")
|
b.Property<string>("PictureId")
|
||||||
.HasColumnType("character varying(128)")
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
.HasColumnName("picture_id");
|
.HasColumnName("picture_id");
|
||||||
|
|
||||||
b.Property<string>("Slug")
|
b.Property<string>("Slug")
|
||||||
@ -2101,7 +2107,8 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
|
|
||||||
b.Property<string>("ImageId")
|
b.Property<string>("ImageId")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("character varying(128)")
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
.HasColumnName("image_id");
|
.HasColumnName("image_id");
|
||||||
|
|
||||||
b.Property<Guid>("PackId")
|
b.Property<Guid>("PackId")
|
||||||
@ -2127,6 +2134,9 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.HasIndex("PackId")
|
b.HasIndex("PackId")
|
||||||
.HasDatabaseName("ix_stickers_pack_id");
|
.HasDatabaseName("ix_stickers_pack_id");
|
||||||
|
|
||||||
|
b.HasIndex("Slug")
|
||||||
|
.HasDatabaseName("ix_stickers_slug");
|
||||||
|
|
||||||
b.ToTable("stickers", (string)null);
|
b.ToTable("stickers", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2174,6 +2184,10 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_sticker_packs");
|
.HasName("pk_sticker_packs");
|
||||||
|
|
||||||
|
b.HasIndex("Prefix")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_sticker_packs_prefix");
|
||||||
|
|
||||||
b.HasIndex("PublisherId")
|
b.HasIndex("PublisherId")
|
||||||
.HasDatabaseName("ix_sticker_packs_publisher_id");
|
.HasDatabaseName("ix_sticker_packs_publisher_id");
|
||||||
|
|
||||||
@ -2183,8 +2197,8 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
modelBuilder.Entity("DysonNetwork.Sphere.Storage.CloudFile", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Storage.CloudFile", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
.HasMaxLength(128)
|
.HasMaxLength(32)
|
||||||
.HasColumnType("character varying(128)")
|
.HasColumnType("character varying(32)")
|
||||||
.HasColumnName("id");
|
.HasColumnName("id");
|
||||||
|
|
||||||
b.Property<Guid>("AccountId")
|
b.Property<Guid>("AccountId")
|
||||||
@ -2240,10 +2254,24 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("uuid")
|
.HasColumnType("uuid")
|
||||||
.HasColumnName("post_id");
|
.HasColumnName("post_id");
|
||||||
|
|
||||||
|
b.Property<List<CloudFileSensitiveMark>>("SensitiveMarks")
|
||||||
|
.HasColumnType("jsonb")
|
||||||
|
.HasColumnName("sensitive_marks");
|
||||||
|
|
||||||
b.Property<long>("Size")
|
b.Property<long>("Size")
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("size");
|
.HasColumnName("size");
|
||||||
|
|
||||||
|
b.Property<string>("StorageId")
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
|
.HasColumnName("storage_id");
|
||||||
|
|
||||||
|
b.Property<string>("StorageUrl")
|
||||||
|
.HasMaxLength(4096)
|
||||||
|
.HasColumnType("character varying(4096)")
|
||||||
|
.HasColumnName("storage_url");
|
||||||
|
|
||||||
b.Property<Instant>("UpdatedAt")
|
b.Property<Instant>("UpdatedAt")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("updated_at");
|
.HasColumnName("updated_at");
|
||||||
|
@ -177,8 +177,7 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs, Ac
|
|||||||
}
|
}
|
||||||
|
|
||||||
var query = db.RealmMembers
|
var query = db.RealmMembers
|
||||||
.Where(m => m.RealmId == realm.Id)
|
.Where(m => m.RealmId == realm.Id);
|
||||||
.Where(m => m.JoinedAt != null);
|
|
||||||
|
|
||||||
var total = await query.CountAsync();
|
var total = await query.CountAsync();
|
||||||
Response.Headers["X-Total"] = total.ToString();
|
Response.Headers["X-Total"] = total.ToString();
|
||||||
@ -372,6 +371,118 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs, Ac
|
|||||||
return Ok(realm);
|
return Ok(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("{slug}/members/me")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<RealmMember>> JoinRealm(string slug)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var realm = await db.Realms
|
||||||
|
.Where(r => r.Slug == slug)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (realm is null) return NotFound();
|
||||||
|
|
||||||
|
if (!realm.IsCommunity)
|
||||||
|
return StatusCode(403, "Only community realms can be joined without invitation.");
|
||||||
|
|
||||||
|
var existingMember = await db.RealmMembers
|
||||||
|
.Where(m => m.AccountId == currentUser.Id && m.RealmId == realm.Id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (existingMember is not null)
|
||||||
|
return BadRequest("You are already a member of this realm.");
|
||||||
|
|
||||||
|
var member = new RealmMember
|
||||||
|
{
|
||||||
|
AccountId = currentUser.Id,
|
||||||
|
RealmId = realm.Id,
|
||||||
|
Role = RealmMemberRole.Normal,
|
||||||
|
JoinedAt = NodaTime.Instant.FromDateTimeUtc(DateTime.UtcNow)
|
||||||
|
};
|
||||||
|
|
||||||
|
db.RealmMembers.Add(member);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
als.CreateActionLogFromRequest(
|
||||||
|
ActionLogType.RealmJoin,
|
||||||
|
new Dictionary<string, object> { { "realm_id", realm.Id }, { "account_id", currentUser.Id } },
|
||||||
|
Request
|
||||||
|
);
|
||||||
|
|
||||||
|
return Ok(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{slug}/members/{memberId:guid}")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult> RemoveMember(string slug, Guid memberId)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var realm = await db.Realms
|
||||||
|
.Where(r => r.Slug == slug)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (realm is null) return NotFound();
|
||||||
|
|
||||||
|
var currentMember = await db.RealmMembers
|
||||||
|
.Where(m => m.AccountId == currentUser.Id && m.RealmId == realm.Id && m.JoinedAt != null)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (currentMember is null || currentMember.Role < RealmMemberRole.Moderator)
|
||||||
|
return StatusCode(403, "You do not have permission to remove members from this realm.");
|
||||||
|
|
||||||
|
var memberToRemove = await db.RealmMembers
|
||||||
|
.Where(m => m.AccountId == memberId && m.RealmId == realm.Id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (memberToRemove is null) return NotFound();
|
||||||
|
|
||||||
|
if (memberToRemove.Role >= currentMember.Role)
|
||||||
|
return StatusCode(403, "You cannot remove members with equal or higher roles.");
|
||||||
|
|
||||||
|
db.RealmMembers.Remove(memberToRemove);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
als.CreateActionLogFromRequest(
|
||||||
|
ActionLogType.ChatroomKick,
|
||||||
|
new Dictionary<string, object> { { "realm_id", realm.Id }, { "account_id", memberId } },
|
||||||
|
Request
|
||||||
|
);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPatch("{slug}/members/{memberId:guid}/role")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<RealmMember>> UpdateMemberRole(string slug, Guid memberId,
|
||||||
|
[FromBody] RealmMemberRole newRole)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var realm = await db.Realms
|
||||||
|
.Where(r => r.Slug == slug)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (realm is null) return NotFound();
|
||||||
|
|
||||||
|
var member = await db.RealmMembers
|
||||||
|
.Where(m => m.AccountId == memberId && m.RealmId == realm.Id)
|
||||||
|
.Include(m => m.Account)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (member is null) return NotFound();
|
||||||
|
|
||||||
|
if (!await rs.IsMemberWithRole(realm.Id, currentUser.Id, RealmMemberRole.Moderator, member.Role, newRole))
|
||||||
|
return StatusCode(403, "You do not have permission to update member roles in this realm.");
|
||||||
|
|
||||||
|
member.Role = newRole;
|
||||||
|
db.RealmMembers.Update(member);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
als.CreateActionLogFromRequest(
|
||||||
|
ActionLogType.RealmAdjustRole,
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{ { "realm_id", realm.Id }, { "account_id", memberId }, { "new_role", newRole } },
|
||||||
|
Request
|
||||||
|
);
|
||||||
|
|
||||||
|
return Ok(member);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpDelete("{slug}")]
|
[HttpDelete("{slug}")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ActionResult> Delete(string slug)
|
public async Task<ActionResult> Delete(string slug)
|
||||||
@ -385,10 +496,7 @@ public class RealmController(AppDatabase db, RealmService rs, FileService fs, Ac
|
|||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (realm is null) return NotFound();
|
if (realm is null) return NotFound();
|
||||||
|
|
||||||
var member = await db.RealmMembers
|
if (!await rs.IsMemberWithRole(realm.Id, currentUser.Id, RealmMemberRole.Owner))
|
||||||
.Where(m => m.AccountId == currentUser.Id && m.RealmId == realm.Id && m.JoinedAt != null)
|
|
||||||
.FirstOrDefaultAsync();
|
|
||||||
if (member is null || member.Role < RealmMemberRole.Owner)
|
|
||||||
return StatusCode(403, "Only the owner can delete this realm.");
|
return StatusCode(403, "Only the owner can delete this realm.");
|
||||||
|
|
||||||
db.Realms.Remove(realm);
|
db.Realms.Remove(realm);
|
||||||
|
@ -11,10 +11,14 @@ public class RealmService(AppDatabase db, NotificationService nty)
|
|||||||
$"You just got invited to join {member.Realm.Name}");
|
$"You just got invited to join {member.Realm.Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsMemberWithRole(Guid realmId, Guid accountId, RealmMemberRole requiredRole)
|
public async Task<bool> IsMemberWithRole(Guid realmId, Guid accountId, params RealmMemberRole[] requiredRoles)
|
||||||
{
|
{
|
||||||
|
if (requiredRoles.Length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var maxRequiredRole = requiredRoles.Max();
|
||||||
var member = await db.RealmMembers
|
var member = await db.RealmMembers
|
||||||
.FirstOrDefaultAsync(m => m.RealmId == realmId && m.AccountId == accountId);
|
.FirstOrDefaultAsync(m => m.RealmId == realmId && m.AccountId == accountId);
|
||||||
return member?.Role >= requiredRole;
|
return member?.Role >= maxRequiredRole;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,20 +1,23 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using DysonNetwork.Sphere.Storage;
|
using DysonNetwork.Sphere.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Sticker;
|
namespace DysonNetwork.Sphere.Sticker;
|
||||||
|
|
||||||
|
[Index(nameof(Slug))] // The slug index shouldn't be unique, the sticker slug can be repeated across packs.
|
||||||
public class Sticker : ModelBase
|
public class Sticker : ModelBase
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; } = Guid.NewGuid();
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
[MaxLength(128)] public string Slug { get; set; } = null!;
|
[MaxLength(128)] public string Slug { get; set; } = null!;
|
||||||
|
|
||||||
public string ImageId { get; set; } = null!;
|
[MaxLength(32)] public string ImageId { get; set; } = null!;
|
||||||
public CloudFile Image { get; set; } = null!;
|
public CloudFile Image { get; set; } = null!;
|
||||||
|
|
||||||
public Guid PackId { get; set; }
|
public Guid PackId { get; set; }
|
||||||
public StickerPack Pack { get; set; } = null!;
|
public StickerPack Pack { get; set; } = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Index(nameof(Prefix), IsUnique = true)]
|
||||||
public class StickerPack : ModelBase
|
public class StickerPack : ModelBase
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; } = Guid.NewGuid();
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
@ -34,7 +34,7 @@ public class StickerService(AppDatabase db, FileService fs, IMemoryCache cache)
|
|||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
// Invalidate cache for this sticker
|
// Invalidate cache for this sticker
|
||||||
InvalidateStickerCache(sticker);
|
PurgeStickerCache(sticker);
|
||||||
|
|
||||||
return sticker;
|
return sticker;
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ public class StickerService(AppDatabase db, FileService fs, IMemoryCache cache)
|
|||||||
await fs.MarkUsageAsync(sticker.Image, -1);
|
await fs.MarkUsageAsync(sticker.Image, -1);
|
||||||
|
|
||||||
// Invalidate cache for this sticker
|
// Invalidate cache for this sticker
|
||||||
InvalidateStickerCache(sticker);
|
PurgeStickerCache(sticker);
|
||||||
}
|
}
|
||||||
public async Task DeleteStickerPackAsync(StickerPack pack)
|
public async Task DeleteStickerPackAsync(StickerPack pack)
|
||||||
{
|
{
|
||||||
@ -65,7 +65,7 @@ public class StickerService(AppDatabase db, FileService fs, IMemoryCache cache)
|
|||||||
// Invalidate cache for all stickers in this pack
|
// Invalidate cache for all stickers in this pack
|
||||||
foreach (var sticker in stickers)
|
foreach (var sticker in stickers)
|
||||||
{
|
{
|
||||||
InvalidateStickerCache(sticker);
|
PurgeStickerCache(sticker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,14 +91,12 @@ public class StickerService(AppDatabase db, FileService fs, IMemoryCache cache)
|
|||||||
|
|
||||||
// Store in cache if found
|
// Store in cache if found
|
||||||
if (sticker != null)
|
if (sticker != null)
|
||||||
{
|
|
||||||
cache.Set(cacheKey, sticker, CacheDuration);
|
cache.Set(cacheKey, sticker, CacheDuration);
|
||||||
}
|
|
||||||
|
|
||||||
return sticker;
|
return sticker;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InvalidateStickerCache(Sticker sticker)
|
private void PurgeStickerCache(Sticker sticker)
|
||||||
{
|
{
|
||||||
// Remove both possible cache entries
|
// Remove both possible cache entries
|
||||||
cache.Remove($"StickerLookup_{sticker.Id}");
|
cache.Remove($"StickerLookup_{sticker.Id}");
|
||||||
|
@ -28,7 +28,7 @@ public class CloudFile : ModelBase
|
|||||||
[MaxLength(4096)] public string? Description { get; set; }
|
[MaxLength(4096)] public string? Description { get; set; }
|
||||||
[Column(TypeName = "jsonb")] public Dictionary<string, object>? FileMeta { get; set; } = null!;
|
[Column(TypeName = "jsonb")] public Dictionary<string, object>? FileMeta { get; set; } = null!;
|
||||||
[Column(TypeName = "jsonb")] public Dictionary<string, object>? UserMeta { get; set; } = null!;
|
[Column(TypeName = "jsonb")] public Dictionary<string, object>? UserMeta { get; set; } = null!;
|
||||||
[Column(TypeName = "jsonb")] public List<CloudFileSensitiveMark> SensitiveMarks { get; set; } = new();
|
[Column(TypeName = "jsonb")] public List<CloudFileSensitiveMark>? SensitiveMarks { get; set; } = [];
|
||||||
[MaxLength(256)] public string? MimeType { get; set; }
|
[MaxLength(256)] public string? MimeType { get; set; }
|
||||||
[MaxLength(256)] public string? Hash { get; set; }
|
[MaxLength(256)] public string? Hash { get; set; }
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user