Mare 0.8 (#13)
* get rid of file handling through grpc and signalr * make non empty uri for serialization * adjust api DataHash calc into their respective classes * Revert "adapt api extensions" This reverts commit5fb78ba367. * Revert "adapt userpermissions" This reverts commit28dd123ca6. * Revert "Revert "adapt api extensions"" This reverts commit 12c9bc3c53566532e2984ace0b3d2e0a8b7facc0. * Revert "Revert "adapt userpermissions"" This reverts commit 4193ac008cccf2f5b42e9cdf50b2a084877cbf9a. * update flags * add upload status to server --------- Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
@@ -1,11 +1,30 @@
|
|||||||
using MareSynchronos.API.Data.Enum;
|
using MareSynchronos.API.Data.Enum;
|
||||||
using MessagePack;
|
using MessagePack;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace MareSynchronos.API.Data;
|
namespace MareSynchronos.API.Data;
|
||||||
|
|
||||||
[MessagePackObject(keyAsPropertyName: true)]
|
[MessagePackObject(keyAsPropertyName: true)]
|
||||||
public class CharacterData : HashableDataBase
|
public class CharacterData
|
||||||
{
|
{
|
||||||
|
public CharacterData()
|
||||||
|
{
|
||||||
|
DataHash = new(() =>
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(this);
|
||||||
|
#pragma warning disable SYSLIB0021 // Type or member is obsolete
|
||||||
|
using SHA256CryptoServiceProvider cryptoProvider = new();
|
||||||
|
#pragma warning restore SYSLIB0021 // Type or member is obsolete
|
||||||
|
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(json))).Replace("-", "", StringComparison.Ordinal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public Lazy<string> DataHash { get; }
|
||||||
|
|
||||||
public Dictionary<ObjectKind, List<FileReplacementData>> FileReplacements { get; set; } = new();
|
public Dictionary<ObjectKind, List<FileReplacementData>> FileReplacements { get; set; } = new();
|
||||||
public Dictionary<ObjectKind, string> GlamourerData { get; set; } = new();
|
public Dictionary<ObjectKind, string> GlamourerData { get; set; } = new();
|
||||||
public string ManipulationData { get; set; } = string.Empty;
|
public string ManipulationData { get; set; } = string.Empty;
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
[Flags]
|
[Flags]
|
||||||
public enum UserPermissions
|
public enum UserPermissions
|
||||||
{
|
{
|
||||||
NoneSet,
|
NoneSet = 0,
|
||||||
Paired,
|
Paired = 1,
|
||||||
Paused
|
Paused = 2,
|
||||||
|
DisableAnimations = 4,
|
||||||
|
DisableSounds = 8
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,29 @@
|
|||||||
using MessagePack;
|
using MessagePack;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
|
||||||
namespace MareSynchronos.API.Data;
|
namespace MareSynchronos.API.Data;
|
||||||
|
|
||||||
[MessagePackObject(keyAsPropertyName: true)]
|
[MessagePackObject(keyAsPropertyName: true)]
|
||||||
public class FileReplacementData : HashableDataBase
|
public class FileReplacementData
|
||||||
{
|
{
|
||||||
|
public FileReplacementData()
|
||||||
|
{
|
||||||
|
DataHash = new(() =>
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(this);
|
||||||
|
#pragma warning disable SYSLIB0021 // Type or member is obsolete
|
||||||
|
using SHA256CryptoServiceProvider cryptoProvider = new();
|
||||||
|
#pragma warning restore SYSLIB0021 // Type or member is obsolete
|
||||||
|
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(json))).Replace("-", "", StringComparison.Ordinal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public Lazy<string> DataHash { get; }
|
||||||
public string[] GamePaths { get; set; } = Array.Empty<string>();
|
public string[] GamePaths { get; set; } = Array.Empty<string>();
|
||||||
public string Hash { get; set; } = string.Empty;
|
public string Hash { get; set; } = string.Empty;
|
||||||
public string FileSwapPath { get; set; } = string.Empty;
|
public string FileSwapPath { get; set; } = string.Empty;
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
using MessagePack;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace MareSynchronos.API.Data;
|
|
||||||
|
|
||||||
public abstract class HashableDataBase
|
|
||||||
{
|
|
||||||
protected HashableDataBase()
|
|
||||||
{
|
|
||||||
DataHash = new(() =>
|
|
||||||
{
|
|
||||||
var json = JsonConvert.SerializeObject(this, Formatting.None);
|
|
||||||
using SHA256CryptoServiceProvider cryptoProvider = new();
|
|
||||||
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(json))).Replace("-", "", StringComparison.Ordinal);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Lazy<string> DataHash { get; }
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ namespace MareSynchronos.API.Dto;
|
|||||||
[MessagePackObject(keyAsPropertyName: true)]
|
[MessagePackObject(keyAsPropertyName: true)]
|
||||||
public record ConnectionDto(UserData User)
|
public record ConnectionDto(UserData User)
|
||||||
{
|
{
|
||||||
|
public Version CurrentClientVersion { get; set; } = new(0, 0, 0);
|
||||||
public int ServerVersion { get; set; }
|
public int ServerVersion { get; set; }
|
||||||
public bool IsAdmin { get; set; }
|
public bool IsAdmin { get; set; }
|
||||||
public bool IsModerator { get; set; }
|
public bool IsModerator { get; set; }
|
||||||
@@ -19,4 +20,5 @@ public record ServerInfo
|
|||||||
public int MaxGroupUserCount { get; set; }
|
public int MaxGroupUserCount { get; set; }
|
||||||
public int MaxGroupsCreatedByUser { get; set; }
|
public int MaxGroupsCreatedByUser { get; set; }
|
||||||
public int MaxGroupsJoinedByUser { get; set; }
|
public int MaxGroupsJoinedByUser { get; set; }
|
||||||
|
public Uri FileServerAddress { get; set; } = new Uri("http://nonemptyuri");
|
||||||
}
|
}
|
||||||
13
MareSynchronosAPI/Dto/Files/FilesSendDto.cs
Normal file
13
MareSynchronosAPI/Dto/Files/FilesSendDto.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MareSynchronos.API.Dto.Files;
|
||||||
|
|
||||||
|
public class FilesSendDto
|
||||||
|
{
|
||||||
|
public List<string> FileHashes { get; set; } = new();
|
||||||
|
public List<string> UIDs { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using MessagePack;
|
|
||||||
|
|
||||||
namespace MareSynchronos.API.Dto.Files;
|
|
||||||
|
|
||||||
[MessagePackObject(keyAsPropertyName: true)]
|
|
||||||
public record ForbiddenFileDto
|
|
||||||
{
|
|
||||||
public string Hash { get; set; } = string.Empty;
|
|
||||||
public string ForbiddenBy { get; set; } = string.Empty;
|
|
||||||
}
|
|
||||||
@@ -7,8 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MessagePack.Annotations" Version="2.1.90" />
|
<PackageReference Include="MessagePack.Annotations" Version="2.4.59" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -8,7 +8,15 @@ public class MareFiles
|
|||||||
|
|
||||||
public const string ServerFiles = "/files";
|
public const string ServerFiles = "/files";
|
||||||
public const string ServerFiles_Get = "get";
|
public const string ServerFiles_Get = "get";
|
||||||
|
public const string ServerFiles_Upload = "upload";
|
||||||
|
public const string ServerFiles_GetSizes = "getFileSizes";
|
||||||
|
public const string ServerFiles_FilesSend = "filesSend";
|
||||||
|
public const string ServerFiles_DeleteAll = "deleteAll";
|
||||||
public static Uri ServerFilesGetFullPath(Uri baseUri, string hash) => new(baseUri, ServerFiles + "/" + ServerFiles_Get + "/" + hash);
|
public static Uri ServerFilesGetFullPath(Uri baseUri, string hash) => new(baseUri, ServerFiles + "/" + ServerFiles_Get + "/" + hash);
|
||||||
|
public static Uri ServerFilesUploadFullPath(Uri baseUri, string hash) => new(baseUri, ServerFiles + "/" + ServerFiles_Upload + "/" + hash);
|
||||||
|
public static Uri ServerFilesGetSizesFullPath(Uri baseUri) => new(baseUri, ServerFiles + "/" + ServerFiles_GetSizes);
|
||||||
|
public static Uri ServerFilesFilesSendFullPath(Uri baseUri) => new(baseUri, ServerFiles + "/" + ServerFiles_FilesSend);
|
||||||
|
public static Uri ServerFilesDeleteAllFullPath(Uri baseUri) => new(baseUri, ServerFiles + "/" + ServerFiles_DeleteAll);
|
||||||
|
|
||||||
public const string Request = "/request";
|
public const string Request = "/request";
|
||||||
public const string Request_Enqueue = "enqueue";
|
public const string Request_Enqueue = "enqueue";
|
||||||
|
|||||||
@@ -2,70 +2,101 @@
|
|||||||
using MareSynchronos.API.Dto;
|
using MareSynchronos.API.Dto;
|
||||||
using MareSynchronos.API.Dto.Group;
|
using MareSynchronos.API.Dto.Group;
|
||||||
using MareSynchronos.API.Dto.User;
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.API.Dto.Files;
|
|
||||||
|
|
||||||
namespace MareSynchronos.API.SignalR;
|
namespace MareSynchronos.API.SignalR;
|
||||||
|
|
||||||
public interface IMareHub
|
public interface IMareHub
|
||||||
{
|
{
|
||||||
const int ApiVersion = 22;
|
const int ApiVersion = 23;
|
||||||
const string Path = "/mare";
|
const string Path = "/mare";
|
||||||
|
|
||||||
Task<ConnectionDto> GetConnectionDto();
|
|
||||||
Task<bool> CheckClientHealth();
|
Task<bool> CheckClientHealth();
|
||||||
|
|
||||||
Task<List<DownloadFileDto>> FilesGetSizes(List<string> hashes);
|
Task Client_DownloadReady(Guid requestId);
|
||||||
Task FilesAbortUpload();
|
|
||||||
Task FilesDeleteAll();
|
Task Client_GroupChangePermissions(GroupPermissionDto groupPermission);
|
||||||
Task<bool> FilesIsUploadFinished();
|
|
||||||
Task<List<UploadFileDto>> FilesSend(List<string> fileListHashes);
|
Task Client_GroupDelete(GroupDto groupDto);
|
||||||
Task FilesUploadStreamAsync(string hash, IAsyncEnumerable<byte[]> fileContent);
|
|
||||||
|
Task Client_GroupPairChangePermissions(GroupPairUserPermissionDto permissionDto);
|
||||||
|
|
||||||
|
Task Client_GroupPairChangeUserInfo(GroupPairUserInfoDto userInfo);
|
||||||
|
|
||||||
|
Task Client_GroupPairJoined(GroupPairFullInfoDto groupPairInfoDto);
|
||||||
|
|
||||||
|
Task Client_GroupPairLeft(GroupPairDto groupPairDto);
|
||||||
|
|
||||||
|
Task Client_GroupSendFullInfo(GroupFullInfoDto groupInfo);
|
||||||
|
|
||||||
|
Task Client_GroupSendInfo(GroupInfoDto groupInfo);
|
||||||
|
|
||||||
|
Task Client_ReceiveServerMessage(MessageSeverity messageSeverity, string message);
|
||||||
|
|
||||||
|
Task Client_UpdateSystemInfo(SystemInfoDto systemInfo);
|
||||||
|
|
||||||
|
Task Client_UserAddClientPair(UserPairDto dto);
|
||||||
|
|
||||||
|
Task Client_UserReceiveCharacterData(OnlineUserCharaDataDto dataDto);
|
||||||
|
|
||||||
|
Task Client_UserReceiveUploadStatus(UserDto dto);
|
||||||
|
|
||||||
|
Task Client_UserRemoveClientPair(UserDto dto);
|
||||||
|
|
||||||
|
Task Client_UserSendOffline(UserDto dto);
|
||||||
|
|
||||||
|
Task Client_UserSendOnline(OnlineUserIdentDto dto);
|
||||||
|
|
||||||
|
Task Client_UserUpdateOtherPairPermissions(UserPermissionsDto dto);
|
||||||
|
|
||||||
|
Task Client_UserUpdateSelfPairPermissions(UserPermissionsDto dto);
|
||||||
|
|
||||||
|
Task<ConnectionDto> GetConnectionDto();
|
||||||
|
|
||||||
|
Task GroupBanUser(GroupPairDto dto, string reason);
|
||||||
|
|
||||||
|
Task GroupChangeGroupPermissionState(GroupPermissionDto dto);
|
||||||
|
|
||||||
|
Task GroupChangeIndividualPermissionState(GroupPairUserPermissionDto dto);
|
||||||
|
|
||||||
|
Task GroupChangeOwnership(GroupPairDto groupPair);
|
||||||
|
|
||||||
|
Task<bool> GroupChangePassword(GroupPasswordDto groupPassword);
|
||||||
|
|
||||||
|
Task GroupClear(GroupDto group);
|
||||||
|
|
||||||
|
Task<GroupPasswordDto> GroupCreate();
|
||||||
|
|
||||||
|
Task<List<string>> GroupCreateTempInvite(GroupDto group, int amount);
|
||||||
|
|
||||||
|
Task GroupDelete(GroupDto group);
|
||||||
|
|
||||||
|
Task<List<BannedGroupUserDto>> GroupGetBannedUsers(GroupDto group);
|
||||||
|
|
||||||
|
Task<bool> GroupJoin(GroupPasswordDto passwordedGroup);
|
||||||
|
|
||||||
|
Task GroupLeave(GroupDto group);
|
||||||
|
|
||||||
|
Task GroupRemoveUser(GroupPairDto groupPair);
|
||||||
|
|
||||||
|
Task GroupSetUserInfo(GroupPairUserInfoDto groupPair);
|
||||||
|
|
||||||
|
Task<List<GroupFullInfoDto>> GroupsGetAll();
|
||||||
|
|
||||||
|
Task<List<GroupPairFullInfoDto>> GroupsGetUsersInGroup(GroupDto group);
|
||||||
|
|
||||||
|
Task GroupUnbanUser(GroupPairDto groupPair);
|
||||||
|
|
||||||
Task<List<UserPairDto>> UserGetPairedClients();
|
|
||||||
Task UserAddPair(UserDto user);
|
Task UserAddPair(UserDto user);
|
||||||
Task UserRemovePair(UserDto userDto);
|
|
||||||
Task UserSetPairPermissions(UserPermissionsDto userPermissions);
|
|
||||||
Task<List<OnlineUserIdentDto>> UserGetOnlinePairs();
|
|
||||||
Task UserPushData(UserCharaDataMessageDto dto);
|
|
||||||
|
|
||||||
Task UserDelete();
|
Task UserDelete();
|
||||||
|
|
||||||
Task<List<BannedGroupUserDto>> GroupGetBannedUsers(GroupDto group);
|
Task<List<OnlineUserIdentDto>> UserGetOnlinePairs();
|
||||||
Task GroupClear(GroupDto group);
|
|
||||||
Task GroupChangeOwnership(GroupPairDto groupPair);
|
|
||||||
Task<bool> GroupChangePassword(GroupPasswordDto groupPassword);
|
|
||||||
Task<GroupPasswordDto> GroupCreate();
|
|
||||||
Task<List<GroupFullInfoDto>> GroupsGetAll();
|
|
||||||
Task<List<GroupPairFullInfoDto>> GroupsGetUsersInGroup(GroupDto group);
|
|
||||||
Task GroupBanUser(GroupPairDto dto, string reason);
|
|
||||||
Task GroupChangeGroupPermissionState(GroupPermissionDto dto);
|
|
||||||
Task GroupChangeIndividualPermissionState(GroupPairUserPermissionDto dto);
|
|
||||||
Task GroupDelete(GroupDto group);
|
|
||||||
Task<bool> GroupJoin(GroupPasswordDto passwordedGroup);
|
|
||||||
Task GroupLeave(GroupDto group);
|
|
||||||
Task GroupRemoveUser(GroupPairDto groupPair);
|
|
||||||
Task GroupUnbanUser(GroupPairDto groupPair);
|
|
||||||
Task GroupSetUserInfo(GroupPairUserInfoDto groupPair);
|
|
||||||
Task<List<string>> GroupCreateTempInvite(GroupDto group, int amount);
|
|
||||||
|
|
||||||
Task Client_UpdateSystemInfo(SystemInfoDto systemInfo);
|
Task<List<UserPairDto>> UserGetPairedClients();
|
||||||
Task Client_ReceiveServerMessage(MessageSeverity messageSeverity, string message);
|
|
||||||
Task Client_DownloadReady(Guid requestId);
|
|
||||||
|
|
||||||
Task Client_UserSendOnline(OnlineUserIdentDto dto);
|
Task UserPushData(UserCharaDataMessageDto dto);
|
||||||
Task Client_UserSendOffline(UserDto dto);
|
|
||||||
Task Client_UserAddClientPair(UserPairDto dto);
|
|
||||||
Task Client_UserRemoveClientPair(UserDto dto);
|
|
||||||
Task Client_UserUpdateSelfPairPermissions(UserPermissionsDto dto);
|
|
||||||
Task Client_UserUpdateOtherPairPermissions(UserPermissionsDto dto);
|
|
||||||
Task Client_UserReceiveCharacterData(OnlineUserCharaDataDto dataDto);
|
|
||||||
|
|
||||||
Task Client_GroupSendFullInfo(GroupFullInfoDto groupInfo);
|
Task UserRemovePair(UserDto userDto);
|
||||||
Task Client_GroupSendInfo(GroupInfoDto groupInfo);
|
|
||||||
Task Client_GroupDelete(GroupDto groupDto);
|
Task UserSetPairPermissions(UserPermissionsDto userPermissions);
|
||||||
Task Client_GroupPairJoined(GroupPairFullInfoDto groupPairInfoDto);
|
|
||||||
Task Client_GroupPairLeft(GroupPairDto groupPairDto);
|
|
||||||
Task Client_GroupChangePermissions(GroupPermissionDto groupPermission);
|
|
||||||
Task Client_GroupPairChangePermissions(GroupPairUserPermissionDto permissionDto);
|
|
||||||
Task Client_GroupPairChangeUserInfo(GroupPairUserInfoDto userInfo);
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using MareSynchronos.API.Data.Enum;
|
using MareSynchronos.API.Data.Enum;
|
||||||
using MareSynchronos.API.Dto;
|
using MareSynchronos.API.Dto;
|
||||||
using MareSynchronos.API.Dto.Files;
|
|
||||||
using MareSynchronos.API.Dto.Group;
|
using MareSynchronos.API.Dto.Group;
|
||||||
using MareSynchronos.API.Dto.User;
|
using MareSynchronos.API.Dto.User;
|
||||||
|
|
||||||
@@ -8,24 +7,41 @@ namespace MareSynchronos.API.SignalR;
|
|||||||
|
|
||||||
public interface IMareHubClient : IMareHub
|
public interface IMareHubClient : IMareHub
|
||||||
{
|
{
|
||||||
void OnUpdateSystemInfo(Action<SystemInfoDto> act);
|
|
||||||
void OnReceiveServerMessage(Action<MessageSeverity, string> act);
|
|
||||||
void OnDownloadReady(Action<Guid> act);
|
void OnDownloadReady(Action<Guid> act);
|
||||||
|
|
||||||
void OnUserSendOnline(Action<OnlineUserIdentDto> act);
|
void OnGroupChangePermissions(Action<GroupPermissionDto> act);
|
||||||
void OnUserSendOffline(Action<UserDto> act);
|
|
||||||
void OnUserAddClientPair(Action<UserPairDto> act);
|
void OnGroupDelete(Action<GroupDto> act);
|
||||||
void OnUserRemoveClientPair(Action<UserDto> act);
|
|
||||||
void OnUserUpdateSelfPairPermissions(Action<UserPermissionsDto> act);
|
void OnGroupPairChangePermissions(Action<GroupPairUserPermissionDto> act);
|
||||||
void OnUserUpdateOtherPairPermissions(Action<UserPermissionsDto> act);
|
|
||||||
void OnUserReceiveCharacterData(Action<OnlineUserCharaDataDto> act);
|
void OnGroupPairChangeUserInfo(Action<GroupPairUserInfoDto> act);
|
||||||
|
|
||||||
|
void OnGroupPairJoined(Action<GroupPairFullInfoDto> act);
|
||||||
|
|
||||||
|
void OnGroupPairLeft(Action<GroupPairDto> act);
|
||||||
|
|
||||||
void OnGroupSendFullInfo(Action<GroupFullInfoDto> act);
|
void OnGroupSendFullInfo(Action<GroupFullInfoDto> act);
|
||||||
|
|
||||||
void OnGroupSendInfo(Action<GroupInfoDto> act);
|
void OnGroupSendInfo(Action<GroupInfoDto> act);
|
||||||
void OnGroupDelete(Action<GroupDto> act);
|
|
||||||
void OnGroupPairJoined(Action<GroupPairFullInfoDto> act);
|
void OnReceiveServerMessage(Action<MessageSeverity, string> act);
|
||||||
void OnGroupPairLeft(Action<GroupPairDto> act);
|
|
||||||
void OnGroupChangePermissions(Action<GroupPermissionDto> act);
|
void OnUpdateSystemInfo(Action<SystemInfoDto> act);
|
||||||
void OnGroupPairChangePermissions(Action<GroupPairUserPermissionDto> act);
|
|
||||||
void OnGroupPairChangeUserInfo(Action<GroupPairUserInfoDto> act);
|
void OnUserAddClientPair(Action<UserPairDto> act);
|
||||||
|
|
||||||
|
void OnUserReceiveCharacterData(Action<OnlineUserCharaDataDto> act);
|
||||||
|
|
||||||
|
void OnUserReceiveUploadStatus(Action<UserDto> act);
|
||||||
|
|
||||||
|
void OnUserRemoveClientPair(Action<UserDto> act);
|
||||||
|
|
||||||
|
void OnUserSendOffline(Action<UserDto> act);
|
||||||
|
|
||||||
|
void OnUserSendOnline(Action<OnlineUserIdentDto> act);
|
||||||
|
|
||||||
|
void OnUserUpdateOtherPairPermissions(Action<UserPermissionsDto> act);
|
||||||
|
|
||||||
|
void OnUserUpdateSelfPairPermissions(Action<UserPermissionsDto> act);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user