add mare profiles
This commit is contained in:
@@ -2,6 +2,8 @@ services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
restart: always
|
||||
ports:
|
||||
- 5432:5432/tcp
|
||||
environment:
|
||||
POSTGRES_DB: mare
|
||||
POSTGRES_USER: mare
|
||||
@@ -48,6 +50,7 @@ services:
|
||||
environment:
|
||||
MareSynchronos__DiscordBotToken: "${DEV_MARE_DISCORDTOKEN}"
|
||||
MareSynchronos__DiscordChannelForMessages: "${DEV_MARE_DISCORDCHANNEL}"
|
||||
MareSynchronos__DiscordChannelForReports: "${DEV_MARE_DISCORDCHANNEL}"
|
||||
DOTNET_USE_POLLING_FILE_WATCHER: 1
|
||||
volumes:
|
||||
- ../config/standalone/services-standalone.json:/opt/MareSynchronosServices/appsettings.json
|
||||
|
||||
@@ -43,6 +43,8 @@ namespace MareSynchronosServer.Hubs
|
||||
|
||||
public Task Client_UserUpdateOtherPairPermissions(UserPermissionsDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||
|
||||
public Task Client_UserUpdateProfile(UserDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||
|
||||
public Task Client_UserUpdateSelfPairPermissions(UserPermissionsDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||
}
|
||||
}
|
||||
@@ -12,26 +12,53 @@ namespace MareSynchronosServer.Hubs;
|
||||
|
||||
public partial class MareHub
|
||||
{
|
||||
private async Task UpdateUserOnRedis()
|
||||
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");
|
||||
|
||||
private async Task DeleteUser(User user)
|
||||
{
|
||||
await _redis.AddAsync("UID:" + UserUID, UserCharaIdent, TimeSpan.FromSeconds(60), StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false);
|
||||
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);
|
||||
|
||||
if (lodestone != null)
|
||||
{
|
||||
_dbContext.Remove(lodestone);
|
||||
}
|
||||
|
||||
private async Task RemoveUserFromRedis()
|
||||
if (userProfileData != null)
|
||||
{
|
||||
await _redis.RemoveAsync("UID:" + UserUID, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false);
|
||||
_dbContext.Remove(userProfileData);
|
||||
}
|
||||
|
||||
private async Task<string> GetUserIdent(string uid)
|
||||
while (_dbContext.Files.Any(f => f.Uploader == user))
|
||||
{
|
||||
if (uid.IsNullOrEmpty()) return string.Empty;
|
||||
return await _redis.GetAsync<string>("UID:" + uid).ConfigureAwait(false);
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<Dictionary<string, string>> GetOnlineUsers(List<string> uids)
|
||||
_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)
|
||||
{
|
||||
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);
|
||||
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.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)
|
||||
@@ -79,43 +106,21 @@ public partial class MareHub
|
||||
return ret.Where(k => !k.IsPaused).Select(k => k.UID).ToList();
|
||||
}
|
||||
|
||||
private async Task<List<string>> SendOnlineToAllPairedUsers()
|
||||
private async Task<Dictionary<string, string>> GetOnlineUsers(List<string> uids)
|
||||
{
|
||||
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;
|
||||
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<List<string>> SendOfflineToAllPairedUsers()
|
||||
private async Task<string> GetUserIdent(string uid)
|
||||
{
|
||||
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;
|
||||
if (uid.IsNullOrEmpty()) return string.Empty;
|
||||
return await _redis.GetAsync<string>("UID:" + uid).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
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 UserCharaIdent => Context.User?.Claims?.SingleOrDefault(c => string.Equals(c.Type, MareClaimTypes.CharaIdent, StringComparison.Ordinal))?.Value ?? throw new Exception("No Chara Ident in Claims");
|
||||
|
||||
private async Task UserGroupLeave(GroupPair groupUserPair, List<PausedEntry> allUserPairs, string userIdent, string? uid = null)
|
||||
private async Task RemoveUserFromRedis()
|
||||
{
|
||||
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);
|
||||
}
|
||||
await _redis.RemoveAsync("UID:" + UserUID, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task SendGroupDeletedToAll(List<GroupPair> groupUsers)
|
||||
@@ -134,15 +139,22 @@ public partial class MareHub
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool IsValid, GroupPair ReferredPair)> TryValidateUserInGroup(string gid, string? uid = null)
|
||||
private async Task<List<string>> SendOfflineToAllPairedUsers()
|
||||
{
|
||||
uid ??= UserUID;
|
||||
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);
|
||||
|
||||
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 usersToSendDataTo;
|
||||
}
|
||||
|
||||
return (true, groupPair);
|
||||
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)
|
||||
@@ -166,43 +178,38 @@ public partial class MareHub
|
||||
return (string.Equals(group.OwnerUID, UserUID, StringComparison.Ordinal), group);
|
||||
}
|
||||
|
||||
private async Task DeleteUser(User user)
|
||||
private async Task<(bool IsValid, GroupPair ReferredPair)> TryValidateUserInGroup(string gid, string? uid = null)
|
||||
{
|
||||
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);
|
||||
uid ??= UserUID;
|
||||
|
||||
if (lodestone != null)
|
||||
{
|
||||
_dbContext.Remove(lodestone);
|
||||
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);
|
||||
}
|
||||
|
||||
while (_dbContext.Files.Any(f => f.Uploader == user))
|
||||
private async Task UpdateUserOnRedis()
|
||||
{
|
||||
await Task.Delay(1000).ConfigureAwait(false);
|
||||
await _redis.AddAsync("UID:" + UserUID, UserCharaIdent, TimeSpan.FromSeconds(60), StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_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)
|
||||
private async Task UserGroupLeave(GroupPair groupUserPair, List<PausedEntry> allUserPairs, string userIdent, string? uid = null)
|
||||
{
|
||||
await Clients.User(pair.UserUID).Client_UserRemoveClientPair(new(user.ToUserData())).ConfigureAwait(false);
|
||||
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;
|
||||
}
|
||||
|
||||
foreach (var pair in groupPairs)
|
||||
var groupUserIdent = await GetUserIdent(groupUserPair.GroupUserUID).ConfigureAwait(false);
|
||||
if (!string.IsNullOrEmpty(groupUserIdent))
|
||||
{
|
||||
await UserLeaveGroup(new GroupDto(new GroupData(pair.GroupGID)), user.UID).ConfigureAwait(false);
|
||||
await Clients.User(uid).Client_UserSendOffline(new(new(groupUserPair.GroupUserUID))).ConfigureAwait(false);
|
||||
await Clients.User(groupUserPair.GroupUserUID).Client_UserSendOffline(new(new(uid))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_mareMetrics.IncCounter(MetricsAPI.CounterUsersRegisteredDeleted, 1);
|
||||
|
||||
_dbContext.ClientPairs.RemoveRange(otherPairData);
|
||||
_dbContext.Users.Remove(user);
|
||||
_dbContext.Auth.Remove(auth);
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task UserLeaveGroup(GroupDto dto, string userUid)
|
||||
|
||||
@@ -14,140 +14,8 @@ namespace MareSynchronosServer.Hubs;
|
||||
|
||||
public partial class MareHub
|
||||
{
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserDelete()
|
||||
{
|
||||
_logger.LogCallInfo();
|
||||
|
||||
var userEntry = await _dbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
|
||||
var secondaryUsers = await _dbContext.Auth.Include(u => u.User).Where(u => u.PrimaryUserUID == UserUID).Select(c => c.User).ToListAsync().ConfigureAwait(false);
|
||||
foreach (var user in secondaryUsers)
|
||||
{
|
||||
await DeleteUser(user).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await DeleteUser(userEntry).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<List<OnlineUserIdentDto>> UserGetOnlinePairs()
|
||||
{
|
||||
_logger.LogCallInfo();
|
||||
|
||||
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
||||
var pairs = await GetOnlineUsers(allPairedUsers).ConfigureAwait(false);
|
||||
|
||||
return pairs.Select(p => new OnlineUserIdentDto(new UserData(p.Key), p.Value)).ToList();
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<List<UserPairDto>> UserGetPairedClients()
|
||||
{
|
||||
_logger.LogCallInfo();
|
||||
|
||||
var query =
|
||||
from userToOther in _dbContext.ClientPairs
|
||||
join otherToUser in _dbContext.ClientPairs
|
||||
on new
|
||||
{
|
||||
user = userToOther.UserUID,
|
||||
other = userToOther.OtherUserUID,
|
||||
|
||||
} equals new
|
||||
{
|
||||
user = otherToUser.OtherUserUID,
|
||||
other = otherToUser.UserUID,
|
||||
} into leftJoin
|
||||
from otherEntry in leftJoin.DefaultIfEmpty()
|
||||
where
|
||||
userToOther.UserUID == UserUID
|
||||
select new
|
||||
{
|
||||
userToOther.OtherUser.Alias,
|
||||
userToOther.IsPaused,
|
||||
OtherIsPaused = otherEntry != null && otherEntry.IsPaused,
|
||||
userToOther.OtherUserUID,
|
||||
IsSynced = otherEntry != null,
|
||||
DisableOwnAnimations = userToOther.DisableAnimations,
|
||||
DisableOwnSounds = userToOther.DisableSounds,
|
||||
DisableOtherAnimations = otherEntry == null ? false : otherEntry.DisableAnimations,
|
||||
DisableOtherSounds = otherEntry == null ? false : otherEntry.DisableSounds
|
||||
};
|
||||
|
||||
var results = await query.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||
|
||||
return results.Select(c =>
|
||||
{
|
||||
var ownPerm = UserPermissions.Paired;
|
||||
ownPerm.SetPaused(c.IsPaused);
|
||||
ownPerm.SetDisableAnimations(c.DisableOwnAnimations);
|
||||
ownPerm.SetDisableSounds(c.DisableOwnSounds);
|
||||
var otherPerm = UserPermissions.NoneSet;
|
||||
otherPerm.SetPaired(c.IsSynced);
|
||||
otherPerm.SetPaused(c.OtherIsPaused);
|
||||
otherPerm.SetDisableAnimations(c.DisableOtherAnimations);
|
||||
otherPerm.SetDisableSounds(c.DisableOtherSounds);
|
||||
return new UserPairDto(new(c.OtherUserUID, c.Alias), ownPerm, otherPerm);
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^[A-Z0-9]{40}$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ECMAScript)]
|
||||
private static partial Regex HashRegex();
|
||||
|
||||
[GeneratedRegex(@"^([a-z0-9_ '+&,\.\-\{\}]+\/)+([a-z0-9_ '+&,\.\-\{\}]+\.[a-z]{3,4})$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ECMAScript)]
|
||||
private static partial Regex GamePathRegex();
|
||||
|
||||
private static readonly string[] AllowedExtensionsForGamePaths = { ".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".scd", ".skp", ".shpk" };
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserPushData(UserCharaDataMessageDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(MareHubLogger.Args(dto.CharaData.FileReplacements.Count));
|
||||
|
||||
bool hadInvalidData = false;
|
||||
List<string> invalidGamePaths = new();
|
||||
List<string> invalidFileSwapPaths = new();
|
||||
foreach (var replacement in dto.CharaData.FileReplacements.SelectMany(p => p.Value))
|
||||
{
|
||||
var invalidPaths = replacement.GamePaths.Where(p => !GamePathRegex().IsMatch(p)).ToList();
|
||||
invalidPaths.AddRange(replacement.GamePaths.Where(p => !AllowedExtensionsForGamePaths.Any(e => p.EndsWith(e, StringComparison.OrdinalIgnoreCase))));
|
||||
replacement.GamePaths = replacement.GamePaths.Where(p => !invalidPaths.Contains(p, StringComparer.OrdinalIgnoreCase)).ToArray();
|
||||
bool validGamePaths = replacement.GamePaths.Any();
|
||||
bool validHash = string.IsNullOrEmpty(replacement.Hash) || HashRegex().IsMatch(replacement.Hash);
|
||||
bool validFileSwapPath = string.IsNullOrEmpty(replacement.FileSwapPath) || GamePathRegex().IsMatch(replacement.FileSwapPath);
|
||||
if (!validGamePaths || !validHash || !validFileSwapPath)
|
||||
{
|
||||
_logger.LogCallWarning(MareHubLogger.Args("Invalid Data", "GamePaths", validGamePaths, string.Join(",", invalidPaths), "Hash", validHash, replacement.Hash, "FileSwap", validFileSwapPath, replacement.FileSwapPath));
|
||||
hadInvalidData = true;
|
||||
if (!validFileSwapPath) invalidFileSwapPaths.Add(replacement.FileSwapPath);
|
||||
if (!validGamePaths) invalidGamePaths.AddRange(replacement.GamePaths);
|
||||
if (!validHash) invalidFileSwapPaths.Add(replacement.Hash);
|
||||
}
|
||||
}
|
||||
|
||||
if (hadInvalidData)
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "One or more of your supplied mods were rejected from the server. Consult /xllog for more information.").ConfigureAwait(false);
|
||||
throw new HubException("Invalid data provided, contact the appropriate mod creator to resolve those issues"
|
||||
+ Environment.NewLine
|
||||
+ string.Join(Environment.NewLine, invalidGamePaths.Select(p => "Invalid Game Path: " + p))
|
||||
+ Environment.NewLine
|
||||
+ string.Join(Environment.NewLine, invalidFileSwapPaths.Select(p => "Invalid FileSwap Path: " + p)));
|
||||
}
|
||||
|
||||
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
||||
var idents = await GetOnlineUsers(allPairedUsers).ConfigureAwait(false);
|
||||
|
||||
var recipients = allPairedUsers.Where(f => dto.Recipients.Select(r => r.UID).Contains(f, StringComparer.Ordinal)).ToList();
|
||||
|
||||
_logger.LogCallInfo(MareHubLogger.Args(idents.Count, recipients.Count()));
|
||||
|
||||
await Clients.Users(recipients).Client_UserReceiveCharacterData(new OnlineUserCharaDataDto(new UserData(UserUID), dto.CharaData)).ConfigureAwait(false);
|
||||
|
||||
_mareMetrics.IncCounter(MetricsAPI.CounterUserPushData);
|
||||
_mareMetrics.IncCounter(MetricsAPI.CounterUserPushDataTo, recipients.Count());
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserAddPair(UserDto dto)
|
||||
{
|
||||
@@ -214,6 +82,255 @@ public partial class MareHub
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserDelete()
|
||||
{
|
||||
_logger.LogCallInfo();
|
||||
|
||||
var userEntry = await _dbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
|
||||
var secondaryUsers = await _dbContext.Auth.Include(u => u.User).Where(u => u.PrimaryUserUID == UserUID).Select(c => c.User).ToListAsync().ConfigureAwait(false);
|
||||
foreach (var user in secondaryUsers)
|
||||
{
|
||||
await DeleteUser(user).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await DeleteUser(userEntry).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<List<OnlineUserIdentDto>> UserGetOnlinePairs()
|
||||
{
|
||||
_logger.LogCallInfo();
|
||||
|
||||
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
||||
var pairs = await GetOnlineUsers(allPairedUsers).ConfigureAwait(false);
|
||||
|
||||
return pairs.Select(p => new OnlineUserIdentDto(new UserData(p.Key), p.Value)).ToList();
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<List<UserPairDto>> UserGetPairedClients()
|
||||
{
|
||||
_logger.LogCallInfo();
|
||||
|
||||
var query =
|
||||
from userToOther in _dbContext.ClientPairs
|
||||
join otherToUser in _dbContext.ClientPairs
|
||||
on new
|
||||
{
|
||||
user = userToOther.UserUID,
|
||||
other = userToOther.OtherUserUID,
|
||||
} equals new
|
||||
{
|
||||
user = otherToUser.OtherUserUID,
|
||||
other = otherToUser.UserUID,
|
||||
} into leftJoin
|
||||
from otherEntry in leftJoin.DefaultIfEmpty()
|
||||
where
|
||||
userToOther.UserUID == UserUID
|
||||
select new
|
||||
{
|
||||
userToOther.OtherUser.Alias,
|
||||
userToOther.IsPaused,
|
||||
OtherIsPaused = otherEntry != null && otherEntry.IsPaused,
|
||||
userToOther.OtherUserUID,
|
||||
IsSynced = otherEntry != null,
|
||||
DisableOwnAnimations = userToOther.DisableAnimations,
|
||||
DisableOwnSounds = userToOther.DisableSounds,
|
||||
DisableOtherAnimations = otherEntry == null ? false : otherEntry.DisableAnimations,
|
||||
DisableOtherSounds = otherEntry == null ? false : otherEntry.DisableSounds
|
||||
};
|
||||
|
||||
var results = await query.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||
|
||||
return results.Select(c =>
|
||||
{
|
||||
var ownPerm = UserPermissions.Paired;
|
||||
ownPerm.SetPaused(c.IsPaused);
|
||||
ownPerm.SetDisableAnimations(c.DisableOwnAnimations);
|
||||
ownPerm.SetDisableSounds(c.DisableOwnSounds);
|
||||
var otherPerm = UserPermissions.NoneSet;
|
||||
otherPerm.SetPaired(c.IsSynced);
|
||||
otherPerm.SetPaused(c.OtherIsPaused);
|
||||
otherPerm.SetDisableAnimations(c.DisableOtherAnimations);
|
||||
otherPerm.SetDisableSounds(c.DisableOtherSounds);
|
||||
return new UserPairDto(new(c.OtherUserUID, c.Alias), ownPerm, otherPerm);
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task<UserProfileDto> UserGetProfile(UserDto user)
|
||||
{
|
||||
_logger.LogCallInfo(MareHubLogger.Args(user));
|
||||
|
||||
var allUserPairs = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
||||
|
||||
if (!allUserPairs.Contains(user.User.UID, StringComparer.Ordinal) && !string.Equals(user.User.UID, UserUID, StringComparison.Ordinal))
|
||||
{
|
||||
return new UserProfileDto(user.User, false, null, null, "Due to the pause status you cannot access this users profile.");
|
||||
}
|
||||
|
||||
var data = await _dbContext.UserProfileData.SingleOrDefaultAsync(u => u.UserUID == user.User.UID).ConfigureAwait(false);
|
||||
if (data == null) return new UserProfileDto(user.User, false, null, null, null);
|
||||
|
||||
if (data.FlaggedForReport) return new UserProfileDto(user.User, true, null, null, "This profile is flagged for report and pending evaluation");
|
||||
if (data.ProfileDisabled) return new UserProfileDto(user.User, true, null, null, "This profile was permanently disabled");
|
||||
|
||||
return new UserProfileDto(user.User, false, data.IsNSFW, data.Base64ProfileImage, data.UserDescription);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserPushData(UserCharaDataMessageDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(MareHubLogger.Args(dto.CharaData.FileReplacements.Count));
|
||||
|
||||
bool hadInvalidData = false;
|
||||
List<string> invalidGamePaths = new();
|
||||
List<string> invalidFileSwapPaths = new();
|
||||
foreach (var replacement in dto.CharaData.FileReplacements.SelectMany(p => p.Value))
|
||||
{
|
||||
var invalidPaths = replacement.GamePaths.Where(p => !GamePathRegex().IsMatch(p)).ToList();
|
||||
invalidPaths.AddRange(replacement.GamePaths.Where(p => !AllowedExtensionsForGamePaths.Any(e => p.EndsWith(e, StringComparison.OrdinalIgnoreCase))));
|
||||
replacement.GamePaths = replacement.GamePaths.Where(p => !invalidPaths.Contains(p, StringComparer.OrdinalIgnoreCase)).ToArray();
|
||||
bool validGamePaths = replacement.GamePaths.Any();
|
||||
bool validHash = string.IsNullOrEmpty(replacement.Hash) || HashRegex().IsMatch(replacement.Hash);
|
||||
bool validFileSwapPath = string.IsNullOrEmpty(replacement.FileSwapPath) || GamePathRegex().IsMatch(replacement.FileSwapPath);
|
||||
if (!validGamePaths || !validHash || !validFileSwapPath)
|
||||
{
|
||||
_logger.LogCallWarning(MareHubLogger.Args("Invalid Data", "GamePaths", validGamePaths, string.Join(",", invalidPaths), "Hash", validHash, replacement.Hash, "FileSwap", validFileSwapPath, replacement.FileSwapPath));
|
||||
hadInvalidData = true;
|
||||
if (!validFileSwapPath) invalidFileSwapPaths.Add(replacement.FileSwapPath);
|
||||
if (!validGamePaths) invalidGamePaths.AddRange(replacement.GamePaths);
|
||||
if (!validHash) invalidFileSwapPaths.Add(replacement.Hash);
|
||||
}
|
||||
}
|
||||
|
||||
if (hadInvalidData)
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "One or more of your supplied mods were rejected from the server. Consult /xllog for more information.").ConfigureAwait(false);
|
||||
throw new HubException("Invalid data provided, contact the appropriate mod creator to resolve those issues"
|
||||
+ Environment.NewLine
|
||||
+ string.Join(Environment.NewLine, invalidGamePaths.Select(p => "Invalid Game Path: " + p))
|
||||
+ Environment.NewLine
|
||||
+ string.Join(Environment.NewLine, invalidFileSwapPaths.Select(p => "Invalid FileSwap Path: " + p)));
|
||||
}
|
||||
|
||||
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
||||
var idents = await GetOnlineUsers(allPairedUsers).ConfigureAwait(false);
|
||||
|
||||
var recipients = allPairedUsers.Where(f => dto.Recipients.Select(r => r.UID).Contains(f, StringComparer.Ordinal)).ToList();
|
||||
|
||||
_logger.LogCallInfo(MareHubLogger.Args(idents.Count, recipients.Count()));
|
||||
|
||||
await Clients.Users(recipients).Client_UserReceiveCharacterData(new OnlineUserCharaDataDto(new UserData(UserUID), dto.CharaData)).ConfigureAwait(false);
|
||||
|
||||
_mareMetrics.IncCounter(MetricsAPI.CounterUserPushData);
|
||||
_mareMetrics.IncCounter(MetricsAPI.CounterUserPushDataTo, recipients.Count());
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserRemovePair(UserDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(MareHubLogger.Args(dto));
|
||||
|
||||
if (string.Equals(dto.User.UID, UserUID, StringComparison.Ordinal)) return;
|
||||
|
||||
// check if client pair even exists
|
||||
ClientPair callerPair =
|
||||
await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false);
|
||||
if (callerPair == null) return;
|
||||
|
||||
bool callerHadPaused = callerPair.IsPaused;
|
||||
|
||||
// delete from database, send update info to users pair list
|
||||
_dbContext.ClientPairs.Remove(callerPair);
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
||||
|
||||
await Clients.User(UserUID).Client_UserRemoveClientPair(dto).ConfigureAwait(false);
|
||||
|
||||
// check if opposite entry exists
|
||||
var oppositeClientPair = OppositeEntry(dto.User.UID);
|
||||
if (oppositeClientPair == null) return;
|
||||
|
||||
// check if other user is online, if no then there is no need to do anything further
|
||||
var otherIdent = await GetUserIdent(dto.User.UID).ConfigureAwait(false);
|
||||
if (otherIdent == null) return;
|
||||
|
||||
// get own ident and
|
||||
await Clients.User(dto.User.UID)
|
||||
.Client_UserUpdateOtherPairPermissions(new UserPermissionsDto(new UserData(UserUID),
|
||||
UserPermissions.NoneSet)).ConfigureAwait(false);
|
||||
|
||||
// if the other user had paused the user the state will be offline for either, do nothing
|
||||
bool otherHadPaused = oppositeClientPair.IsPaused;
|
||||
if (!callerHadPaused && otherHadPaused) return;
|
||||
|
||||
var allUsers = await GetAllPairedClientsWithPauseState().ConfigureAwait(false);
|
||||
var pauseEntry = allUsers.SingleOrDefault(f => string.Equals(f.UID, dto.User.UID, StringComparison.Ordinal));
|
||||
var isPausedInGroup = pauseEntry == null || pauseEntry.IsPausedPerGroup is PauseInfo.Paused or PauseInfo.NoConnection;
|
||||
|
||||
// if neither user had paused each other and both are in unpaused groups, state will be online for both, do nothing
|
||||
if (!callerHadPaused && !otherHadPaused && !isPausedInGroup) return;
|
||||
|
||||
// if neither user had paused each other and either is not in an unpaused group with each other, change state to offline
|
||||
if (!callerHadPaused && !otherHadPaused && isPausedInGroup)
|
||||
{
|
||||
await Clients.User(UserUID).Client_UserSendOffline(dto).ConfigureAwait(false);
|
||||
await Clients.User(dto.User.UID).Client_UserSendOffline(new(new(UserUID))).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// if the caller had paused other but not the other has paused the caller and they are in an unpaused group together, change state to online
|
||||
if (callerHadPaused && !otherHadPaused && !isPausedInGroup)
|
||||
{
|
||||
await Clients.User(UserUID).Client_UserSendOnline(new(dto.User, otherIdent)).ConfigureAwait(false);
|
||||
await Clients.User(dto.User.UID).Client_UserSendOnline(new(new(UserUID), UserCharaIdent)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserReportProfile(UserProfileReportDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(MareHubLogger.Args(dto));
|
||||
|
||||
UserProfileDataReport report = await _dbContext.UserProfileReports.SingleOrDefaultAsync(u => u.ReportedUserUID == dto.User.UID && u.ReportingUserUID == UserUID).ConfigureAwait(false);
|
||||
if (report != null)
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "You already reported this profile and it's pending validation").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
UserProfileData profile = await _dbContext.UserProfileData.SingleOrDefaultAsync(u => u.UserUID == dto.User.UID).ConfigureAwait(false);
|
||||
if (profile == null)
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "This user has no profile").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
UserProfileDataReport reportToAdd = new()
|
||||
{
|
||||
ReportDate = DateTime.UtcNow,
|
||||
ReportingUserUID = UserUID,
|
||||
ReportReason = dto.ProfileReport,
|
||||
ReportedUserUID = dto.User.UID,
|
||||
};
|
||||
|
||||
profile.FlaggedForReport = true;
|
||||
|
||||
await _dbContext.UserProfileReports.AddAsync(reportToAdd).ConfigureAwait(false);
|
||||
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
await Clients.User(dto.User.UID).Client_ReceiveServerMessage(MessageSeverity.Warning, "Your Mare profile has been reported and disabled for admin validation").ConfigureAwait(false);
|
||||
|
||||
var allPairedUsers = await GetAllPairedUnpausedUsers(dto.User.UID).ConfigureAwait(false);
|
||||
var pairs = await GetOnlineUsers(allPairedUsers).ConfigureAwait(false);
|
||||
|
||||
await Clients.Users(pairs.Select(p => p.Key)).Client_UserUpdateProfile(new(dto.User)).ConfigureAwait(false);
|
||||
await Clients.Users(dto.User.UID).Client_UserUpdateProfile(new(dto.User)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserSetPairPermissions(UserPermissionsDto dto)
|
||||
{
|
||||
@@ -262,66 +379,93 @@ public partial class MareHub
|
||||
}
|
||||
|
||||
[Authorize(Policy = "Identified")]
|
||||
public async Task UserRemovePair(UserDto dto)
|
||||
public async Task UserSetProfile(UserProfileDto dto)
|
||||
{
|
||||
_logger.LogCallInfo(MareHubLogger.Args(dto));
|
||||
|
||||
if (string.Equals(dto.User.UID, UserUID, StringComparison.Ordinal)) return;
|
||||
if (!string.Equals(dto.User.UID, UserUID, StringComparison.Ordinal)) throw new HubException("Cannot modify profile data for anyone but yourself");
|
||||
|
||||
// check if client pair even exists
|
||||
ClientPair callerPair =
|
||||
await _dbContext.ClientPairs.SingleOrDefaultAsync(w => w.UserUID == UserUID && w.OtherUserUID == dto.User.UID).ConfigureAwait(false);
|
||||
if (callerPair == null) return;
|
||||
var existingData = await _dbContext.UserProfileData.SingleOrDefaultAsync(u => u.UserUID == dto.User.UID).ConfigureAwait(false);
|
||||
|
||||
bool callerHadPaused = callerPair.IsPaused;
|
||||
if (existingData.FlaggedForReport)
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your profile is currently flagged for report and cannot be edited").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingData.ProfileDisabled)
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your profile was permanently disabled and cannot be edited").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(dto.ProfilePictureBase64))
|
||||
{
|
||||
byte[] imageData = Convert.FromBase64String(dto.ProfilePictureBase64);
|
||||
using MemoryStream ms = new(imageData);
|
||||
var format = await Image.DetectFormatAsync(ms).ConfigureAwait(false);
|
||||
if (!format.FileExtensions.Contains("png", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your provided image file is not in PNG format").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
using var image = Image.Load<Rgba32>(imageData);
|
||||
|
||||
if (image.Width > 256 || image.Height > 256 || (imageData.Length > 250 * 1024))
|
||||
{
|
||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your provided image file is larger than 256x256 or more than 250KiB.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (existingData != null)
|
||||
{
|
||||
if (string.Equals("", dto.ProfilePictureBase64, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
existingData.Base64ProfileImage = null;
|
||||
}
|
||||
else if (dto.ProfilePictureBase64 != null)
|
||||
{
|
||||
existingData.Base64ProfileImage = dto.ProfilePictureBase64;
|
||||
}
|
||||
|
||||
if (dto.IsNSFW != null)
|
||||
{
|
||||
existingData.IsNSFW = dto.IsNSFW.Value;
|
||||
}
|
||||
|
||||
if (dto.Description != null)
|
||||
{
|
||||
existingData.UserDescription = dto.Description;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UserProfileData userProfileData = new()
|
||||
{
|
||||
UserUID = dto.User.UID,
|
||||
Base64ProfileImage = dto.ProfilePictureBase64 ?? null,
|
||||
UserDescription = dto.Description ?? null,
|
||||
IsNSFW = dto.IsNSFW ?? false
|
||||
};
|
||||
|
||||
await _dbContext.UserProfileData.AddAsync(userProfileData).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// delete from database, send update info to users pair list
|
||||
_dbContext.ClientPairs.Remove(callerPair);
|
||||
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
_logger.LogCallInfo(MareHubLogger.Args(dto, "Success"));
|
||||
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
||||
var pairs = await GetOnlineUsers(allPairedUsers).ConfigureAwait(false);
|
||||
|
||||
await Clients.User(UserUID).Client_UserRemoveClientPair(dto).ConfigureAwait(false);
|
||||
|
||||
// check if opposite entry exists
|
||||
var oppositeClientPair = OppositeEntry(dto.User.UID);
|
||||
if (oppositeClientPair == null) return;
|
||||
|
||||
// check if other user is online, if no then there is no need to do anything further
|
||||
var otherIdent = await GetUserIdent(dto.User.UID).ConfigureAwait(false);
|
||||
if (otherIdent == null) return;
|
||||
|
||||
// get own ident and
|
||||
await Clients.User(dto.User.UID)
|
||||
.Client_UserUpdateOtherPairPermissions(new UserPermissionsDto(new UserData(UserUID),
|
||||
UserPermissions.NoneSet)).ConfigureAwait(false);
|
||||
|
||||
|
||||
// if the other user had paused the user the state will be offline for either, do nothing
|
||||
bool otherHadPaused = oppositeClientPair.IsPaused;
|
||||
if (!callerHadPaused && otherHadPaused) return;
|
||||
|
||||
var allUsers = await GetAllPairedClientsWithPauseState().ConfigureAwait(false);
|
||||
var pauseEntry = allUsers.SingleOrDefault(f => string.Equals(f.UID, dto.User.UID, StringComparison.Ordinal));
|
||||
var isPausedInGroup = pauseEntry == null || pauseEntry.IsPausedPerGroup is PauseInfo.Paused or PauseInfo.NoConnection;
|
||||
|
||||
// if neither user had paused each other and both are in unpaused groups, state will be online for both, do nothing
|
||||
if (!callerHadPaused && !otherHadPaused && !isPausedInGroup) return;
|
||||
|
||||
// if neither user had paused each other and either is not in an unpaused group with each other, change state to offline
|
||||
if (!callerHadPaused && !otherHadPaused && isPausedInGroup)
|
||||
{
|
||||
await Clients.User(UserUID).Client_UserSendOffline(dto).ConfigureAwait(false);
|
||||
await Clients.User(dto.User.UID).Client_UserSendOffline(new(new(UserUID))).ConfigureAwait(false);
|
||||
await Clients.Users(pairs.Select(p => p.Key)).Client_UserUpdateProfile(new(dto.User)).ConfigureAwait(false);
|
||||
await Clients.Caller.Client_UserUpdateProfile(new(dto.User)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// if the caller had paused other but not the other has paused the caller and they are in an unpaused group together, change state to online
|
||||
if (callerHadPaused && !otherHadPaused && !isPausedInGroup)
|
||||
{
|
||||
await Clients.User(UserUID).Client_UserSendOnline(new(dto.User, otherIdent)).ConfigureAwait(false);
|
||||
await Clients.User(dto.User.UID).Client_UserSendOnline(new(new(UserUID), UserCharaIdent)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
[GeneratedRegex(@"^([a-z0-9_ '+&,\.\-\{\}]+\/)+([a-z0-9_ '+&,\.\-\{\}]+\.[a-z]{3,4})$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ECMAScript)]
|
||||
private static partial Regex GamePathRegex();
|
||||
|
||||
[GeneratedRegex(@"^[A-Z0-9]{40}$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ECMAScript)]
|
||||
private static partial Regex HashRegex();
|
||||
|
||||
private ClientPair OppositeEntry(string otherUID) =>
|
||||
_dbContext.ClientPairs.AsNoTracking().SingleOrDefault(w => w.User.UID == otherUID && w.OtherUser.UID == UserUID);
|
||||
|
||||
@@ -28,10 +28,9 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.27.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,32 +2,42 @@
|
||||
using Discord.Interactions;
|
||||
using Discord.Rest;
|
||||
using Discord.WebSocket;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.API.Dto.User;
|
||||
using MareSynchronos.API.SignalR;
|
||||
using MareSynchronosServer.Hubs;
|
||||
using MareSynchronosShared.Data;
|
||||
using MareSynchronosShared.Services;
|
||||
using MareSynchronosShared.Utils;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using StackExchange.Redis;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronosServices.Discord;
|
||||
|
||||
internal class DiscordBot : IHostedService
|
||||
{
|
||||
private readonly DiscordBotServices _botServices;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IConfigurationService<ServicesConfiguration> _configurationService;
|
||||
private readonly ILogger<DiscordBot> _logger;
|
||||
private readonly IConnectionMultiplexer _connectionMultiplexer;
|
||||
private readonly DiscordSocketClient _discordClient;
|
||||
private readonly ILogger<DiscordBot> _logger;
|
||||
private readonly IHubContext<MareHub> _mareHubContext;
|
||||
private readonly IServiceProvider _services;
|
||||
private InteractionService _interactionModule;
|
||||
private CancellationTokenSource? _processReportQueueCts;
|
||||
private CancellationTokenSource? _updateStatusCts;
|
||||
private CancellationTokenSource? _vanityUpdateCts;
|
||||
private InteractionService _interactionModule;
|
||||
|
||||
public DiscordBot(DiscordBotServices botServices, IServiceProvider services, IConfigurationService<ServicesConfiguration> configuration,
|
||||
IHubContext<MareHub> mareHubContext,
|
||||
ILogger<DiscordBot> logger, IConnectionMultiplexer connectionMultiplexer)
|
||||
{
|
||||
_botServices = botServices;
|
||||
_services = services;
|
||||
_configurationService = configuration;
|
||||
_mareHubContext = mareHubContext;
|
||||
_logger = logger;
|
||||
_connectionMultiplexer = connectionMultiplexer;
|
||||
_discordClient = new(new DiscordSocketConfig()
|
||||
@@ -38,12 +48,131 @@ internal class DiscordBot : IHostedService
|
||||
_discordClient.Log += Log;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var token = _configurationService.GetValueOrDefault(nameof(ServicesConfiguration.DiscordBotToken), string.Empty);
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
_interactionModule = new InteractionService(_discordClient);
|
||||
await _interactionModule.AddModuleAsync(typeof(MareModule), _services).ConfigureAwait(false);
|
||||
|
||||
await _discordClient.LoginAsync(TokenType.Bot, token).ConfigureAwait(false);
|
||||
await _discordClient.StartAsync().ConfigureAwait(false);
|
||||
|
||||
_discordClient.Ready += DiscordClient_Ready;
|
||||
_discordClient.ButtonExecuted += ButtonExecutedHandler;
|
||||
_discordClient.InteractionCreated += async (x) =>
|
||||
{
|
||||
var ctx = new SocketInteractionContext(_discordClient, x);
|
||||
await _interactionModule.ExecuteCommandAsync(ctx, _services);
|
||||
};
|
||||
|
||||
await _botServices.Start();
|
||||
_ = UpdateStatusAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_configurationService.GetValueOrDefault(nameof(ServicesConfiguration.DiscordBotToken), string.Empty)))
|
||||
{
|
||||
_discordClient.ButtonExecuted -= ButtonExecutedHandler;
|
||||
|
||||
await _botServices.Stop();
|
||||
_processReportQueueCts?.Cancel();
|
||||
_updateStatusCts?.Cancel();
|
||||
_vanityUpdateCts?.Cancel();
|
||||
|
||||
await _discordClient.LogoutAsync().ConfigureAwait(false);
|
||||
await _discordClient.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ButtonExecutedHandler(SocketMessageComponent arg)
|
||||
{
|
||||
var id = arg.Data.CustomId;
|
||||
if (!id.StartsWith("mare-report-button", StringComparison.Ordinal)) return;
|
||||
|
||||
var userId = arg.User.Id;
|
||||
using var scope = _services.CreateScope();
|
||||
using var dbContext = scope.ServiceProvider.GetRequiredService<MareDbContext>();
|
||||
var user = await dbContext.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.DiscordId == userId).ConfigureAwait(false);
|
||||
|
||||
if (user == null || (!user.User.IsModerator && !user.User.IsAdmin))
|
||||
{
|
||||
EmbedBuilder eb = new();
|
||||
eb.WithTitle($"Cannot resolve report");
|
||||
eb.WithDescription($"<@{userId}>: You have no rights to resolve this report");
|
||||
await arg.RespondAsync(embed: eb.Build()).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
id = id.Remove(0, "mare-report-button-".Length);
|
||||
var split = id.Split('-', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var profile = await dbContext.UserProfileData.SingleAsync(u => u.UserUID == split[1]).ConfigureAwait(false);
|
||||
|
||||
var embed = arg.Message.Embeds.First();
|
||||
|
||||
var builder = embed.ToEmbedBuilder();
|
||||
var otherPairs = await dbContext.ClientPairs.Where(p => p.UserUID == split[1]).Select(p => p.OtherUserUID).ToListAsync().ConfigureAwait(false);
|
||||
switch (split[0])
|
||||
{
|
||||
case "dismiss":
|
||||
builder.AddField("Resolution", $"Dismissed by <@{userId}>");
|
||||
builder.WithColor(Color.Green);
|
||||
profile.FlaggedForReport = false;
|
||||
await _mareHubContext.Clients.User(split[1]).SendAsync(nameof(IMareHub.Client_ReceiveServerMessage),
|
||||
MessageSeverity.Warning, "The Mare profile report against you has been evaluated and your profile re-enabled.")
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
|
||||
case "banprofile":
|
||||
builder.AddField("Resolution", $"Profile has been banned by <@{userId}>");
|
||||
builder.WithColor(Color.Red);
|
||||
profile.Base64ProfileImage = null;
|
||||
profile.UserDescription = null;
|
||||
profile.ProfileDisabled = true;
|
||||
profile.FlaggedForReport = false;
|
||||
await _mareHubContext.Clients.User(split[1]).SendAsync(nameof(IMareHub.Client_ReceiveServerMessage),
|
||||
MessageSeverity.Warning, "The Mare profile report against you has been evaluated and the profile functionality permanently disabled.")
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
|
||||
case "banuser":
|
||||
builder.AddField("Resolution", $"User has been banned by <@{userId}>");
|
||||
builder.WithColor(Color.DarkRed);
|
||||
var offendingUser = await dbContext.Auth.SingleAsync(u => u.UserUID == split[1]).ConfigureAwait(false);
|
||||
offendingUser.IsBanned = true;
|
||||
profile.Base64ProfileImage = null;
|
||||
profile.UserDescription = null;
|
||||
profile.ProfileDisabled = true;
|
||||
await _mareHubContext.Clients.User(split[1]).SendAsync(nameof(IMareHub.Client_ReceiveServerMessage),
|
||||
MessageSeverity.Warning, "The Mare profile report against you has been evaluated and your account permanently banned.")
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
await _mareHubContext.Clients.Users(otherPairs).SendAsync(nameof(IMareHub.Client_UserUpdateProfile), new UserDto(new(split[1]))).ConfigureAwait(false);
|
||||
await _mareHubContext.Clients.User(split[1]).SendAsync(nameof(IMareHub.Client_UserUpdateProfile), new UserDto(new(split[1]))).ConfigureAwait(false);
|
||||
|
||||
await arg.Message.ModifyAsync(msg =>
|
||||
{
|
||||
msg.Content = arg.Message.Content;
|
||||
msg.Components = null;
|
||||
msg.Embed = new Optional<Embed>(builder.Build());
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task DiscordClient_Ready()
|
||||
{
|
||||
var guild = (await _discordClient.Rest.GetGuildsAsync()).First();
|
||||
await _interactionModule.RegisterCommandsToGuildAsync(guild.Id, true).ConfigureAwait(false);
|
||||
|
||||
_ = RemoveUsersNotInVanityRole();
|
||||
_ = ProcessReportsQueue();
|
||||
}
|
||||
|
||||
private Task Log(LogMessage msg)
|
||||
@@ -53,15 +182,107 @@ internal class DiscordBot : IHostedService
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task ProcessReportsQueue()
|
||||
{
|
||||
var guild = (await _discordClient.Rest.GetGuildsAsync()).First();
|
||||
|
||||
_processReportQueueCts?.Cancel();
|
||||
_processReportQueueCts?.Dispose();
|
||||
_processReportQueueCts = new();
|
||||
var token = _processReportQueueCts.Token;
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(30)).ConfigureAwait(false);
|
||||
|
||||
if (_discordClient.ConnectionState != ConnectionState.Connected) continue;
|
||||
var reportChannelId = _configurationService.GetValue<ulong?>(nameof(ServicesConfiguration.DiscordChannelForReports));
|
||||
if (reportChannelId == null) continue;
|
||||
|
||||
try
|
||||
{
|
||||
using (var scope = _services.CreateScope())
|
||||
{
|
||||
_logger.LogInformation("Checking for Profile Reports");
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<MareDbContext>();
|
||||
if (!dbContext.UserProfileReports.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var reports = await dbContext.UserProfileReports.ToListAsync().ConfigureAwait(false);
|
||||
var restChannel = await guild.GetTextChannelAsync(reportChannelId.Value).ConfigureAwait(false);
|
||||
|
||||
foreach (var report in reports)
|
||||
{
|
||||
var reportedUser = await dbContext.Users.SingleAsync(u => u.UID == report.ReportedUserUID).ConfigureAwait(false);
|
||||
var reportedUserLodestone = await dbContext.LodeStoneAuth.SingleOrDefaultAsync(u => u.User.UID == report.ReportedUserUID).ConfigureAwait(false);
|
||||
var reportingUser = await dbContext.Users.SingleAsync(u => u.UID == report.ReportingUserUID).ConfigureAwait(false);
|
||||
var reportingUserLodestone = await dbContext.LodeStoneAuth.SingleOrDefaultAsync(u => u.User.UID == report.ReportingUserUID).ConfigureAwait(false);
|
||||
var reportedUserProfile = await dbContext.UserProfileData.SingleAsync(u => u.UserUID == report.ReportedUserUID).ConfigureAwait(false);
|
||||
EmbedBuilder eb = new();
|
||||
eb.WithTitle("Mare Synchronos Profile Report");
|
||||
|
||||
StringBuilder reportedUserSb = new();
|
||||
StringBuilder reportingUserSb = new();
|
||||
reportedUserSb.Append(reportedUser.UID);
|
||||
reportingUserSb.Append(reportingUser.UID);
|
||||
if (reportedUserLodestone != null)
|
||||
{
|
||||
reportedUserSb.AppendLine($" (<@{reportedUserLodestone.DiscordId}>)");
|
||||
}
|
||||
if (reportingUserLodestone != null)
|
||||
{
|
||||
reportingUserSb.AppendLine($" (<@{reportingUserLodestone.DiscordId}>)");
|
||||
}
|
||||
eb.AddField("Reported User", reportedUserSb.ToString());
|
||||
eb.AddField("Reporting User", reportingUserSb.ToString());
|
||||
eb.AddField("Report Date (UTC)", report.ReportDate);
|
||||
eb.AddField("Report Reason", report.ReportReason ?? "-");
|
||||
eb.AddField("Reported User Profile Description", string.IsNullOrEmpty(reportedUserProfile.UserDescription) ? "-" : reportedUserProfile.UserDescription);
|
||||
eb.AddField("Reported User Profile Is NSFW", reportedUserProfile.IsNSFW);
|
||||
|
||||
var cb = new ComponentBuilder();
|
||||
cb.WithButton("Dismiss Report", customId: $"mare-report-button-dismiss-{reportedUser.UID}", style: ButtonStyle.Primary);
|
||||
cb.WithButton("Ban profile", customId: $"mare-report-button-banprofile-{reportedUser.UID}", style: ButtonStyle.Secondary);
|
||||
cb.WithButton("Ban user", customId: $"mare-report-button-banuser-{reportedUser.UID}", style: ButtonStyle.Danger);
|
||||
|
||||
if (!string.IsNullOrEmpty(reportedUserProfile.Base64ProfileImage))
|
||||
{
|
||||
var fileName = reportedUser.UID + "_profile_" + Guid.NewGuid().ToString("N") + ".png";
|
||||
eb.WithImageUrl($"attachment://{fileName}");
|
||||
using MemoryStream ms = new(Convert.FromBase64String(reportedUserProfile.Base64ProfileImage));
|
||||
await restChannel.SendFileAsync(ms, fileName, "User Report", embed: eb.Build(), components: cb.Build(), isSpoiler: true).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = await restChannel.SendMessageAsync(embed: eb.Build(), components: cb.Build()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
dbContext.Remove(report);
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to process reports");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RemoveUsersNotInVanityRole()
|
||||
{
|
||||
_vanityUpdateCts?.Cancel();
|
||||
_vanityUpdateCts?.Dispose();
|
||||
_vanityUpdateCts = new();
|
||||
var token = _vanityUpdateCts.Token;
|
||||
var guild = (await _discordClient.Rest.GetGuildsAsync()).First();
|
||||
var commands = await guild.GetApplicationCommandsAsync();
|
||||
var appId = await _discordClient.GetApplicationInfoAsync().ConfigureAwait(false);
|
||||
var vanityCommandId = commands.First(c => c.ApplicationId == appId.Id && c.Name == "setvanityuid").Id;
|
||||
|
||||
while (!_vanityUpdateCts.IsCancellationRequested)
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -179,40 +400,4 @@ internal class DiscordBot : IHostedService
|
||||
await Task.Delay(TimeSpan.FromSeconds(15)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var token = _configurationService.GetValueOrDefault(nameof(ServicesConfiguration.DiscordBotToken), string.Empty);
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
{
|
||||
_interactionModule = new InteractionService(_discordClient);
|
||||
await _interactionModule.AddModuleAsync(typeof(MareModule), _services).ConfigureAwait(false);
|
||||
|
||||
await _discordClient.LoginAsync(TokenType.Bot, token).ConfigureAwait(false);
|
||||
await _discordClient.StartAsync().ConfigureAwait(false);
|
||||
|
||||
_discordClient.Ready += DiscordClient_Ready;
|
||||
_discordClient.InteractionCreated += async (x) =>
|
||||
{
|
||||
var ctx = new SocketInteractionContext(_discordClient, x);
|
||||
await _interactionModule.ExecuteCommandAsync(ctx, _services);
|
||||
};
|
||||
|
||||
await _botServices.Start();
|
||||
_ = UpdateStatusAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_configurationService.GetValueOrDefault(nameof(ServicesConfiguration.DiscordBotToken), string.Empty)))
|
||||
{
|
||||
await _botServices.Stop();
|
||||
_updateStatusCts?.Cancel();
|
||||
_vanityUpdateCts?.Cancel();
|
||||
|
||||
await _discordClient.LogoutAsync().ConfigureAwait(false);
|
||||
await _discordClient.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,16 +5,12 @@ namespace MareSynchronosServices.Discord;
|
||||
|
||||
public class DiscordBotServices
|
||||
{
|
||||
public ConcurrentQueue<KeyValuePair<ulong, Action<IServiceProvider>>> VerificationQueue { get; } = new();
|
||||
public ConcurrentDictionary<ulong, DateTime> LastVanityChange = new();
|
||||
public ConcurrentDictionary<string, DateTime> LastVanityGidChange = new();
|
||||
public readonly string[] LodestoneServers = new[] { "eu", "na", "jp", "fr", "de" };
|
||||
public ConcurrentDictionary<ulong, string> DiscordLodestoneMapping = new();
|
||||
public ConcurrentDictionary<ulong, string> DiscordRelinkLodestoneMapping = new();
|
||||
public readonly string[] LodestoneServers = new[] { "eu", "na", "jp", "fr", "de" };
|
||||
public ConcurrentDictionary<ulong, DateTime> LastVanityChange = new();
|
||||
public ConcurrentDictionary<string, DateTime> LastVanityGidChange = new();
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public ILogger<DiscordBotServices> Logger { get; init; }
|
||||
public MareMetrics Metrics { get; init; }
|
||||
private CancellationTokenSource? verificationTaskCts;
|
||||
|
||||
public DiscordBotServices(IServiceProvider serviceProvider, ILogger<DiscordBotServices> logger, MareMetrics metrics)
|
||||
@@ -24,6 +20,10 @@ public class DiscordBotServices
|
||||
Metrics = metrics;
|
||||
}
|
||||
|
||||
public ILogger<DiscordBotServices> Logger { get; init; }
|
||||
public MareMetrics Metrics { get; init; }
|
||||
public ConcurrentQueue<KeyValuePair<ulong, Action<IServiceProvider>>> VerificationQueue { get; } = new();
|
||||
|
||||
public Task Start()
|
||||
{
|
||||
_ = ProcessVerificationQueue();
|
||||
|
||||
25
MareSynchronosServer/MareSynchronosServices/DummyHub.cs
Normal file
25
MareSynchronosServer/MareSynchronosServices/DummyHub.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
// this is a very hacky way to attach this file server to the main mare hub signalr instance via redis
|
||||
// signalr publishes the namespace and hubname into the redis backend so this needs to be equal to the original
|
||||
// but I don't need to reimplement the hub completely as I only exclusively use it for internal connection calling
|
||||
// from the queue service so I keep the namespace and name of the class the same so it can connect to the same channel
|
||||
// if anyone finds a better way to do this let me know
|
||||
|
||||
#pragma warning disable IDE0130 // Namespace does not match folder structure
|
||||
#pragma warning disable MA0048 // File name must match type name
|
||||
namespace MareSynchronosServer.Hubs;
|
||||
public class MareHub : Hub
|
||||
{
|
||||
public override Task OnConnectedAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override Task OnDisconnectedAsync(Exception exception)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
#pragma warning restore IDE0130 // Namespace does not match folder structure
|
||||
#pragma warning restore MA0048 // File name must match type name
|
||||
@@ -32,6 +32,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MareAPI\MareSynchronosAPI\MareSynchronos.API.csproj" />
|
||||
<ProjectReference Include="..\MareSynchronosShared\MareSynchronosShared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ using Grpc.Net.Client.Configuration;
|
||||
using MareSynchronosShared.Protos;
|
||||
using MareSynchronosShared.Services;
|
||||
using StackExchange.Redis;
|
||||
using MessagePack.Resolvers;
|
||||
using MessagePack;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace MareSynchronosServices;
|
||||
|
||||
@@ -20,6 +23,20 @@ public class Startup
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
var config = app.ApplicationServices.GetRequiredService<IConfigurationService<MareConfigurationAuthBase>>();
|
||||
|
||||
var metricServer = new KestrelMetricServer(config.GetValueOrDefault<int>(nameof(MareConfigurationBase.MetricsPort), 4982));
|
||||
metricServer.Start();
|
||||
|
||||
app.UseRouting();
|
||||
app.UseEndpoints(e =>
|
||||
{
|
||||
e.MapHub<MareSynchronosServer.Hubs.MareHub>("/dummyhub");
|
||||
});
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var mareConfig = Configuration.GetSection("MareSynchronos");
|
||||
@@ -61,6 +78,35 @@ public class Startup
|
||||
};
|
||||
});
|
||||
|
||||
var signalRServiceBuilder = services.AddSignalR(hubOptions =>
|
||||
{
|
||||
hubOptions.MaximumReceiveMessageSize = long.MaxValue;
|
||||
hubOptions.EnableDetailedErrors = true;
|
||||
hubOptions.MaximumParallelInvocationsPerClient = 10;
|
||||
hubOptions.StreamBufferCapacity = 200;
|
||||
}).AddMessagePackProtocol(opt =>
|
||||
{
|
||||
var resolver = CompositeResolver.Create(StandardResolverAllowPrivate.Instance,
|
||||
BuiltinResolver.Instance,
|
||||
AttributeFormatterResolver.Instance,
|
||||
// replace enum resolver
|
||||
DynamicEnumAsStringResolver.Instance,
|
||||
DynamicGenericResolver.Instance,
|
||||
DynamicUnionResolver.Instance,
|
||||
DynamicObjectResolver.Instance,
|
||||
PrimitiveObjectResolver.Instance,
|
||||
// final fallback(last priority)
|
||||
StandardResolver.Instance);
|
||||
|
||||
opt.SerializerOptions = MessagePackSerializerOptions.Standard
|
||||
.WithCompression(MessagePackCompression.Lz4Block)
|
||||
.WithResolver(resolver);
|
||||
});
|
||||
|
||||
// configure redis for SignalR
|
||||
var redisConnection = mareConfig.GetValue(nameof(MareConfigurationBase.RedisConnectionString), string.Empty);
|
||||
signalRServiceBuilder.AddStackExchangeRedis(redisConnection, options => { });
|
||||
|
||||
services.Configure<ServicesConfiguration>(Configuration.GetRequiredSection("MareSynchronos"));
|
||||
services.Configure<ServerConfiguration>(Configuration.GetRequiredSection("MareSynchronos"));
|
||||
services.Configure<MareConfigurationAuthBase>(Configuration.GetRequiredSection("MareSynchronos"));
|
||||
@@ -75,12 +121,4 @@ public class Startup
|
||||
services.AddHostedService(p => (MareConfigurationServiceClient<MareConfigurationAuthBase>)p.GetService<IConfigurationService<MareConfigurationAuthBase>>());
|
||||
services.AddHostedService(p => (MareConfigurationServiceClient<ServerConfiguration>)p.GetService<IConfigurationService<ServerConfiguration>>());
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
var config = app.ApplicationServices.GetRequiredService<IConfigurationService<MareConfigurationAuthBase>>();
|
||||
|
||||
var metricServer = new KestrelMetricServer(config.GetValueOrDefault<int>(nameof(MareConfigurationBase.MetricsPort), 4982));
|
||||
metricServer.Start();
|
||||
}
|
||||
}
|
||||
@@ -31,19 +31,20 @@ public class MareDbContext : DbContext
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<FileCache> Files { get; set; }
|
||||
public DbSet<ClientPair> ClientPairs { get; set; }
|
||||
public DbSet<ForbiddenUploadEntry> ForbiddenUploadEntries { get; set; }
|
||||
public DbSet<Banned> BannedUsers { get; set; }
|
||||
public DbSet<Auth> Auth { get; set; }
|
||||
public DbSet<LodeStoneAuth> LodeStoneAuth { get; set; }
|
||||
public DbSet<BannedRegistrations> BannedRegistrations { get; set; }
|
||||
public DbSet<Group> Groups { get; set; }
|
||||
public DbSet<GroupPair> GroupPairs { get; set; }
|
||||
public DbSet<Banned> BannedUsers { get; set; }
|
||||
public DbSet<ClientPair> ClientPairs { get; set; }
|
||||
public DbSet<FileCache> Files { get; set; }
|
||||
public DbSet<ForbiddenUploadEntry> ForbiddenUploadEntries { get; set; }
|
||||
public DbSet<GroupBan> GroupBans { get; set; }
|
||||
public DbSet<GroupPair> GroupPairs { get; set; }
|
||||
public DbSet<Group> Groups { get; set; }
|
||||
public DbSet<GroupTempInvite> GroupTempInvites { get; set; }
|
||||
|
||||
public DbSet<LodeStoneAuth> LodeStoneAuth { get; set; }
|
||||
public DbSet<UserProfileData> UserProfileData { get; set; }
|
||||
public DbSet<UserProfileDataReport> UserProfileReports { get; set; }
|
||||
public DbSet<User> Users { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -73,5 +74,8 @@ public class MareDbContext : DbContext
|
||||
modelBuilder.Entity<GroupTempInvite>().HasKey(u => new { u.GroupGID, u.Invite });
|
||||
modelBuilder.Entity<GroupTempInvite>().HasIndex(c => c.GroupGID);
|
||||
modelBuilder.Entity<GroupTempInvite>().HasIndex(c => c.Invite);
|
||||
modelBuilder.Entity<UserProfileData>().ToTable("user_profile_data");
|
||||
modelBuilder.Entity<UserProfileData>().HasKey(c => c.UserUID);
|
||||
modelBuilder.Entity<UserProfileDataReport>().ToTable("user_profile_data_reports");
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Core" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.4">
|
||||
|
||||
584
MareSynchronosServer/MareSynchronosShared/Migrations/20230319015307_UserProfileData.Designer.cs
generated
Normal file
584
MareSynchronosServer/MareSynchronosShared/Migrations/20230319015307_UserProfileData.Designer.cs
generated
Normal file
@@ -0,0 +1,584 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using MareSynchronosShared.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace MareSynchronosServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(MareDbContext))]
|
||||
[Migration("20230319015307_UserProfileData")]
|
||||
partial class UserProfileData
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
|
||||
{
|
||||
b.Property<string>("HashedKey")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("hashed_key");
|
||||
|
||||
b.Property<bool>("IsBanned")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_banned");
|
||||
|
||||
b.Property<string>("PrimaryUserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("primary_user_uid");
|
||||
|
||||
b.Property<string>("UserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("user_uid");
|
||||
|
||||
b.HasKey("HashedKey")
|
||||
.HasName("pk_auth");
|
||||
|
||||
b.HasIndex("PrimaryUserUID")
|
||||
.HasDatabaseName("ix_auth_primary_user_uid");
|
||||
|
||||
b.HasIndex("UserUID")
|
||||
.HasDatabaseName("ix_auth_user_uid");
|
||||
|
||||
b.ToTable("auth", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Banned", b =>
|
||||
{
|
||||
b.Property<string>("CharacterIdentification")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("character_identification");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("reason");
|
||||
|
||||
b.Property<byte[]>("Timestamp")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("timestamp");
|
||||
|
||||
b.HasKey("CharacterIdentification")
|
||||
.HasName("pk_banned_users");
|
||||
|
||||
b.ToTable("banned_users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.BannedRegistrations", b =>
|
||||
{
|
||||
b.Property<string>("DiscordIdOrLodestoneAuth")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("discord_id_or_lodestone_auth");
|
||||
|
||||
b.HasKey("DiscordIdOrLodestoneAuth")
|
||||
.HasName("pk_banned_registrations");
|
||||
|
||||
b.ToTable("banned_registrations", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b =>
|
||||
{
|
||||
b.Property<string>("UserUID")
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("user_uid");
|
||||
|
||||
b.Property<string>("OtherUserUID")
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("other_user_uid");
|
||||
|
||||
b.Property<bool>("AllowReceivingMessages")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("allow_receiving_messages");
|
||||
|
||||
b.Property<bool>("DisableAnimations")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_animations");
|
||||
|
||||
b.Property<bool>("DisableSounds")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_sounds");
|
||||
|
||||
b.Property<bool>("IsPaused")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_paused");
|
||||
|
||||
b.Property<byte[]>("Timestamp")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("timestamp");
|
||||
|
||||
b.HasKey("UserUID", "OtherUserUID")
|
||||
.HasName("pk_client_pairs");
|
||||
|
||||
b.HasIndex("OtherUserUID")
|
||||
.HasDatabaseName("ix_client_pairs_other_user_uid");
|
||||
|
||||
b.HasIndex("UserUID")
|
||||
.HasDatabaseName("ix_client_pairs_user_uid");
|
||||
|
||||
b.ToTable("client_pairs", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b =>
|
||||
{
|
||||
b.Property<string>("Hash")
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)")
|
||||
.HasColumnName("hash");
|
||||
|
||||
b.Property<long>("Size")
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("size");
|
||||
|
||||
b.Property<byte[]>("Timestamp")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("timestamp");
|
||||
|
||||
b.Property<DateTime>("UploadDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("upload_date");
|
||||
|
||||
b.Property<bool>("Uploaded")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("uploaded");
|
||||
|
||||
b.Property<string>("UploaderUID")
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("uploader_uid");
|
||||
|
||||
b.HasKey("Hash")
|
||||
.HasName("pk_file_caches");
|
||||
|
||||
b.HasIndex("UploaderUID")
|
||||
.HasDatabaseName("ix_file_caches_uploader_uid");
|
||||
|
||||
b.ToTable("file_caches", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.ForbiddenUploadEntry", b =>
|
||||
{
|
||||
b.Property<string>("Hash")
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)")
|
||||
.HasColumnName("hash");
|
||||
|
||||
b.Property<string>("ForbiddenBy")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("forbidden_by");
|
||||
|
||||
b.Property<byte[]>("Timestamp")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("timestamp");
|
||||
|
||||
b.HasKey("Hash")
|
||||
.HasName("pk_forbidden_upload_entries");
|
||||
|
||||
b.ToTable("forbidden_upload_entries", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
|
||||
{
|
||||
b.Property<string>("GID")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("gid");
|
||||
|
||||
b.Property<string>("Alias")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("alias");
|
||||
|
||||
b.Property<bool>("DisableAnimations")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_animations");
|
||||
|
||||
b.Property<bool>("DisableSounds")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_sounds");
|
||||
|
||||
b.Property<string>("HashedPassword")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("hashed_password");
|
||||
|
||||
b.Property<bool>("InvitesEnabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("invites_enabled");
|
||||
|
||||
b.Property<string>("OwnerUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("owner_uid");
|
||||
|
||||
b.HasKey("GID")
|
||||
.HasName("pk_groups");
|
||||
|
||||
b.HasIndex("OwnerUID")
|
||||
.HasDatabaseName("ix_groups_owner_uid");
|
||||
|
||||
b.ToTable("groups", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupBan", b =>
|
||||
{
|
||||
b.Property<string>("GroupGID")
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("group_gid");
|
||||
|
||||
b.Property<string>("BannedUserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("banned_user_uid");
|
||||
|
||||
b.Property<string>("BannedByUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("banned_by_uid");
|
||||
|
||||
b.Property<DateTime>("BannedOn")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("banned_on");
|
||||
|
||||
b.Property<string>("BannedReason")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("banned_reason");
|
||||
|
||||
b.HasKey("GroupGID", "BannedUserUID")
|
||||
.HasName("pk_group_bans");
|
||||
|
||||
b.HasIndex("BannedByUID")
|
||||
.HasDatabaseName("ix_group_bans_banned_by_uid");
|
||||
|
||||
b.HasIndex("BannedUserUID")
|
||||
.HasDatabaseName("ix_group_bans_banned_user_uid");
|
||||
|
||||
b.HasIndex("GroupGID")
|
||||
.HasDatabaseName("ix_group_bans_group_gid");
|
||||
|
||||
b.ToTable("group_bans", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
|
||||
{
|
||||
b.Property<string>("GroupGID")
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("group_gid");
|
||||
|
||||
b.Property<string>("GroupUserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("group_user_uid");
|
||||
|
||||
b.Property<bool>("DisableAnimations")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_animations");
|
||||
|
||||
b.Property<bool>("DisableSounds")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_sounds");
|
||||
|
||||
b.Property<bool>("IsModerator")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_moderator");
|
||||
|
||||
b.Property<bool>("IsPaused")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_paused");
|
||||
|
||||
b.Property<bool>("IsPinned")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_pinned");
|
||||
|
||||
b.HasKey("GroupGID", "GroupUserUID")
|
||||
.HasName("pk_group_pairs");
|
||||
|
||||
b.HasIndex("GroupGID")
|
||||
.HasDatabaseName("ix_group_pairs_group_gid");
|
||||
|
||||
b.HasIndex("GroupUserUID")
|
||||
.HasDatabaseName("ix_group_pairs_group_user_uid");
|
||||
|
||||
b.ToTable("group_pairs", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupTempInvite", b =>
|
||||
{
|
||||
b.Property<string>("GroupGID")
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("group_gid");
|
||||
|
||||
b.Property<string>("Invite")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("invite");
|
||||
|
||||
b.Property<DateTime>("ExpirationDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expiration_date");
|
||||
|
||||
b.HasKey("GroupGID", "Invite")
|
||||
.HasName("pk_group_temp_invites");
|
||||
|
||||
b.HasIndex("GroupGID")
|
||||
.HasDatabaseName("ix_group_temp_invites_group_gid");
|
||||
|
||||
b.HasIndex("Invite")
|
||||
.HasDatabaseName("ix_group_temp_invites_invite");
|
||||
|
||||
b.ToTable("group_temp_invites", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
|
||||
{
|
||||
b.Property<decimal>("DiscordId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("numeric(20,0)")
|
||||
.HasColumnName("discord_id");
|
||||
|
||||
b.Property<string>("HashedLodestoneId")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("hashed_lodestone_id");
|
||||
|
||||
b.Property<string>("LodestoneAuthString")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("lodestone_auth_string");
|
||||
|
||||
b.Property<DateTime?>("StartedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("started_at");
|
||||
|
||||
b.Property<string>("UserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("user_uid");
|
||||
|
||||
b.HasKey("DiscordId")
|
||||
.HasName("pk_lodestone_auth");
|
||||
|
||||
b.HasIndex("UserUID")
|
||||
.HasDatabaseName("ix_lodestone_auth_user_uid");
|
||||
|
||||
b.ToTable("lodestone_auth", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.User", b =>
|
||||
{
|
||||
b.Property<string>("UID")
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("uid");
|
||||
|
||||
b.Property<string>("Alias")
|
||||
.HasMaxLength(15)
|
||||
.HasColumnType("character varying(15)")
|
||||
.HasColumnName("alias");
|
||||
|
||||
b.Property<bool>("IsAdmin")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_admin");
|
||||
|
||||
b.Property<bool>("IsModerator")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_moderator");
|
||||
|
||||
b.Property<DateTime>("LastLoggedIn")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_logged_in");
|
||||
|
||||
b.Property<byte[]>("Timestamp")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("timestamp");
|
||||
|
||||
b.HasKey("UID")
|
||||
.HasName("pk_users");
|
||||
|
||||
b.ToTable("users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.UserProfileData", b =>
|
||||
{
|
||||
b.Property<string>("UserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("user_uid");
|
||||
|
||||
b.Property<string>("Base64ProfileImage")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("base64profile_image");
|
||||
|
||||
b.Property<string>("UserDescription")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("user_description");
|
||||
|
||||
b.HasKey("UserUID")
|
||||
.HasName("pk_user_profile_data");
|
||||
|
||||
b.ToTable("user_profile_data", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "PrimaryUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("PrimaryUserUID")
|
||||
.HasConstraintName("fk_auth_users_primary_user_temp_id");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserUID")
|
||||
.HasConstraintName("fk_auth_users_user_temp_id1");
|
||||
|
||||
b.Navigation("PrimaryUser");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "OtherUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("OtherUserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_client_pairs_users_other_user_temp_id2");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_client_pairs_users_user_temp_id3");
|
||||
|
||||
b.Navigation("OtherUser");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "Uploader")
|
||||
.WithMany()
|
||||
.HasForeignKey("UploaderUID")
|
||||
.HasConstraintName("fk_file_caches_users_uploader_uid");
|
||||
|
||||
b.Navigation("Uploader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "Owner")
|
||||
.WithMany()
|
||||
.HasForeignKey("OwnerUID")
|
||||
.HasConstraintName("fk_groups_users_owner_temp_id8");
|
||||
|
||||
b.Navigation("Owner");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupBan", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "BannedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("BannedByUID")
|
||||
.HasConstraintName("fk_group_bans_users_banned_by_temp_id5");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.User", "BannedUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("BannedUserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_group_bans_users_banned_user_temp_id6");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.Group", "Group")
|
||||
.WithMany()
|
||||
.HasForeignKey("GroupGID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_group_bans_groups_group_temp_id");
|
||||
|
||||
b.Navigation("BannedBy");
|
||||
|
||||
b.Navigation("BannedUser");
|
||||
|
||||
b.Navigation("Group");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.Group", "Group")
|
||||
.WithMany()
|
||||
.HasForeignKey("GroupGID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_group_pairs_groups_group_temp_id1");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.User", "GroupUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("GroupUserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_group_pairs_users_group_user_temp_id7");
|
||||
|
||||
b.Navigation("Group");
|
||||
|
||||
b.Navigation("GroupUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupTempInvite", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.Group", "Group")
|
||||
.WithMany()
|
||||
.HasForeignKey("GroupGID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_group_temp_invites_groups_group_gid");
|
||||
|
||||
b.Navigation("Group");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserUID")
|
||||
.HasConstraintName("fk_lodestone_auth_users_user_uid");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.UserProfileData", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_profile_data_users_user_uid");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace MareSynchronosServer.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UserProfileData : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user_profile_data",
|
||||
columns: table => new
|
||||
{
|
||||
user_uid = table.Column<string>(type: "character varying(10)", nullable: false),
|
||||
base64profile_image = table.Column<string>(type: "text", nullable: true),
|
||||
user_description = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_user_profile_data", x => x.user_uid);
|
||||
table.ForeignKey(
|
||||
name: "fk_user_profile_data_users_user_uid",
|
||||
column: x => x.user_uid,
|
||||
principalTable: "users",
|
||||
principalColumn: "uid",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_profile_data");
|
||||
}
|
||||
}
|
||||
}
|
||||
650
MareSynchronosServer/MareSynchronosShared/Migrations/20230319114005_UserProfileReports.Designer.cs
generated
Normal file
650
MareSynchronosServer/MareSynchronosShared/Migrations/20230319114005_UserProfileReports.Designer.cs
generated
Normal file
@@ -0,0 +1,650 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using MareSynchronosShared.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace MareSynchronosServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(MareDbContext))]
|
||||
[Migration("20230319114005_UserProfileReports")]
|
||||
partial class UserProfileReports
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
|
||||
{
|
||||
b.Property<string>("HashedKey")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("hashed_key");
|
||||
|
||||
b.Property<bool>("IsBanned")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_banned");
|
||||
|
||||
b.Property<string>("PrimaryUserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("primary_user_uid");
|
||||
|
||||
b.Property<string>("UserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("user_uid");
|
||||
|
||||
b.HasKey("HashedKey")
|
||||
.HasName("pk_auth");
|
||||
|
||||
b.HasIndex("PrimaryUserUID")
|
||||
.HasDatabaseName("ix_auth_primary_user_uid");
|
||||
|
||||
b.HasIndex("UserUID")
|
||||
.HasDatabaseName("ix_auth_user_uid");
|
||||
|
||||
b.ToTable("auth", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Banned", b =>
|
||||
{
|
||||
b.Property<string>("CharacterIdentification")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("character_identification");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("reason");
|
||||
|
||||
b.Property<byte[]>("Timestamp")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("timestamp");
|
||||
|
||||
b.HasKey("CharacterIdentification")
|
||||
.HasName("pk_banned_users");
|
||||
|
||||
b.ToTable("banned_users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.BannedRegistrations", b =>
|
||||
{
|
||||
b.Property<string>("DiscordIdOrLodestoneAuth")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("discord_id_or_lodestone_auth");
|
||||
|
||||
b.HasKey("DiscordIdOrLodestoneAuth")
|
||||
.HasName("pk_banned_registrations");
|
||||
|
||||
b.ToTable("banned_registrations", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b =>
|
||||
{
|
||||
b.Property<string>("UserUID")
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("user_uid");
|
||||
|
||||
b.Property<string>("OtherUserUID")
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("other_user_uid");
|
||||
|
||||
b.Property<bool>("AllowReceivingMessages")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("allow_receiving_messages");
|
||||
|
||||
b.Property<bool>("DisableAnimations")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_animations");
|
||||
|
||||
b.Property<bool>("DisableSounds")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_sounds");
|
||||
|
||||
b.Property<bool>("IsPaused")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_paused");
|
||||
|
||||
b.Property<byte[]>("Timestamp")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("timestamp");
|
||||
|
||||
b.HasKey("UserUID", "OtherUserUID")
|
||||
.HasName("pk_client_pairs");
|
||||
|
||||
b.HasIndex("OtherUserUID")
|
||||
.HasDatabaseName("ix_client_pairs_other_user_uid");
|
||||
|
||||
b.HasIndex("UserUID")
|
||||
.HasDatabaseName("ix_client_pairs_user_uid");
|
||||
|
||||
b.ToTable("client_pairs", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b =>
|
||||
{
|
||||
b.Property<string>("Hash")
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)")
|
||||
.HasColumnName("hash");
|
||||
|
||||
b.Property<long>("Size")
|
||||
.HasColumnType("bigint")
|
||||
.HasColumnName("size");
|
||||
|
||||
b.Property<byte[]>("Timestamp")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("timestamp");
|
||||
|
||||
b.Property<DateTime>("UploadDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("upload_date");
|
||||
|
||||
b.Property<bool>("Uploaded")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("uploaded");
|
||||
|
||||
b.Property<string>("UploaderUID")
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("uploader_uid");
|
||||
|
||||
b.HasKey("Hash")
|
||||
.HasName("pk_file_caches");
|
||||
|
||||
b.HasIndex("UploaderUID")
|
||||
.HasDatabaseName("ix_file_caches_uploader_uid");
|
||||
|
||||
b.ToTable("file_caches", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.ForbiddenUploadEntry", b =>
|
||||
{
|
||||
b.Property<string>("Hash")
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("character varying(40)")
|
||||
.HasColumnName("hash");
|
||||
|
||||
b.Property<string>("ForbiddenBy")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("forbidden_by");
|
||||
|
||||
b.Property<byte[]>("Timestamp")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("timestamp");
|
||||
|
||||
b.HasKey("Hash")
|
||||
.HasName("pk_forbidden_upload_entries");
|
||||
|
||||
b.ToTable("forbidden_upload_entries", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
|
||||
{
|
||||
b.Property<string>("GID")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("gid");
|
||||
|
||||
b.Property<string>("Alias")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)")
|
||||
.HasColumnName("alias");
|
||||
|
||||
b.Property<bool>("DisableAnimations")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_animations");
|
||||
|
||||
b.Property<bool>("DisableSounds")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_sounds");
|
||||
|
||||
b.Property<string>("HashedPassword")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("hashed_password");
|
||||
|
||||
b.Property<bool>("InvitesEnabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("invites_enabled");
|
||||
|
||||
b.Property<string>("OwnerUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("owner_uid");
|
||||
|
||||
b.HasKey("GID")
|
||||
.HasName("pk_groups");
|
||||
|
||||
b.HasIndex("OwnerUID")
|
||||
.HasDatabaseName("ix_groups_owner_uid");
|
||||
|
||||
b.ToTable("groups", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupBan", b =>
|
||||
{
|
||||
b.Property<string>("GroupGID")
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("group_gid");
|
||||
|
||||
b.Property<string>("BannedUserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("banned_user_uid");
|
||||
|
||||
b.Property<string>("BannedByUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("banned_by_uid");
|
||||
|
||||
b.Property<DateTime>("BannedOn")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("banned_on");
|
||||
|
||||
b.Property<string>("BannedReason")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("banned_reason");
|
||||
|
||||
b.HasKey("GroupGID", "BannedUserUID")
|
||||
.HasName("pk_group_bans");
|
||||
|
||||
b.HasIndex("BannedByUID")
|
||||
.HasDatabaseName("ix_group_bans_banned_by_uid");
|
||||
|
||||
b.HasIndex("BannedUserUID")
|
||||
.HasDatabaseName("ix_group_bans_banned_user_uid");
|
||||
|
||||
b.HasIndex("GroupGID")
|
||||
.HasDatabaseName("ix_group_bans_group_gid");
|
||||
|
||||
b.ToTable("group_bans", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
|
||||
{
|
||||
b.Property<string>("GroupGID")
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("group_gid");
|
||||
|
||||
b.Property<string>("GroupUserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("group_user_uid");
|
||||
|
||||
b.Property<bool>("DisableAnimations")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_animations");
|
||||
|
||||
b.Property<bool>("DisableSounds")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disable_sounds");
|
||||
|
||||
b.Property<bool>("IsModerator")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_moderator");
|
||||
|
||||
b.Property<bool>("IsPaused")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_paused");
|
||||
|
||||
b.Property<bool>("IsPinned")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_pinned");
|
||||
|
||||
b.HasKey("GroupGID", "GroupUserUID")
|
||||
.HasName("pk_group_pairs");
|
||||
|
||||
b.HasIndex("GroupGID")
|
||||
.HasDatabaseName("ix_group_pairs_group_gid");
|
||||
|
||||
b.HasIndex("GroupUserUID")
|
||||
.HasDatabaseName("ix_group_pairs_group_user_uid");
|
||||
|
||||
b.ToTable("group_pairs", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupTempInvite", b =>
|
||||
{
|
||||
b.Property<string>("GroupGID")
|
||||
.HasColumnType("character varying(20)")
|
||||
.HasColumnName("group_gid");
|
||||
|
||||
b.Property<string>("Invite")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)")
|
||||
.HasColumnName("invite");
|
||||
|
||||
b.Property<DateTime>("ExpirationDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expiration_date");
|
||||
|
||||
b.HasKey("GroupGID", "Invite")
|
||||
.HasName("pk_group_temp_invites");
|
||||
|
||||
b.HasIndex("GroupGID")
|
||||
.HasDatabaseName("ix_group_temp_invites_group_gid");
|
||||
|
||||
b.HasIndex("Invite")
|
||||
.HasDatabaseName("ix_group_temp_invites_invite");
|
||||
|
||||
b.ToTable("group_temp_invites", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
|
||||
{
|
||||
b.Property<decimal>("DiscordId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("numeric(20,0)")
|
||||
.HasColumnName("discord_id");
|
||||
|
||||
b.Property<string>("HashedLodestoneId")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("hashed_lodestone_id");
|
||||
|
||||
b.Property<string>("LodestoneAuthString")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasColumnName("lodestone_auth_string");
|
||||
|
||||
b.Property<DateTime?>("StartedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("started_at");
|
||||
|
||||
b.Property<string>("UserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("user_uid");
|
||||
|
||||
b.HasKey("DiscordId")
|
||||
.HasName("pk_lodestone_auth");
|
||||
|
||||
b.HasIndex("UserUID")
|
||||
.HasDatabaseName("ix_lodestone_auth_user_uid");
|
||||
|
||||
b.ToTable("lodestone_auth", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.User", b =>
|
||||
{
|
||||
b.Property<string>("UID")
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("uid");
|
||||
|
||||
b.Property<string>("Alias")
|
||||
.HasMaxLength(15)
|
||||
.HasColumnType("character varying(15)")
|
||||
.HasColumnName("alias");
|
||||
|
||||
b.Property<bool>("IsAdmin")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_admin");
|
||||
|
||||
b.Property<bool>("IsModerator")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_moderator");
|
||||
|
||||
b.Property<DateTime>("LastLoggedIn")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("last_logged_in");
|
||||
|
||||
b.Property<byte[]>("Timestamp")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea")
|
||||
.HasColumnName("timestamp");
|
||||
|
||||
b.HasKey("UID")
|
||||
.HasName("pk_users");
|
||||
|
||||
b.ToTable("users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.UserProfileData", b =>
|
||||
{
|
||||
b.Property<string>("UserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("user_uid");
|
||||
|
||||
b.Property<string>("Base64ProfileImage")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("base64profile_image");
|
||||
|
||||
b.Property<bool>("FlaggedForReport")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("flagged_for_report");
|
||||
|
||||
b.Property<bool>("IsNSFW")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_nsfw");
|
||||
|
||||
b.Property<bool>("ProfileDisabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("profile_disabled");
|
||||
|
||||
b.Property<string>("UserDescription")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("user_description");
|
||||
|
||||
b.HasKey("UserUID")
|
||||
.HasName("pk_user_profile_data");
|
||||
|
||||
b.ToTable("user_profile_data", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.UserProfileDataReport", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("ReportDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("report_date");
|
||||
|
||||
b.Property<string>("ReportReason")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("report_reason");
|
||||
|
||||
b.Property<string>("ReportedUserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("reported_user_uid");
|
||||
|
||||
b.Property<string>("ReportingUserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("reporting_user_uid");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_user_profile_data_reports");
|
||||
|
||||
b.HasIndex("ReportedUserUID")
|
||||
.HasDatabaseName("ix_user_profile_data_reports_reported_user_uid");
|
||||
|
||||
b.HasIndex("ReportingUserUID")
|
||||
.HasDatabaseName("ix_user_profile_data_reports_reporting_user_uid");
|
||||
|
||||
b.ToTable("user_profile_data_reports", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "PrimaryUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("PrimaryUserUID")
|
||||
.HasConstraintName("fk_auth_users_primary_user_temp_id");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserUID")
|
||||
.HasConstraintName("fk_auth_users_user_temp_id1");
|
||||
|
||||
b.Navigation("PrimaryUser");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.ClientPair", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "OtherUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("OtherUserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_client_pairs_users_other_user_temp_id2");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_client_pairs_users_user_temp_id3");
|
||||
|
||||
b.Navigation("OtherUser");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.FileCache", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "Uploader")
|
||||
.WithMany()
|
||||
.HasForeignKey("UploaderUID")
|
||||
.HasConstraintName("fk_file_caches_users_uploader_uid");
|
||||
|
||||
b.Navigation("Uploader");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Group", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "Owner")
|
||||
.WithMany()
|
||||
.HasForeignKey("OwnerUID")
|
||||
.HasConstraintName("fk_groups_users_owner_temp_id8");
|
||||
|
||||
b.Navigation("Owner");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupBan", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "BannedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("BannedByUID")
|
||||
.HasConstraintName("fk_group_bans_users_banned_by_temp_id5");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.User", "BannedUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("BannedUserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_group_bans_users_banned_user_temp_id6");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.Group", "Group")
|
||||
.WithMany()
|
||||
.HasForeignKey("GroupGID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_group_bans_groups_group_temp_id");
|
||||
|
||||
b.Navigation("BannedBy");
|
||||
|
||||
b.Navigation("BannedUser");
|
||||
|
||||
b.Navigation("Group");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupPair", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.Group", "Group")
|
||||
.WithMany()
|
||||
.HasForeignKey("GroupGID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_group_pairs_groups_group_temp_id1");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.User", "GroupUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("GroupUserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_group_pairs_users_group_user_temp_id7");
|
||||
|
||||
b.Navigation("Group");
|
||||
|
||||
b.Navigation("GroupUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.GroupTempInvite", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.Group", "Group")
|
||||
.WithMany()
|
||||
.HasForeignKey("GroupGID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_group_temp_invites_groups_group_gid");
|
||||
|
||||
b.Navigation("Group");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.LodeStoneAuth", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserUID")
|
||||
.HasConstraintName("fk_lodestone_auth_users_user_uid");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.UserProfileData", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_profile_data_users_user_uid");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.UserProfileDataReport", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "ReportedUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("ReportedUserUID")
|
||||
.HasConstraintName("fk_user_profile_data_reports_users_reported_user_uid");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.User", "ReportingUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("ReportingUserUID")
|
||||
.HasConstraintName("fk_user_profile_data_reports_users_reporting_user_uid");
|
||||
|
||||
b.Navigation("ReportedUser");
|
||||
|
||||
b.Navigation("ReportingUser");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace MareSynchronosServer.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UserProfileReports : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "flagged_for_report",
|
||||
table: "user_profile_data",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "is_nsfw",
|
||||
table: "user_profile_data",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "profile_disabled",
|
||||
table: "user_profile_data",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "user_profile_data_reports",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
report_date = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
reported_user_uid = table.Column<string>(type: "character varying(10)", nullable: true),
|
||||
reporting_user_uid = table.Column<string>(type: "character varying(10)", nullable: true),
|
||||
report_reason = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_user_profile_data_reports", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "fk_user_profile_data_reports_users_reported_user_uid",
|
||||
column: x => x.reported_user_uid,
|
||||
principalTable: "users",
|
||||
principalColumn: "uid");
|
||||
table.ForeignKey(
|
||||
name: "fk_user_profile_data_reports_users_reporting_user_uid",
|
||||
column: x => x.reporting_user_uid,
|
||||
principalTable: "users",
|
||||
principalColumn: "uid");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_user_profile_data_reports_reported_user_uid",
|
||||
table: "user_profile_data_reports",
|
||||
column: "reported_user_uid");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_user_profile_data_reports_reporting_user_uid",
|
||||
table: "user_profile_data_reports",
|
||||
column: "reporting_user_uid");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "user_profile_data_reports");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "flagged_for_report",
|
||||
table: "user_profile_data");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "is_nsfw",
|
||||
table: "user_profile_data");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "profile_disabled",
|
||||
table: "user_profile_data");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace MareSynchronosServer.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.3")
|
||||
.HasAnnotation("ProductVersion", "7.0.4")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
@@ -415,6 +415,75 @@ namespace MareSynchronosServer.Migrations
|
||||
b.ToTable("users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.UserProfileData", b =>
|
||||
{
|
||||
b.Property<string>("UserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("user_uid");
|
||||
|
||||
b.Property<string>("Base64ProfileImage")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("base64profile_image");
|
||||
|
||||
b.Property<bool>("FlaggedForReport")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("flagged_for_report");
|
||||
|
||||
b.Property<bool>("IsNSFW")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_nsfw");
|
||||
|
||||
b.Property<bool>("ProfileDisabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("profile_disabled");
|
||||
|
||||
b.Property<string>("UserDescription")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("user_description");
|
||||
|
||||
b.HasKey("UserUID")
|
||||
.HasName("pk_user_profile_data");
|
||||
|
||||
b.ToTable("user_profile_data", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.UserProfileDataReport", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("ReportDate")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("report_date");
|
||||
|
||||
b.Property<string>("ReportReason")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("report_reason");
|
||||
|
||||
b.Property<string>("ReportedUserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("reported_user_uid");
|
||||
|
||||
b.Property<string>("ReportingUserUID")
|
||||
.HasColumnType("character varying(10)")
|
||||
.HasColumnName("reporting_user_uid");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_user_profile_data_reports");
|
||||
|
||||
b.HasIndex("ReportedUserUID")
|
||||
.HasDatabaseName("ix_user_profile_data_reports_reported_user_uid");
|
||||
|
||||
b.HasIndex("ReportingUserUID")
|
||||
.HasDatabaseName("ix_user_profile_data_reports_reporting_user_uid");
|
||||
|
||||
b.ToTable("user_profile_data_reports", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.Auth", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "PrimaryUser")
|
||||
@@ -543,6 +612,35 @@ namespace MareSynchronosServer.Migrations
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.UserProfileData", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserUID")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_user_profile_data_users_user_uid");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MareSynchronosShared.Models.UserProfileDataReport", b =>
|
||||
{
|
||||
b.HasOne("MareSynchronosShared.Models.User", "ReportedUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("ReportedUserUID")
|
||||
.HasConstraintName("fk_user_profile_data_reports_users_reported_user_uid");
|
||||
|
||||
b.HasOne("MareSynchronosShared.Models.User", "ReportingUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("ReportingUserUID")
|
||||
.HasConstraintName("fk_user_profile_data_reports_users_reporting_user_uid");
|
||||
|
||||
b.Navigation("ReportedUser");
|
||||
|
||||
b.Navigation("ReportingUser");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace MareSynchronosShared.Models;
|
||||
|
||||
public class UserProfileData
|
||||
{
|
||||
public string Base64ProfileImage { get; set; }
|
||||
public bool FlaggedForReport { get; set; }
|
||||
public bool IsNSFW { get; set; }
|
||||
public bool ProfileDisabled { get; set; }
|
||||
public User User { get; set; }
|
||||
|
||||
public string UserDescription { get; set; }
|
||||
|
||||
[Required]
|
||||
[Key]
|
||||
[ForeignKey(nameof(User))]
|
||||
public string UserUID { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace MareSynchronosShared.Models;
|
||||
|
||||
public class UserProfileDataReport
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
public DateTime ReportDate { get; set; }
|
||||
public User ReportedUser { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ReportedUser))]
|
||||
public string ReportedUserUID { get; set; }
|
||||
|
||||
public User ReportingUser { get; set; }
|
||||
|
||||
[ForeignKey(nameof(ReportingUser))]
|
||||
public string ReportingUserUID { get; set; }
|
||||
|
||||
public string ReportReason { get; set; }
|
||||
}
|
||||
@@ -6,11 +6,12 @@ namespace MareSynchronosShared.Utils;
|
||||
|
||||
public class MareConfigurationBase : IMareConfiguration
|
||||
{
|
||||
public Uri MainServerAddress { get; set; }
|
||||
public int DbContextPoolSize { get; set; } = 100;
|
||||
public string ShardName { get; set; } = string.Empty;
|
||||
public int MetricsPort { get; set; }
|
||||
public string Jwt { get; set; } = string.Empty;
|
||||
public Uri MainServerAddress { get; set; }
|
||||
public int MetricsPort { get; set; }
|
||||
public string RedisConnectionString { get; set; } = string.Empty;
|
||||
public string ShardName { get; set; } = string.Empty;
|
||||
|
||||
public T GetValue<T>(string key)
|
||||
{
|
||||
@@ -41,6 +42,7 @@ public class MareConfigurationBase : IMareConfiguration
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine(base.ToString());
|
||||
sb.AppendLine($"{nameof(MainServerAddress)} => {MainServerAddress}");
|
||||
sb.AppendLine($"{nameof(RedisConnectionString)} => {RedisConnectionString}");
|
||||
sb.AppendLine($"{nameof(ShardName)} => {ShardName}");
|
||||
sb.AppendLine($"{nameof(DbContextPoolSize)} => {DbContextPoolSize}");
|
||||
return sb.ToString();
|
||||
|
||||
@@ -4,23 +4,29 @@ namespace MareSynchronosShared.Utils;
|
||||
|
||||
public class ServerConfiguration : MareConfigurationAuthBase
|
||||
{
|
||||
public string RedisConnectionString { get; set; } = string.Empty;
|
||||
public int RedisPool { get; set; } = 50;
|
||||
[RemoteConfiguration]
|
||||
public Version ExpectedClientVersion { get; set; } = new Version(0, 0, 0);
|
||||
[RemoteConfiguration]
|
||||
public Uri CdnFullUrl { get; set; } = null;
|
||||
|
||||
[RemoteConfiguration]
|
||||
public Version ExpectedClientVersion { get; set; } = new Version(0, 0, 0);
|
||||
|
||||
[RemoteConfiguration]
|
||||
public int MaxExistingGroupsByUser { get; set; } = 3;
|
||||
[RemoteConfiguration]
|
||||
public int MaxJoinedGroupsByUser { get; set; } = 6;
|
||||
|
||||
[RemoteConfiguration]
|
||||
public int MaxGroupUserCount { get; set; } = 100;
|
||||
|
||||
[RemoteConfiguration]
|
||||
public int MaxJoinedGroupsByUser { get; set; } = 6;
|
||||
|
||||
[RemoteConfiguration]
|
||||
public bool PurgeUnusedAccounts { get; set; } = false;
|
||||
|
||||
[RemoteConfiguration]
|
||||
public int PurgeUnusedAccountsPeriodInDays { get; set; } = 14;
|
||||
|
||||
public int RedisPool { get; set; } = 50;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
|
||||
@@ -5,8 +5,9 @@ namespace MareSynchronosShared.Utils;
|
||||
public class ServicesConfiguration : MareConfigurationBase
|
||||
{
|
||||
public string DiscordBotToken { get; set; } = string.Empty;
|
||||
public Uri MainServerGrpcAddress { get; set; } = null;
|
||||
public ulong? DiscordChannelForMessages { get; set; } = null;
|
||||
public ulong? DiscordChannelForReports { get; set; } = null;
|
||||
public Uri MainServerGrpcAddress { get; set; } = null;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
@@ -14,6 +15,8 @@ public class ServicesConfiguration : MareConfigurationBase
|
||||
sb.AppendLine(base.ToString());
|
||||
sb.AppendLine($"{nameof(DiscordBotToken)} => {DiscordBotToken}");
|
||||
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
|
||||
sb.AppendLine($"{nameof(DiscordChannelForMessages)} => {DiscordChannelForMessages}");
|
||||
sb.AppendLine($"{nameof(DiscordChannelForReports)} => {DiscordChannelForReports}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@@ -7,17 +7,54 @@ namespace MareSynchronosShared.Utils;
|
||||
|
||||
public static class SharedDbFunctions
|
||||
{
|
||||
public static async Task<(bool, string)> MigrateOrDeleteGroup(MareDbContext context, Group group, List<GroupPair> groupPairs, int maxGroupsByUser)
|
||||
{
|
||||
bool groupHasMigrated = false;
|
||||
string newOwner = string.Empty;
|
||||
foreach (var potentialNewOwner in groupPairs.OrderByDescending(p => p.IsModerator).ThenByDescending(p => p.IsPinned).ToList())
|
||||
{
|
||||
groupHasMigrated = await TryMigrateGroup(context, group, potentialNewOwner.GroupUserUID, maxGroupsByUser).ConfigureAwait(false);
|
||||
|
||||
if (groupHasMigrated)
|
||||
{
|
||||
newOwner = potentialNewOwner.GroupUserUID;
|
||||
potentialNewOwner.IsPinned = true;
|
||||
potentialNewOwner.IsModerator = false;
|
||||
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!groupHasMigrated)
|
||||
{
|
||||
context.GroupPairs.RemoveRange(groupPairs);
|
||||
context.Groups.Remove(group);
|
||||
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return (groupHasMigrated, newOwner);
|
||||
}
|
||||
|
||||
public static async Task PurgeUser(ILogger _logger, User user, MareDbContext dbContext, int maxGroupsByUser)
|
||||
{
|
||||
_logger.LogInformation("Purging user: {uid}", user.UID);
|
||||
|
||||
var lodestone = dbContext.LodeStoneAuth.SingleOrDefault(a => a.User.UID == user.UID);
|
||||
|
||||
var userProfileData = await dbContext.UserProfileData.SingleOrDefaultAsync(u => u.UserUID == user.UID).ConfigureAwait(false);
|
||||
|
||||
if (lodestone != null)
|
||||
{
|
||||
dbContext.Remove(lodestone);
|
||||
}
|
||||
|
||||
if (userProfileData != null)
|
||||
{
|
||||
dbContext.Remove(userProfileData);
|
||||
}
|
||||
|
||||
var auth = dbContext.Auth.Single(a => a.UserUID == user.UID);
|
||||
|
||||
var userFiles = dbContext.Files.Where(f => f.Uploaded && f.Uploader.UID == user.UID).ToList();
|
||||
@@ -63,36 +100,6 @@ public static class SharedDbFunctions
|
||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static async Task<(bool, string)> MigrateOrDeleteGroup(MareDbContext context, Group group, List<GroupPair> groupPairs, int maxGroupsByUser)
|
||||
{
|
||||
bool groupHasMigrated = false;
|
||||
string newOwner = string.Empty;
|
||||
foreach (var potentialNewOwner in groupPairs.OrderByDescending(p => p.IsModerator).ThenByDescending(p => p.IsPinned).ToList())
|
||||
{
|
||||
groupHasMigrated = await TryMigrateGroup(context, group, potentialNewOwner.GroupUserUID, maxGroupsByUser).ConfigureAwait(false);
|
||||
|
||||
if (groupHasMigrated)
|
||||
{
|
||||
newOwner = potentialNewOwner.GroupUserUID;
|
||||
potentialNewOwner.IsPinned = true;
|
||||
potentialNewOwner.IsModerator = false;
|
||||
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!groupHasMigrated)
|
||||
{
|
||||
context.GroupPairs.RemoveRange(groupPairs);
|
||||
context.Groups.Remove(group);
|
||||
|
||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return (groupHasMigrated, newOwner);
|
||||
}
|
||||
|
||||
private static async Task<bool> TryMigrateGroup(MareDbContext context, Group group, string potentialNewOwnerUid, int maxGroupsByUser)
|
||||
{
|
||||
var newOwnerOwnedGroups = await context.Groups.CountAsync(g => g.OwnerUID == potentialNewOwnerUid).ConfigureAwait(false);
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="7.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user