Compare commits
15 Commits
1.12.0-ser
...
unbanbydis
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d758f58f8 | ||
|
|
9631f521bc | ||
|
|
f6b33425b5 | ||
|
|
42ed164c54 | ||
|
|
9cd77d2b55 | ||
|
|
332f7f7bb2 | ||
|
|
56bc277436 | ||
|
|
176f0e7e56 | ||
|
|
b700f58d88 | ||
|
|
232b3d535c | ||
|
|
b5aa817d0b | ||
|
|
7c81f880e1 | ||
|
|
4f249a2db9 | ||
|
|
3f76121e26 | ||
|
|
4feb64f015 |
Submodule LightlessAPI updated: 167508d27b...4ce70bee83
@@ -1,72 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace LightlessSyncServer.Configuration;
|
|
||||||
|
|
||||||
public class BroadcastConfiguration : IBroadcastConfiguration
|
|
||||||
{
|
|
||||||
private static readonly TimeSpan DefaultEntryTtl = TimeSpan.FromMinutes(180);
|
|
||||||
private const int DefaultMaxStatusBatchSize = 30;
|
|
||||||
private const string DefaultNotificationTemplate = "{DisplayName} sent you a pair request. To accept, right-click them, open the context menu, and send a request back.";
|
|
||||||
|
|
||||||
private readonly IOptionsMonitor<BroadcastOptions> _optionsMonitor;
|
|
||||||
|
|
||||||
public BroadcastConfiguration(IOptionsMonitor<BroadcastOptions> optionsMonitor)
|
|
||||||
{
|
|
||||||
_optionsMonitor = optionsMonitor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BroadcastOptions Options => _optionsMonitor.CurrentValue ?? new BroadcastOptions();
|
|
||||||
|
|
||||||
public string RedisKeyPrefix
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var prefix = Options.RedisKeyPrefix;
|
|
||||||
return string.IsNullOrWhiteSpace(prefix) ? "broadcast:" : prefix!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimeSpan BroadcastEntryTtl
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var seconds = Options.EntryTtlSeconds;
|
|
||||||
return seconds > 0 ? TimeSpan.FromSeconds(seconds) : DefaultEntryTtl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int MaxStatusBatchSize
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var value = Options.MaxStatusBatchSize;
|
|
||||||
return value > 0 ? value : DefaultMaxStatusBatchSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool NotifyOwnerOnPairRequest => Options.NotifyOwnerOnPairRequest;
|
|
||||||
|
|
||||||
public bool EnableBroadcasting => Options.EnableBroadcasting;
|
|
||||||
|
|
||||||
public bool EnableSyncshellBroadcastPayloads => Options.EnableSyncshellBroadcastPayloads;
|
|
||||||
|
|
||||||
public string BuildRedisKey(string hashedCid)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(hashedCid))
|
|
||||||
return RedisKeyPrefix;
|
|
||||||
|
|
||||||
return string.Concat(RedisKeyPrefix, hashedCid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string BuildPairRequestNotification()
|
|
||||||
{
|
|
||||||
var template = Options.PairRequestNotificationTemplate;
|
|
||||||
if (string.IsNullOrWhiteSpace(template))
|
|
||||||
{
|
|
||||||
template = DefaultNotificationTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace LightlessSyncServer.Configuration;
|
|
||||||
|
|
||||||
public class BroadcastOptions
|
|
||||||
{
|
|
||||||
[Required]
|
|
||||||
public string RedisKeyPrefix { get; set; } = "broadcast:";
|
|
||||||
|
|
||||||
[Range(1, int.MaxValue)]
|
|
||||||
public int EntryTtlSeconds { get; set; } = 10800;
|
|
||||||
|
|
||||||
[Range(1, int.MaxValue)]
|
|
||||||
public int MaxStatusBatchSize { get; set; } = 30;
|
|
||||||
|
|
||||||
public bool NotifyOwnerOnPairRequest { get; set; } = true;
|
|
||||||
|
|
||||||
public bool EnableBroadcasting { get; set; } = true;
|
|
||||||
|
|
||||||
public bool EnableSyncshellBroadcastPayloads { get; set; } = true;
|
|
||||||
|
|
||||||
public string PairRequestNotificationTemplate { get; set; } = "{DisplayName} sent you a pair request. To accept, right-click them, open the context menu, and send a request back.";
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace LightlessSyncServer.Configuration;
|
|
||||||
|
|
||||||
public interface IBroadcastConfiguration
|
|
||||||
{
|
|
||||||
string RedisKeyPrefix { get; }
|
|
||||||
TimeSpan BroadcastEntryTtl { get; }
|
|
||||||
int MaxStatusBatchSize { get; }
|
|
||||||
bool NotifyOwnerOnPairRequest { get; }
|
|
||||||
bool EnableBroadcasting { get; }
|
|
||||||
bool EnableSyncshellBroadcastPayloads { get; }
|
|
||||||
|
|
||||||
string BuildRedisKey(string hashedCid);
|
|
||||||
string BuildPairRequestNotification();
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -10,25 +10,41 @@ namespace LightlessSyncServer.Hubs
|
|||||||
public partial class LightlessHub
|
public partial class LightlessHub
|
||||||
{
|
{
|
||||||
public Task Client_DownloadReady(Guid requestId) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_DownloadReady(Guid requestId) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_GroupChangePermissions(GroupPermissionDto groupPermission) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_GroupChangePermissions(GroupPermissionDto groupPermission) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_GroupDelete(GroupDto groupDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_GroupDelete(GroupDto groupDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_GroupPairChangeUserInfo(GroupPairUserInfoDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_GroupPairChangeUserInfo(GroupPairUserInfoDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_GroupPairJoined(GroupPairFullInfoDto groupPairInfoDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_GroupPairJoined(GroupPairFullInfoDto groupPairInfoDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_GroupPairLeft(GroupPairDto groupPairDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_GroupPairLeft(GroupPairDto groupPairDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_GroupSendFullInfo(GroupFullInfoDto groupInfo) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_GroupSendFullInfo(GroupFullInfoDto groupInfo) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
public Task Client_GroupSendProfile(GroupProfileDto groupProfile) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
|
||||||
public Task Client_GroupSendInfo(GroupInfoDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_GroupSendInfo(GroupInfoDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_ReceiveServerMessage(MessageSeverity messageSeverity, string message) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_ReceiveServerMessage(MessageSeverity messageSeverity, string message) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
public Task Client_ReceiveBroadcastPairRequest(UserPairNotificationDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
|
||||||
public Task Client_UpdateSystemInfo(SystemInfoDto systemInfo) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_UpdateSystemInfo(SystemInfoDto systemInfo) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_UserAddClientPair(UserPairDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_UserAddClientPair(UserPairDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_UserReceiveCharacterData(OnlineUserCharaDataDto dataDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_UserReceiveCharacterData(OnlineUserCharaDataDto dataDto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_UserReceiveUploadStatus(UserDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_UserReceiveUploadStatus(UserDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_UserRemoveClientPair(UserDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_UserRemoveClientPair(UserDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_UserSendOffline(UserDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_UserSendOffline(UserDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_UserSendOnline(OnlineUserIdentDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_UserSendOnline(OnlineUserIdentDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
|
|
||||||
public Task Client_UserUpdateOtherPairPermissions(UserPermissionsDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
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_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");
|
public Task Client_UserUpdateSelfPairPermissions(UserPermissionsDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
public Task Client_UserUpdateDefaultPermissions(DefaultPermissionsDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_UserUpdateDefaultPermissions(DefaultPermissionsDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
public Task Client_UpdateUserIndividualPairStatusDto(UserIndividualPairStatusDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
public Task Client_UpdateUserIndividualPairStatusDto(UserIndividualPairStatusDto dto) => throw new PlatformNotSupportedException("Calling clientside method on server not supported");
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using LightlessSyncShared.Models;
|
using LightlessSyncShared.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using LightlessSyncServer.Utils;
|
using LightlessSyncServer.Utils;
|
||||||
using LightlessSyncShared.Utils;
|
using LightlessSyncShared.Utils;
|
||||||
@@ -6,7 +6,6 @@ using LightlessSync.API.Data;
|
|||||||
using LightlessSync.API.Dto.Group;
|
using LightlessSync.API.Dto.Group;
|
||||||
using LightlessSyncShared.Metrics;
|
using LightlessSyncShared.Metrics;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace LightlessSyncServer.Hubs;
|
namespace LightlessSyncServer.Hubs;
|
||||||
|
|
||||||
@@ -98,28 +97,6 @@ public partial class LightlessHub
|
|||||||
await _redis.RemoveAsync("UID:" + UserUID, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false);
|
await _redis.RemoveAsync("UID:" + UserUID, StackExchange.Redis.CommandFlags.FireAndForget).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<User?> EnsureUserHasVanity(string uid, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
cancellationToken = cancellationToken == default && _contextAccessor.HttpContext != null
|
|
||||||
? _contextAccessor.HttpContext.RequestAborted
|
|
||||||
: cancellationToken;
|
|
||||||
|
|
||||||
var user = await DbContext.Users.SingleOrDefaultAsync(u => u.UID == uid, cancellationToken).ConfigureAwait(false);
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
_logger.LogCallWarning(LightlessHubLogger.Args("vanity check", uid, "missing user"));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user.HasVanity)
|
|
||||||
{
|
|
||||||
_logger.LogCallWarning(LightlessHubLogger.Args("vanity check", uid, "no vanity"));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendGroupDeletedToAll(List<GroupPair> groupUsers)
|
private async Task SendGroupDeletedToAll(List<GroupPair> groupUsers)
|
||||||
{
|
{
|
||||||
foreach (var pair in groupUsers)
|
foreach (var pair in groupUsers)
|
||||||
@@ -346,12 +323,7 @@ public partial class LightlessHub
|
|||||||
GID = user.Gid,
|
GID = user.Gid,
|
||||||
Synced = user.Synced,
|
Synced = user.Synced,
|
||||||
OwnPermissions = ownperm,
|
OwnPermissions = ownperm,
|
||||||
OtherPermissions = otherperm,
|
OtherPermissions = otherperm
|
||||||
OtherUserIsAdmin = u.IsAdmin,
|
|
||||||
OtherUserIsModerator = u.IsModerator,
|
|
||||||
OtherUserHasVanity = u.HasVanity,
|
|
||||||
OtherUserTextColorHex = u.TextColorHex,
|
|
||||||
OtherUserTextGlowColorHex = u.TextGlowColorHex
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var resultList = await result.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
var resultList = await result.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||||
@@ -359,18 +331,12 @@ public partial class LightlessHub
|
|||||||
if (!resultList.Any()) return null;
|
if (!resultList.Any()) return null;
|
||||||
|
|
||||||
var groups = resultList.Select(g => g.GID).ToList();
|
var groups = resultList.Select(g => g.GID).ToList();
|
||||||
return new UserInfo(
|
return new UserInfo(resultList[0].OtherUserAlias,
|
||||||
resultList[0].OtherUserAlias,
|
|
||||||
resultList.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
resultList.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
||||||
resultList.Max(p => p.Synced),
|
resultList.Max(p => p.Synced),
|
||||||
resultList.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
resultList.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
||||||
resultList[0].OwnPermissions,
|
resultList[0].OwnPermissions,
|
||||||
resultList[0].OtherPermissions,
|
resultList[0].OtherPermissions);
|
||||||
resultList[0].OtherUserIsAdmin,
|
|
||||||
resultList[0].OtherUserIsModerator,
|
|
||||||
resultList[0].OtherUserHasVanity,
|
|
||||||
resultList[0].OtherUserTextColorHex ?? string.Empty,
|
|
||||||
resultList[0].OtherUserTextGlowColorHex ?? string.Empty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, UserInfo>> GetAllPairInfo(string uid)
|
private async Task<Dictionary<string, UserInfo>> GetAllPairInfo(string uid)
|
||||||
@@ -442,29 +408,18 @@ public partial class LightlessHub
|
|||||||
GID = user.Gid,
|
GID = user.Gid,
|
||||||
Synced = user.Synced,
|
Synced = user.Synced,
|
||||||
OwnPermissions = ownperm,
|
OwnPermissions = ownperm,
|
||||||
OtherPermissions = otherperm,
|
OtherPermissions = otherperm
|
||||||
OtherUserIsAdmin = u.IsAdmin,
|
|
||||||
OtherUserIsModerator = u.IsModerator,
|
|
||||||
OtherUserHasVanity = u.HasVanity,
|
|
||||||
OtherUserTextColorHex = u.TextColorHex,
|
|
||||||
OtherUserTextGlowColorHex = u.TextGlowColorHex
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var resultList = await result.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
var resultList = await result.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||||
return resultList.GroupBy(g => g.OtherUserUID, StringComparer.Ordinal).ToDictionary(g => g.Key, g =>
|
return resultList.GroupBy(g => g.OtherUserUID, StringComparer.Ordinal).ToDictionary(g => g.Key, g =>
|
||||||
{
|
{
|
||||||
return new UserInfo(
|
return new UserInfo(g.First().OtherUserAlias,
|
||||||
g.First().OtherUserAlias,
|
|
||||||
g.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
g.SingleOrDefault(p => string.IsNullOrEmpty(p.GID))?.Synced ?? false,
|
||||||
g.Max(p => p.Synced),
|
g.Max(p => p.Synced),
|
||||||
g.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
g.Select(p => string.IsNullOrEmpty(p.GID) ? Constants.IndividualKeyword : p.GID).ToList(),
|
||||||
g.First().OwnPermissions,
|
g.First().OwnPermissions,
|
||||||
g.First().OtherPermissions,
|
g.First().OtherPermissions);
|
||||||
g.First().OtherUserIsAdmin,
|
|
||||||
g.First().OtherUserIsModerator,
|
|
||||||
g.First().OtherUserHasVanity,
|
|
||||||
g.First().OtherUserTextColorHex ?? string.Empty,
|
|
||||||
g.First().OtherUserTextGlowColorHex ?? string.Empty);
|
|
||||||
}, StringComparer.Ordinal);
|
}, StringComparer.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,17 +484,5 @@ public partial class LightlessHub
|
|||||||
return await result.Distinct().AsNoTracking().ToListAsync().ConfigureAwait(false);
|
return await result.Distinct().AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record UserInfo(
|
public record UserInfo(string Alias, bool IndividuallyPaired, bool IsSynced, List<string> GIDs, UserPermissionSet? OwnPermissions, UserPermissionSet? OtherPermissions);
|
||||||
string Alias,
|
|
||||||
bool IndividuallyPaired,
|
|
||||||
bool IsSynced,
|
|
||||||
List<string> GIDs,
|
|
||||||
UserPermissionSet? OwnPermissions,
|
|
||||||
UserPermissionSet? OtherPermissions,
|
|
||||||
bool IsAdmin,
|
|
||||||
bool IsModerator,
|
|
||||||
bool HasVanity,
|
|
||||||
string? TextColorHex,
|
|
||||||
string? TextGlowColorHex
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data.Enum;
|
||||||
using LightlessSync.API.Data.Enum;
|
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
using LightlessSync.API.Dto.Group;
|
using LightlessSync.API.Dto.Group;
|
||||||
using LightlessSync.API.Dto.User;
|
|
||||||
using LightlessSyncServer.Utils;
|
using LightlessSyncServer.Utils;
|
||||||
using LightlessSyncShared.Models;
|
using LightlessSyncShared.Models;
|
||||||
using LightlessSyncShared.Utils;
|
using LightlessSyncShared.Utils;
|
||||||
@@ -59,7 +57,7 @@ public partial class LightlessHub
|
|||||||
group.PreferDisableAnimations = dto.Permissions.HasFlag(GroupPermissions.PreferDisableAnimations);
|
group.PreferDisableAnimations = dto.Permissions.HasFlag(GroupPermissions.PreferDisableAnimations);
|
||||||
group.PreferDisableVFX = dto.Permissions.HasFlag(GroupPermissions.PreferDisableVFX);
|
group.PreferDisableVFX = dto.Permissions.HasFlag(GroupPermissions.PreferDisableVFX);
|
||||||
|
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var groupPairs = DbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).ToList();
|
var groupPairs = DbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).ToList();
|
||||||
await Clients.Users(groupPairs).Client_GroupChangePermissions(new GroupPermissionDto(dto.Group, dto.Permissions)).ConfigureAwait(false);
|
await Clients.Users(groupPairs).Client_GroupChangePermissions(new GroupPermissionDto(dto.Group, dto.Permissions)).ConfigureAwait(false);
|
||||||
@@ -137,7 +135,7 @@ public partial class LightlessHub
|
|||||||
|
|
||||||
var allUserPairs = await GetAllPairInfo(pair.GroupUserUID).ConfigureAwait(false);
|
var allUserPairs = await GetAllPairInfo(pair.GroupUserUID).ConfigureAwait(false);
|
||||||
|
|
||||||
var sharedData = await DbContext.CharaDataAllowances.Where(u => u.AllowedGroup != null && u.AllowedGroupGID == dto.GID && u.ParentUploaderUID == pair.GroupUserUID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var sharedData = await DbContext.CharaDataAllowances.Where(u => u.AllowedGroup != null && u.AllowedGroupGID == dto.GID && u.ParentUploaderUID == pair.GroupUserUID).ToListAsync().ConfigureAwait(false);
|
||||||
DbContext.CharaDataAllowances.RemoveRange(sharedData);
|
DbContext.CharaDataAllowances.RemoveRange(sharedData);
|
||||||
|
|
||||||
foreach (var groupUserPair in groupPairs.Where(p => !string.Equals(p.GroupUserUID, pair.GroupUserUID, StringComparison.Ordinal)))
|
foreach (var groupUserPair in groupPairs.Where(p => !string.Equals(p.GroupUserUID, pair.GroupUserUID, StringComparison.Ordinal)))
|
||||||
@@ -149,76 +147,29 @@ public partial class LightlessHub
|
|||||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
|
||||||
public async Task GroupClearFinder(GroupDto dto)
|
|
||||||
{
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
|
||||||
|
|
||||||
var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
|
||||||
if (!hasRights) return;
|
|
||||||
|
|
||||||
var groupPairs = await DbContext.GroupPairs.Include(p => p.GroupUser).Where(p => p.GroupGID == dto.Group.GID).ToListAsync().ConfigureAwait(false);
|
|
||||||
var finder_only = groupPairs.Where(g => g.FromFinder && !g.IsPinned && !g.IsModerator).ToList();
|
|
||||||
|
|
||||||
if (finder_only.Count == 0)
|
|
||||||
{
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "No Users To Clear"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Clients.Users(finder_only.Select(g => g.GroupUserUID)).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false);
|
|
||||||
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Cleared Finder users ", finder_only.Count));
|
|
||||||
|
|
||||||
DbContext.GroupPairs.RemoveRange(finder_only);
|
|
||||||
|
|
||||||
foreach (var pair in finder_only)
|
|
||||||
{
|
|
||||||
await Clients.Users(groupPairs.Where(p => p.IsPinned || p.IsModerator).Select(g => g.GroupUserUID)).Client_GroupPairLeft(new GroupPairDto(dto.Group, pair.GroupUser.ToUserData())).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var pairIdent = await GetUserIdent(pair.GroupUserUID).ConfigureAwait(false);
|
|
||||||
if (string.IsNullOrEmpty(pairIdent)) continue;
|
|
||||||
|
|
||||||
var allUserPairs = await GetAllPairInfo(pair.GroupUserUID).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var sharedData = await DbContext.CharaDataAllowances.Where(u => u.AllowedGroup != null && u.AllowedGroupGID == dto.GID && u.ParentUploaderUID == pair.GroupUserUID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
|
||||||
|
|
||||||
DbContext.CharaDataAllowances.RemoveRange(sharedData);
|
|
||||||
|
|
||||||
foreach (var groupUserPair in groupPairs.Where(p => !string.Equals(p.GroupUserUID, pair.GroupUserUID, StringComparison.Ordinal)))
|
|
||||||
{
|
|
||||||
await UserGroupLeave(pair, pairIdent, allUserPairs, pair.GroupUserUID).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
[Authorize(Policy = "Identified")]
|
||||||
public async Task<GroupJoinDto> GroupCreate()
|
public async Task<GroupJoinDto> GroupCreate()
|
||||||
{
|
{
|
||||||
_logger.LogCallInfo();
|
_logger.LogCallInfo();
|
||||||
var existingGroupsByUser = await DbContext.Groups.CountAsync(u => u.OwnerUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var existingGroupsByUser = await DbContext.Groups.CountAsync(u => u.OwnerUID == UserUID).ConfigureAwait(false);
|
||||||
var existingJoinedGroups = await DbContext.GroupPairs.CountAsync(u => u.GroupUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var existingJoinedGroups = await DbContext.GroupPairs.CountAsync(u => u.GroupUserUID == UserUID).ConfigureAwait(false);
|
||||||
if (existingGroupsByUser >= _maxExistingGroupsByUser || existingJoinedGroups >= _maxJoinedGroupsByUser)
|
if (existingGroupsByUser >= _maxExistingGroupsByUser || existingJoinedGroups >= _maxJoinedGroupsByUser)
|
||||||
{
|
{
|
||||||
throw new System.Exception($"Max groups for user is {_maxExistingGroupsByUser}, max joined groups is {_maxJoinedGroupsByUser}.");
|
throw new System.Exception($"Max groups for user is {_maxExistingGroupsByUser}, max joined groups is {_maxJoinedGroupsByUser}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var gid = StringUtils.GenerateRandomString(12);
|
var gid = StringUtils.GenerateRandomString(12);
|
||||||
while (await DbContext.Groups.AnyAsync(g => g.GID == "LLS-" + gid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false))
|
while (await DbContext.Groups.AnyAsync(g => g.GID == "MSS-" + gid).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
gid = StringUtils.GenerateRandomString(12);
|
gid = StringUtils.GenerateRandomString(12);
|
||||||
}
|
}
|
||||||
gid = "LLS-" + gid;
|
gid = "MSS-" + gid;
|
||||||
|
|
||||||
var passwd = StringUtils.GenerateRandomString(16);
|
var passwd = StringUtils.GenerateRandomString(16);
|
||||||
using var sha = SHA256.Create();
|
using var sha = SHA256.Create();
|
||||||
var hashedPw = StringUtils.Sha256String(passwd);
|
var hashedPw = StringUtils.Sha256String(passwd);
|
||||||
var currentTime = DateTime.UtcNow;
|
|
||||||
|
|
||||||
UserDefaultPreferredPermission defaultPermissions = await DbContext.UserDefaultPreferredPermissions.SingleAsync(u => u.UserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
UserDefaultPreferredPermission defaultPermissions = await DbContext.UserDefaultPreferredPermissions.SingleAsync(u => u.UserUID == UserUID).ConfigureAwait(false);
|
||||||
|
|
||||||
Group newGroup = new()
|
Group newGroup = new()
|
||||||
{
|
{
|
||||||
@@ -228,8 +179,7 @@ public partial class LightlessHub
|
|||||||
OwnerUID = UserUID,
|
OwnerUID = UserUID,
|
||||||
PreferDisableAnimations = defaultPermissions.DisableGroupAnimations,
|
PreferDisableAnimations = defaultPermissions.DisableGroupAnimations,
|
||||||
PreferDisableSounds = defaultPermissions.DisableGroupSounds,
|
PreferDisableSounds = defaultPermissions.DisableGroupSounds,
|
||||||
PreferDisableVFX = defaultPermissions.DisableGroupVFX,
|
PreferDisableVFX = defaultPermissions.DisableGroupVFX
|
||||||
CreatedDate = currentTime,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
GroupPair initialPair = new()
|
GroupPair initialPair = new()
|
||||||
@@ -237,8 +187,6 @@ public partial class LightlessHub
|
|||||||
GroupGID = newGroup.GID,
|
GroupGID = newGroup.GID,
|
||||||
GroupUserUID = UserUID,
|
GroupUserUID = UserUID,
|
||||||
IsPinned = true,
|
IsPinned = true,
|
||||||
JoinedGroupOn = currentTime,
|
|
||||||
FromFinder = false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
GroupPairPreferredPermission initialPrefPermissions = new()
|
GroupPairPreferredPermission initialPrefPermissions = new()
|
||||||
@@ -247,20 +195,20 @@ public partial class LightlessHub
|
|||||||
GroupGID = newGroup.GID,
|
GroupGID = newGroup.GID,
|
||||||
DisableSounds = defaultPermissions.DisableGroupSounds,
|
DisableSounds = defaultPermissions.DisableGroupSounds,
|
||||||
DisableAnimations = defaultPermissions.DisableGroupAnimations,
|
DisableAnimations = defaultPermissions.DisableGroupAnimations,
|
||||||
DisableVFX = defaultPermissions.DisableGroupAnimations,
|
DisableVFX = defaultPermissions.DisableGroupAnimations
|
||||||
};
|
};
|
||||||
|
|
||||||
await DbContext.Groups.AddAsync(newGroup, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.Groups.AddAsync(newGroup).ConfigureAwait(false);
|
||||||
await DbContext.GroupPairs.AddAsync(initialPair, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.GroupPairs.AddAsync(initialPair).ConfigureAwait(false);
|
||||||
await DbContext.GroupPairPreferredPermissions.AddAsync(initialPrefPermissions, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.GroupPairPreferredPermissions.AddAsync(initialPrefPermissions).ConfigureAwait(false);
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var self = await DbContext.Users.SingleAsync(u => u.UID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var self = await DbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
|
||||||
|
|
||||||
await Clients.User(UserUID).Client_GroupSendFullInfo(new GroupFullInfoDto(newGroup.ToGroupData(), self.ToUserData(),
|
await Clients.User(UserUID).Client_GroupSendFullInfo(new GroupFullInfoDto(newGroup.ToGroupData(), self.ToUserData(),
|
||||||
newGroup.ToEnum(), initialPrefPermissions.ToEnum(), initialPair.ToEnum(), new(StringComparer.Ordinal)))
|
newGroup.ToEnum(), initialPrefPermissions.ToEnum(), initialPair.ToEnum(), new(StringComparer.Ordinal)))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(gid));
|
_logger.LogCallInfo(LightlessHubLogger.Args(gid));
|
||||||
|
|
||||||
return new GroupJoinDto(newGroup.ToGroupData(), passwd, initialPrefPermissions.ToEnum());
|
return new GroupJoinDto(newGroup.ToGroupData(), passwd, initialPrefPermissions.ToEnum());
|
||||||
@@ -314,10 +262,10 @@ public partial class LightlessHub
|
|||||||
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||||
|
|
||||||
var groupPairs = await DbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var groupPairs = await DbContext.GroupPairs.Where(p => p.GroupGID == dto.Group.GID).ToListAsync().ConfigureAwait(false);
|
||||||
DbContext.RemoveRange(groupPairs);
|
DbContext.RemoveRange(groupPairs);
|
||||||
DbContext.Remove(group);
|
DbContext.Remove(group);
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
await Clients.Users(groupPairs.Select(g => g.GroupUserUID)).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false);
|
await Clients.Users(groupPairs.Select(g => g.GroupUserUID)).Client_GroupDelete(new GroupDto(group.ToGroupData())).ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -330,9 +278,9 @@ public partial class LightlessHub
|
|||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||||
|
|
||||||
var (userHasRights, group) = await TryValidateGroupModeratorOrOwner(dto.GID).ConfigureAwait(false);
|
var (userHasRights, group) = await TryValidateGroupModeratorOrOwner(dto.GID).ConfigureAwait(false);
|
||||||
if (!userHasRights) return [];
|
if (!userHasRights) return new List<BannedGroupUserDto>();
|
||||||
|
|
||||||
var banEntries = await DbContext.GroupBans.Include(b => b.BannedUser).Where(g => g.GroupGID == dto.Group.GID).AsNoTracking().ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var banEntries = await DbContext.GroupBans.Include(b => b.BannedUser).Where(g => g.GroupGID == dto.Group.GID).AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
List<BannedGroupUserDto> bannedGroupUsers = banEntries.Select(b =>
|
List<BannedGroupUserDto> bannedGroupUsers = banEntries.Select(b =>
|
||||||
new BannedGroupUserDto(group.ToGroupData(), b.BannedUser.ToUserData(), b.BannedReason, b.BannedOn,
|
new BannedGroupUserDto(group.ToGroupData(), b.BannedUser.ToUserData(), b.BannedReason, b.BannedOn,
|
||||||
@@ -350,14 +298,14 @@ public partial class LightlessHub
|
|||||||
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||||
|
|
||||||
var group = await DbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == aliasOrGid || g.Alias == aliasOrGid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var group = await DbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == aliasOrGid || g.Alias == aliasOrGid).ConfigureAwait(false);
|
||||||
var groupGid = group?.GID ?? string.Empty;
|
var groupGid = group?.GID ?? string.Empty;
|
||||||
var existingPair = await DbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.GroupUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var existingPair = await DbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.GroupUserUID == UserUID).ConfigureAwait(false);
|
||||||
var hashedPw = StringUtils.Sha256String(dto.Password);
|
var hashedPw = StringUtils.Sha256String(dto.Password);
|
||||||
var existingUserCount = await DbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == groupGid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var existingUserCount = await DbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == groupGid).ConfigureAwait(false);
|
||||||
var joinedGroups = await DbContext.GroupPairs.CountAsync(g => g.GroupUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var joinedGroups = await DbContext.GroupPairs.CountAsync(g => g.GroupUserUID == UserUID).ConfigureAwait(false);
|
||||||
var isBanned = await DbContext.GroupBans.AnyAsync(g => g.GroupGID == groupGid && g.BannedUserUID == UserUID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var isBanned = await DbContext.GroupBans.AnyAsync(g => g.GroupGID == groupGid && g.BannedUserUID == UserUID).ConfigureAwait(false);
|
||||||
var oneTimeInvite = await DbContext.GroupTempInvites.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.Invite == hashedPw, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var oneTimeInvite = await DbContext.GroupTempInvites.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.Invite == hashedPw).ConfigureAwait(false);
|
||||||
|
|
||||||
if (group == null
|
if (group == null
|
||||||
|| (!string.Equals(group.HashedPassword, hashedPw, StringComparison.Ordinal) && oneTimeInvite == null)
|
|| (!string.Equals(group.HashedPassword, hashedPw, StringComparison.Ordinal) && oneTimeInvite == null)
|
||||||
@@ -378,13 +326,10 @@ public partial class LightlessHub
|
|||||||
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||||
|
|
||||||
var group = await DbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == aliasOrGid || g.Alias == aliasOrGid, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var group = await DbContext.Groups.Include(g => g.Owner).AsNoTracking().SingleOrDefaultAsync(g => g.GID == aliasOrGid || g.Alias == aliasOrGid).ConfigureAwait(false);
|
||||||
var groupGid = group?.GID ?? string.Empty;
|
var groupGid = group?.GID ?? string.Empty;
|
||||||
var existingPair = await DbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.GroupUserUID == UserUID).ConfigureAwait(false);
|
var existingPair = await DbContext.GroupPairs.AsNoTracking().SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.GroupUserUID == UserUID).ConfigureAwait(false);
|
||||||
var isHashedPassword = dto.Password.Length == 64 && dto.Password.All(Uri.IsHexDigit);
|
var hashedPw = StringUtils.Sha256String(dto.Password);
|
||||||
var hashedPw = isHashedPassword
|
|
||||||
? dto.Password
|
|
||||||
: StringUtils.Sha256String(dto.Password);
|
|
||||||
var existingUserCount = await DbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == groupGid).ConfigureAwait(false);
|
var existingUserCount = await DbContext.GroupPairs.AsNoTracking().CountAsync(g => g.GroupGID == groupGid).ConfigureAwait(false);
|
||||||
var joinedGroups = await DbContext.GroupPairs.CountAsync(g => g.GroupUserUID == UserUID).ConfigureAwait(false);
|
var joinedGroups = await DbContext.GroupPairs.CountAsync(g => g.GroupUserUID == UserUID).ConfigureAwait(false);
|
||||||
var isBanned = await DbContext.GroupBans.AnyAsync(g => g.GroupGID == groupGid && g.BannedUserUID == UserUID).ConfigureAwait(false);
|
var isBanned = await DbContext.GroupBans.AnyAsync(g => g.GroupGID == groupGid && g.BannedUserUID == UserUID).ConfigureAwait(false);
|
||||||
@@ -412,11 +357,9 @@ public partial class LightlessHub
|
|||||||
{
|
{
|
||||||
GroupGID = group.GID,
|
GroupGID = group.GID,
|
||||||
GroupUserUID = UserUID,
|
GroupUserUID = UserUID,
|
||||||
JoinedGroupOn = DateTime.UtcNow,
|
|
||||||
FromFinder = isHashedPassword
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var preferredPermissions = await DbContext.GroupPairPreferredPermissions.SingleOrDefaultAsync(u => u.UserUID == UserUID && u.GroupGID == group.GID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var preferredPermissions = await DbContext.GroupPairPreferredPermissions.SingleOrDefaultAsync(u => u.UserUID == UserUID && u.GroupGID == group.GID).ConfigureAwait(false);
|
||||||
if (preferredPermissions == null)
|
if (preferredPermissions == null)
|
||||||
{
|
{
|
||||||
GroupPairPreferredPermission newPerms = new()
|
GroupPairPreferredPermission newPerms = new()
|
||||||
@@ -426,7 +369,7 @@ public partial class LightlessHub
|
|||||||
DisableSounds = dto.GroupUserPreferredPermissions.IsDisableSounds(),
|
DisableSounds = dto.GroupUserPreferredPermissions.IsDisableSounds(),
|
||||||
DisableVFX = dto.GroupUserPreferredPermissions.IsDisableVFX(),
|
DisableVFX = dto.GroupUserPreferredPermissions.IsDisableVFX(),
|
||||||
DisableAnimations = dto.GroupUserPreferredPermissions.IsDisableAnimations(),
|
DisableAnimations = dto.GroupUserPreferredPermissions.IsDisableAnimations(),
|
||||||
IsPaused = false,
|
IsPaused = false
|
||||||
};
|
};
|
||||||
|
|
||||||
DbContext.Add(newPerms);
|
DbContext.Add(newPerms);
|
||||||
@@ -441,13 +384,13 @@ public partial class LightlessHub
|
|||||||
DbContext.Update(preferredPermissions);
|
DbContext.Update(preferredPermissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
await DbContext.GroupPairs.AddAsync(newPair, _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.GroupPairs.AddAsync(newPair).ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(aliasOrGid, "Success"));
|
_logger.LogCallInfo(LightlessHubLogger.Args(aliasOrGid, "Success"));
|
||||||
|
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var groupInfos = await DbContext.GroupPairs.Where(u => u.GroupGID == group.GID && (u.IsPinned || u.IsModerator)).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var groupInfos = await DbContext.GroupPairs.Where(u => u.GroupGID == group.GID && (u.IsPinned || u.IsModerator)).ToListAsync().ConfigureAwait(false);
|
||||||
await Clients.User(UserUID).Client_GroupSendFullInfo(new GroupFullInfoDto(group.ToGroupData(), group.Owner.ToUserData(),
|
await Clients.User(UserUID).Client_GroupSendFullInfo(new GroupFullInfoDto(group.ToGroupData(), group.Owner.ToUserData(),
|
||||||
group.ToEnum(), preferredPermissions.ToEnum(), newPair.ToEnum(),
|
group.ToEnum(), preferredPermissions.ToEnum(), newPair.ToEnum(),
|
||||||
groupInfos.ToDictionary(u => u.GroupUserUID, u => u.ToEnum(), StringComparer.Ordinal))).ConfigureAwait(false);
|
groupInfos.ToDictionary(u => u.GroupUserUID, u => u.ToEnum(), StringComparer.Ordinal))).ConfigureAwait(false);
|
||||||
@@ -575,92 +518,11 @@ public partial class LightlessHub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
|
||||||
public async Task<GroupJoinInfoDto> GroupJoinHashed(GroupJoinHashedDto dto)
|
|
||||||
{
|
|
||||||
var aliasOrGid = dto.Group.GID.Trim();
|
|
||||||
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
|
||||||
|
|
||||||
var group = await DbContext.Groups.Include(g => g.Owner)
|
|
||||||
.AsNoTracking()
|
|
||||||
.SingleOrDefaultAsync(g => g.GID == aliasOrGid || g.Alias == aliasOrGid)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
var groupGid = group?.GID ?? string.Empty;
|
|
||||||
|
|
||||||
var existingPair = await DbContext.GroupPairs
|
|
||||||
.AsNoTracking()
|
|
||||||
.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.GroupUserUID == UserUID)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
var isBanned = await DbContext.GroupBans
|
|
||||||
.AnyAsync(g => g.GroupGID == groupGid && g.BannedUserUID == UserUID)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
var oneTimeInvite = await DbContext.GroupTempInvites
|
|
||||||
.SingleOrDefaultAsync(g => g.GroupGID == groupGid && g.Invite == dto.HashedPassword)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
var existingUserCount = await DbContext.GroupPairs
|
|
||||||
.AsNoTracking()
|
|
||||||
.CountAsync(g => g.GroupGID == groupGid)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
var joinedGroups = await DbContext.GroupPairs
|
|
||||||
.CountAsync(g => g.GroupUserUID == UserUID)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (group == null)
|
|
||||||
{
|
|
||||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "Syncshell not found.");
|
|
||||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.Equals(group.HashedPassword, dto.HashedPassword, StringComparison.Ordinal) && oneTimeInvite == null)
|
|
||||||
{
|
|
||||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "Incorrect or expired password.");
|
|
||||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingPair != null)
|
|
||||||
{
|
|
||||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "You are already a member of this syncshell.");
|
|
||||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingUserCount >= _maxGroupUserCount)
|
|
||||||
{
|
|
||||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "This syncshell is full.");
|
|
||||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!group.InvitesEnabled)
|
|
||||||
{
|
|
||||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "Invites to this syncshell are currently disabled.");
|
|
||||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (joinedGroups >= _maxJoinedGroupsByUser)
|
|
||||||
{
|
|
||||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "You have reached the maximum number of syncshells you can join.");
|
|
||||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBanned)
|
|
||||||
{
|
|
||||||
await Clients.User(UserUID).Client_ReceiveServerMessage(MessageSeverity.Warning, "You are banned from this syncshell.");
|
|
||||||
return new GroupJoinInfoDto(null, null, GroupPermissions.NoneSet, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new GroupJoinInfoDto(group.ToGroupData(), group.Owner.ToUserData(), group.ToEnum(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
[Authorize(Policy = "Identified")]
|
||||||
public async Task GroupLeave(GroupDto dto)
|
public async Task GroupLeave(GroupDto dto)
|
||||||
{
|
{
|
||||||
@@ -679,8 +541,8 @@ public partial class LightlessHub
|
|||||||
.Where(g => g.GroupGID == dto.Group.GID)
|
.Where(g => g.GroupGID == dto.Group.GID)
|
||||||
.ToListAsync().ConfigureAwait(false);
|
.ToListAsync().ConfigureAwait(false);
|
||||||
var usersToPrune = allGroupUsers.Where(p => !p.IsPinned && !p.IsModerator
|
var usersToPrune = allGroupUsers.Where(p => !p.IsPinned && !p.IsModerator
|
||||||
&& !string.Equals(p.GroupUserUID, UserUID, StringComparison.Ordinal)
|
&& p.GroupUserUID != UserUID
|
||||||
&& !string.Equals(p.Group.OwnerUID, p.GroupUserUID, StringComparison.Ordinal)
|
&& p.Group.OwnerUID != p.GroupUserUID
|
||||||
&& p.GroupUser.LastLoggedIn.AddDays(days) < DateTime.UtcNow);
|
&& p.GroupUser.LastLoggedIn.AddDays(days) < DateTime.UtcNow);
|
||||||
|
|
||||||
if (!execute) return usersToPrune.Count();
|
if (!execute) return usersToPrune.Count();
|
||||||
@@ -693,7 +555,7 @@ public partial class LightlessHub
|
|||||||
.Client_GroupPairLeft(new GroupPairDto(dto.Group, pair.GroupUser.ToUserData())).ConfigureAwait(false);
|
.Client_GroupPairLeft(new GroupPairDto(dto.Group, pair.GroupUser.ToUserData())).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
return usersToPrune.Count();
|
return usersToPrune.Count();
|
||||||
}
|
}
|
||||||
@@ -717,15 +579,15 @@ public partial class LightlessHub
|
|||||||
var groupPairs = DbContext.GroupPairs.Where(p => p.GroupGID == group.GID).AsNoTracking().ToList();
|
var groupPairs = DbContext.GroupPairs.Where(p => p.GroupGID == group.GID).AsNoTracking().ToList();
|
||||||
await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupPairLeft(dto).ConfigureAwait(false);
|
await Clients.Users(groupPairs.Select(p => p.GroupUserUID)).Client_GroupPairLeft(dto).ConfigureAwait(false);
|
||||||
|
|
||||||
var sharedData = await DbContext.CharaDataAllowances.Where(u => u.AllowedGroup != null && u.AllowedGroupGID == dto.GID && u.ParentUploaderUID == dto.UID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var sharedData = await DbContext.CharaDataAllowances.Where(u => u.AllowedGroup != null && u.AllowedGroupGID == dto.GID && u.ParentUploaderUID == dto.UID).ToListAsync().ConfigureAwait(false);
|
||||||
DbContext.CharaDataAllowances.RemoveRange(sharedData);
|
DbContext.CharaDataAllowances.RemoveRange(sharedData);
|
||||||
|
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var userIdent = await GetUserIdent(dto.User.UID).ConfigureAwait(false);
|
var userIdent = await GetUserIdent(dto.User.UID).ConfigureAwait(false);
|
||||||
if (userIdent == null)
|
if (userIdent == null)
|
||||||
{
|
{
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,75 +600,6 @@ public partial class LightlessHub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
|
||||||
public async Task<GroupProfileDto> GroupGetProfile(GroupDto dto)
|
|
||||||
{
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
|
||||||
|
|
||||||
var cancellationToken = _contextAccessor.HttpContext.RequestAborted;
|
|
||||||
|
|
||||||
var data = await DbContext.GroupProfiles
|
|
||||||
.FirstOrDefaultAsync(g => g.GroupGID == dto.Group.GID, cancellationToken)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
var profileDto = new GroupProfileDto(dto.Group, Description: null, Tags: null, PictureBase64: null);
|
|
||||||
|
|
||||||
if (data is not null)
|
|
||||||
{
|
|
||||||
profileDto = profileDto with
|
|
||||||
{
|
|
||||||
Description = data.Description,
|
|
||||||
Tags = data.Tags,
|
|
||||||
PictureBase64 = data.Base64GroupProfileImage,
|
|
||||||
};
|
|
||||||
|
|
||||||
await Clients.User(UserUID)
|
|
||||||
.Client_GroupSendProfile(profileDto)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return profileDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
|
||||||
public async Task GroupSetProfile(GroupProfileDto dto)
|
|
||||||
{
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
|
||||||
|
|
||||||
if (dto.Group == null) return;
|
|
||||||
|
|
||||||
var (hasRights, group) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
|
||||||
if (!hasRights) return;
|
|
||||||
|
|
||||||
var groupProfileDb = await DbContext.GroupProfiles
|
|
||||||
.FirstOrDefaultAsync(g => g.GroupGID == dto.Group.GID,
|
|
||||||
_contextAccessor.HttpContext.RequestAborted)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (groupProfileDb != null)
|
|
||||||
{
|
|
||||||
groupProfileDb.Description = dto.Description;
|
|
||||||
groupProfileDb.Tags = dto.Tags;
|
|
||||||
groupProfileDb.Base64GroupProfileImage = dto.PictureBase64;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var groupProfile = new GroupProfile
|
|
||||||
{
|
|
||||||
GroupGID = dto.Group.GID,
|
|
||||||
Description = dto.Description,
|
|
||||||
Tags = dto.Tags,
|
|
||||||
Base64GroupProfileImage = dto.PictureBase64,
|
|
||||||
};
|
|
||||||
|
|
||||||
await DbContext.GroupProfiles.AddAsync(groupProfile,
|
|
||||||
_contextAccessor.HttpContext.RequestAborted)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
[Authorize(Policy = "Identified")]
|
||||||
public async Task GroupSetUserInfo(GroupPairUserInfoDto dto)
|
public async Task GroupSetUserInfo(GroupPairUserInfoDto dto)
|
||||||
{
|
{
|
||||||
@@ -836,9 +629,9 @@ public partial class LightlessHub
|
|||||||
userPair.IsModerator = false;
|
userPair.IsModerator = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var groupPairs = await DbContext.GroupPairs.AsNoTracking().Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).ToListAsync(cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var groupPairs = await DbContext.GroupPairs.AsNoTracking().Where(p => p.GroupGID == dto.Group.GID).Select(p => p.GroupUserUID).ToListAsync().ConfigureAwait(false);
|
||||||
await Clients.Users(groupPairs).Client_GroupPairChangeUserInfo(new GroupPairUserInfoDto(dto.Group, dto.User, userPair.ToEnum())).ConfigureAwait(false);
|
await Clients.Users(groupPairs).Client_GroupPairChangeUserInfo(new GroupPairUserInfoDto(dto.Group, dto.User, userPair.ToEnum())).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,48 +640,17 @@ public partial class LightlessHub
|
|||||||
{
|
{
|
||||||
_logger.LogCallInfo();
|
_logger.LogCallInfo();
|
||||||
|
|
||||||
var ct = _contextAccessor.HttpContext.RequestAborted;
|
var groups = await DbContext.GroupPairs.Include(g => g.Group).Include(g => g.Group.Owner).Where(g => g.GroupUserUID == UserUID).AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||||
|
var preferredPermissions = (await DbContext.GroupPairPreferredPermissions.Where(u => u.UserUID == UserUID).ToListAsync().ConfigureAwait(false))
|
||||||
|
.Where(u => groups.Exists(k => string.Equals(k.GroupGID, u.GroupGID, StringComparison.Ordinal)))
|
||||||
|
.ToDictionary(u => groups.First(f => string.Equals(f.GroupGID, u.GroupGID, StringComparison.Ordinal)), u => u);
|
||||||
|
var groupInfos = await DbContext.GroupPairs.Where(u => groups.Select(g => g.GroupGID).Contains(u.GroupGID) && (u.IsPinned || u.IsModerator))
|
||||||
|
.ToListAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var result = await (
|
return preferredPermissions.Select(g => new GroupFullInfoDto(g.Key.Group.ToGroupData(), g.Key.Group.Owner.ToUserData(),
|
||||||
from gp in DbContext.GroupPairs
|
g.Key.Group.ToEnum(), g.Value.ToEnum(), g.Key.ToEnum(),
|
||||||
.Include(gp => gp.Group)
|
groupInfos.Where(i => string.Equals(i.GroupGID, g.Key.GroupGID, StringComparison.Ordinal))
|
||||||
.ThenInclude(g => g.Owner)
|
.ToDictionary(i => i.GroupUserUID, i => i.ToEnum(), StringComparer.Ordinal))).ToList();
|
||||||
join pp in DbContext.GroupPairPreferredPermissions
|
|
||||||
on new { gp.GroupGID, UserUID } equals new { pp.GroupGID, pp.UserUID }
|
|
||||||
where gp.GroupUserUID == UserUID
|
|
||||||
select new
|
|
||||||
{
|
|
||||||
GroupPair = gp,
|
|
||||||
PreferredPermission = pp,
|
|
||||||
GroupInfos = DbContext.GroupPairs
|
|
||||||
.Where(x => x.GroupGID == gp.GroupGID && (x.IsPinned || x.IsModerator))
|
|
||||||
.Select(x => new { x.GroupUserUID, EnumValue = x.ToEnum() })
|
|
||||||
.ToList(),
|
|
||||||
})
|
|
||||||
.AsNoTracking()
|
|
||||||
.ToListAsync(ct)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(result));
|
|
||||||
|
|
||||||
List<GroupFullInfoDto> List = [.. result.Select(r =>
|
|
||||||
{
|
|
||||||
var groupInfoDict = r.GroupInfos
|
|
||||||
.ToDictionary(x => x.GroupUserUID, x => x.EnumValue, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(r));
|
|
||||||
|
|
||||||
return new GroupFullInfoDto(
|
|
||||||
r.GroupPair.Group.ToGroupData(),
|
|
||||||
r.GroupPair.Group.Owner.ToUserData(),
|
|
||||||
r.GroupPair.Group.ToEnum(),
|
|
||||||
r.PreferredPermission.ToEnum(),
|
|
||||||
r.GroupPair.ToEnum(),
|
|
||||||
groupInfoDict
|
|
||||||
);
|
|
||||||
}),];
|
|
||||||
return List;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
[Authorize(Policy = "Identified")]
|
||||||
@@ -899,97 +661,12 @@ public partial class LightlessHub
|
|||||||
var (userHasRights, _) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
var (userHasRights, _) = await TryValidateGroupModeratorOrOwner(dto.Group.GID).ConfigureAwait(false);
|
||||||
if (!userHasRights) return;
|
if (!userHasRights) return;
|
||||||
|
|
||||||
var banEntry = await DbContext.GroupBans.SingleOrDefaultAsync(g => g.GroupGID == dto.Group.GID && g.BannedUserUID == dto.User.UID, cancellationToken: _contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
var banEntry = await DbContext.GroupBans.SingleOrDefaultAsync(g => g.GroupGID == dto.Group.GID && g.BannedUserUID == dto.User.UID).ConfigureAwait(false);
|
||||||
if (banEntry == null) return;
|
if (banEntry == null) return;
|
||||||
|
|
||||||
DbContext.Remove(banEntry);
|
DbContext.Remove(banEntry);
|
||||||
await DbContext.SaveChangesAsync(_contextAccessor.HttpContext.RequestAborted).ConfigureAwait(false);
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
|
||||||
public async Task<bool> SetGroupBroadcastStatus(GroupBroadcastRequestDto dto)
|
|
||||||
{
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(dto.HashedCID))
|
|
||||||
{
|
|
||||||
_logger.LogCallWarning(LightlessHubLogger.Args("missing CID in syncshell broadcast request", "User", UserUID, "GID", dto.GID));
|
|
||||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Internal error: missing CID.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_broadcastConfiguration.EnableBroadcasting || !_broadcastConfiguration.EnableSyncshellBroadcastPayloads)
|
|
||||||
{
|
|
||||||
_logger.LogCallWarning(LightlessHubLogger.Args("syncshell broadcast disabled", "User", UserUID, "GID", dto.GID));
|
|
||||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Syncshell broadcasting is currently disabled.").ConfigureAwait(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var (isOwner, _) = await TryValidateOwner(dto.GID).ConfigureAwait(false);
|
|
||||||
if (!isOwner)
|
|
||||||
{
|
|
||||||
_logger.LogCallWarning(LightlessHubLogger.Args("Unauthorized syncshell broadcast change", "User", UserUID, "GID", dto.GID));
|
|
||||||
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "You must be the owner of the syncshell to broadcast it.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Authorize(Policy = "Identified")]
|
|
||||||
public async Task<List<GroupJoinDto>> GetBroadcastedGroups(List<BroadcastStatusInfoDto> broadcastEntries)
|
|
||||||
{
|
|
||||||
_logger.LogCallInfo(LightlessHubLogger.Args("Requested Syncshells", broadcastEntries.Select(b => b.GID)));
|
|
||||||
|
|
||||||
if (!_broadcastConfiguration.EnableBroadcasting || !_broadcastConfiguration.EnableSyncshellBroadcastPayloads)
|
|
||||||
return new List<GroupJoinDto>();
|
|
||||||
|
|
||||||
var results = new List<GroupJoinDto>();
|
|
||||||
var gidsToValidate = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
foreach (var entry in broadcastEntries)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(entry.HashedCID) || string.IsNullOrWhiteSpace(entry.GID))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var redisKey = _broadcastConfiguration.BuildRedisKey(entry.HashedCID);
|
|
||||||
var redisEntry = await _redis.GetAsync<BroadcastRedisEntry>(redisKey).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (redisEntry is null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(redisEntry.HashedCID) && !string.Equals(redisEntry.HashedCID, entry.HashedCID, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
_logger.LogCallWarning(LightlessHubLogger.Args("mismatched broadcast cid for group lookup", "Requested", entry.HashedCID, "EntryCID", redisEntry.HashedCID));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redisEntry.GID != null && string.Equals(redisEntry.GID, entry.GID, StringComparison.OrdinalIgnoreCase))
|
|
||||||
gidsToValidate.Add(entry.GID);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gidsToValidate.Count == 0)
|
|
||||||
return results;
|
|
||||||
|
|
||||||
var groups = await DbContext.Groups
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(g => gidsToValidate.Contains(g.GID) && g.InvitesEnabled)
|
|
||||||
.ToListAsync()
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
foreach (var group in groups)
|
|
||||||
{
|
|
||||||
results.Add(new GroupJoinDto(
|
|
||||||
Group: new GroupData(group.GID, group.Alias),
|
|
||||||
Password: group.HashedPassword,
|
|
||||||
GroupUserPreferredPermissions: new GroupUserPreferredPermissions()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
437
LightlessSyncServer/LightlessSyncServer/Hubs/MareHub.User.cs
Normal file
437
LightlessSyncServer/LightlessSyncServer/Hubs/MareHub.User.cs
Normal file
@@ -0,0 +1,437 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using LightlessSync.API.Data;
|
||||||
|
using LightlessSync.API.Data.Enum;
|
||||||
|
using LightlessSync.API.Data.Extensions;
|
||||||
|
using LightlessSync.API.Dto.User;
|
||||||
|
using LightlessSyncServer.Utils;
|
||||||
|
using LightlessSyncShared.Metrics;
|
||||||
|
using LightlessSyncShared.Models;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
|
||||||
|
namespace LightlessSyncServer.Hubs;
|
||||||
|
|
||||||
|
public partial class LightlessHub
|
||||||
|
{
|
||||||
|
private static readonly string[] AllowedExtensionsForGamePaths = { ".mdl", ".tex", ".mtrl", ".tmb", ".pap", ".avfx", ".atex", ".sklb", ".eid", ".phyb", ".pbd", ".scd", ".skp", ".shpk" };
|
||||||
|
|
||||||
|
[Authorize(Policy = "Identified")]
|
||||||
|
public async Task UserAddPair(UserDto dto)
|
||||||
|
{
|
||||||
|
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||||
|
|
||||||
|
// don't allow adding nothing
|
||||||
|
var uid = dto.User.UID.Trim();
|
||||||
|
if (string.Equals(dto.User.UID, UserUID, StringComparison.Ordinal) || string.IsNullOrWhiteSpace(dto.User.UID)) return;
|
||||||
|
|
||||||
|
// grab other user, check if it exists and if a pair already exists
|
||||||
|
var otherUser = await DbContext.Users.SingleOrDefaultAsync(u => u.UID == uid || u.Alias == uid).ConfigureAwait(false);
|
||||||
|
if (otherUser == null)
|
||||||
|
{
|
||||||
|
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, $"Cannot pair with {dto.User.UID}, UID does not exist").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(otherUser.UID, UserUID, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, $"My god you can't pair with yourself why would you do that please stop").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingEntry =
|
||||||
|
await DbContext.ClientPairs.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(p =>
|
||||||
|
p.User.UID == UserUID && p.OtherUserUID == otherUser.UID).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (existingEntry != null)
|
||||||
|
{
|
||||||
|
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Warning, $"Cannot pair with {dto.User.UID}, already paired").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab self create new client pair and save
|
||||||
|
var user = await DbContext.Users.SingleAsync(u => u.UID == UserUID).ConfigureAwait(false);
|
||||||
|
|
||||||
|
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||||
|
|
||||||
|
ClientPair wl = new ClientPair()
|
||||||
|
{
|
||||||
|
OtherUser = otherUser,
|
||||||
|
User = user,
|
||||||
|
};
|
||||||
|
await DbContext.ClientPairs.AddAsync(wl).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var existingData = await GetPairInfo(UserUID, otherUser.UID).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var permissions = existingData?.OwnPermissions;
|
||||||
|
if (permissions == null || !permissions.Sticky)
|
||||||
|
{
|
||||||
|
var ownDefaultPermissions = await DbContext.UserDefaultPreferredPermissions.AsNoTracking().SingleOrDefaultAsync(f => f.UserUID == UserUID).ConfigureAwait(false);
|
||||||
|
|
||||||
|
permissions = new UserPermissionSet()
|
||||||
|
{
|
||||||
|
User = user,
|
||||||
|
OtherUser = otherUser,
|
||||||
|
DisableAnimations = ownDefaultPermissions.DisableIndividualAnimations,
|
||||||
|
DisableSounds = ownDefaultPermissions.DisableIndividualSounds,
|
||||||
|
DisableVFX = ownDefaultPermissions.DisableIndividualVFX,
|
||||||
|
IsPaused = false,
|
||||||
|
Sticky = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var existingDbPerms = await DbContext.Permissions.SingleOrDefaultAsync(u => u.UserUID == UserUID && u.OtherUserUID == otherUser.UID).ConfigureAwait(false);
|
||||||
|
if (existingDbPerms == null)
|
||||||
|
{
|
||||||
|
await DbContext.Permissions.AddAsync(permissions).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
existingDbPerms.DisableAnimations = permissions.DisableAnimations;
|
||||||
|
existingDbPerms.DisableSounds = permissions.DisableSounds;
|
||||||
|
existingDbPerms.DisableVFX = permissions.DisableVFX;
|
||||||
|
existingDbPerms.IsPaused = false;
|
||||||
|
existingDbPerms.Sticky = true;
|
||||||
|
|
||||||
|
DbContext.Permissions.Update(existingDbPerms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
// get the opposite entry of the client pair
|
||||||
|
var otherEntry = OppositeEntry(otherUser.UID);
|
||||||
|
var otherIdent = await GetUserIdent(otherUser.UID).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var otherPermissions = existingData?.OtherPermissions ?? null;
|
||||||
|
|
||||||
|
var ownPerm = permissions.ToUserPermissions(setSticky: true);
|
||||||
|
var otherPerm = otherPermissions.ToUserPermissions();
|
||||||
|
|
||||||
|
var userPairResponse = new UserPairDto(otherUser.ToUserData(),
|
||||||
|
otherEntry == null ? IndividualPairStatus.OneSided : IndividualPairStatus.Bidirectional,
|
||||||
|
ownPerm, otherPerm);
|
||||||
|
|
||||||
|
await Clients.User(user.UID).Client_UserAddClientPair(userPairResponse).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// check if other user is online
|
||||||
|
if (otherIdent == null || otherEntry == null) return;
|
||||||
|
|
||||||
|
// send push with update to other user if other user is online
|
||||||
|
await Clients.User(otherUser.UID)
|
||||||
|
.Client_UserUpdateOtherPairPermissions(new UserPermissionsDto(user.ToUserData(),
|
||||||
|
permissions.ToUserPermissions())).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await Clients.User(otherUser.UID)
|
||||||
|
.Client_UpdateUserIndividualPairStatusDto(new(user.ToUserData(), IndividualPairStatus.Bidirectional))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!ownPerm.IsPaused() && !otherPerm.IsPaused())
|
||||||
|
{
|
||||||
|
await Clients.User(UserUID).Client_UserSendOnline(new(otherUser.ToUserData(), otherIdent)).ConfigureAwait(false);
|
||||||
|
await Clients.User(otherUser.UID).Client_UserSendOnline(new(user.ToUserData(), UserCharaIdent)).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[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(CensusDataDto? censusData)
|
||||||
|
{
|
||||||
|
_logger.LogCallInfo();
|
||||||
|
|
||||||
|
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
||||||
|
var pairs = await GetOnlineUsers(allPairedUsers).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await SendOnlineToAllPairedUsers().ConfigureAwait(false);
|
||||||
|
|
||||||
|
_lightlessCensus.PublishStatistics(UserUID, censusData);
|
||||||
|
|
||||||
|
return pairs.Select(p => new OnlineUserIdentDto(new UserData(p.Key), p.Value)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(Policy = "Identified")]
|
||||||
|
public async Task<List<UserFullPairDto>> UserGetPairedClients()
|
||||||
|
{
|
||||||
|
_logger.LogCallInfo();
|
||||||
|
|
||||||
|
var pairs = await GetAllPairInfo(UserUID).ConfigureAwait(false);
|
||||||
|
return pairs.Select(p =>
|
||||||
|
{
|
||||||
|
return new UserFullPairDto(new UserData(p.Key, p.Value.Alias),
|
||||||
|
p.Value.ToIndividualPairStatus(),
|
||||||
|
p.Value.GIDs.Where(g => !string.Equals(g, Constants.IndividualKeyword, StringComparison.OrdinalIgnoreCase)).ToList(),
|
||||||
|
p.Value.OwnPermissions.ToUserPermissions(setSticky: true),
|
||||||
|
p.Value.OtherPermissions.ToUserPermissions());
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(Policy = "Identified")]
|
||||||
|
public async Task<UserProfileDto> UserGetProfile(UserDto user)
|
||||||
|
{
|
||||||
|
_logger.LogCallInfo(LightlessHubLogger.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(LightlessHubLogger.Args(dto.CharaData.FileReplacements.Count));
|
||||||
|
|
||||||
|
// check for honorific containing . and /
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var honorificJson = Encoding.Default.GetString(Convert.FromBase64String(dto.CharaData.HonorificData));
|
||||||
|
var deserialized = JsonSerializer.Deserialize<JsonElement>(honorificJson);
|
||||||
|
if (deserialized.TryGetProperty("Title", out var honorificTitle))
|
||||||
|
{
|
||||||
|
var title = honorificTitle.GetString().Normalize(NormalizationForm.FormKD);
|
||||||
|
if (UrlRegex().IsMatch(title))
|
||||||
|
{
|
||||||
|
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your data was not pushed: The usage of URLs the Honorific titles is prohibited. Remove them to be able to continue to push data.").ConfigureAwait(false);
|
||||||
|
throw new HubException("Invalid data provided, Honorific title invalid: " + title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (HubException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// swallow
|
||||||
|
}
|
||||||
|
|
||||||
|
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(LightlessHubLogger.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 recipientUids = dto.Recipients.Select(r => r.UID).ToList();
|
||||||
|
bool allCached = await _onlineSyncedPairCacheService.AreAllPlayersCached(UserUID,
|
||||||
|
recipientUids, Context.ConnectionAborted).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!allCached)
|
||||||
|
{
|
||||||
|
var allPairedUsers = await GetAllPairedUnpausedUsers().ConfigureAwait(false);
|
||||||
|
|
||||||
|
recipientUids = allPairedUsers.Where(f => recipientUids.Contains(f, StringComparer.Ordinal)).ToList();
|
||||||
|
|
||||||
|
await _onlineSyncedPairCacheService.CachePlayers(UserUID, allPairedUsers, Context.ConnectionAborted).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogCallInfo(LightlessHubLogger.Args(recipientUids.Count));
|
||||||
|
|
||||||
|
await Clients.Users(recipientUids).Client_UserReceiveCharacterData(new OnlineUserCharaDataDto(new UserData(UserUID), dto.CharaData)).ConfigureAwait(false);
|
||||||
|
|
||||||
|
_lightlessCensus.PublishStatistics(UserUID, dto.CensusDataDto);
|
||||||
|
|
||||||
|
_lightlessMetrics.IncCounter(MetricsAPI.CounterUserPushData);
|
||||||
|
_lightlessMetrics.IncCounter(MetricsAPI.CounterUserPushDataTo, recipientUids.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(Policy = "Identified")]
|
||||||
|
public async Task UserRemovePair(UserDto dto)
|
||||||
|
{
|
||||||
|
_logger.LogCallInfo(LightlessHubLogger.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;
|
||||||
|
|
||||||
|
var pairData = await GetPairInfo(UserUID, dto.User.UID).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// delete from database, send update info to users pair list
|
||||||
|
DbContext.ClientPairs.Remove(callerPair);
|
||||||
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
_logger.LogCallInfo(LightlessHubLogger.Args(dto, "Success"));
|
||||||
|
|
||||||
|
await Clients.User(UserUID).Client_UserRemoveClientPair(dto).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// check if opposite entry exists
|
||||||
|
if (!pairData.IndividuallyPaired) 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;
|
||||||
|
|
||||||
|
// if the other user had paused the user the state will be offline for either, do nothing
|
||||||
|
bool callerHadPaused = pairData.OwnPermissions?.IsPaused ?? false;
|
||||||
|
|
||||||
|
// send updated individual pair status
|
||||||
|
await Clients.User(dto.User.UID)
|
||||||
|
.Client_UpdateUserIndividualPairStatusDto(new(new(UserUID), IndividualPairStatus.OneSided))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
UserPermissionSet? otherPermissions = pairData.OtherPermissions;
|
||||||
|
bool otherHadPaused = otherPermissions?.IsPaused ?? true;
|
||||||
|
|
||||||
|
// if the either had paused, do nothing
|
||||||
|
if (callerHadPaused && otherHadPaused) return;
|
||||||
|
|
||||||
|
var currentPairData = await GetPairInfo(UserUID, dto.User.UID).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// if neither user had paused each other and either is not in an unpaused group with each other, change state to offline
|
||||||
|
if (!currentPairData?.IsSynced ?? true)
|
||||||
|
{
|
||||||
|
await Clients.User(UserUID).Client_UserSendOffline(dto).ConfigureAwait(false);
|
||||||
|
await Clients.User(dto.User.UID).Client_UserSendOffline(new(new(UserUID))).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(Policy = "Identified")]
|
||||||
|
public async Task UserSetProfile(UserProfileDto dto)
|
||||||
|
{
|
||||||
|
_logger.LogCallInfo(LightlessHubLogger.Args(dto));
|
||||||
|
|
||||||
|
if (!string.Equals(dto.User.UID, UserUID, StringComparison.Ordinal)) throw new HubException("Cannot modify profile data for anyone but yourself");
|
||||||
|
|
||||||
|
var existingData = await DbContext.UserProfileData.SingleOrDefaultAsync(u => u.UserUID == dto.User.UID).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (existingData?.FlaggedForReport ?? false)
|
||||||
|
{
|
||||||
|
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your profile is currently flagged for report and cannot be edited").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingData?.ProfileDisabled ?? false)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
await DbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
var allPairedUsers = await GetAllPairedUnpausedUsers().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.Caller.Client_UserUpdateProfile(new(dto.User)).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();
|
||||||
|
|
||||||
|
[GeneratedRegex("^[-a-zA-Z0-9@:%._\\+~#=]{1,256}[\\.,][a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&\\/=]*)$")]
|
||||||
|
private static partial Regex UrlRegex();
|
||||||
|
|
||||||
|
private ClientPair OppositeEntry(string otherUID) =>
|
||||||
|
DbContext.ClientPairs.AsNoTracking().SingleOrDefault(w => w.User.UID == otherUID && w.OtherUser.UID == UserUID);
|
||||||
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
using LightlessSync.API.Data.Enum;
|
using LightlessSync.API.Data.Enum;
|
||||||
using LightlessSync.API.Dto;
|
using LightlessSync.API.Dto;
|
||||||
using LightlessSync.API.SignalR;
|
using LightlessSync.API.SignalR;
|
||||||
using LightlessSyncServer.Services;
|
using LightlessSyncServer.Services;
|
||||||
using LightlessSyncServer.Configuration;
|
|
||||||
using LightlessSyncServer.Utils;
|
using LightlessSyncServer.Utils;
|
||||||
using LightlessSyncShared;
|
using LightlessSyncShared;
|
||||||
using LightlessSyncShared.Data;
|
using LightlessSyncShared.Data;
|
||||||
@@ -25,12 +24,10 @@ public partial class LightlessHub : Hub<ILightlessHub>, ILightlessHub
|
|||||||
private static readonly ConcurrentDictionary<string, string> _userConnections = new(StringComparer.Ordinal);
|
private static readonly ConcurrentDictionary<string, string> _userConnections = new(StringComparer.Ordinal);
|
||||||
private readonly LightlessMetrics _lightlessMetrics;
|
private readonly LightlessMetrics _lightlessMetrics;
|
||||||
private readonly SystemInfoService _systemInfoService;
|
private readonly SystemInfoService _systemInfoService;
|
||||||
private readonly PairService _pairService;
|
|
||||||
private readonly IHttpContextAccessor _contextAccessor;
|
private readonly IHttpContextAccessor _contextAccessor;
|
||||||
private readonly LightlessHubLogger _logger;
|
private readonly LightlessHubLogger _logger;
|
||||||
private readonly string _shardName;
|
private readonly string _shardName;
|
||||||
private readonly int _maxExistingGroupsByUser;
|
private readonly int _maxExistingGroupsByUser;
|
||||||
private readonly IBroadcastConfiguration _broadcastConfiguration;
|
|
||||||
private readonly int _maxJoinedGroupsByUser;
|
private readonly int _maxJoinedGroupsByUser;
|
||||||
private readonly int _maxGroupUserCount;
|
private readonly int _maxGroupUserCount;
|
||||||
private readonly IRedisDatabase _redis;
|
private readonly IRedisDatabase _redis;
|
||||||
@@ -48,7 +45,7 @@ public partial class LightlessHub : Hub<ILightlessHub>, ILightlessHub
|
|||||||
IDbContextFactory<LightlessDbContext> lightlessDbContextFactory, ILogger<LightlessHub> logger, SystemInfoService systemInfoService,
|
IDbContextFactory<LightlessDbContext> lightlessDbContextFactory, ILogger<LightlessHub> logger, SystemInfoService systemInfoService,
|
||||||
IConfigurationService<ServerConfiguration> configuration, IHttpContextAccessor contextAccessor,
|
IConfigurationService<ServerConfiguration> configuration, IHttpContextAccessor contextAccessor,
|
||||||
IRedisDatabase redisDb, OnlineSyncedPairCacheService onlineSyncedPairCacheService, LightlessCensus lightlessCensus,
|
IRedisDatabase redisDb, OnlineSyncedPairCacheService onlineSyncedPairCacheService, LightlessCensus lightlessCensus,
|
||||||
GPoseLobbyDistributionService gPoseLobbyDistributionService, IBroadcastConfiguration broadcastConfiguration, PairService pairService)
|
GPoseLobbyDistributionService gPoseLobbyDistributionService)
|
||||||
{
|
{
|
||||||
_lightlessMetrics = lightlessMetrics;
|
_lightlessMetrics = lightlessMetrics;
|
||||||
_systemInfoService = systemInfoService;
|
_systemInfoService = systemInfoService;
|
||||||
@@ -67,8 +64,6 @@ public partial class LightlessHub : Hub<ILightlessHub>, ILightlessHub
|
|||||||
_gPoseLobbyDistributionService = gPoseLobbyDistributionService;
|
_gPoseLobbyDistributionService = gPoseLobbyDistributionService;
|
||||||
_logger = new LightlessHubLogger(this, logger);
|
_logger = new LightlessHubLogger(this, logger);
|
||||||
_dbContextLazy = new Lazy<LightlessDbContext>(() => lightlessDbContextFactory.CreateDbContext());
|
_dbContextLazy = new Lazy<LightlessDbContext>(() => lightlessDbContextFactory.CreateDbContext());
|
||||||
_broadcastConfiguration = broadcastConfiguration;
|
|
||||||
_pairService = pairService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
@@ -114,9 +109,6 @@ public partial class LightlessHub : Hub<ILightlessHub>, ILightlessHub
|
|||||||
ServerVersion = ILightlessHub.ApiVersion,
|
ServerVersion = ILightlessHub.ApiVersion,
|
||||||
IsAdmin = dbUser.IsAdmin,
|
IsAdmin = dbUser.IsAdmin,
|
||||||
IsModerator = dbUser.IsModerator,
|
IsModerator = dbUser.IsModerator,
|
||||||
HasVanity = dbUser.HasVanity,
|
|
||||||
TextColorHex = dbUser.TextColorHex,
|
|
||||||
TextGlowColorHex = dbUser.TextGlowColorHex,
|
|
||||||
ServerInfo = new ServerInfo()
|
ServerInfo = new ServerInfo()
|
||||||
{
|
{
|
||||||
MaxGroupsCreatedByUser = _maxExistingGroupsByUser,
|
MaxGroupsCreatedByUser = _maxExistingGroupsByUser,
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
using LightlessSyncShared.Data;
|
|
||||||
using LightlessSyncShared.Models;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public class PairService
|
|
||||||
{
|
|
||||||
private readonly IDbContextFactory<LightlessDbContext> _dbFactory;
|
|
||||||
private readonly ILogger<PairService> _logger;
|
|
||||||
|
|
||||||
public PairService(IDbContextFactory<LightlessDbContext> dbFactory, ILogger<PairService> logger)
|
|
||||||
{
|
|
||||||
_dbFactory = dbFactory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> TryAddPairAsync(string userUid, string otherUid)
|
|
||||||
{
|
|
||||||
if (userUid == otherUid || string.IsNullOrWhiteSpace(userUid) || string.IsNullOrWhiteSpace(otherUid))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
await using var db = await _dbFactory.CreateDbContextAsync();
|
|
||||||
|
|
||||||
var user = await db.Users.SingleOrDefaultAsync(u => u.UID == userUid);
|
|
||||||
var other = await db.Users.SingleOrDefaultAsync(u => u.UID == otherUid);
|
|
||||||
|
|
||||||
if (user == null || other == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool modified = false;
|
|
||||||
|
|
||||||
if (!await db.ClientPairs.AnyAsync(p => p.UserUID == userUid && p.OtherUserUID == otherUid))
|
|
||||||
{
|
|
||||||
db.ClientPairs.Add(new ClientPair
|
|
||||||
{
|
|
||||||
UserUID = userUid,
|
|
||||||
OtherUserUID = otherUid
|
|
||||||
});
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await db.ClientPairs.AnyAsync(p => p.UserUID == otherUid && p.OtherUserUID == userUid))
|
|
||||||
{
|
|
||||||
db.ClientPairs.Add(new ClientPair
|
|
||||||
{
|
|
||||||
UserUID = otherUid,
|
|
||||||
OtherUserUID = userUid
|
|
||||||
});
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await db.Permissions.AnyAsync(p => p.UserUID == userUid && p.OtherUserUID == otherUid))
|
|
||||||
{
|
|
||||||
var defaultPerms = await db.UserDefaultPreferredPermissions
|
|
||||||
.SingleOrDefaultAsync(p => p.UserUID == userUid);
|
|
||||||
|
|
||||||
if (defaultPerms != null)
|
|
||||||
{
|
|
||||||
db.Permissions.Add(new UserPermissionSet
|
|
||||||
{
|
|
||||||
UserUID = userUid,
|
|
||||||
OtherUserUID = otherUid,
|
|
||||||
DisableAnimations = defaultPerms.DisableIndividualAnimations,
|
|
||||||
DisableSounds = defaultPerms.DisableIndividualSounds,
|
|
||||||
DisableVFX = defaultPerms.DisableIndividualVFX,
|
|
||||||
IsPaused = false,
|
|
||||||
Sticky = true,
|
|
||||||
});
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await db.Permissions.AnyAsync(p => p.UserUID == otherUid && p.OtherUserUID == userUid))
|
|
||||||
{
|
|
||||||
var defaultPerms = await db.UserDefaultPreferredPermissions
|
|
||||||
.SingleOrDefaultAsync(p => p.UserUID == otherUid);
|
|
||||||
|
|
||||||
if (defaultPerms != null)
|
|
||||||
{
|
|
||||||
db.Permissions.Add(new UserPermissionSet
|
|
||||||
{
|
|
||||||
UserUID = otherUid,
|
|
||||||
OtherUserUID = userUid,
|
|
||||||
DisableAnimations = defaultPerms.DisableIndividualAnimations,
|
|
||||||
DisableSounds = defaultPerms.DisableIndividualSounds,
|
|
||||||
DisableVFX = defaultPerms.DisableIndividualVFX,
|
|
||||||
IsPaused = false,
|
|
||||||
Sticky = true,
|
|
||||||
});
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
await db.SaveChangesAsync();
|
|
||||||
_logger.LogInformation("Mutual pair established between {UserUID} and {OtherUID}", userUid, otherUid);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Pair already exists between {UserUID} and {OtherUID}", userUid, otherUid);
|
|
||||||
}
|
|
||||||
|
|
||||||
return modified;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ using AspNetCoreRateLimit;
|
|||||||
using LightlessSync.API.SignalR;
|
using LightlessSync.API.SignalR;
|
||||||
using LightlessSyncAuthService.Controllers;
|
using LightlessSyncAuthService.Controllers;
|
||||||
using LightlessSyncServer.Controllers;
|
using LightlessSyncServer.Controllers;
|
||||||
using LightlessSyncServer.Configuration;
|
|
||||||
using LightlessSyncServer.Hubs;
|
using LightlessSyncServer.Hubs;
|
||||||
using LightlessSyncServer.Services;
|
using LightlessSyncServer.Services;
|
||||||
using LightlessSyncShared.Data;
|
using LightlessSyncShared.Data;
|
||||||
@@ -88,9 +87,7 @@ public class Startup
|
|||||||
|
|
||||||
services.Configure<ServerConfiguration>(Configuration.GetRequiredSection("LightlessSync"));
|
services.Configure<ServerConfiguration>(Configuration.GetRequiredSection("LightlessSync"));
|
||||||
services.Configure<LightlessConfigurationBase>(Configuration.GetRequiredSection("LightlessSync"));
|
services.Configure<LightlessConfigurationBase>(Configuration.GetRequiredSection("LightlessSync"));
|
||||||
services.Configure<BroadcastOptions>(Configuration.GetSection("Broadcast"));
|
|
||||||
|
|
||||||
services.AddSingleton<IBroadcastConfiguration, BroadcastConfiguration>();
|
|
||||||
services.AddSingleton<ServerTokenGenerator>();
|
services.AddSingleton<ServerTokenGenerator>();
|
||||||
services.AddSingleton<SystemInfoService>();
|
services.AddSingleton<SystemInfoService>();
|
||||||
services.AddSingleton<OnlineSyncedPairCacheService>();
|
services.AddSingleton<OnlineSyncedPairCacheService>();
|
||||||
@@ -108,7 +105,6 @@ public class Startup
|
|||||||
services.AddSingleton<CharaDataCleanupService>();
|
services.AddSingleton<CharaDataCleanupService>();
|
||||||
services.AddHostedService(provider => provider.GetService<CharaDataCleanupService>());
|
services.AddHostedService(provider => provider.GetService<CharaDataCleanupService>());
|
||||||
services.AddHostedService<ClientPairPermissionsCleanupService>();
|
services.AddHostedService<ClientPairPermissionsCleanupService>();
|
||||||
services.AddScoped<PairService>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
services.AddSingleton<GPoseLobbyDistributionService>();
|
services.AddSingleton<GPoseLobbyDistributionService>();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public static class Extensions
|
|||||||
{
|
{
|
||||||
public static GroupData ToGroupData(this Group group)
|
public static GroupData ToGroupData(this Group group)
|
||||||
{
|
{
|
||||||
return new GroupData(group.GID, group.Alias, group.CreatedDate);
|
return new GroupData(group.GID, group.Alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserData ToUserData(this GroupPair pair)
|
public static UserData ToUserData(this GroupPair pair)
|
||||||
|
|||||||
@@ -29,15 +29,6 @@
|
|||||||
"ServiceAddress": "http://localhost:5002",
|
"ServiceAddress": "http://localhost:5002",
|
||||||
"StaticFileServiceAddress": "http://localhost:5003"
|
"StaticFileServiceAddress": "http://localhost:5003"
|
||||||
},
|
},
|
||||||
"Broadcast": {
|
|
||||||
"RedisKeyPrefix": "broadcast:",
|
|
||||||
"EntryTtlSeconds": 10800,
|
|
||||||
"MaxStatusBatchSize": 30,
|
|
||||||
"NotifyOwnerOnPairRequest": true,
|
|
||||||
"EnableBroadcasting": true,
|
|
||||||
"EnableSyncshellBroadcastPayloads": true,
|
|
||||||
"PairRequestNotificationTemplate": "{DisplayName} sent you a pair request. To accept, right-click them, open the context menu, and send a request back."
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"Kestrel": {
|
"Kestrel": {
|
||||||
"Endpoints": {
|
"Endpoints": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Discord;
|
using Discord;
|
||||||
using Discord.Interactions;
|
using Discord.Interactions;
|
||||||
using Discord.Rest;
|
using Discord.Rest;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
@@ -384,50 +384,13 @@ internal class DiscordBot : IHostedService
|
|||||||
|
|
||||||
_logger.LogInformation($"Checking Group: {group.GID} [{group.Alias}], owned by {group.OwnerUID} ({groupPrimaryUser}), User in Roles: {string.Join(", ", discordUser?.RoleIds ?? new List<ulong>())}");
|
_logger.LogInformation($"Checking Group: {group.GID} [{group.Alias}], owned by {group.OwnerUID} ({groupPrimaryUser}), User in Roles: {string.Join(", ", discordUser?.RoleIds ?? new List<ulong>())}");
|
||||||
|
|
||||||
var hasAllowedRole = lodestoneUser != null && discordUser != null && discordUser.RoleIds.Any(allowedRoleIds.Keys.Contains);
|
if (lodestoneUser == null || discordUser == null || !discordUser.RoleIds.Any(allowedRoleIds.Keys.Contains))
|
||||||
|
|
||||||
if (!hasAllowedRole)
|
|
||||||
{
|
{
|
||||||
await _botServices.LogToChannel($"VANITY GID REMOVAL: <@{lodestoneUser?.DiscordId ?? 0}> ({lodestoneUser?.User?.UID}) - GID: {group.GID}, Vanity: {group.Alias}").ConfigureAwait(false);
|
await _botServices.LogToChannel($"VANITY GID REMOVAL: <@{lodestoneUser?.DiscordId ?? 0}> ({lodestoneUser?.User?.UID}) - GID: {group.GID}, Vanity: {group.Alias}").ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.LogInformation($"User {lodestoneUser?.User?.UID ?? "unknown"} not in allowed roles, deleting group alias for {group.GID}");
|
_logger.LogInformation($"User {lodestoneUser?.User?.UID ?? "unknown"} not in allowed roles, deleting group alias for {group.GID}");
|
||||||
group.Alias = null;
|
group.Alias = null;
|
||||||
db.Update(group);
|
db.Update(group);
|
||||||
|
|
||||||
if (lodestoneUser?.User != null)
|
|
||||||
{
|
|
||||||
lodestoneUser.User.HasVanity = false;
|
|
||||||
db.Update(lodestoneUser.User);
|
|
||||||
|
|
||||||
var secondaryUsers = await db.Auth.Include(u => u.User)
|
|
||||||
.Where(u => u.PrimaryUserUID == lodestoneUser.User.UID).ToListAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
foreach (var secondaryUser in secondaryUsers)
|
|
||||||
{
|
|
||||||
secondaryUser.User.HasVanity = false;
|
|
||||||
db.Update(secondaryUser.User);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.SaveChangesAsync(token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else if (lodestoneUser?.User != null && !lodestoneUser.User.HasVanity)
|
|
||||||
{
|
|
||||||
lodestoneUser.User.HasVanity = true;
|
|
||||||
db.Update(lodestoneUser.User);
|
|
||||||
|
|
||||||
var secondaryUsers = await db.Auth.Include(u => u.User)
|
|
||||||
.Where(u => u.PrimaryUserUID == lodestoneUser.User.UID).ToListAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
foreach (var secondaryUser in secondaryUsers)
|
|
||||||
{
|
|
||||||
if (!secondaryUser.User.HasVanity)
|
|
||||||
{
|
|
||||||
secondaryUser.User.HasVanity = true;
|
|
||||||
db.Update(secondaryUser.User);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.SaveChangesAsync(token).ConfigureAwait(false);
|
await db.SaveChangesAsync(token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -437,55 +400,22 @@ internal class DiscordBot : IHostedService
|
|||||||
var discordUser = await restGuild.GetUserAsync(lodestoneAuth.DiscordId).ConfigureAwait(false);
|
var discordUser = await restGuild.GetUserAsync(lodestoneAuth.DiscordId).ConfigureAwait(false);
|
||||||
_logger.LogInformation($"Checking User: {lodestoneAuth.DiscordId}, {lodestoneAuth.User.UID} ({lodestoneAuth.User.Alias}), User in Roles: {string.Join(", ", discordUser?.RoleIds ?? new List<ulong>())}");
|
_logger.LogInformation($"Checking User: {lodestoneAuth.DiscordId}, {lodestoneAuth.User.UID} ({lodestoneAuth.User.Alias}), User in Roles: {string.Join(", ", discordUser?.RoleIds ?? new List<ulong>())}");
|
||||||
|
|
||||||
var hasAllowedRole = discordUser != null && discordUser.RoleIds.Any(u => allowedRoleIds.Keys.Contains(u));
|
if (discordUser == null || !discordUser.RoleIds.Any(u => allowedRoleIds.Keys.Contains(u)))
|
||||||
|
|
||||||
if (!hasAllowedRole)
|
|
||||||
{
|
{
|
||||||
_logger.LogInformation($"User {lodestoneAuth.User.UID} not in allowed roles, deleting alias");
|
_logger.LogInformation($"User {lodestoneAuth.User.UID} not in allowed roles, deleting alias");
|
||||||
await _botServices.LogToChannel($"VANITY UID REMOVAL: <@{lodestoneAuth.DiscordId}> - UID: {lodestoneAuth.User.UID}, Vanity: {lodestoneAuth.User.Alias}").ConfigureAwait(false);
|
await _botServices.LogToChannel($"VANITY UID REMOVAL: <@{lodestoneAuth.DiscordId}> - UID: {lodestoneAuth.User.UID}, Vanity: {lodestoneAuth.User.Alias}").ConfigureAwait(false);
|
||||||
lodestoneAuth.User.Alias = null;
|
lodestoneAuth.User.Alias = null;
|
||||||
lodestoneAuth.User.HasVanity = false;
|
|
||||||
var secondaryUsers = await db.Auth.Include(u => u.User).Where(u => u.PrimaryUserUID == lodestoneAuth.User.UID).ToListAsync().ConfigureAwait(false);
|
var secondaryUsers = await db.Auth.Include(u => u.User).Where(u => u.PrimaryUserUID == lodestoneAuth.User.UID).ToListAsync().ConfigureAwait(false);
|
||||||
foreach (var secondaryUser in secondaryUsers)
|
foreach (var secondaryUser in secondaryUsers)
|
||||||
{
|
{
|
||||||
_logger.LogInformation($"Secondary User {secondaryUser.User.UID} not in allowed roles, deleting alias");
|
_logger.LogInformation($"Secondary User {secondaryUser.User.UID} not in allowed roles, deleting alias");
|
||||||
|
|
||||||
secondaryUser.User.Alias = null;
|
secondaryUser.User.Alias = null;
|
||||||
secondaryUser.User.HasVanity = false;
|
|
||||||
db.Update(secondaryUser.User);
|
db.Update(secondaryUser.User);
|
||||||
}
|
}
|
||||||
db.Update(lodestoneAuth.User);
|
db.Update(lodestoneAuth.User);
|
||||||
await db.SaveChangesAsync(token).ConfigureAwait(false);
|
await db.SaveChangesAsync(token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
var secondaryUsers = await db.Auth.Include(u => u.User)
|
|
||||||
.Where(u => u.PrimaryUserUID == lodestoneAuth.User.UID).ToListAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
var hasChanges = false;
|
|
||||||
|
|
||||||
if (!lodestoneAuth.User.HasVanity)
|
|
||||||
{
|
|
||||||
lodestoneAuth.User.HasVanity = true;
|
|
||||||
db.Update(lodestoneAuth.User);
|
|
||||||
hasChanges = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var secondaryUser in secondaryUsers)
|
|
||||||
{
|
|
||||||
if (!secondaryUser.User.HasVanity)
|
|
||||||
{
|
|
||||||
secondaryUser.User.HasVanity = true;
|
|
||||||
db.Update(secondaryUser.User);
|
|
||||||
hasChanges = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasChanges)
|
|
||||||
{
|
|
||||||
await db.SaveChangesAsync(token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateStatusAsync(CancellationToken token)
|
private async Task UpdateStatusAsync(CancellationToken token)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Discord.Interactions;
|
using Discord.Interactions;
|
||||||
using Discord;
|
using Discord;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@@ -92,22 +92,13 @@ public partial class LightlessWizardModule
|
|||||||
var desiredVanityUid = modal.DesiredVanityUID;
|
var desiredVanityUid = modal.DesiredVanityUID;
|
||||||
using var db = await GetDbContext().ConfigureAwait(false);
|
using var db = await GetDbContext().ConfigureAwait(false);
|
||||||
bool canAddVanityId = !db.Users.Any(u => u.UID == modal.DesiredVanityUID || u.Alias == modal.DesiredVanityUID);
|
bool canAddVanityId = !db.Users.Any(u => u.UID == modal.DesiredVanityUID || u.Alias == modal.DesiredVanityUID);
|
||||||
var forbiddenWords = new[] { "null", "nil" };
|
|
||||||
|
|
||||||
Regex rgx = new(@"^[_\-a-zA-Z0-9]{3,15}$", RegexOptions.ECMAScript);
|
Regex rgx = new(@"^[_\-a-zA-Z0-9]{5,15}$", RegexOptions.ECMAScript);
|
||||||
if (!rgx.Match(desiredVanityUid).Success)
|
if (!rgx.Match(desiredVanityUid).Success)
|
||||||
{
|
{
|
||||||
eb.WithColor(Color.Red);
|
eb.WithColor(Color.Red);
|
||||||
eb.WithTitle("Invalid Vanity UID");
|
eb.WithTitle("Invalid Vanity UID");
|
||||||
eb.WithDescription("A Vanity UID must be between 3 and 15 characters long and only contain the letters A-Z, numbers 0-9, dashes (-) and underscores (_).");
|
eb.WithDescription("A Vanity UID must be between 5 and 15 characters long and only contain the letters A-Z, numbers 0-9, dashes (-) and underscores (_).");
|
||||||
cb.WithButton("Cancel", "wizard-vanity", ButtonStyle.Secondary, emote: new Emoji("❌"));
|
|
||||||
cb.WithButton("Pick Different UID", "wizard-vanity-uid-set:" + uid, ButtonStyle.Primary, new Emoji("💅"));
|
|
||||||
}
|
|
||||||
else if (forbiddenWords.Contains(desiredVanityUid.Trim().ToLowerInvariant()))
|
|
||||||
{
|
|
||||||
eb.WithColor(Color.Red);
|
|
||||||
eb.WithTitle("Invalid Vanity UID");
|
|
||||||
eb.WithDescription("You cannot use 'Null' or 'Nil' (any case) as a Vanity UID. Please pick a different one.");
|
|
||||||
cb.WithButton("Cancel", "wizard-vanity", ButtonStyle.Secondary, emote: new Emoji("❌"));
|
cb.WithButton("Cancel", "wizard-vanity", ButtonStyle.Secondary, emote: new Emoji("❌"));
|
||||||
cb.WithButton("Pick Different UID", "wizard-vanity-uid-set:" + uid, ButtonStyle.Primary, new Emoji("💅"));
|
cb.WithButton("Pick Different UID", "wizard-vanity-uid-set:" + uid, ButtonStyle.Primary, new Emoji("💅"));
|
||||||
}
|
}
|
||||||
@@ -123,20 +114,6 @@ public partial class LightlessWizardModule
|
|||||||
{
|
{
|
||||||
var user = await db.Users.SingleAsync(u => u.UID == uid).ConfigureAwait(false);
|
var user = await db.Users.SingleAsync(u => u.UID == uid).ConfigureAwait(false);
|
||||||
user.Alias = desiredVanityUid;
|
user.Alias = desiredVanityUid;
|
||||||
user.HasVanity = true;
|
|
||||||
|
|
||||||
var secondaryUsers = await db.Auth.Include(u => u.User)
|
|
||||||
.Where(u => u.PrimaryUserUID == user.UID).ToListAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
foreach (var secondaryUser in secondaryUsers)
|
|
||||||
{
|
|
||||||
if (!secondaryUser.User.HasVanity)
|
|
||||||
{
|
|
||||||
secondaryUser.User.HasVanity = true;
|
|
||||||
db.Update(secondaryUser.User);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Update(user);
|
db.Update(user);
|
||||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||||
eb.WithColor(Color.Green);
|
eb.WithColor(Color.Green);
|
||||||
@@ -213,25 +190,6 @@ public partial class LightlessWizardModule
|
|||||||
{
|
{
|
||||||
var group = await db.Groups.SingleAsync(u => u.GID == gid).ConfigureAwait(false);
|
var group = await db.Groups.SingleAsync(u => u.GID == gid).ConfigureAwait(false);
|
||||||
group.Alias = desiredVanityGid;
|
group.Alias = desiredVanityGid;
|
||||||
|
|
||||||
var ownerAuth = await db.Auth.SingleOrDefaultAsync(u => u.UserUID == group.OwnerUID).ConfigureAwait(false);
|
|
||||||
var ownerUid = string.IsNullOrEmpty(ownerAuth?.PrimaryUserUID) ? group.OwnerUID : ownerAuth.PrimaryUserUID;
|
|
||||||
var ownerUser = await db.Users.SingleAsync(u => u.UID == ownerUid).ConfigureAwait(false);
|
|
||||||
ownerUser.HasVanity = true;
|
|
||||||
db.Update(ownerUser);
|
|
||||||
|
|
||||||
var secondaryUsers = await db.Auth.Include(u => u.User)
|
|
||||||
.Where(u => u.PrimaryUserUID == ownerUser.UID).ToListAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
foreach (var secondaryUser in secondaryUsers)
|
|
||||||
{
|
|
||||||
if (!secondaryUser.User.HasVanity)
|
|
||||||
{
|
|
||||||
secondaryUser.User.HasVanity = true;
|
|
||||||
db.Update(secondaryUser.User);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Update(group);
|
db.Update(group);
|
||||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||||
eb.WithColor(Color.Green);
|
eb.WithColor(Color.Green);
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ public partial class LightlessWizardModule : InteractionModuleBase
|
|||||||
public string Title => "Set Vanity UID";
|
public string Title => "Set Vanity UID";
|
||||||
|
|
||||||
[InputLabel("Set your Vanity UID")]
|
[InputLabel("Set your Vanity UID")]
|
||||||
[ModalTextInput("vanity_uid", TextInputStyle.Short, "3-15 characters, underscore, dash", 3, 15)]
|
[ModalTextInput("vanity_uid", TextInputStyle.Short, "5-15 characters, underscore, dash", 5, 15)]
|
||||||
public string DesiredVanityUID { get; set; }
|
public string DesiredVanityUID { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ public class LightlessDbContext : DbContext
|
|||||||
public DbSet<CharaDataOriginalFile> CharaDataOriginalFiles { get; set; }
|
public DbSet<CharaDataOriginalFile> CharaDataOriginalFiles { get; set; }
|
||||||
public DbSet<CharaDataPose> CharaDataPoses { get; set; }
|
public DbSet<CharaDataPose> CharaDataPoses { get; set; }
|
||||||
public DbSet<CharaDataAllowance> CharaDataAllowances { get; set; }
|
public DbSet<CharaDataAllowance> CharaDataAllowances { get; set; }
|
||||||
public DbSet<GroupProfile> GroupProfiles { get; set; }
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder mb)
|
protected override void OnModelCreating(ModelBuilder mb)
|
||||||
{
|
{
|
||||||
@@ -71,14 +70,6 @@ public class LightlessDbContext : DbContext
|
|||||||
mb.Entity<BannedRegistrations>().ToTable("banned_registrations");
|
mb.Entity<BannedRegistrations>().ToTable("banned_registrations");
|
||||||
mb.Entity<Group>().ToTable("groups");
|
mb.Entity<Group>().ToTable("groups");
|
||||||
mb.Entity<Group>().HasIndex(c => c.OwnerUID);
|
mb.Entity<Group>().HasIndex(c => c.OwnerUID);
|
||||||
mb.Entity<Group>()
|
|
||||||
.Property(g => g.CreatedDate)
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
mb.Entity<Group>()
|
|
||||||
.HasOne(g => g.Profile)
|
|
||||||
.WithOne(p => p.Group)
|
|
||||||
.HasForeignKey<GroupProfile>(p => p.GroupGID)
|
|
||||||
.IsRequired(false);
|
|
||||||
mb.Entity<GroupPair>().ToTable("group_pairs");
|
mb.Entity<GroupPair>().ToTable("group_pairs");
|
||||||
mb.Entity<GroupPair>().HasKey(u => new { u.GroupGID, u.GroupUserUID });
|
mb.Entity<GroupPair>().HasKey(u => new { u.GroupGID, u.GroupUserUID });
|
||||||
mb.Entity<GroupPair>().HasIndex(c => c.GroupUserUID);
|
mb.Entity<GroupPair>().HasIndex(c => c.GroupUserUID);
|
||||||
@@ -87,9 +78,6 @@ public class LightlessDbContext : DbContext
|
|||||||
mb.Entity<GroupBan>().HasKey(u => new { u.GroupGID, u.BannedUserUID });
|
mb.Entity<GroupBan>().HasKey(u => new { u.GroupGID, u.BannedUserUID });
|
||||||
mb.Entity<GroupBan>().HasIndex(c => c.BannedUserUID);
|
mb.Entity<GroupBan>().HasIndex(c => c.BannedUserUID);
|
||||||
mb.Entity<GroupBan>().HasIndex(c => c.GroupGID);
|
mb.Entity<GroupBan>().HasIndex(c => c.GroupGID);
|
||||||
mb.Entity<GroupProfile>().ToTable("group_profiles");
|
|
||||||
mb.Entity<GroupProfile>().HasKey(u => u.GroupGID);
|
|
||||||
mb.Entity<GroupProfile>().HasIndex(c => c.GroupGID);
|
|
||||||
mb.Entity<GroupTempInvite>().ToTable("group_temp_invites");
|
mb.Entity<GroupTempInvite>().ToTable("group_temp_invites");
|
||||||
mb.Entity<GroupTempInvite>().HasKey(u => new { u.GroupGID, u.Invite });
|
mb.Entity<GroupTempInvite>().HasKey(u => new { u.GroupGID, u.Invite });
|
||||||
mb.Entity<GroupTempInvite>().HasIndex(c => c.GroupGID);
|
mb.Entity<GroupTempInvite>().HasIndex(c => c.GroupGID);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,79 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace LightlessSyncServer.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddGroupProfilesAndDates : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
|
||||||
name: "created_date",
|
|
||||||
table: "groups",
|
|
||||||
type: "timestamp with time zone",
|
|
||||||
nullable: false,
|
|
||||||
defaultValueSql: "CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<bool>(
|
|
||||||
name: "from_finder",
|
|
||||||
table: "group_pairs",
|
|
||||||
type: "boolean",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: false);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
|
||||||
name: "joined_group_on",
|
|
||||||
table: "group_pairs",
|
|
||||||
type: "timestamp with time zone",
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "group_profiles",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
group_gid = table.Column<string>(type: "character varying(20)", nullable: false),
|
|
||||||
description = table.Column<string>(type: "text", nullable: true),
|
|
||||||
tags = table.Column<string>(type: "text", nullable: true),
|
|
||||||
base64group_profile_image = table.Column<string>(type: "text", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("pk_group_profiles", x => x.group_gid);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "fk_group_profiles_groups_group_gid",
|
|
||||||
column: x => x.group_gid,
|
|
||||||
principalTable: "groups",
|
|
||||||
principalColumn: "gid",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "ix_group_profiles_group_gid",
|
|
||||||
table: "group_profiles",
|
|
||||||
column: "group_gid");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "group_profiles");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "created_date",
|
|
||||||
table: "groups");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "from_finder",
|
|
||||||
table: "group_pairs");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "joined_group_on",
|
|
||||||
table: "group_pairs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,41 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace LightlessSyncServer.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddProfilesToGroup : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropForeignKey(
|
|
||||||
name: "fk_group_profiles_groups_group_gid",
|
|
||||||
table: "group_profiles");
|
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
|
||||||
name: "fk_group_profiles_groups_group_gid",
|
|
||||||
table: "group_profiles",
|
|
||||||
column: "group_gid",
|
|
||||||
principalTable: "groups",
|
|
||||||
principalColumn: "gid");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropForeignKey(
|
|
||||||
name: "fk_group_profiles_groups_group_gid",
|
|
||||||
table: "group_profiles");
|
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
|
||||||
name: "fk_group_profiles_groups_group_gid",
|
|
||||||
table: "group_profiles",
|
|
||||||
column: "group_gid",
|
|
||||||
principalTable: "groups",
|
|
||||||
principalColumn: "gid",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,51 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace LightlessSyncServer.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddUserVanity : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<bool>(
|
|
||||||
name: "has_vanity",
|
|
||||||
table: "users",
|
|
||||||
type: "boolean",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: false);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "text_color_hex",
|
|
||||||
table: "users",
|
|
||||||
type: "character varying(9)",
|
|
||||||
maxLength: 9,
|
|
||||||
nullable: true);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "text_glow_color_hex",
|
|
||||||
table: "users",
|
|
||||||
type: "character varying(9)",
|
|
||||||
maxLength: 9,
|
|
||||||
nullable: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "has_vanity",
|
|
||||||
table: "users");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "text_color_hex",
|
|
||||||
table: "users");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "text_glow_color_hex",
|
|
||||||
table: "users");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -430,12 +430,6 @@ namespace LightlessSyncServer.Migrations
|
|||||||
.HasColumnType("character varying(50)")
|
.HasColumnType("character varying(50)")
|
||||||
.HasColumnName("alias");
|
.HasColumnName("alias");
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedDate")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("created_date")
|
|
||||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
|
||||||
|
|
||||||
b.Property<string>("HashedPassword")
|
b.Property<string>("HashedPassword")
|
||||||
.HasColumnType("text")
|
.HasColumnType("text")
|
||||||
.HasColumnName("hashed_password");
|
.HasColumnName("hashed_password");
|
||||||
@@ -516,10 +510,6 @@ namespace LightlessSyncServer.Migrations
|
|||||||
.HasColumnType("character varying(10)")
|
.HasColumnType("character varying(10)")
|
||||||
.HasColumnName("group_user_uid");
|
.HasColumnName("group_user_uid");
|
||||||
|
|
||||||
b.Property<bool>("FromFinder")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("from_finder");
|
|
||||||
|
|
||||||
b.Property<bool>("IsModerator")
|
b.Property<bool>("IsModerator")
|
||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
.HasColumnName("is_moderator");
|
.HasColumnName("is_moderator");
|
||||||
@@ -528,10 +518,6 @@ namespace LightlessSyncServer.Migrations
|
|||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
.HasColumnName("is_pinned");
|
.HasColumnName("is_pinned");
|
||||||
|
|
||||||
b.Property<DateTime?>("JoinedGroupOn")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("joined_group_on");
|
|
||||||
|
|
||||||
b.HasKey("GroupGID", "GroupUserUID")
|
b.HasKey("GroupGID", "GroupUserUID")
|
||||||
.HasName("pk_group_pairs");
|
.HasName("pk_group_pairs");
|
||||||
|
|
||||||
@@ -582,34 +568,6 @@ namespace LightlessSyncServer.Migrations
|
|||||||
b.ToTable("group_pair_preferred_permissions", (string)null);
|
b.ToTable("group_pair_preferred_permissions", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LightlessSyncShared.Models.GroupProfile", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("GroupGID")
|
|
||||||
.HasMaxLength(20)
|
|
||||||
.HasColumnType("character varying(20)")
|
|
||||||
.HasColumnName("group_gid");
|
|
||||||
|
|
||||||
b.Property<string>("Base64GroupProfileImage")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("base64group_profile_image");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("description");
|
|
||||||
|
|
||||||
b.Property<string>("Tags")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("tags");
|
|
||||||
|
|
||||||
b.HasKey("GroupGID")
|
|
||||||
.HasName("pk_group_profiles");
|
|
||||||
|
|
||||||
b.HasIndex("GroupGID")
|
|
||||||
.HasDatabaseName("ix_group_profiles_group_gid");
|
|
||||||
|
|
||||||
b.ToTable("group_profiles", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("LightlessSyncShared.Models.GroupTempInvite", b =>
|
modelBuilder.Entity("LightlessSyncShared.Models.GroupTempInvite", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("GroupGID")
|
b.Property<string>("GroupGID")
|
||||||
@@ -683,10 +641,6 @@ namespace LightlessSyncServer.Migrations
|
|||||||
.HasColumnType("character varying(15)")
|
.HasColumnType("character varying(15)")
|
||||||
.HasColumnName("alias");
|
.HasColumnName("alias");
|
||||||
|
|
||||||
b.Property<bool>("HasVanity")
|
|
||||||
.HasColumnType("boolean")
|
|
||||||
.HasColumnName("has_vanity");
|
|
||||||
|
|
||||||
b.Property<bool>("IsAdmin")
|
b.Property<bool>("IsAdmin")
|
||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
.HasColumnName("is_admin");
|
.HasColumnName("is_admin");
|
||||||
@@ -699,16 +653,6 @@ namespace LightlessSyncServer.Migrations
|
|||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("last_logged_in");
|
.HasColumnName("last_logged_in");
|
||||||
|
|
||||||
b.Property<string>("TextColorHex")
|
|
||||||
.HasMaxLength(9)
|
|
||||||
.HasColumnType("character varying(9)")
|
|
||||||
.HasColumnName("text_color_hex");
|
|
||||||
|
|
||||||
b.Property<string>("TextGlowColorHex")
|
|
||||||
.HasMaxLength(9)
|
|
||||||
.HasColumnType("character varying(9)")
|
|
||||||
.HasColumnName("text_glow_color_hex");
|
|
||||||
|
|
||||||
b.Property<byte[]>("Timestamp")
|
b.Property<byte[]>("Timestamp")
|
||||||
.IsConcurrencyToken()
|
.IsConcurrencyToken()
|
||||||
.ValueGeneratedOnAddOrUpdate()
|
.ValueGeneratedOnAddOrUpdate()
|
||||||
@@ -1066,16 +1010,6 @@ namespace LightlessSyncServer.Migrations
|
|||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LightlessSyncShared.Models.GroupProfile", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("LightlessSyncShared.Models.Group", "Group")
|
|
||||||
.WithOne("Profile")
|
|
||||||
.HasForeignKey("LightlessSyncShared.Models.GroupProfile", "GroupGID")
|
|
||||||
.HasConstraintName("fk_group_profiles_groups_group_gid");
|
|
||||||
|
|
||||||
b.Navigation("Group");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("LightlessSyncShared.Models.GroupTempInvite", b =>
|
modelBuilder.Entity("LightlessSyncShared.Models.GroupTempInvite", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("LightlessSyncShared.Models.Group", "Group")
|
b.HasOne("LightlessSyncShared.Models.Group", "Group")
|
||||||
@@ -1155,11 +1089,6 @@ namespace LightlessSyncServer.Migrations
|
|||||||
|
|
||||||
b.Navigation("Poses");
|
b.Navigation("Poses");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("LightlessSyncShared.Models.Group", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Profile");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,9 @@ public class Group
|
|||||||
public User Owner { get; set; }
|
public User Owner { get; set; }
|
||||||
[MaxLength(50)]
|
[MaxLength(50)]
|
||||||
public string Alias { get; set; }
|
public string Alias { get; set; }
|
||||||
public GroupProfile? Profile { get; set; }
|
|
||||||
public bool InvitesEnabled { get; set; }
|
public bool InvitesEnabled { get; set; }
|
||||||
public string HashedPassword { get; set; }
|
public string HashedPassword { get; set; }
|
||||||
public bool PreferDisableSounds { get; set; }
|
public bool PreferDisableSounds { get; set; }
|
||||||
public bool PreferDisableAnimations { get; set; }
|
public bool PreferDisableAnimations { get; set; }
|
||||||
public bool PreferDisableVFX { get; set; }
|
public bool PreferDisableVFX { get; set; }
|
||||||
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,4 @@ public class GroupPair
|
|||||||
public User GroupUser { get; set; }
|
public User GroupUser { get; set; }
|
||||||
public bool IsPinned { get; set; }
|
public bool IsPinned { get; set; }
|
||||||
public bool IsModerator { get; set; }
|
public bool IsModerator { get; set; }
|
||||||
public bool FromFinder { get; set; } = false;
|
|
||||||
public DateTime? JoinedGroupOn { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LightlessSyncShared.Models;
|
|
||||||
public class GroupProfile
|
|
||||||
{
|
|
||||||
[Key]
|
|
||||||
[MaxLength(20)]
|
|
||||||
public string GroupGID { get; set; }
|
|
||||||
public Group Group { get; set; }
|
|
||||||
public string Description { get; set; }
|
|
||||||
public string Tags { get; set; }
|
|
||||||
public string Base64GroupProfileImage { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace LightlessSyncShared.Models;
|
namespace LightlessSyncShared.Models;
|
||||||
|
|
||||||
@@ -14,14 +14,6 @@ public class User
|
|||||||
|
|
||||||
public bool IsAdmin { get; set; } = false;
|
public bool IsAdmin { get; set; } = false;
|
||||||
|
|
||||||
public bool HasVanity { get; set; } = false;
|
|
||||||
|
|
||||||
[MaxLength(9)]
|
|
||||||
public string? TextColorHex { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
[MaxLength(9)]
|
|
||||||
public string? TextGlowColorHex { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
public DateTime LastLoggedIn { get; set; }
|
public DateTime LastLoggedIn { get; set; }
|
||||||
[MaxLength(15)]
|
[MaxLength(15)]
|
||||||
public string Alias { get; set; }
|
public string Alias { get; set; }
|
||||||
|
|||||||
Reference in New Issue
Block a user