Files
ClubPenguinServer/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.Functions.cs
Stanley Dimant 156c3d80ed MCDO fixes
add groups to individuals stuff

don't show your own data lmao

don't allow to share to not joined groups

fix shared bug maybe

fucking braces

improve CharaDataGetShared()

remove unnecessary double-query on get shared data

optimize

remove shit
2025-06-28 14:37:25 +00:00

283 lines
13 KiB
C#

using MareSynchronosShared.Models;
using Microsoft.EntityFrameworkCore;
using MareSynchronosServer.Utils;
using MareSynchronosShared.Utils;
using MareSynchronos.API.Data;
using MareSynchronos.API.Dto.Group;
using MareSynchronosShared.Metrics;
using Microsoft.AspNetCore.SignalR;
namespace MareSynchronosServer.Hubs;
public partial class MareHub
{
public string UserCharaIdent => Context.User?.Claims?.SingleOrDefault(c => string.Equals(c.Type, MareClaimTypes.CharaIdent, StringComparison.Ordinal))?.Value ?? throw new Exception("No Chara Ident in Claims");
public string UserUID => Context.User?.Claims?.SingleOrDefault(c => string.Equals(c.Type, MareClaimTypes.Uid, StringComparison.Ordinal))?.Value ?? throw new Exception("No UID in Claims");
public string Continent => Context.User?.Claims?.SingleOrDefault(c => string.Equals(c.Type, MareClaimTypes.Continent, StringComparison.Ordinal))?.Value ?? "UNK";
private async Task DeleteUser(User user)
{
var ownPairData = await DbContext.ClientPairs.Where(u => u.User.UID == user.UID).ToListAsync().ConfigureAwait(false);
var auth = await DbContext.Auth.SingleAsync(u => u.UserUID == user.UID).ConfigureAwait(false);
var lodestone = await DbContext.LodeStoneAuth.SingleOrDefaultAsync(a => a.User.UID == user.UID).ConfigureAwait(false);
var groupPairs = await DbContext.GroupPairs.Where(g => g.GroupUserUID == user.UID).ToListAsync().ConfigureAwait(false);
var userProfileData = await DbContext.UserProfileData.SingleOrDefaultAsync(u => u.UserUID == user.UID).ConfigureAwait(false);
var bannedEntries = await DbContext.GroupBans.Where(u => u.BannedUserUID == user.UID).ToListAsync().ConfigureAwait(false);
if (lodestone != null)
{
DbContext.Remove(lodestone);
}
if (userProfileData != null)
{
DbContext.Remove(userProfileData);
}
DbContext.ClientPairs.RemoveRange(ownPairData);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
var otherPairData = await DbContext.ClientPairs.Include(u => u.User)
.Where(u => u.OtherUser.UID == user.UID).AsNoTracking().ToListAsync().ConfigureAwait(false);
foreach (var pair in otherPairData)
{
await Clients.User(pair.UserUID).Client_UserRemoveClientPair(new(user.ToUserData())).ConfigureAwait(false);
}
foreach (var pair in groupPairs)
{
await UserLeaveGroup(new GroupDto(new GroupData(pair.GroupGID)), user.UID).ConfigureAwait(false);
}
_mareMetrics.IncCounter(MetricsAPI.CounterUsersRegisteredDeleted, 1);
DbContext.GroupBans.RemoveRange(bannedEntries);
DbContext.ClientPairs.RemoveRange(otherPairData);
DbContext.Users.Remove(user);
DbContext.Auth.Remove(auth);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
}
private async Task<List<PausedEntry>> GetAllPairedClientsWithPauseState(string? uid = null)
{
uid ??= UserUID;
var query = await (from userPair in DbContext.ClientPairs
join otherUserPair in DbContext.ClientPairs on userPair.OtherUserUID equals otherUserPair.UserUID
where otherUserPair.OtherUserUID == uid && userPair.UserUID == uid
select new
{
UID = Convert.ToString(userPair.OtherUserUID),
GID = "DIRECT",
PauseStateSelf = userPair.IsPaused,
PauseStateOther = otherUserPair.IsPaused,
})
.Union(
(from userGroupPair in DbContext.GroupPairs
join otherGroupPair in DbContext.GroupPairs on userGroupPair.GroupGID equals otherGroupPair.GroupGID
where
userGroupPair.GroupUserUID == uid
&& otherGroupPair.GroupUserUID != uid
select new
{
UID = Convert.ToString(otherGroupPair.GroupUserUID),
GID = Convert.ToString(otherGroupPair.GroupGID),
PauseStateSelf = userGroupPair.IsPaused,
PauseStateOther = otherGroupPair.IsPaused,
})
).AsNoTracking().ToListAsync().ConfigureAwait(false);
return query.GroupBy(g => g.UID, g => (g.GID, g.PauseStateSelf, g.PauseStateOther),
(key, g) => new PausedEntry
{
UID = key,
PauseStates = g.Select(p => new PauseState() { GID = string.Equals(p.GID, "DIRECT", StringComparison.Ordinal) ? null : p.GID, IsSelfPaused = p.PauseStateSelf, IsOtherPaused = p.PauseStateOther })
.ToList(),
}, StringComparer.Ordinal).ToList();
}
private async Task<List<string>> GetAllPairedUnpausedUsers(string? uid = null)
{
uid ??= UserUID;
var ret = await GetAllPairedClientsWithPauseState(uid).ConfigureAwait(false);
return ret.Where(k => !k.IsPaused).Select(k => k.UID).ToList();
}
private async Task<Dictionary<string, string>> GetOnlineUsers(List<string> uids)
{
var result = await _redis.GetAllAsync<string>(uids.Select(u => "UID:" + u).ToHashSet(StringComparer.Ordinal)).ConfigureAwait(false);
return uids.Where(u => result.TryGetValue("UID:" + u, out var ident) && !string.IsNullOrEmpty(ident)).ToDictionary(u => u, u => result["UID:" + u], StringComparer.Ordinal);
}
private async Task<string> GetUserIdent(string uid)
{
if (string.IsNullOrEmpty(uid)) return string.Empty;
return await _redis.GetAsync<string>("UID:" + uid).ConfigureAwait(false);
}
private async Task RemoveUserFromRedis()
{
await _redis.RemoveAsync("UID:" + UserUID, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false);
}
private async Task SendGroupDeletedToAll(List<GroupPair> groupUsers)
{
foreach (var pair in groupUsers)
{
var pairIdent = await GetUserIdent(pair.GroupUserUID).ConfigureAwait(false);
if (string.IsNullOrEmpty(pairIdent)) continue;
var pairs = await GetAllPairedClientsWithPauseState(pair.GroupUserUID).ConfigureAwait(false);
foreach (var groupUserPair in groupUsers.Where(g => !string.Equals(g.GroupUserUID, pair.GroupUserUID, StringComparison.Ordinal)))
{
await UserGroupLeave(groupUserPair, pairs, pairIdent, pair.GroupUserUID).ConfigureAwait(false);
}
}
}
private async Task<List<string>> SendOfflineToAllPairedUsers()
{
var usersToSendDataTo = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
var self = await DbContext.Users.AsNoTracking().SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
await Clients.Users(usersToSendDataTo).Client_UserSendOffline(new(self.ToUserData())).ConfigureAwait(false);
return usersToSendDataTo;
}
private async Task<List<string>> SendOnlineToAllPairedUsers()
{
var usersToSendDataTo = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
var self = await DbContext.Users.AsNoTracking().SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
await Clients.Users(usersToSendDataTo).Client_UserSendOnline(new(self.ToUserData(), UserCharaIdent)).ConfigureAwait(false);
return usersToSendDataTo;
}
private async Task<(bool IsValid, Group ReferredGroup)> TryValidateGroupModeratorOrOwner(string gid)
{
var isOwnerResult = await TryValidateOwner(gid).ConfigureAwait(false);
if (isOwnerResult.isValid) return (true, isOwnerResult.ReferredGroup);
if (isOwnerResult.ReferredGroup == null) return (false, null);
var groupPairSelf = await DbContext.GroupPairs.SingleOrDefaultAsync(g => g.GroupGID == gid && g.GroupUserUID == UserUID).ConfigureAwait(false);
if (groupPairSelf == null || !groupPairSelf.IsModerator) return (false, null);
return (true, isOwnerResult.ReferredGroup);
}
private async Task<(bool isValid, Group ReferredGroup)> TryValidateOwner(string gid)
{
var group = await DbContext.Groups.SingleOrDefaultAsync(g => g.GID == gid).ConfigureAwait(false);
if (group == null) return (false, null);
return (string.Equals(group.OwnerUID, UserUID, StringComparison.Ordinal), group);
}
private async Task<(bool IsValid, GroupPair ReferredPair)> TryValidateUserInGroup(string gid, string? uid = null)
{
uid ??= UserUID;
var groupPair = await DbContext.GroupPairs.Include(c => c.GroupUser)
.SingleOrDefaultAsync(g => g.GroupGID == gid && (g.GroupUserUID == uid || g.GroupUser.Alias == uid)).ConfigureAwait(false);
if (groupPair == null) return (false, null);
return (true, groupPair);
}
private async Task UpdateUserOnRedis()
{
await _redis.AddAsync("UID:" + UserUID, UserCharaIdent, TimeSpan.FromSeconds(60), StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false);
}
private async Task UserGroupLeave(GroupPair groupUserPair, List<PausedEntry> allUserPairs, string userIdent, string? uid = null)
{
uid ??= UserUID;
var userPair = allUserPairs.SingleOrDefault(p => string.Equals(p.UID, groupUserPair.GroupUserUID, StringComparison.Ordinal));
if (userPair != null)
{
if (userPair.IsDirectlyPaused != PauseInfo.NoConnection) return;
if (userPair.IsPausedPerGroup is PauseInfo.Unpaused) return;
}
var groupUserIdent = await GetUserIdent(groupUserPair.GroupUserUID).ConfigureAwait(false);
if (!string.IsNullOrEmpty(groupUserIdent))
{
await Clients.User(uid).Client_UserSendOffline(new(new(groupUserPair.GroupUserUID))).ConfigureAwait(false);
await Clients.User(groupUserPair.GroupUserUID).Client_UserSendOffline(new(new(uid))).ConfigureAwait(false);
}
}
private async Task UserLeaveGroup(GroupDto dto, string userUid)
{
_logger.LogCallInfo(MareHubLogger.Args(dto));
var (exists, groupPair) = await TryValidateUserInGroup(dto.Group.GID, userUid).ConfigureAwait(false);
if (!exists) return;
var group = await DbContext.Groups.SingleOrDefaultAsync(g => g.GID == dto.Group.GID).ConfigureAwait(false);
var groupPairs = await DbContext.GroupPairs.Where(p => p.GroupGID == group.GID).ToListAsync().ConfigureAwait(false);
var groupPairsWithoutSelf = groupPairs.Where(p => !string.Equals(p.GroupUserUID, userUid, StringComparison.Ordinal)).ToList();
DbContext.GroupPairs.Remove(groupPair);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
await Clients.User(userUid).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false);
bool ownerHasLeft = string.Equals(group.OwnerUID, userUid, StringComparison.Ordinal);
if (ownerHasLeft)
{
if (!groupPairsWithoutSelf.Any())
{
_logger.LogCallInfo(MareHubLogger.Args(dto, "Deleted"));
DbContext.Groups.Remove(group);
}
else
{
var groupHasMigrated = await SharedDbFunctions.MigrateOrDeleteGroup(DbContext, group, groupPairsWithoutSelf, _maxExistingGroupsByUser).ConfigureAwait(false);
if (groupHasMigrated.Item1)
{
_logger.LogCallInfo(MareHubLogger.Args(dto, "Migrated", groupHasMigrated.Item2));
var user = await DbContext.Users.SingleAsync(u => u.UID == groupHasMigrated.Item2).ConfigureAwait(false);
await Clients.Users(groupPairsWithoutSelf.Select(p => p.GroupUserUID)).Client_GroupSendInfo(new GroupInfoDto(group.ToGroupData(),
user.ToUserData(), group.GetGroupPermissions())).ConfigureAwait(false);
}
else
{
_logger.LogCallInfo(MareHubLogger.Args(dto, "Deleted"));
await Clients.Users(groupPairsWithoutSelf.Select(p => p.GroupUserUID)).Client_GroupDelete(dto).ConfigureAwait(false);
await SendGroupDeletedToAll(groupPairs).ConfigureAwait(false);
return;
}
}
}
var sharedData = await DbContext.CharaDataAllowances.Where(u => u.AllowedGroup != null && u.AllowedGroupGID == dto.GID && u.ParentUploaderUID == userUid).ToListAsync().ConfigureAwait(false);
DbContext.CharaDataAllowances.RemoveRange(sharedData);
await DbContext.SaveChangesAsync().ConfigureAwait(false);
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
await Clients.Users(groupPairsWithoutSelf.Select(p => p.GroupUserUID)).Client_GroupPairLeft(new GroupPairDto(dto.Group, groupPair.GroupUser.ToUserData())).ConfigureAwait(false);
var allUserPairs = await GetAllPairedClientsWithPauseState().ConfigureAwait(false);
var ident = await GetUserIdent(userUid).ConfigureAwait(false);
foreach (var groupUserPair in groupPairsWithoutSelf)
{
await UserGroupLeave(groupUserPair, allUserPairs, ident, userUid).ConfigureAwait(false);
}
}
}