idk if this will help but this is a certified attempt
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:latest
|
image: postgres:latest
|
||||||
|
command: ["postgres", "-c", "log_statement=all"]
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432/tcp
|
- 5432:5432/tcp
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using MareSynchronos.API.Data;
|
|||||||
using MareSynchronos.API.Dto.Group;
|
using MareSynchronos.API.Dto.Group;
|
||||||
using MareSynchronosShared.Metrics;
|
using MareSynchronosShared.Metrics;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using MareSynchronosShared.Data;
|
||||||
|
|
||||||
namespace MareSynchronosServer.Hubs;
|
namespace MareSynchronosServer.Hubs;
|
||||||
|
|
||||||
@@ -70,16 +71,13 @@ public partial class MareHub
|
|||||||
_dbContext.Users.Remove(user);
|
_dbContext.Users.Remove(user);
|
||||||
_dbContext.Auth.Remove(auth);
|
_dbContext.Auth.Remove(auth);
|
||||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
_cacheService.ClearCache(user.UID);
|
|
||||||
_cacheService.MarkAsStale(null, user.UID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<List<string>> GetAllPairedUnpausedUsers(string? uid = null)
|
private async Task<List<string>> GetAllPairedUnpausedUsers(string? uid = null)
|
||||||
{
|
{
|
||||||
uid ??= UserUID;
|
uid ??= UserUID;
|
||||||
|
|
||||||
return (await _cacheService.GetAllPairs(UserUID, _dbContext).ConfigureAwait(false))
|
return (await GetAllPairInfo(UserUID).ConfigureAwait(false))
|
||||||
.Where(u => !u.Value.OwnPermissions.IsPaused && u.Value.OtherPermissions != null && !u.Value.OtherPermissions.IsPaused)
|
.Where(u => !u.Value.OwnPermissions.IsPaused && u.Value.OtherPermissions != null && !u.Value.OtherPermissions.IsPaused)
|
||||||
.Select(u => u.Key).ToList();
|
.Select(u => u.Key).ToList();
|
||||||
}
|
}
|
||||||
@@ -173,7 +171,7 @@ public partial class MareHub
|
|||||||
private async Task UserGroupLeave(GroupPair groupUserPair, string userIdent, string? uid = null)
|
private async Task UserGroupLeave(GroupPair groupUserPair, string userIdent, string? uid = null)
|
||||||
{
|
{
|
||||||
uid ??= UserUID;
|
uid ??= UserUID;
|
||||||
var allUserPairs = await _cacheService.GetAllPairs(uid, _dbContext).ConfigureAwait(false);
|
var allUserPairs = await GetAllPairInfo(uid).ConfigureAwait(false);
|
||||||
if (!allUserPairs.TryGetValue(groupUserPair.GroupUserUID, out var info) || !info.IsSynced)
|
if (!allUserPairs.TryGetValue(groupUserPair.GroupUserUID, out var info) || !info.IsSynced)
|
||||||
{
|
{
|
||||||
var groupUserIdent = await GetUserIdent(groupUserPair.GroupUserUID).ConfigureAwait(false);
|
var groupUserIdent = await GetUserIdent(groupUserPair.GroupUserUID).ConfigureAwait(false);
|
||||||
@@ -238,7 +236,6 @@ public partial class MareHub
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
_cacheService.MarkAsStale(userUid, null);
|
|
||||||
|
|
||||||
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
||||||
|
|
||||||
@@ -252,6 +249,126 @@ public partial class MareHub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record UserQueryPermissionEntry(UserQueryEntry OtherUser, UserPermissionSet OwnPermissions, UserPermissionSet? OtherPermissions);
|
private async Task<UserInfo?> GetPairInfo(string uid, string otheruid)
|
||||||
public record UserQueryEntry(string UID, bool IsPaired, string GID, string Alias);
|
{
|
||||||
|
var query = _dbContext.ClientPairs.Where(u => u.UserUID == uid && u.OtherUserUID == otheruid)
|
||||||
|
.Join(_dbContext.ClientPairs.Where(u => u.UserUID == otheruid && u.OtherUserUID == uid),
|
||||||
|
user => user.OtherUserUID, user => user.UserUID, (user, otheruser) =>
|
||||||
|
new
|
||||||
|
{
|
||||||
|
UserUID = user.UserUID,
|
||||||
|
OtherUserUID = user.OtherUserUID,
|
||||||
|
Gid = string.Empty,
|
||||||
|
Synced = otheruser != null
|
||||||
|
});
|
||||||
|
|
||||||
|
var query2 = _dbContext.GroupPairs.Where(u => u.GroupUserUID == uid)
|
||||||
|
.Join(_dbContext.GroupPairs.Where(u => u.GroupUserUID == otheruid),
|
||||||
|
ownPair => ownPair.GroupGID, otherPair => otherPair.GroupGID,
|
||||||
|
(ownPair, otherPair) =>
|
||||||
|
new
|
||||||
|
{
|
||||||
|
UserUID = ownPair.GroupUserUID,
|
||||||
|
OtherUserUID = otherPair.GroupUserUID,
|
||||||
|
Gid = ownPair.GroupGID,
|
||||||
|
Synced = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = from user in query.Union(query2)
|
||||||
|
join u in _dbContext.Users on user.OtherUserUID equals u.UID
|
||||||
|
join o in _dbContext.Permissions
|
||||||
|
on new { user.UserUID, user.OtherUserUID }
|
||||||
|
equals new { o.UserUID, o.OtherUserUID }
|
||||||
|
into ownperms
|
||||||
|
from ownperm in ownperms.DefaultIfEmpty()
|
||||||
|
join p in _dbContext.Permissions
|
||||||
|
on new { UserUID = user.OtherUserUID, OtherUserUID = user.UserUID }
|
||||||
|
equals new { p.UserUID, p.OtherUserUID }
|
||||||
|
into otherperms
|
||||||
|
from otherperm in otherperms.DefaultIfEmpty()
|
||||||
|
select new
|
||||||
|
{
|
||||||
|
UserUID = user.UserUID,
|
||||||
|
OtherUserUID = user.OtherUserUID,
|
||||||
|
OtherUserAlias = u.Alias,
|
||||||
|
GID = user.Gid,
|
||||||
|
Synced = user.Synced,
|
||||||
|
OwnPermissions = ownperm,
|
||||||
|
OtherPermissions = otherperm
|
||||||
|
};
|
||||||
|
|
||||||
|
var resultList = await result.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!resultList.Any()) return null;
|
||||||
|
|
||||||
|
var groups = resultList.Select(g => g.GID).ToList();
|
||||||
|
return new UserInfo(resultList[0].OtherUserAlias,
|
||||||
|
resultList.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
||||||
|
resultList.Max(p => p.Synced),
|
||||||
|
resultList.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
||||||
|
resultList[0].OwnPermissions,
|
||||||
|
resultList[0].OtherPermissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Dictionary<string, UserInfo>> GetAllPairInfo(string uid)
|
||||||
|
{
|
||||||
|
var query = _dbContext.ClientPairs.Where(u => u.UserUID == uid)
|
||||||
|
.Join(_dbContext.ClientPairs.Where(u => u.OtherUserUID == uid),
|
||||||
|
user => user.OtherUserUID, user => user.UserUID, (user, otheruser) =>
|
||||||
|
new
|
||||||
|
{
|
||||||
|
UserUID = Convert.ToString(user.UserUID),
|
||||||
|
OtherUserUID = Convert.ToString(user.OtherUserUID),
|
||||||
|
Gid = string.Empty,
|
||||||
|
Synced = otheruser != null
|
||||||
|
});
|
||||||
|
|
||||||
|
var query2 = _dbContext.GroupPairs.Where(u => u.GroupUserUID == uid)
|
||||||
|
.Join(_dbContext.GroupPairs.Where(u => u.GroupUserUID != uid),
|
||||||
|
ownPair => ownPair.GroupGID, otherPair => otherPair.GroupGID,
|
||||||
|
(ownPair, otherPair) =>
|
||||||
|
new
|
||||||
|
{
|
||||||
|
UserUID = Convert.ToString(ownPair.GroupUserUID),
|
||||||
|
OtherUserUID = Convert.ToString(otherPair.GroupUserUID),
|
||||||
|
Gid = Convert.ToString(ownPair.GroupGID),
|
||||||
|
Synced = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = from user in query.Union(query2)
|
||||||
|
join u in _dbContext.Users on user.OtherUserUID equals Convert.ToString(u.UID)
|
||||||
|
join o in _dbContext.Permissions
|
||||||
|
on new { UserUID = user.UserUID, OtherUserUID = user.OtherUserUID }
|
||||||
|
equals new { UserUID = Convert.ToString(o.UserUID), OtherUserUID = Convert.ToString(o.OtherUserUID) }
|
||||||
|
into ownperms
|
||||||
|
from ownperm in ownperms.DefaultIfEmpty()
|
||||||
|
join p in _dbContext.Permissions
|
||||||
|
on new { UserUID = user.OtherUserUID, OtherUserUID = user.UserUID }
|
||||||
|
equals new { UserUID = Convert.ToString(p.UserUID), OtherUserUID = Convert.ToString(p.OtherUserUID) }
|
||||||
|
into otherperms
|
||||||
|
from otherperm in otherperms.DefaultIfEmpty()
|
||||||
|
select new
|
||||||
|
{
|
||||||
|
UserUID = user.UserUID,
|
||||||
|
OtherUserUID = user.OtherUserUID,
|
||||||
|
OtherUserAlias = u.Alias,
|
||||||
|
GID = user.Gid,
|
||||||
|
Synced = user.Synced,
|
||||||
|
OwnPermissions = ownperm,
|
||||||
|
OtherPermissions = otherperm
|
||||||
|
};
|
||||||
|
|
||||||
|
var resultList = await result.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||||
|
return resultList.GroupBy(g => g.OtherUserUID, StringComparer.Ordinal).ToDictionary(g => g.Key, g =>
|
||||||
|
{
|
||||||
|
return new UserInfo(g.First().OtherUserAlias,
|
||||||
|
g.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
||||||
|
g.Max(p => p.Synced),
|
||||||
|
g.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
||||||
|
g.First().OwnPermissions,
|
||||||
|
g.First().OtherPermissions);
|
||||||
|
}, StringComparer.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record UserInfo(string Alias, bool IndividuallyPaired, bool IsSynced, List<string> GIDs, UserPermissionSet? OwnPermissions, UserPermissionSet? OtherPermissions);
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ public partial class MareHub
|
|||||||
groupPreferredPermissions.DisableVFX = dto.GroupPairPermissions.IsDisableVFX();
|
groupPreferredPermissions.DisableVFX = dto.GroupPairPermissions.IsDisableVFX();
|
||||||
|
|
||||||
// set the permissions for every group pair that is not sticky
|
// set the permissions for every group pair that is not sticky
|
||||||
var allPairs = (await _cacheService.GetAllPairs(UserUID, _dbContext).ConfigureAwait(false))
|
var allPairs = (await GetAllPairInfo(UserUID).ConfigureAwait(false))
|
||||||
.Where(u => !u.Value.OwnPermissions.Sticky)
|
.Where(u => !u.Value.OwnPermissions.Sticky)
|
||||||
.ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
.ToDictionary(d => d.Key, d => d.Value, StringComparer.Ordinal);
|
||||||
|
|
||||||
@@ -100,11 +100,6 @@ public partial class MareHub
|
|||||||
}
|
}
|
||||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
foreach (var item in affectedGroupPairs)
|
|
||||||
{
|
|
||||||
_cacheService.MarkAsStale(UserUID, item.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// send messages
|
// send messages
|
||||||
UserPermissions permissions = UserPermissions.NoneSet;
|
UserPermissions permissions = UserPermissions.NoneSet;
|
||||||
permissions.SetPaused(groupPreferredPermissions.IsPaused);
|
permissions.SetPaused(groupPreferredPermissions.IsPaused);
|
||||||
@@ -228,10 +223,6 @@ public partial class MareHub
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
foreach (var user in notPinned)
|
|
||||||
{
|
|
||||||
_cacheService.MarkAsStale(user.GroupUserUID, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
[Authorize(Policy = "Identified")]
|
||||||
@@ -432,8 +423,7 @@ public partial class MareHub
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// get all pairs before we join
|
// get all pairs before we join
|
||||||
var allUserPairs = (await _cacheService.GetAllPairs(UserUID, _dbContext).ConfigureAwait(false))
|
var allUserPairs = (await GetAllPairInfo(UserUID).ConfigureAwait(false));
|
||||||
.ToDictionary(u => u.Key, u => u.Value, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
if (oneTimeInvite != null)
|
if (oneTimeInvite != null)
|
||||||
{
|
{
|
||||||
@@ -485,7 +475,7 @@ public partial class MareHub
|
|||||||
|
|
||||||
var groupPairs = await _dbContext.GroupPairs.Include(p => p.GroupUser).Where(p => p.GroupGID == group.GID && p.GroupUserUID != UserUID).ToListAsync().ConfigureAwait(false);
|
var groupPairs = await _dbContext.GroupPairs.Include(p => p.GroupUser).Where(p => p.GroupGID == group.GID && p.GroupUserUID != UserUID).ToListAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var userPairsAfterJoin = await _cacheService.GetAllPairs(UserUID, _dbContext, true).ConfigureAwait(false);
|
var userPairsAfterJoin = await GetAllPairInfo(UserUID).ConfigureAwait(false);
|
||||||
|
|
||||||
foreach (var pair in groupPairs)
|
foreach (var pair in groupPairs)
|
||||||
{
|
{
|
||||||
@@ -584,11 +574,6 @@ public partial class MareHub
|
|||||||
|
|
||||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
foreach (var pair in groupPairs)
|
|
||||||
{
|
|
||||||
_cacheService.MarkAsStale(UserUID, pair.GroupUserUID);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,7 +616,6 @@ public partial class MareHub
|
|||||||
foreach (var groupUserPair in groupPairs)
|
foreach (var groupUserPair in groupPairs)
|
||||||
{
|
{
|
||||||
await UserGroupLeave(groupUserPair, userIdent, dto.User.UID).ConfigureAwait(false);
|
await UserGroupLeave(groupUserPair, userIdent, dto.User.UID).ConfigureAwait(false);
|
||||||
_cacheService.MarkAsStale(dto.User.UID, groupUserPair.GroupUserUID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,8 +65,7 @@ public partial class MareHub
|
|||||||
};
|
};
|
||||||
await _dbContext.ClientPairs.AddAsync(wl).ConfigureAwait(false);
|
await _dbContext.ClientPairs.AddAsync(wl).ConfigureAwait(false);
|
||||||
|
|
||||||
_cacheService.MarkAsStale(UserUID, otherUser.UID);
|
var existingData = await GetPairInfo(UserUID, otherUser.UID).ConfigureAwait(false);
|
||||||
var existingData = await _cacheService.GetPairData(UserUID, otherUser.UID, _dbContext).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var permissions = existingData?.OwnPermissions;
|
var permissions = existingData?.OwnPermissions;
|
||||||
if (permissions == null || !permissions.Sticky)
|
if (permissions == null || !permissions.Sticky)
|
||||||
@@ -102,7 +101,6 @@ public partial class MareHub
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
_cacheService.MarkAsStale(UserUID, otherUser.UID);
|
|
||||||
|
|
||||||
// get the opposite entry of the client pair
|
// get the opposite entry of the client pair
|
||||||
var otherEntry = OppositeEntry(otherUser.UID);
|
var otherEntry = OppositeEntry(otherUser.UID);
|
||||||
@@ -171,7 +169,7 @@ public partial class MareHub
|
|||||||
{
|
{
|
||||||
_logger.LogCallInfo();
|
_logger.LogCallInfo();
|
||||||
|
|
||||||
var pairs = await _cacheService.GetAllPairs(UserUID, _dbContext, true).ConfigureAwait(false);
|
var pairs = await GetAllPairInfo(UserUID).ConfigureAwait(false);
|
||||||
return pairs.Select(p =>
|
return pairs.Select(p =>
|
||||||
{
|
{
|
||||||
return new UserFullPairDto(new UserData(p.Key, p.Value.Alias),
|
return new UserFullPairDto(new UserData(p.Key, p.Value.Alias),
|
||||||
@@ -263,7 +261,7 @@ public partial class MareHub
|
|||||||
+ string.Join(Environment.NewLine, invalidFileSwapPaths.Select(p => "Invalid FileSwap Path: " + p)));
|
+ string.Join(Environment.NewLine, invalidFileSwapPaths.Select(p => "Invalid FileSwap Path: " + p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var allPairs = await _cacheService.GetAllPairs(UserUID, _dbContext).ConfigureAwait(false);
|
var allPairs = await GetAllPairInfo(UserUID).ConfigureAwait(false);
|
||||||
allPairs.Where(p => !p.Value.OwnPermissions.IsPaused && p.Value.OtherPermissions != null && !p.Value.OtherPermissions.IsPaused).ToList();
|
allPairs.Where(p => !p.Value.OwnPermissions.IsPaused && p.Value.OtherPermissions != null && !p.Value.OtherPermissions.IsPaused).ToList();
|
||||||
|
|
||||||
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
||||||
@@ -291,12 +289,11 @@ public partial class MareHub
|
|||||||
await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false);
|
await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false);
|
||||||
if (callerPair == null) return;
|
if (callerPair == null) return;
|
||||||
|
|
||||||
var pairData = await _cacheService.GetPairData(UserUID, dto.User.UID, _dbContext).ConfigureAwait(false);
|
var pairData = await GetPairInfo(UserUID, dto.User.UID).ConfigureAwait(false);
|
||||||
|
|
||||||
// delete from database, send update info to users pair list
|
// delete from database, send update info to users pair list
|
||||||
_dbContext.ClientPairs.Remove(callerPair);
|
_dbContext.ClientPairs.Remove(callerPair);
|
||||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
_cacheService.MarkAsStale(UserUID, dto.User.UID);
|
|
||||||
|
|
||||||
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
||||||
|
|
||||||
@@ -323,7 +320,7 @@ public partial class MareHub
|
|||||||
// if the either had paused, do nothing
|
// if the either had paused, do nothing
|
||||||
if (callerHadPaused && otherHadPaused) return;
|
if (callerHadPaused && otherHadPaused) return;
|
||||||
|
|
||||||
var currentPairData = await _cacheService.GetPairData(UserUID, dto.User.UID, _dbContext).ConfigureAwait(false);
|
var currentPairData = await GetPairInfo(UserUID, dto.User.UID).ConfigureAwait(false);
|
||||||
|
|
||||||
// if neither user had paused each other and either is not in an unpaused group with each other, change state to offline
|
// if neither user had paused each other and either is not in an unpaused group with each other, change state to offline
|
||||||
if (!currentPairData?.IsSynced ?? true)
|
if (!currentPairData?.IsSynced ?? true)
|
||||||
@@ -384,7 +381,7 @@ public partial class MareHub
|
|||||||
UserPermissionSet prevPermissions = await _dbContext.Permissions.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false);
|
UserPermissionSet prevPermissions = await _dbContext.Permissions.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false);
|
||||||
if (prevPermissions == null) return; // you always should have permissions to another user
|
if (prevPermissions == null) return; // you always should have permissions to another user
|
||||||
|
|
||||||
var oldPairData = await _cacheService.GetPairData(UserUID, dto.User.UID, _dbContext).ConfigureAwait(false);
|
var oldPairData = await GetPairInfo(UserUID, dto.User.UID).ConfigureAwait(false);
|
||||||
bool setSticky = false;
|
bool setSticky = false;
|
||||||
if (!oldPairData.GIDs.Contains(Constants.IndividualKeyword, StringComparer.Ordinal))
|
if (!oldPairData.GIDs.Contains(Constants.IndividualKeyword, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
@@ -404,7 +401,6 @@ public partial class MareHub
|
|||||||
prevPermissions.Sticky = dto.Permissions.IsSticky() || setSticky;
|
prevPermissions.Sticky = dto.Permissions.IsSticky() || setSticky;
|
||||||
_dbContext.Update(prevPermissions);
|
_dbContext.Update(prevPermissions);
|
||||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
_cacheService.MarkAsStale(UserUID, dto.User.UID);
|
|
||||||
|
|
||||||
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
||||||
|
|
||||||
@@ -414,7 +410,7 @@ public partial class MareHub
|
|||||||
await Clients.User(UserUID).Client_UserUpdateSelfPairPermissions(new UserPermissionsDto(dto.User, permCopy)).ConfigureAwait(false);
|
await Clients.User(UserUID).Client_UserUpdateSelfPairPermissions(new UserPermissionsDto(dto.User, permCopy)).ConfigureAwait(false);
|
||||||
await Clients.User(dto.User.UID).Client_UserUpdateOtherPairPermissions(new UserPermissionsDto(new UserData(UserUID), dto.Permissions)).ConfigureAwait(false);
|
await Clients.User(dto.User.UID).Client_UserUpdateOtherPairPermissions(new UserPermissionsDto(new UserData(UserUID), dto.Permissions)).ConfigureAwait(false);
|
||||||
|
|
||||||
var newPairData = await _cacheService.GetPairData(UserUID, dto.User.UID, _dbContext).ConfigureAwait(false);
|
var newPairData = await GetPairInfo(UserUID, dto.User.UID).ConfigureAwait(false);
|
||||||
|
|
||||||
if (newPairData.OwnPermissions.IsPaused != oldPairData.OwnPermissions.IsPaused)
|
if (newPairData.OwnPermissions.IsPaused != oldPairData.OwnPermissions.IsPaused)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,14 +30,13 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
|||||||
private readonly int _maxJoinedGroupsByUser;
|
private readonly int _maxJoinedGroupsByUser;
|
||||||
private readonly int _maxGroupUserCount;
|
private readonly int _maxGroupUserCount;
|
||||||
private readonly IRedisDatabase _redis;
|
private readonly IRedisDatabase _redis;
|
||||||
private readonly UserPairCacheService _cacheService;
|
|
||||||
private readonly Uri _fileServerAddress;
|
private readonly Uri _fileServerAddress;
|
||||||
private readonly Version _expectedClientVersion;
|
private readonly Version _expectedClientVersion;
|
||||||
|
|
||||||
public MareHub(MareMetrics mareMetrics,
|
public MareHub(MareMetrics mareMetrics,
|
||||||
MareDbContext mareDbContext, ILogger<MareHub> logger, SystemInfoService systemInfoService,
|
MareDbContext mareDbContext, ILogger<MareHub> logger, SystemInfoService systemInfoService,
|
||||||
IConfigurationService<ServerConfiguration> configuration, IHttpContextAccessor contextAccessor,
|
IConfigurationService<ServerConfiguration> configuration, IHttpContextAccessor contextAccessor,
|
||||||
IRedisDatabase redisDb, UserPairCacheService cacheService)
|
IRedisDatabase redisDb)
|
||||||
{
|
{
|
||||||
_mareMetrics = mareMetrics;
|
_mareMetrics = mareMetrics;
|
||||||
_systemInfoService = systemInfoService;
|
_systemInfoService = systemInfoService;
|
||||||
@@ -49,7 +48,6 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
|||||||
_expectedClientVersion = configuration.GetValueOrDefault(nameof(ServerConfiguration.ExpectedClientVersion), new Version(0, 0, 0));
|
_expectedClientVersion = configuration.GetValueOrDefault(nameof(ServerConfiguration.ExpectedClientVersion), new Version(0, 0, 0));
|
||||||
_contextAccessor = contextAccessor;
|
_contextAccessor = contextAccessor;
|
||||||
_redis = redisDb;
|
_redis = redisDb;
|
||||||
_cacheService = cacheService;
|
|
||||||
_logger = new MareHubLogger(this, logger);
|
_logger = new MareHubLogger(this, logger);
|
||||||
_dbContext = mareDbContext;
|
_dbContext = mareDbContext;
|
||||||
}
|
}
|
||||||
@@ -144,7 +142,6 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
|||||||
_logger.LogCallWarning(MareHubLogger.Args(_contextAccessor.GetIpAddress(), exception.Message, exception.StackTrace));
|
_logger.LogCallWarning(MareHubLogger.Args(_contextAccessor.GetIpAddress(), exception.Message, exception.StackTrace));
|
||||||
|
|
||||||
await RemoveUserFromRedis().ConfigureAwait(false);
|
await RemoveUserFromRedis().ConfigureAwait(false);
|
||||||
_cacheService.ClearCache(UserUID);
|
|
||||||
|
|
||||||
await SendOfflineToAllPairedUsers().ConfigureAwait(false);
|
await SendOfflineToAllPairedUsers().ConfigureAwait(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,217 +0,0 @@
|
|||||||
using MareSynchronos.API.Data;
|
|
||||||
using MareSynchronosShared.Data;
|
|
||||||
using MareSynchronosShared.Models;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace MareSynchronosServer.Services;
|
|
||||||
|
|
||||||
public class UserPairCacheService : IHostedService
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, Dictionary<string, UserInfo>> _cache;
|
|
||||||
private readonly ILogger<UserPairCacheService> _logger;
|
|
||||||
private readonly IDbContextFactory<MareDbContext> _dbContextFactory;
|
|
||||||
private readonly ConcurrentQueue<(string? UID, string? OtherUID)> _staleUserData;
|
|
||||||
public UserPairCacheService(ILogger<UserPairCacheService> logger, IDbContextFactory<MareDbContext> dbContextFactory)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_dbContextFactory = dbContextFactory;
|
|
||||||
_staleUserData = new();
|
|
||||||
_cache = new(StringComparer.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearCache(string uid)
|
|
||||||
{
|
|
||||||
_cache.TryRemove(uid, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Dictionary<string, UserInfo>> GetAllPairs(string uid, MareDbContext dbContext, bool ignoreCache = false)
|
|
||||||
{
|
|
||||||
if (ignoreCache)
|
|
||||||
{
|
|
||||||
_cache[uid] = await BuildFullCache(dbContext, uid).ConfigureAwait(false);
|
|
||||||
return _cache[uid];
|
|
||||||
}
|
|
||||||
|
|
||||||
await WaitForProcessing(uid).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!_cache.ContainsKey(uid))
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Building full cache: Did not find PairData for {uid}", uid);
|
|
||||||
|
|
||||||
_cache[uid] = await BuildFullCache(dbContext, uid).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _cache[uid];
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<UserInfo?> GetPairData(string uid, string otheruid, MareDbContext dbContext)
|
|
||||||
{
|
|
||||||
await WaitForProcessing(uid, otheruid).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!_cache.TryGetValue(uid, out var cachedInfos))
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Building full cache: Did not find PairData for {uid}:{otheruid}", uid, otheruid);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_cache[uid] = cachedInfos = await BuildFullCache(dbContext, uid).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error during full PairCache calculation of {uid}:{otheruid}", uid, otheruid);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cachedInfos.TryGetValue(otheruid, out var info))
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Building individual cache: Did not find PairData for {uid}:{otheruid}", uid, otheruid);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
info = await BuildIndividualCache(dbContext, uid, otheruid).ConfigureAwait(false);
|
|
||||||
_cache[uid][otheruid] = info;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error during individual PairCache calculation of {uid}:{otheruid}", uid, otheruid);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MarkAsStale(string? uid, string? otheruid)
|
|
||||||
{
|
|
||||||
if (!_staleUserData.Any(u => string.Equals(u.UID, uid, StringComparison.Ordinal)
|
|
||||||
&& string.Equals(u.OtherUID, otheruid, StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
_staleUserData.Enqueue((uid, otheruid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_ = ProcessStaleEntries();
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Dictionary<string, UserInfo>> BuildFullCache(MareDbContext dbContext, string uid)
|
|
||||||
{
|
|
||||||
var pairs = await dbContext.GetAllPairsForUser(uid).ToListAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
var pairsDict = pairs.GroupBy(g => g.OtherUserUID, StringComparer.Ordinal)
|
|
||||||
.ToDictionary(g => g.Key, g =>
|
|
||||||
{
|
|
||||||
return new UserInfo(g.First().Alias,
|
|
||||||
g.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
|
||||||
g.Max(p => p.Synced),
|
|
||||||
g.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
|
||||||
g.First().OwnPermissions,
|
|
||||||
g.First().OtherPermissions);
|
|
||||||
}, StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
return pairsDict.Where(p => p.Value.OwnPermissions != null).ToDictionary(u => u.Key, u => u.Value, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<UserInfo?> BuildIndividualCache(MareDbContext dbContext, string uid, string otheruid)
|
|
||||||
{
|
|
||||||
var pairs = (await dbContext.GetAllPairsForUser(uid).ToListAsync().ConfigureAwait(false)).Where(u => string.Equals(u.OtherUserUID, otheruid, StringComparison.Ordinal)).ToList();
|
|
||||||
|
|
||||||
if (!pairs.Any()) return null;
|
|
||||||
|
|
||||||
var groups = pairs.Select(g => g.GID).ToList();
|
|
||||||
var userInfo = new UserInfo(pairs[0].Alias,
|
|
||||||
pairs.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
|
||||||
pairs.Max(p => p.Synced),
|
|
||||||
pairs.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
|
||||||
pairs[0].OwnPermissions,
|
|
||||||
pairs[0].OtherPermissions);
|
|
||||||
|
|
||||||
return userInfo.OwnPermissions == null ? null : userInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ProcessStaleEntries()
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
await Task.Delay(250).ConfigureAwait(false);
|
|
||||||
if (_staleUserData.Any())
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Processing Stale Entries");
|
|
||||||
|
|
||||||
using var dbContext = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
|
|
||||||
while (_staleUserData.TryDequeue(out var staleUserPair))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (staleUserPair.UID == null)
|
|
||||||
{
|
|
||||||
foreach (var entry in _cache.Where(c => c.Value.ContainsKey(staleUserPair.OtherUID)).Select(k => k.Key).Distinct(StringComparer.Ordinal).ToList())
|
|
||||||
{
|
|
||||||
_logger.LogDebug("UID is null; Building Individual Cache for {user}:{user2}", staleUserPair.UID, entry);
|
|
||||||
_staleUserData.Enqueue(new(staleUserPair.OtherUID, entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (staleUserPair.OtherUID == null)
|
|
||||||
{
|
|
||||||
foreach (var entry in _cache.Where(c => c.Value.ContainsKey(staleUserPair.UID)).Select(k => k.Key).Distinct(StringComparer.Ordinal).ToList())
|
|
||||||
{
|
|
||||||
_logger.LogDebug("OtherUID is null; Building Individual Cache for {user}:{user2}", staleUserPair.UID, entry);
|
|
||||||
_staleUserData.Enqueue(new(staleUserPair.UID, entry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_cache.ContainsKey(staleUserPair.UID))
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Building Individual Cache for {user}:{user2}", staleUserPair.UID, staleUserPair.OtherUID);
|
|
||||||
|
|
||||||
var userInfo = await BuildIndividualCache(dbContext, staleUserPair.UID, staleUserPair.OtherUID).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (userInfo == null) _cache[staleUserPair.UID].Remove(staleUserPair.OtherUID);
|
|
||||||
else _cache[staleUserPair.UID][staleUserPair.OtherUID] = userInfo;
|
|
||||||
|
|
||||||
if (_cache.ContainsKey(staleUserPair.OtherUID))
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Building Individual Cache for {user}:{user2}", staleUserPair.OtherUID, staleUserPair.UID);
|
|
||||||
var otherUserInfo = await BuildIndividualCache(dbContext, staleUserPair.OtherUID, staleUserPair.UID).ConfigureAwait(false);
|
|
||||||
if (otherUserInfo == null) _cache[staleUserPair.OtherUID].Remove(staleUserPair.UID);
|
|
||||||
else _cache[staleUserPair.OtherUID][staleUserPair.UID] = otherUserInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error during Stale entry processing");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task WaitForProcessing(string uid)
|
|
||||||
{
|
|
||||||
while (_staleUserData.Any(u => string.Equals(u.UID, uid, StringComparison.Ordinal)))
|
|
||||||
{
|
|
||||||
await Task.Delay(50).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task WaitForProcessing(string uid, string otheruid)
|
|
||||||
{
|
|
||||||
while (_staleUserData.Any(u => string.Equals(u.UID, uid, StringComparison.Ordinal) && string.Equals(u.OtherUID, otheruid, StringComparison.Ordinal)))
|
|
||||||
{
|
|
||||||
await Task.Delay(50).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public record UserInfo(string Alias, bool IndividuallyPaired, bool IsSynced, List<string> GIDs, UserPermissionSet? OwnPermissions, UserPermissionSet? OtherPermissions);
|
|
||||||
}
|
|
||||||
@@ -90,9 +90,7 @@ public class Startup
|
|||||||
|
|
||||||
services.AddSingleton<ServerTokenGenerator>();
|
services.AddSingleton<ServerTokenGenerator>();
|
||||||
services.AddSingleton<SystemInfoService>();
|
services.AddSingleton<SystemInfoService>();
|
||||||
services.AddSingleton<UserPairCacheService>();
|
|
||||||
services.AddHostedService(provider => provider.GetService<SystemInfoService>());
|
services.AddHostedService(provider => provider.GetService<SystemInfoService>());
|
||||||
services.AddHostedService(p => p.GetService<UserPairCacheService>());
|
|
||||||
// configure services based on main server status
|
// configure services based on main server status
|
||||||
ConfigureServicesBasedOnShardType(services, mareConfig, isMainServer);
|
ConfigureServicesBasedOnShardType(services, mareConfig, isMainServer);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using MareSynchronos.API.Data.Enum;
|
using MareSynchronos.API.Data.Enum;
|
||||||
using MareSynchronos.API.Data.Extensions;
|
using MareSynchronos.API.Data.Extensions;
|
||||||
using MareSynchronosShared.Models;
|
using MareSynchronosShared.Models;
|
||||||
using static MareSynchronosServer.Services.UserPairCacheService;
|
using static MareSynchronosServer.Hubs.MareHub;
|
||||||
|
|
||||||
namespace MareSynchronosServer.Utils;
|
namespace MareSynchronosServer.Utils;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user