Renamed hub
This commit is contained in:
@@ -0,0 +1,981 @@
|
||||
using LightlessSync.API.Data;
|
||||
using LightlessSync.API.Data.Enum;
|
||||
using LightlessSync.API.Data.Extensions;
|
||||
using LightlessSync.API.Dto.Group;
|
||||
using LightlessSync.API.Dto.User;
|
||||
using LightlessSyncServer.Utils;
|
||||
using LightlessSyncShared.Models;
|
||||
using LightlessSyncShared.Utils;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace LightlessSyncServer.Hubs;
|
||||
|
||||
public partial class LightlessHub
|
||||
{
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupBanUser(GroupPairDto dto, string reason)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, reason));
|
||||
|
||||
var (userHasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!userHasRights) return;
|
||||
|
||||
var (userExists, groupPair) = await TryValidateUserInGroup(dto.Group.GID, dto.User.UID).ConfigureAwait(false);
|
||||
if (!userExists) return;
|
||||
|
||||
if (groupPair.IsModerator || string.Equals(group.OwnerUID, dto.User.UID, StringComparison.Ordinal)) return;
|
||||
|
||||
var alias = string.IsNullOrEmpty(groupPair.GroupUser.Alias) ? "-" : groupPair.GroupUser.Alias;
|
||||
var ban = new GroupBan()
|
||||
{
|
||||
BannedByUID = UserUID,
|
||||
BannedReason = $"{reason} (Alias at time of ban: {alias})",
|
||||
BannedOn = DateTime.UtcNow,
|
||||
BannedUserUID = dto.User.UID,
|
||||
GroupGID = dto.Group.GID,
|
||||
};
|
||||
|
||||
DbContext.Add(ban);
|
||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
await GroupRemoveUser(dto).ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupChangeGroupPermissionState(GroupPermissionDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!hasRights) return;
|
||||
|
||||
group.InvitesEnabled = !dto.Permissions.HasFlag(GroupPermissions.DisableInvites);
|
||||
group.PreferDisableSounds = dto.Permissions.HasFlag(GroupPermissions.PreferDisableSounds);
|
||||
group.PreferDisableAnimations = dto.Permissions.HasFlag(GroupPermissions.PreferDisableAnimations);
|
||||
group.PreferDisableVFX = dto.Permissions.HasFlag(GroupPermissions.PreferDisableVFX);
|
||||
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
var groupPairs = DbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).ToList();
|
||||
await Clients.Users(groupPairs).Client_GroupChangePermissions(new GroupPermissionDto(dto.Group, dto.Permissions)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupChangeOwnership(GroupPairDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var (isOwner, group) = await TryValidateOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!isOwner) return;
|
||||
|
||||
var (isInGroup, newOwnerPair) = await TryValidateUserInGroup(dto.Group.GID, dto.User.UID).ConfigureAwait(false);
|
||||
if (!isInGroup) return;
|
||||
|
||||
var ownedShells = await DbContext.Groups.CountAsync(g => g.OwnerUID == dto.User.UID).ConfigureAwait(false);
|
||||
if (ownedShells >= _maxExistingGroupsByUser) return;
|
||||
|
||||
var prevOwner = await DbContext.GroupPairs.SingleOrDefaultAsync(g => g.GroupGID == dto.Group.GID && g.GroupUserUID == UserUID).ConfigureAwait(false);
|
||||
prevOwner.IsPinned = false;
|
||||
group.Owner = newOwnerPair.GroupUser;
|
||||
group.Alias = null;
|
||||
newOwnerPair.IsPinned = true;
|
||||
newOwnerPair.IsModerator = false;
|
||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||
|
||||
var groupPairs = await DbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||
|
||||
await Clients.Users(groupPairs).Client_GroupSendInfo(new GroupInfoDto(group.ToGroupData(), newOwnerPair.GroupUser.ToUserData(), group.ToEnum())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<bool> GroupChangePassword(GroupPasswordDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var (isOwner, group) = await TryValidateOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!isOwner || dto.Password.Length < 10) return false;
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||
|
||||
group.HashedPassword = StringUtils.Sha256String(dto.Password);
|
||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupClear(GroupDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!hasRights) return;
|
||||
|
||||
var groupPairs = await DbContext.GroupPairs.Include(p => p.GroupUser).Where(p => p.GroupGID == dto.Group.GID).ToListAsync().ConfigureAwait(false);
|
||||
var notPinned = groupPairs.Where(g => !g.IsPinned && !g.IsModerator).ToList();
|
||||
|
||||
await Clients.Users(notPinned.Select(g => g.GroupUserUID)).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||
|
||||
DbContext.GroupPairs.RemoveRange(notPinned);
|
||||
|
||||
foreach (var pair in notPinned)
|
||||
{
|
||||
await Clients.Users(groupPairs.Where(p => p.IsPinned || p.IsModerator).Select(g => g.GroupUserUID))
|
||||
.Client_GroupPairLeft(new GroupPairDto(dto.Group, pair.GroupUser.ToUserData())).ConfigureAwait(false);
|
||||
|
||||
var pairIdent = await GetUserIdent(pair.GroupUserUID).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(pairIdent)) continue;
|
||||
|
||||
var allUserPairs = await GetAllPairInfo(pair.GroupUserUID).ConfigureAwait(false);
|
||||
|
||||
var sharedData = await DbContext.CharaDataAllowances.Where(u => u.AllowedGroup != null && u.AllowedGroupGID == dto.GID && u.ParentUploaderUID == pair.GroupUserUID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
DbContext.CharaDataAllowances.RemoveRange(sharedData);
|
||||
|
||||
foreach (var groupUserPair in groupPairs.Where(p => !string.Equals(p.GroupUserUID, pair.GroupUserUID, StringComparison.Ordinal)))
|
||||
{
|
||||
await UserGroupLeave(pair, pairIdent, allUserPairs, pair.GroupUserUID).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupClearFinder(GroupDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!hasRights) return;
|
||||
|
||||
var groupPairs = await DbContext.GroupPairs.Include(p => p.GroupUser).Where(p => p.GroupGID == dto.Group.GID).ToListAsync().ConfigureAwait(false);
|
||||
var finder_only = groupPairs.Where(g => g.FromFinder && !g.IsPinned && !g.IsModerator).ToList();
|
||||
|
||||
if (finder_only.Count == 0)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "No Users To Clear"));
|
||||
return;
|
||||
}
|
||||
|
||||
await Clients.Users(finder_only.Select(g => g.GroupUserUID)).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Cleared Finder users ", finder_only.Count));
|
||||
|
||||
DbContext.GroupPairs.RemoveRange(finder_only);
|
||||
|
||||
foreach (var pair in finder_only)
|
||||
{
|
||||
await Clients.Users(groupPairs.Where(p => p.IsPinned || p.IsModerator).Select(g => g.GroupUserUID)).Client_GroupPairLeft(new GroupPairDto(dto.Group, pair.GroupUser.ToUserData())).ConfigureAwait(false);
|
||||
|
||||
var pairIdent = await GetUserIdent(pair.GroupUserUID).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(pairIdent)) continue;
|
||||
|
||||
var allUserPairs = await GetAllPairInfo(pair.GroupUserUID).ConfigureAwait(false);
|
||||
|
||||
var sharedData = await DbContext.CharaDataAllowances.Where(u => u.AllowedGroup != null && u.AllowedGroupGID == dto.GID && u.ParentUploaderUID == pair.GroupUserUID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
DbContext.CharaDataAllowances.RemoveRange(sharedData);
|
||||
|
||||
foreach (var groupUserPair in groupPairs.Where(p => !string.Equals(p.GroupUserUID, pair.GroupUserUID, StringComparison.Ordinal)))
|
||||
{
|
||||
await UserGroupLeave(pair, pairIdent, allUserPairs, pair.GroupUserUID).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<GroupJoinDto> GroupCreate()
|
||||
{
|
||||
_logger.LogCallInfo();
|
||||
var existingGroupsByUser = await DbContext.Groups.CountAsync(u => u.OwnerUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
var existingJoinedGroups = await DbContext.GroupPairs.CountAsync(u => u.GroupUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
if (existingGroupsByUser >= _maxExistingGroupsByUser || existingJoinedGroups >= _maxJoinedGroupsByUser)
|
||||
{
|
||||
throw new System.Exception($"Max groups for user is {_maxExistingGroupsByUser}, max joined groups is {_maxJoinedGroupsByUser}.");
|
||||
}
|
||||
|
||||
var gid = StringUtils.GenerateRandomString(12);
|
||||
while (await DbContext.Groups.AnyAsync(g => g.GID == "LLS-" + gid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false))
|
||||
{
|
||||
gid = StringUtils.GenerateRandomString(12);
|
||||
}
|
||||
gid = "LLS-" + gid;
|
||||
|
||||
var passwd = StringUtils.GenerateRandomString(16);
|
||||
using var sha = SHA256.Create();
|
||||
var hashedPw = StringUtils.Sha256String(passwd);
|
||||
var currentTime = DateTime.UtcNow;
|
||||
|
||||
UserDefaultPreferredPermission defaultPermissions = await DbContext.UserDefaultPreferredPermissions.SingleAsync(u => u.UserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
Group newGroup = new()
|
||||
{
|
||||
GID = gid,
|
||||
HashedPassword = hashedPw,
|
||||
InvitesEnabled = true,
|
||||
OwnerUID = UserUID,
|
||||
PreferDisableAnimations = defaultPermissions.DisableGroupAnimations,
|
||||
PreferDisableSounds = defaultPermissions.DisableGroupSounds,
|
||||
PreferDisableVFX = defaultPermissions.DisableGroupVFX,
|
||||
CreatedDate = currentTime,
|
||||
};
|
||||
|
||||
GroupPair initialPair = new()
|
||||
{
|
||||
GroupGID = newGroup.GID,
|
||||
GroupUserUID = UserUID,
|
||||
IsPinned = true,
|
||||
JoinedGroupOn = currentTime,
|
||||
FromFinder = false,
|
||||
};
|
||||
|
||||
GroupPairPreferredPermission initialPrefPermissions = new()
|
||||
{
|
||||
UserUID = UserUID,
|
||||
GroupGID = newGroup.GID,
|
||||
DisableSounds = defaultPermissions.DisableGroupSounds,
|
||||
DisableAnimations = defaultPermissions.DisableGroupAnimations,
|
||||
DisableVFX = defaultPermissions.DisableGroupAnimations,
|
||||
};
|
||||
|
||||
await DbContext.Groups.AddAsync(newGroup, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
await DbContext.GroupPairs.AddAsync(initialPair, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
await DbContext.GroupPairPreferredPermissions.AddAsync(initialPrefPermissions, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
var self = await DbContext.Users.SingleAsync(u => u.UID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
await Clients.User(UserUID).Client_GroupSendFullInfo(new GroupFullInfoDto(newGroup.ToGroupData(), self.ToUserData(),
|
||||
newGroup.ToEnum(), initialPrefPermissions.ToEnum(), initialPair.ToEnum(), new(StringComparer.Ordinal)))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(gid));
|
||||
|
||||
return new GroupJoinDto(newGroup.ToGroupData(), passwd, initialPrefPermissions.ToEnum());
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<List<string>> GroupCreateTempInvite(GroupDto dto, int amount)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, amount));
|
||||
List<string> inviteCodes = new();
|
||||
List<GroupTempInvite> tempInvites = new();
|
||||
var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!hasRights) return new();
|
||||
|
||||
var existingInvites = await DbContext.GroupTempInvites.Where(g => g.GroupGID == group.GID).ToListAsync().ConfigureAwait(false);
|
||||
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
bool hasValidInvite = false;
|
||||
string invite = string.Empty;
|
||||
string hashedInvite = string.Empty;
|
||||
while (!hasValidInvite)
|
||||
{
|
||||
invite = StringUtils.GenerateRandomString(10, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
|
||||
hashedInvite = StringUtils.Sha256String(invite);
|
||||
if (existingInvites.Any(i => string.Equals(i.Invite, hashedInvite, StringComparison.Ordinal))) continue;
|
||||
hasValidInvite = true;
|
||||
inviteCodes.Add(invite);
|
||||
}
|
||||
|
||||
tempInvites.Add(new GroupTempInvite()
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.AddDays(1),
|
||||
GroupGID = group.GID,
|
||||
Invite = hashedInvite,
|
||||
});
|
||||
}
|
||||
|
||||
DbContext.GroupTempInvites.AddRange(tempInvites);
|
||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
return inviteCodes;
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupDelete(GroupDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var (hasRights, group) = await TryValidateOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||
|
||||
var groupPairs = await DbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
DbContext.RemoveRange(groupPairs);
|
||||
DbContext.Remove(group);
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
await Clients.Users(groupPairs.Select(g => g.GroupUserUID)).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false);
|
||||
|
||||
await SendGroupDeletedToAll(groupPairs).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<List<BannedGroupUserDto>> GroupGetBannedUsers(GroupDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var (userHasRights, group) = await TryValidateGroupModeratorOrOwner(dto.GID).ConfigureAwait(false);
|
||||
if (!userHasRights) return [];
|
||||
|
||||
var banEntries = await DbContext.GroupBans.Include(b => b.BannedUser).Where(g => g.GroupGID == dto.Group.GID).AsNoTracking().ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
List<BannedGroupUserDto> bannedGroupUsers = banEntries.Select(b =>
|
||||
new BannedGroupUserDto(group.ToGroupData(), b.BannedUser.ToUserData(), b.BannedReason, b.BannedOn,
|
||||
b.BannedByUID)).ToList();
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, bannedGroupUsers.Count));
|
||||
|
||||
return bannedGroupUsers;
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<GroupJoinInfoDto> GroupJoin(GroupPasswordDto dto)
|
||||
{
|
||||
var aliasOrGid = dto.Group.GID.Trim();
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var group = await DbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == aliasOrGid || g.Alias == aliasOrGid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
var groupGid = group?.GID ?? string.Empty;
|
||||
var existingPair = await DbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.GroupUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
var hashedPw = StringUtils.Sha256String(dto.Password);
|
||||
var existingUserCount = await DbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == groupGid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
var joinedGroups = await DbContext.GroupPairs.CountAsync(g => g.GroupUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
var isBanned = await DbContext.GroupBans.AnyAsync(g => g.GroupGID == groupGid && g.BannedUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
var oneTimeInvite = await DbContext.GroupTempInvites.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.Invite == hashedPw, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
if (group == null
|
||||
|| (!string.Equals(group.HashedPassword, hashedPw, StringComparison.Ordinal) && oneTimeInvite == null)
|
||||
|| existingPair != null
|
||||
|| existingUserCount >= _maxGroupUserCount
|
||||
|| !group.InvitesEnabled
|
||||
|| joinedGroups >= _maxJoinedGroupsByUser
|
||||
|| isBanned)
|
||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
||||
|
||||
return new GroupJoinInfoDto(group.ToGroupData(), group.Owner.ToUserData(), group.ToEnum(), true);
|
||||
}
|
||||
|
||||
private static bool IsHex(char c) =>
|
||||
(c >= '0' && c <= '9') ||
|
||||
(c >= 'a' && c <= 'f') ||
|
||||
(c >= 'A' && c <= 'F');
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<bool> GroupJoinFinalize(GroupJoinDto dto)
|
||||
{
|
||||
var aliasOrGid = dto.Group.GID.Trim();
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var group = await DbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == aliasOrGid || g.Alias == aliasOrGid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
var groupGid = group?.GID ?? string.Empty;
|
||||
var existingPair = await DbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.GroupUserUID == UserUID).ConfigureAwait(false);
|
||||
var isHashedPassword = dto.Password.Length == 64 && dto.Password.All(IsHex);
|
||||
var hashedPw = isHashedPassword
|
||||
? dto.Password
|
||||
: StringUtils.Sha256String(dto.Password);
|
||||
var existingUserCount = await DbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == groupGid).ConfigureAwait(false);
|
||||
var joinedGroups = await DbContext.GroupPairs.CountAsync(g => g.GroupUserUID == UserUID).ConfigureAwait(false);
|
||||
var isBanned = await DbContext.GroupBans.AnyAsync(g => g.GroupGID == groupGid && g.BannedUserUID == UserUID).ConfigureAwait(false);
|
||||
var oneTimeInvite = await DbContext.GroupTempInvites.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.Invite == hashedPw).ConfigureAwait(false);
|
||||
|
||||
if (group == null
|
||||
|| (!string.Equals(group.HashedPassword, hashedPw, StringComparison.Ordinal) && oneTimeInvite == null)
|
||||
|| existingPair != null
|
||||
|| existingUserCount >= _maxGroupUserCount
|
||||
|| !group.InvitesEnabled
|
||||
|| joinedGroups >= _maxJoinedGroupsByUser
|
||||
|| isBanned)
|
||||
return false;
|
||||
|
||||
// get all pairs before we join
|
||||
var allUserPairs = (await GetAllPairInfo(UserUID).ConfigureAwait(false));
|
||||
|
||||
if (oneTimeInvite != null)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(aliasOrGid, "TempInvite", oneTimeInvite.Invite));
|
||||
DbContext.Remove(oneTimeInvite);
|
||||
}
|
||||
|
||||
GroupPair newPair = new()
|
||||
{
|
||||
GroupGID = group.GID,
|
||||
GroupUserUID = UserUID,
|
||||
JoinedGroupOn = DateTime.UtcNow,
|
||||
FromFinder = isHashedPassword
|
||||
};
|
||||
|
||||
var preferredPermissions = await DbContext.GroupPairPreferredPermissions.SingleOrDefaultAsync(u => u.UserUID == UserUID && u.GroupGID == group.GID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
if (preferredPermissions == null)
|
||||
{
|
||||
GroupPairPreferredPermission newPerms = new()
|
||||
{
|
||||
GroupGID = group.GID,
|
||||
UserUID = UserUID,
|
||||
DisableSounds = dto.GroupUserPreferredPermissions.IsDisableSounds(),
|
||||
DisableVFX = dto.GroupUserPreferredPermissions.IsDisableVFX(),
|
||||
DisableAnimations = dto.GroupUserPreferredPermissions.IsDisableAnimations(),
|
||||
IsPaused = false,
|
||||
};
|
||||
|
||||
DbContext.Add(newPerms);
|
||||
preferredPermissions = newPerms;
|
||||
}
|
||||
else
|
||||
{
|
||||
preferredPermissions.DisableSounds = dto.GroupUserPreferredPermissions.IsDisableSounds();
|
||||
preferredPermissions.DisableVFX = dto.GroupUserPreferredPermissions.IsDisableVFX();
|
||||
preferredPermissions.DisableAnimations = dto.GroupUserPreferredPermissions.IsDisableAnimations();
|
||||
preferredPermissions.IsPaused = false;
|
||||
DbContext.Update(preferredPermissions);
|
||||
}
|
||||
|
||||
await DbContext.GroupPairs.AddAsync(newPair, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(aliasOrGid, "Success"));
|
||||
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
var groupInfos = await DbContext.GroupPairs.Where(u => u.GroupGID == group.GID && (u.IsPinned || u.IsModerator)).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
await Clients.User(UserUID).Client_GroupSendFullInfo(new GroupFullInfoDto(group.ToGroupData(), group.Owner.ToUserData(),
|
||||
group.ToEnum(), preferredPermissions.ToEnum(), newPair.ToEnum(),
|
||||
groupInfos.ToDictionary(u => u.GroupUserUID, u => u.ToEnum(), StringComparer.Ordinal))).ConfigureAwait(false);
|
||||
|
||||
var self = DbContext.Users.Single(u => u.UID == UserUID);
|
||||
|
||||
var groupPairs = await DbContext.GroupPairs.Include(p => p.GroupUser)
|
||||
.Where(p => p.GroupGID == group.GID && p.GroupUserUID != UserUID).ToListAsync().ConfigureAwait(false);
|
||||
|
||||
var userPairsAfterJoin = await GetAllPairInfo(UserUID).ConfigureAwait(false);
|
||||
|
||||
foreach (var pair in groupPairs)
|
||||
{
|
||||
var perms = userPairsAfterJoin.TryGetValue(pair.GroupUserUID, out var userinfo);
|
||||
// check if we have had prior permissions to that pair, if not add them
|
||||
var ownPermissionsToOther = userinfo?.OwnPermissions ?? null;
|
||||
if (ownPermissionsToOther == null)
|
||||
{
|
||||
var existingPermissionsOnDb = await DbContext.Permissions.SingleOrDefaultAsync(p => p.UserUID == UserUID && p.OtherUserUID == pair.GroupUserUID).ConfigureAwait(false);
|
||||
|
||||
if (existingPermissionsOnDb == null)
|
||||
{
|
||||
ownPermissionsToOther = new()
|
||||
{
|
||||
UserUID = UserUID,
|
||||
OtherUserUID = pair.GroupUserUID,
|
||||
DisableAnimations = preferredPermissions.DisableAnimations,
|
||||
DisableSounds = preferredPermissions.DisableSounds,
|
||||
DisableVFX = preferredPermissions.DisableVFX,
|
||||
IsPaused = preferredPermissions.IsPaused,
|
||||
Sticky = false
|
||||
};
|
||||
|
||||
await DbContext.Permissions.AddAsync(ownPermissionsToOther).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
existingPermissionsOnDb.DisableAnimations = preferredPermissions.DisableAnimations;
|
||||
existingPermissionsOnDb.DisableSounds = preferredPermissions.DisableSounds;
|
||||
existingPermissionsOnDb.DisableVFX = preferredPermissions.DisableVFX;
|
||||
existingPermissionsOnDb.IsPaused = false;
|
||||
existingPermissionsOnDb.Sticky = false;
|
||||
|
||||
DbContext.Update(existingPermissionsOnDb);
|
||||
|
||||
ownPermissionsToOther = existingPermissionsOnDb;
|
||||
}
|
||||
}
|
||||
else if (!ownPermissionsToOther.Sticky)
|
||||
{
|
||||
ownPermissionsToOther = await DbContext.Permissions.SingleAsync(u => u.UserUID == UserUID && u.OtherUserUID == pair.GroupUserUID).ConfigureAwait(false);
|
||||
|
||||
// update the existing permission only if it was not set to sticky
|
||||
ownPermissionsToOther.DisableAnimations = preferredPermissions.DisableAnimations;
|
||||
ownPermissionsToOther.DisableVFX = preferredPermissions.DisableVFX;
|
||||
ownPermissionsToOther.DisableSounds = preferredPermissions.DisableSounds;
|
||||
ownPermissionsToOther.IsPaused = false;
|
||||
|
||||
DbContext.Update(ownPermissionsToOther);
|
||||
}
|
||||
|
||||
// get others permissionset to self and eventually update it
|
||||
var otherPermissionToSelf = userinfo?.OtherPermissions ?? null;
|
||||
if (otherPermissionToSelf == null)
|
||||
{
|
||||
var otherExistingPermsOnDb = await DbContext.Permissions.SingleOrDefaultAsync(p => p.UserUID == pair.GroupUserUID && p.OtherUserUID == UserUID).ConfigureAwait(false);
|
||||
|
||||
if (otherExistingPermsOnDb == null)
|
||||
{
|
||||
var otherPreferred = await DbContext.GroupPairPreferredPermissions.SingleAsync(u => u.GroupGID == group.GID && u.UserUID == pair.GroupUserUID).ConfigureAwait(false);
|
||||
otherExistingPermsOnDb = new()
|
||||
{
|
||||
UserUID = pair.GroupUserUID,
|
||||
OtherUserUID = UserUID,
|
||||
DisableAnimations = otherPreferred.DisableAnimations,
|
||||
DisableSounds = otherPreferred.DisableSounds,
|
||||
DisableVFX = otherPreferred.DisableVFX,
|
||||
IsPaused = otherPreferred.IsPaused,
|
||||
Sticky = false
|
||||
};
|
||||
|
||||
await DbContext.AddAsync(otherExistingPermsOnDb).ConfigureAwait(false);
|
||||
}
|
||||
else if (!otherExistingPermsOnDb.Sticky)
|
||||
{
|
||||
var otherPreferred = await DbContext.GroupPairPreferredPermissions.SingleAsync(u => u.GroupGID == group.GID && u.UserUID == pair.GroupUserUID).ConfigureAwait(false);
|
||||
otherExistingPermsOnDb.DisableAnimations = otherPreferred.DisableAnimations;
|
||||
otherExistingPermsOnDb.DisableSounds = otherPreferred.DisableSounds;
|
||||
otherExistingPermsOnDb.DisableVFX = otherPreferred.DisableVFX;
|
||||
otherExistingPermsOnDb.IsPaused = otherPreferred.IsPaused;
|
||||
|
||||
DbContext.Update(otherExistingPermsOnDb);
|
||||
}
|
||||
|
||||
otherPermissionToSelf = otherExistingPermsOnDb;
|
||||
}
|
||||
else if (!otherPermissionToSelf.Sticky)
|
||||
{
|
||||
var otherPreferred = await DbContext.GroupPairPreferredPermissions.SingleAsync(u => u.GroupGID == group.GID && u.UserUID == pair.GroupUserUID).ConfigureAwait(false);
|
||||
otherPermissionToSelf.DisableAnimations = otherPreferred.DisableAnimations;
|
||||
otherPermissionToSelf.DisableSounds = otherPreferred.DisableSounds;
|
||||
otherPermissionToSelf.DisableVFX = otherPreferred.DisableVFX;
|
||||
otherPermissionToSelf.IsPaused = otherPreferred.IsPaused;
|
||||
|
||||
DbContext.Update(otherPermissionToSelf);
|
||||
}
|
||||
|
||||
await Clients.User(UserUID).Client_GroupPairJoined(new GroupPairFullInfoDto(group.ToGroupData(),
|
||||
pair.ToUserData(), ownPermissionsToOther.ToUserPermissions(setSticky: ownPermissionsToOther.Sticky),
|
||||
otherPermissionToSelf.ToUserPermissions(setSticky: false))).ConfigureAwait(false);
|
||||
await Clients.User(pair.GroupUserUID).Client_GroupPairJoined(new GroupPairFullInfoDto(group.ToGroupData(),
|
||||
self.ToUserData(), otherPermissionToSelf.ToUserPermissions(setSticky: otherPermissionToSelf.Sticky),
|
||||
ownPermissionsToOther.ToUserPermissions(setSticky: false))).ConfigureAwait(false);
|
||||
|
||||
// if not paired prior and neither has the permissions set to paused, send online
|
||||
if ((!allUserPairs.ContainsKey(pair.GroupUserUID) || (allUserPairs.TryGetValue(pair.GroupUserUID, out var info) && !info.IsSynced))
|
||||
&& !otherPermissionToSelf.IsPaused && !ownPermissionsToOther.IsPaused)
|
||||
{
|
||||
var groupUserIdent = await GetUserIdent(pair.GroupUserUID).ConfigureAwait(false);
|
||||
if (!string.IsNullOrEmpty(groupUserIdent))
|
||||
{
|
||||
await Clients.User(UserUID).Client_UserSendOnline(new(pair.ToUserData(), groupUserIdent)).ConfigureAwait(false);
|
||||
await Clients.User(pair.GroupUserUID).Client_UserSendOnline(new(self.ToUserData(), UserCharaIdent)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<GroupJoinInfoDto> GroupJoinHashed(GroupJoinHashedDto dto)
|
||||
{
|
||||
var aliasOrGid = dto.Group.GID.Trim();
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var group = await DbContext.Groups.Include(g => g.Owner)
|
||||
.AsNoTracking()
|
||||
.SingleOrDefaultAsync(g => g.GID == aliasOrGid || g.Alias == aliasOrGid)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var groupGid = group?.GID ?? string.Empty;
|
||||
|
||||
var existingPair = await DbContext.GroupPairs
|
||||
.AsNoTracking()
|
||||
.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.GroupUserUID == UserUID)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var isBanned = await DbContext.GroupBans
|
||||
.AnyAsync(g => g.GroupGID == groupGid && g.BannedUserUID == UserUID)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var oneTimeInvite = await DbContext.GroupTempInvites
|
||||
.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.Invite == dto.HashedPassword)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var existingUserCount = await DbContext.GroupPairs
|
||||
.AsNoTracking()
|
||||
.CountAsync(g => g.GroupGID == groupGid)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var joinedGroups = await DbContext.GroupPairs
|
||||
.CountAsync(g => g.GroupUserUID == UserUID)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (group == null)
|
||||
{
|
||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "Syncshell not found.");
|
||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
||||
}
|
||||
|
||||
if (!string.Equals(group.HashedPassword, dto.HashedPassword, StringComparison.Ordinal) && oneTimeInvite == null)
|
||||
{
|
||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "Incorrect or expired password.");
|
||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
||||
}
|
||||
|
||||
if (existingPair != null)
|
||||
{
|
||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "You are already a member of this syncshell.");
|
||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
||||
}
|
||||
|
||||
if (existingUserCount >= _maxGroupUserCount)
|
||||
{
|
||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "This syncshell is full.");
|
||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
||||
}
|
||||
|
||||
if (!group.InvitesEnabled)
|
||||
{
|
||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "Invites to this syncshell are currently disabled.");
|
||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
||||
}
|
||||
|
||||
if (joinedGroups >= _maxJoinedGroupsByUser)
|
||||
{
|
||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "You have reached the maximum number of syncshells you can join.");
|
||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
||||
}
|
||||
|
||||
if (isBanned)
|
||||
{
|
||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "You are banned from this syncshell.");
|
||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
||||
}
|
||||
|
||||
return new GroupJoinInfoDto(group.ToGroupData(), group.Owner.ToUserData(), group.ToEnum(), true);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupLeave(GroupDto dto)
|
||||
{
|
||||
await UserLeaveGroup(dto, UserUID).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<int> GroupPrune(GroupDto dto, int days, bool execute)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, days, execute));
|
||||
|
||||
var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!hasRights) return -1;
|
||||
|
||||
var allGroupUsers = await DbContext.GroupPairs.Include(p => p.GroupUser).Include(p => p.Group)
|
||||
.Where(g => g.GroupGID == dto.Group.GID)
|
||||
.ToListAsync().ConfigureAwait(false);
|
||||
var usersToPrune = allGroupUsers.Where(p => !p.IsPinned && !p.IsModerator
|
||||
&& !string.Equals(p.GroupUserUID, UserUID, StringComparison.Ordinal)
|
||||
&& !string.Equals(p.Group.OwnerUID, p.GroupUserUID, StringComparison.Ordinal)
|
||||
&& p.GroupUser.LastLoggedIn.AddDays(days) < DateTime.UtcNow);
|
||||
|
||||
if (!execute) return usersToPrune.Count();
|
||||
|
||||
DbContext.GroupPairs.RemoveRange(usersToPrune);
|
||||
|
||||
foreach (var pair in usersToPrune)
|
||||
{
|
||||
await Clients.Users(allGroupUsers.Where(p => !usersToPrune.Contains(p)).Select(g => g.GroupUserUID))
|
||||
.Client_GroupPairLeft(new GroupPairDto(dto.Group, pair.GroupUser.ToUserData())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
return usersToPrune.Count();
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupRemoveUser(GroupPairDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!hasRights) return;
|
||||
|
||||
var (userExists, groupPair) = await TryValidateUserInGroup(dto.Group.GID, dto.User.UID).ConfigureAwait(false);
|
||||
if (!userExists) return;
|
||||
|
||||
if (groupPair.IsModerator || string.Equals(group.OwnerUID, dto.User.UID, StringComparison.Ordinal)) return;
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||
|
||||
DbContext.GroupPairs.Remove(groupPair);
|
||||
|
||||
var groupPairs = DbContext.GroupPairs.Where(p => p.GroupGID == group.GID).AsNoTracking().ToList();
|
||||
await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupPairLeft(dto).ConfigureAwait(false);
|
||||
|
||||
var sharedData = await DbContext.CharaDataAllowances.Where(u => u.AllowedGroup != null && u.AllowedGroupGID == dto.GID && u.ParentUploaderUID == dto.UID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
DbContext.CharaDataAllowances.RemoveRange(sharedData);
|
||||
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
var userIdent = await GetUserIdent(dto.User.UID).ConfigureAwait(false);
|
||||
if (userIdent == null)
|
||||
{
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await Clients.User(dto.User.UID).Client_GroupDelete(new GroupDto(dto.Group)).ConfigureAwait(false);
|
||||
|
||||
var userPairs = await GetAllPairInfo(dto.User.UID).ConfigureAwait(false);
|
||||
foreach (var groupUserPair in groupPairs)
|
||||
{
|
||||
await UserGroupLeave(groupUserPair, userIdent, userPairs, dto.User.UID).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<GroupProfileDto> GroupGetProfile(GroupDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var cancellationToken = _contextAccessor.HttpContext.RequestAborted;
|
||||
|
||||
var data = await DbContext.GroupProfiles
|
||||
.FirstOrDefaultAsync(g => g.GroupGID == dto.Group.GID, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var profileDto = new GroupProfileDto(dto.Group, Description: null, Tags: null, PictureBase64: null);
|
||||
|
||||
if (data is not null)
|
||||
{
|
||||
profileDto = profileDto with
|
||||
{
|
||||
Description = data.Description,
|
||||
Tags = data.Tags,
|
||||
PictureBase64 = data.Base64GroupProfileImage,
|
||||
};
|
||||
|
||||
await Clients.User(UserUID)
|
||||
.Client_GroupSendProfile(profileDto)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return profileDto;
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupSetProfile(GroupProfileDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
if (dto.Group == null) return;
|
||||
|
||||
var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!hasRights) return;
|
||||
|
||||
var groupProfileDb = await DbContext.GroupProfiles
|
||||
.FirstOrDefaultAsync(g => g.GroupGID == dto.Group.GID,
|
||||
_contextAccessor.HttpContext.RequestAborted)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (groupProfileDb != null)
|
||||
{
|
||||
groupProfileDb.Description = dto.Description;
|
||||
groupProfileDb.Tags = dto.Tags;
|
||||
groupProfileDb.Base64GroupProfileImage = dto.PictureBase64;
|
||||
}
|
||||
else
|
||||
{
|
||||
var groupProfile = new GroupProfile
|
||||
{
|
||||
GroupGID = dto.Group.GID,
|
||||
Description = dto.Description,
|
||||
Tags = dto.Tags,
|
||||
Base64GroupProfileImage = dto.PictureBase64,
|
||||
};
|
||||
|
||||
await DbContext.GroupProfiles.AddAsync(groupProfile,
|
||||
_contextAccessor.HttpContext.RequestAborted)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupSetUserInfo(GroupPairUserInfoDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var (userExists, userPair) = await TryValidateUserInGroup(dto.Group.GID, dto.User.UID).ConfigureAwait(false);
|
||||
if (!userExists) return;
|
||||
|
||||
var (userIsOwner, _) = await TryValidateOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
var (userIsModerator, _) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
|
||||
if (dto.GroupUserInfo.HasFlag(GroupPairUserInfo.IsPinned) && userIsModerator && !userPair.IsPinned)
|
||||
{
|
||||
userPair.IsPinned = true;
|
||||
}
|
||||
else if (userIsModerator && userPair.IsPinned)
|
||||
{
|
||||
userPair.IsPinned = false;
|
||||
}
|
||||
|
||||
if (dto.GroupUserInfo.HasFlag(GroupPairUserInfo.IsModerator) && userIsOwner && !userPair.IsModerator)
|
||||
{
|
||||
userPair.IsModerator = true;
|
||||
}
|
||||
else if (userIsOwner && userPair.IsModerator)
|
||||
{
|
||||
userPair.IsModerator = false;
|
||||
}
|
||||
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
var groupPairs = await DbContext.GroupPairs.AsNoTracking().Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
await Clients.Users(groupPairs).Client_GroupPairChangeUserInfo(new GroupPairUserInfoDto(dto.Group, dto.User, userPair.ToEnum())).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<List<GroupFullInfoDto>> GroupsGetAll()
|
||||
{
|
||||
_logger.LogCallInfo();
|
||||
|
||||
var ct = _contextAccessor.HttpContext.RequestAborted;
|
||||
|
||||
var result = await (
|
||||
from gp in DbContext.GroupPairs
|
||||
.Include(gp => gp.Group)
|
||||
.ThenInclude(g => g.Owner)
|
||||
join pp in DbContext.GroupPairPreferredPermissions
|
||||
on new { gp.GroupGID, UserUID } equals new { pp.GroupGID, pp.UserUID }
|
||||
where gp.GroupUserUID == UserUID
|
||||
select new
|
||||
{
|
||||
GroupPair = gp,
|
||||
PreferredPermission = pp,
|
||||
GroupInfos = DbContext.GroupPairs
|
||||
.Where(x => x.GroupGID == gp.GroupGID && (x.IsPinned || x.IsModerator))
|
||||
.Select(x => new { x.GroupUserUID, EnumValue = x.ToEnum() })
|
||||
.ToList(),
|
||||
})
|
||||
.AsNoTracking()
|
||||
.ToListAsync(ct)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(result));
|
||||
|
||||
List<GroupFullInfoDto> List = [.. result.Select(r =>
|
||||
{
|
||||
var groupInfoDict = r.GroupInfos
|
||||
.ToDictionary(x => x.GroupUserUID, x => x.EnumValue, StringComparer.Ordinal);
|
||||
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(r));
|
||||
|
||||
return new GroupFullInfoDto(
|
||||
r.GroupPair.Group.ToGroupData(),
|
||||
r.GroupPair.Group.Owner.ToUserData(),
|
||||
r.GroupPair.Group.ToEnum(),
|
||||
r.PreferredPermission.ToEnum(),
|
||||
r.GroupPair.ToEnum(),
|
||||
groupInfoDict
|
||||
);
|
||||
}),];
|
||||
return List;
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task GroupUnbanUser(GroupPairDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
var (userHasRights, _) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||
if (!userHasRights) return;
|
||||
|
||||
var banEntry = await DbContext.GroupBans.SingleOrDefaultAsync(g => g.GroupGID == dto.Group.GID && g.BannedUserUID == dto.User.UID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
if (banEntry == null) return;
|
||||
|
||||
DbContext.Remove(banEntry);
|
||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<bool> SetGroupBroadcastStatus(GroupBroadcastRequestDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||
|
||||
if (string.IsNullOrEmpty(dto.HashedCID))
|
||||
{
|
||||
_logger.LogCallWarning(LightlessHubLogger.Args("missing CID in syncshell broadcast request", "User", UserUID, "GID", dto.GID));
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Internal error: missing CID.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var (isOwner, _) = await TryValidateOwner(dto.GID).ConfigureAwait(false);
|
||||
if (!isOwner)
|
||||
{
|
||||
_logger.LogCallWarning(LightlessHubLogger.Args("Unauthorized syncshell broadcast change", "User", UserUID, "GID", dto.GID));
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "You must be the owner of the syncshell to broadcast it.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<List<GroupJoinDto>> GetBroadcastedGroups(List<BroadcastStatusInfoDto> broadcastEntries)
|
||||
{
|
||||
_logger.LogCallInfo(LightlessHubLogger.Args("Requested Syncshells", broadcastEntries.Select(b => b.GID)));
|
||||
|
||||
var results = new List<GroupJoinDto>();
|
||||
var gidsToValidate = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var entry in broadcastEntries)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entry.HashedCID) || string.IsNullOrWhiteSpace(entry.GID))
|
||||
continue;
|
||||
|
||||
var redisKey = $"broadcast:{entry.HashedCID}";
|
||||
var redisEntry = await _redis.GetAsync<BroadcastRedisEntry>(redisKey).ConfigureAwait(false);
|
||||
|
||||
if (redisEntry?.GID != null && string.Equals(redisEntry.GID, entry.GID, StringComparison.OrdinalIgnoreCase))
|
||||
gidsToValidate.Add(entry.GID);
|
||||
}
|
||||
|
||||
if (gidsToValidate.Count == 0)
|
||||
return results;
|
||||
|
||||
var groups = await DbContext.Groups
|
||||
.AsNoTracking()
|
||||
.Where(g => gidsToValidate.Contains(g.GID) && g.InvitesEnabled)
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
results.Add(new GroupJoinDto(
|
||||
Group: new GroupData(group.GID, group.Alias),
|
||||
Password: group.HashedPassword,
|
||||
GroupUserPreferredPermissions: new GroupUserPreferredPermissions()
|
||||
));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user