add api to mare, change all to file scoped namespace
This commit is contained in:
@@ -13,306 +13,305 @@ using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI.Utils;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
|
||||
namespace MareSynchronos.WebAPI
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public partial class ApiController
|
||||
{
|
||||
public partial class ApiController
|
||||
private readonly HashSet<string> _verifiedUploadedHashes;
|
||||
|
||||
private int _downloadId = 0;
|
||||
public void CancelUpload()
|
||||
{
|
||||
private readonly HashSet<string> _verifiedUploadedHashes;
|
||||
|
||||
private int _downloadId = 0;
|
||||
public void CancelUpload()
|
||||
if (_uploadCancellationTokenSource != null)
|
||||
{
|
||||
if (_uploadCancellationTokenSource != null)
|
||||
{
|
||||
Logger.Debug("Cancelling upload");
|
||||
_uploadCancellationTokenSource?.Cancel();
|
||||
_mareHub!.SendAsync(Api.SendFileAbortUpload);
|
||||
CurrentUploads.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAllMyFiles()
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendFileDeleteAllFiles);
|
||||
}
|
||||
|
||||
private async Task<string> DownloadFile(int downloadId, string hash, Uri downloadUri, CancellationToken ct)
|
||||
{
|
||||
using WebClient wc = new();
|
||||
wc.Headers.Add("Authorization", SecretKey);
|
||||
DownloadProgressChangedEventHandler progChanged = (s, e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentDownloads[downloadId].Single(f => f.Hash == hash).Transferred = e.BytesReceived;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn("Could not set download progress for " + hash);
|
||||
Logger.Warn(ex.Message);
|
||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||
}
|
||||
};
|
||||
wc.DownloadProgressChanged += progChanged;
|
||||
|
||||
string fileName = Path.GetTempFileName();
|
||||
|
||||
ct.Register(wc.CancelAsync);
|
||||
|
||||
try
|
||||
{
|
||||
await wc.DownloadFileTaskAsync(downloadUri, fileName);
|
||||
}
|
||||
catch { }
|
||||
|
||||
CurrentDownloads[downloadId].Single(f => f.Hash == hash).Transferred = CurrentDownloads[downloadId].Single(f => f.Hash == hash).Total;
|
||||
|
||||
wc.DownloadProgressChanged -= progChanged;
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public int GetDownloadId() => _downloadId++;
|
||||
|
||||
public async Task DownloadFiles(int currentDownloadId, List<FileReplacementDto> fileReplacementDto, CancellationToken ct)
|
||||
{
|
||||
DownloadStarted?.Invoke();
|
||||
try
|
||||
{
|
||||
await DownloadFilesInternal(currentDownloadId, fileReplacementDto, ct);
|
||||
}
|
||||
catch
|
||||
{
|
||||
CancelDownload(currentDownloadId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DownloadFinished?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadFilesInternal(int currentDownloadId, List<FileReplacementDto> fileReplacementDto, CancellationToken ct)
|
||||
{
|
||||
Logger.Debug("Downloading files (Download ID " + currentDownloadId + ")");
|
||||
|
||||
List<DownloadFileDto> downloadFileInfoFromService = new List<DownloadFileDto>();
|
||||
downloadFileInfoFromService.AddRange(await _mareHub!.InvokeAsync<List<DownloadFileDto>>(Api.InvokeGetFilesSizes, fileReplacementDto.Select(f => f.Hash).ToList(), ct));
|
||||
|
||||
Logger.Debug("Files with size 0 or less: " + string.Join(", ", downloadFileInfoFromService.Where(f => f.Size <= 0).Select(f => f.Hash)));
|
||||
|
||||
CurrentDownloads[currentDownloadId] = downloadFileInfoFromService.Distinct().Select(d => new DownloadFileTransfer(d))
|
||||
.Where(d => d.CanBeTransferred).ToList();
|
||||
|
||||
foreach (var dto in downloadFileInfoFromService.Where(c => c.IsForbidden))
|
||||
{
|
||||
if (ForbiddenTransfers.All(f => f.Hash != dto.Hash))
|
||||
{
|
||||
ForbiddenTransfers.Add(new DownloadFileTransfer(dto));
|
||||
}
|
||||
}
|
||||
|
||||
await Parallel.ForEachAsync(CurrentDownloads[currentDownloadId].Where(f => f.CanBeTransferred), new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = 5,
|
||||
CancellationToken = ct
|
||||
},
|
||||
async (file, token) =>
|
||||
{
|
||||
var hash = file.Hash;
|
||||
var tempFile = await DownloadFile(currentDownloadId, file.Hash, file.DownloadUri, token);
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
Logger.Debug("Detected cancellation, removing " + currentDownloadId);
|
||||
DownloadFinished?.Invoke();
|
||||
CancelDownload(currentDownloadId);
|
||||
return;
|
||||
}
|
||||
|
||||
var tempFileData = await File.ReadAllBytesAsync(tempFile, token);
|
||||
var extractedFile = LZ4Codec.Unwrap(tempFileData);
|
||||
File.Delete(tempFile);
|
||||
var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash);
|
||||
await File.WriteAllBytesAsync(filePath, extractedFile, token);
|
||||
var fi = new FileInfo(filePath);
|
||||
Func<DateTime> RandomDayFunc()
|
||||
{
|
||||
DateTime start = new DateTime(1995, 1, 1);
|
||||
Random gen = new Random();
|
||||
int range = (DateTime.Today - start).Days;
|
||||
return () => start.AddDays(gen.Next(range));
|
||||
}
|
||||
|
||||
fi.CreationTime = RandomDayFunc().Invoke();
|
||||
fi.LastAccessTime = RandomDayFunc().Invoke();
|
||||
fi.LastWriteTime = RandomDayFunc().Invoke();
|
||||
try
|
||||
{
|
||||
_ = _fileDbManager.CreateCacheEntry(filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn("Issue adding file to the DB");
|
||||
Logger.Warn(ex.Message);
|
||||
Logger.Warn(ex.StackTrace);
|
||||
}
|
||||
});
|
||||
|
||||
Logger.Debug("Download complete, removing " + currentDownloadId);
|
||||
CancelDownload(currentDownloadId);
|
||||
}
|
||||
|
||||
public async Task PushCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
Logger.Debug("Sending Character data to service " + ApiUri);
|
||||
|
||||
CancelUpload();
|
||||
_uploadCancellationTokenSource = new CancellationTokenSource();
|
||||
var uploadToken = _uploadCancellationTokenSource.Token;
|
||||
Logger.Verbose("New Token Created");
|
||||
|
||||
List<string> unverifiedUploadHashes = new();
|
||||
foreach (var item in character.FileReplacements.SelectMany(c => c.Value.Where(f => string.IsNullOrEmpty(f.FileSwapPath)).Select(v => v.Hash).Distinct()).Distinct().ToList())
|
||||
{
|
||||
if (!_verifiedUploadedHashes.Contains(item))
|
||||
{
|
||||
unverifiedUploadHashes.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (unverifiedUploadHashes.Any())
|
||||
{
|
||||
Logger.Debug("Verifying " + unverifiedUploadHashes.Count + " files");
|
||||
var filesToUpload = await _mareHub!.InvokeAsync<List<UploadFileDto>>(Api.InvokeFileSendFiles, unverifiedUploadHashes, uploadToken);
|
||||
|
||||
foreach (var file in filesToUpload.Where(f => !f.IsForbidden))
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentUploads.Add(new UploadFileTransfer(file)
|
||||
{
|
||||
Total = new FileInfo(_fileDbManager.GetFileCacheByHash(file.Hash)!.ResolvedFilepath).Length
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn("Tried to request file " + file.Hash + " but file was not present");
|
||||
Logger.Warn(ex.StackTrace!);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var file in filesToUpload.Where(c => c.IsForbidden))
|
||||
{
|
||||
if (ForbiddenTransfers.All(f => f.Hash != file.Hash))
|
||||
{
|
||||
ForbiddenTransfers.Add(new UploadFileTransfer(file)
|
||||
{
|
||||
LocalFile = _fileDbManager.GetFileCacheByHash(file.Hash)?.ResolvedFilepath ?? string.Empty
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var totalSize = CurrentUploads.Sum(c => c.Total);
|
||||
Logger.Debug("Compressing and uploading files");
|
||||
foreach (var file in CurrentUploads.Where(f => f.CanBeTransferred && !f.IsTransferred).ToList())
|
||||
{
|
||||
Logger.Debug("Compressing and uploading " + file);
|
||||
var data = await GetCompressedFileData(file.Hash, uploadToken);
|
||||
CurrentUploads.Single(e => e.Hash == data.Item1).Total = data.Item2.Length;
|
||||
await UploadFile(data.Item2, file.Hash, uploadToken);
|
||||
if (!uploadToken.IsCancellationRequested) continue;
|
||||
Logger.Warn("Cancel in filesToUpload loop detected");
|
||||
CurrentUploads.Clear();
|
||||
break;
|
||||
}
|
||||
|
||||
if (CurrentUploads.Any())
|
||||
{
|
||||
var compressedSize = CurrentUploads.Sum(c => c.Total);
|
||||
Logger.Debug($"Compressed {totalSize} to {compressedSize} ({(compressedSize / (double)totalSize):P2})");
|
||||
}
|
||||
|
||||
Logger.Debug("Upload tasks complete, waiting for server to confirm");
|
||||
var anyUploadsOpen = await _mareHub!.InvokeAsync<bool>(Api.InvokeFileIsUploadFinished, uploadToken);
|
||||
Logger.Debug("Uploads open: " + anyUploadsOpen);
|
||||
while (anyUploadsOpen && !uploadToken.IsCancellationRequested)
|
||||
{
|
||||
anyUploadsOpen = await _mareHub!.InvokeAsync<bool>(Api.InvokeFileIsUploadFinished, uploadToken);
|
||||
await Task.Delay(TimeSpan.FromSeconds(0.5), uploadToken);
|
||||
Logger.Debug("Waiting for uploads to finish");
|
||||
}
|
||||
|
||||
foreach (var item in unverifiedUploadHashes)
|
||||
{
|
||||
_verifiedUploadedHashes.Add(item);
|
||||
}
|
||||
|
||||
CurrentUploads.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug("All files already verified");
|
||||
}
|
||||
|
||||
if (!uploadToken.IsCancellationRequested)
|
||||
{
|
||||
Logger.Info("Pushing character data for " + character.GetHashCode() + " to " + string.Join(", ", visibleCharacterIds));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var item in character.FileReplacements)
|
||||
{
|
||||
sb.AppendLine($"FileReplacements for {item.Key}: {item.Value.Count}");
|
||||
}
|
||||
foreach (var item in character.GlamourerData)
|
||||
{
|
||||
sb.AppendLine($"GlamourerData for {item.Key}: {!string.IsNullOrEmpty(item.Value)}");
|
||||
}
|
||||
Logger.Debug("Chara data contained: " + Environment.NewLine + sb.ToString());
|
||||
await _mareHub!.InvokeAsync(Api.InvokeUserPushCharacterDataToVisibleClients, character, visibleCharacterIds, uploadToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("=== Upload operation was cancelled ===");
|
||||
}
|
||||
|
||||
Logger.Verbose("Upload complete for " + character.GetHashCode());
|
||||
_uploadCancellationTokenSource = null;
|
||||
}
|
||||
|
||||
private async Task<(string, byte[])> GetCompressedFileData(string fileHash, CancellationToken uploadToken)
|
||||
{
|
||||
var fileCache = _fileDbManager.GetFileCacheByHash(fileHash)!.ResolvedFilepath;
|
||||
return (fileHash, LZ4Codec.WrapHC(await File.ReadAllBytesAsync(fileCache, uploadToken), 0,
|
||||
(int)new FileInfo(fileCache).Length));
|
||||
}
|
||||
|
||||
private async Task UploadFile(byte[] compressedFile, string fileHash, CancellationToken uploadToken)
|
||||
{
|
||||
if (uploadToken.IsCancellationRequested) return;
|
||||
|
||||
async IAsyncEnumerable<byte[]> AsyncFileData([EnumeratorCancellation] CancellationToken token)
|
||||
{
|
||||
var chunkSize = 1024 * 512; // 512kb
|
||||
using var ms = new MemoryStream(compressedFile);
|
||||
var buffer = new byte[chunkSize];
|
||||
int bytesRead;
|
||||
while ((bytesRead = await ms.ReadAsync(buffer, 0, chunkSize, token)) > 0 && !token.IsCancellationRequested)
|
||||
{
|
||||
CurrentUploads.Single(f => f.Hash == fileHash).Transferred += bytesRead;
|
||||
token.ThrowIfCancellationRequested();
|
||||
yield return bytesRead == chunkSize ? buffer.ToArray() : buffer.Take(bytesRead).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
await _mareHub!.SendAsync(Api.SendFileUploadFileStreamAsync, fileHash, AsyncFileData(uploadToken), uploadToken);
|
||||
}
|
||||
|
||||
public void CancelDownload(int downloadId)
|
||||
{
|
||||
while (CurrentDownloads.ContainsKey(downloadId))
|
||||
{
|
||||
CurrentDownloads.TryRemove(downloadId, out _);
|
||||
}
|
||||
Logger.Debug("Cancelling upload");
|
||||
_uploadCancellationTokenSource?.Cancel();
|
||||
_mareHub!.SendAsync(Api.SendFileAbortUpload);
|
||||
CurrentUploads.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAllMyFiles()
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendFileDeleteAllFiles);
|
||||
}
|
||||
|
||||
private async Task<string> DownloadFile(int downloadId, string hash, Uri downloadUri, CancellationToken ct)
|
||||
{
|
||||
using WebClient wc = new();
|
||||
wc.Headers.Add("Authorization", SecretKey);
|
||||
DownloadProgressChangedEventHandler progChanged = (s, e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentDownloads[downloadId].Single(f => f.Hash == hash).Transferred = e.BytesReceived;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn("Could not set download progress for " + hash);
|
||||
Logger.Warn(ex.Message);
|
||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||
}
|
||||
};
|
||||
wc.DownloadProgressChanged += progChanged;
|
||||
|
||||
string fileName = Path.GetTempFileName();
|
||||
|
||||
ct.Register(wc.CancelAsync);
|
||||
|
||||
try
|
||||
{
|
||||
await wc.DownloadFileTaskAsync(downloadUri, fileName);
|
||||
}
|
||||
catch { }
|
||||
|
||||
CurrentDownloads[downloadId].Single(f => f.Hash == hash).Transferred = CurrentDownloads[downloadId].Single(f => f.Hash == hash).Total;
|
||||
|
||||
wc.DownloadProgressChanged -= progChanged;
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public int GetDownloadId() => _downloadId++;
|
||||
|
||||
public async Task DownloadFiles(int currentDownloadId, List<FileReplacementDto> fileReplacementDto, CancellationToken ct)
|
||||
{
|
||||
DownloadStarted?.Invoke();
|
||||
try
|
||||
{
|
||||
await DownloadFilesInternal(currentDownloadId, fileReplacementDto, ct);
|
||||
}
|
||||
catch
|
||||
{
|
||||
CancelDownload(currentDownloadId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DownloadFinished?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadFilesInternal(int currentDownloadId, List<FileReplacementDto> fileReplacementDto, CancellationToken ct)
|
||||
{
|
||||
Logger.Debug("Downloading files (Download ID " + currentDownloadId + ")");
|
||||
|
||||
List<DownloadFileDto> downloadFileInfoFromService = new List<DownloadFileDto>();
|
||||
downloadFileInfoFromService.AddRange(await _mareHub!.InvokeAsync<List<DownloadFileDto>>(Api.InvokeGetFilesSizes, fileReplacementDto.Select(f => f.Hash).ToList(), ct));
|
||||
|
||||
Logger.Debug("Files with size 0 or less: " + string.Join(", ", downloadFileInfoFromService.Where(f => f.Size <= 0).Select(f => f.Hash)));
|
||||
|
||||
CurrentDownloads[currentDownloadId] = downloadFileInfoFromService.Distinct().Select(d => new DownloadFileTransfer(d))
|
||||
.Where(d => d.CanBeTransferred).ToList();
|
||||
|
||||
foreach (var dto in downloadFileInfoFromService.Where(c => c.IsForbidden))
|
||||
{
|
||||
if (ForbiddenTransfers.All(f => f.Hash != dto.Hash))
|
||||
{
|
||||
ForbiddenTransfers.Add(new DownloadFileTransfer(dto));
|
||||
}
|
||||
}
|
||||
|
||||
await Parallel.ForEachAsync(CurrentDownloads[currentDownloadId].Where(f => f.CanBeTransferred), new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = 5,
|
||||
CancellationToken = ct
|
||||
},
|
||||
async (file, token) =>
|
||||
{
|
||||
var hash = file.Hash;
|
||||
var tempFile = await DownloadFile(currentDownloadId, file.Hash, file.DownloadUri, token);
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
Logger.Debug("Detected cancellation, removing " + currentDownloadId);
|
||||
DownloadFinished?.Invoke();
|
||||
CancelDownload(currentDownloadId);
|
||||
return;
|
||||
}
|
||||
|
||||
var tempFileData = await File.ReadAllBytesAsync(tempFile, token);
|
||||
var extractedFile = LZ4Codec.Unwrap(tempFileData);
|
||||
File.Delete(tempFile);
|
||||
var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash);
|
||||
await File.WriteAllBytesAsync(filePath, extractedFile, token);
|
||||
var fi = new FileInfo(filePath);
|
||||
Func<DateTime> RandomDayFunc()
|
||||
{
|
||||
DateTime start = new DateTime(1995, 1, 1);
|
||||
Random gen = new Random();
|
||||
int range = (DateTime.Today - start).Days;
|
||||
return () => start.AddDays(gen.Next(range));
|
||||
}
|
||||
|
||||
fi.CreationTime = RandomDayFunc().Invoke();
|
||||
fi.LastAccessTime = RandomDayFunc().Invoke();
|
||||
fi.LastWriteTime = RandomDayFunc().Invoke();
|
||||
try
|
||||
{
|
||||
_ = _fileDbManager.CreateCacheEntry(filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn("Issue adding file to the DB");
|
||||
Logger.Warn(ex.Message);
|
||||
Logger.Warn(ex.StackTrace);
|
||||
}
|
||||
});
|
||||
|
||||
Logger.Debug("Download complete, removing " + currentDownloadId);
|
||||
CancelDownload(currentDownloadId);
|
||||
}
|
||||
|
||||
public async Task PushCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
Logger.Debug("Sending Character data to service " + ApiUri);
|
||||
|
||||
CancelUpload();
|
||||
_uploadCancellationTokenSource = new CancellationTokenSource();
|
||||
var uploadToken = _uploadCancellationTokenSource.Token;
|
||||
Logger.Verbose("New Token Created");
|
||||
|
||||
List<string> unverifiedUploadHashes = new();
|
||||
foreach (var item in character.FileReplacements.SelectMany(c => c.Value.Where(f => string.IsNullOrEmpty(f.FileSwapPath)).Select(v => v.Hash).Distinct()).Distinct().ToList())
|
||||
{
|
||||
if (!_verifiedUploadedHashes.Contains(item))
|
||||
{
|
||||
unverifiedUploadHashes.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (unverifiedUploadHashes.Any())
|
||||
{
|
||||
Logger.Debug("Verifying " + unverifiedUploadHashes.Count + " files");
|
||||
var filesToUpload = await _mareHub!.InvokeAsync<List<UploadFileDto>>(Api.InvokeFileSendFiles, unverifiedUploadHashes, uploadToken);
|
||||
|
||||
foreach (var file in filesToUpload.Where(f => !f.IsForbidden))
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentUploads.Add(new UploadFileTransfer(file)
|
||||
{
|
||||
Total = new FileInfo(_fileDbManager.GetFileCacheByHash(file.Hash)!.ResolvedFilepath).Length
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn("Tried to request file " + file.Hash + " but file was not present");
|
||||
Logger.Warn(ex.StackTrace!);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var file in filesToUpload.Where(c => c.IsForbidden))
|
||||
{
|
||||
if (ForbiddenTransfers.All(f => f.Hash != file.Hash))
|
||||
{
|
||||
ForbiddenTransfers.Add(new UploadFileTransfer(file)
|
||||
{
|
||||
LocalFile = _fileDbManager.GetFileCacheByHash(file.Hash)?.ResolvedFilepath ?? string.Empty
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var totalSize = CurrentUploads.Sum(c => c.Total);
|
||||
Logger.Debug("Compressing and uploading files");
|
||||
foreach (var file in CurrentUploads.Where(f => f.CanBeTransferred && !f.IsTransferred).ToList())
|
||||
{
|
||||
Logger.Debug("Compressing and uploading " + file);
|
||||
var data = await GetCompressedFileData(file.Hash, uploadToken);
|
||||
CurrentUploads.Single(e => e.Hash == data.Item1).Total = data.Item2.Length;
|
||||
await UploadFile(data.Item2, file.Hash, uploadToken);
|
||||
if (!uploadToken.IsCancellationRequested) continue;
|
||||
Logger.Warn("Cancel in filesToUpload loop detected");
|
||||
CurrentUploads.Clear();
|
||||
break;
|
||||
}
|
||||
|
||||
if (CurrentUploads.Any())
|
||||
{
|
||||
var compressedSize = CurrentUploads.Sum(c => c.Total);
|
||||
Logger.Debug($"Compressed {totalSize} to {compressedSize} ({(compressedSize / (double)totalSize):P2})");
|
||||
}
|
||||
|
||||
Logger.Debug("Upload tasks complete, waiting for server to confirm");
|
||||
var anyUploadsOpen = await _mareHub!.InvokeAsync<bool>(Api.InvokeFileIsUploadFinished, uploadToken);
|
||||
Logger.Debug("Uploads open: " + anyUploadsOpen);
|
||||
while (anyUploadsOpen && !uploadToken.IsCancellationRequested)
|
||||
{
|
||||
anyUploadsOpen = await _mareHub!.InvokeAsync<bool>(Api.InvokeFileIsUploadFinished, uploadToken);
|
||||
await Task.Delay(TimeSpan.FromSeconds(0.5), uploadToken);
|
||||
Logger.Debug("Waiting for uploads to finish");
|
||||
}
|
||||
|
||||
foreach (var item in unverifiedUploadHashes)
|
||||
{
|
||||
_verifiedUploadedHashes.Add(item);
|
||||
}
|
||||
|
||||
CurrentUploads.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug("All files already verified");
|
||||
}
|
||||
|
||||
if (!uploadToken.IsCancellationRequested)
|
||||
{
|
||||
Logger.Info("Pushing character data for " + character.GetHashCode() + " to " + string.Join(", ", visibleCharacterIds));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var item in character.FileReplacements)
|
||||
{
|
||||
sb.AppendLine($"FileReplacements for {item.Key}: {item.Value.Count}");
|
||||
}
|
||||
foreach (var item in character.GlamourerData)
|
||||
{
|
||||
sb.AppendLine($"GlamourerData for {item.Key}: {!string.IsNullOrEmpty(item.Value)}");
|
||||
}
|
||||
Logger.Debug("Chara data contained: " + Environment.NewLine + sb.ToString());
|
||||
await _mareHub!.InvokeAsync(Api.InvokeUserPushCharacterDataToVisibleClients, character, visibleCharacterIds, uploadToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("=== Upload operation was cancelled ===");
|
||||
}
|
||||
|
||||
Logger.Verbose("Upload complete for " + character.GetHashCode());
|
||||
_uploadCancellationTokenSource = null;
|
||||
}
|
||||
|
||||
private async Task<(string, byte[])> GetCompressedFileData(string fileHash, CancellationToken uploadToken)
|
||||
{
|
||||
var fileCache = _fileDbManager.GetFileCacheByHash(fileHash)!.ResolvedFilepath;
|
||||
return (fileHash, LZ4Codec.WrapHC(await File.ReadAllBytesAsync(fileCache, uploadToken), 0,
|
||||
(int)new FileInfo(fileCache).Length));
|
||||
}
|
||||
|
||||
private async Task UploadFile(byte[] compressedFile, string fileHash, CancellationToken uploadToken)
|
||||
{
|
||||
if (uploadToken.IsCancellationRequested) return;
|
||||
|
||||
async IAsyncEnumerable<byte[]> AsyncFileData([EnumeratorCancellation] CancellationToken token)
|
||||
{
|
||||
var chunkSize = 1024 * 512; // 512kb
|
||||
using var ms = new MemoryStream(compressedFile);
|
||||
var buffer = new byte[chunkSize];
|
||||
int bytesRead;
|
||||
while ((bytesRead = await ms.ReadAsync(buffer, 0, chunkSize, token)) > 0 && !token.IsCancellationRequested)
|
||||
{
|
||||
CurrentUploads.Single(f => f.Hash == fileHash).Transferred += bytesRead;
|
||||
token.ThrowIfCancellationRequested();
|
||||
yield return bytesRead == chunkSize ? buffer.ToArray() : buffer.Take(bytesRead).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
await _mareHub!.SendAsync(Api.SendFileUploadFileStreamAsync, fileHash, AsyncFileData(uploadToken), uploadToken);
|
||||
}
|
||||
|
||||
public void CancelDownload(int downloadId)
|
||||
{
|
||||
while (CurrentDownloads.ContainsKey(downloadId))
|
||||
{
|
||||
CurrentDownloads.TryRemove(downloadId, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.Utils;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
|
||||
namespace MareSynchronos.WebAPI
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public partial class ApiController
|
||||
{
|
||||
public partial class ApiController
|
||||
public async Task DeleteAccount()
|
||||
{
|
||||
public async Task DeleteAccount()
|
||||
{
|
||||
_pluginConfiguration.ClientSecret.Remove(ApiUri);
|
||||
_pluginConfiguration.Save();
|
||||
await _mareHub!.SendAsync(Api.SendFileDeleteAllFiles);
|
||||
await _mareHub!.SendAsync(Api.SendUserDeleteAccount);
|
||||
await CreateConnections();
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetOnlineCharacters()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<string>>(Api.InvokeUserGetOnlineCharacters);
|
||||
}
|
||||
|
||||
public async Task SendPairedClientAddition(string uid)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _mareHub!.SendAsync(Api.SendUserPairedClientAddition, uid);
|
||||
}
|
||||
|
||||
public async Task SendPairedClientPauseChange(string uid, bool paused)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _mareHub!.SendAsync(Api.SendUserPairedClientPauseChange, uid, paused);
|
||||
}
|
||||
|
||||
public async Task SendPairedClientRemoval(string uid)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _mareHub!.SendAsync(Api.SendUserPairedClientRemoval, uid);
|
||||
}
|
||||
_pluginConfiguration.ClientSecret.Remove(ApiUri);
|
||||
_pluginConfiguration.Save();
|
||||
await _mareHub!.SendAsync(Api.SendFileDeleteAllFiles);
|
||||
await _mareHub!.SendAsync(Api.SendUserDeleteAccount);
|
||||
await CreateConnections();
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetOnlineCharacters()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<string>>(Api.InvokeUserGetOnlineCharacters);
|
||||
}
|
||||
|
||||
public async Task SendPairedClientAddition(string uid)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _mareHub!.SendAsync(Api.SendUserPairedClientAddition, uid);
|
||||
}
|
||||
|
||||
public async Task SendPairedClientPauseChange(string uid, bool paused)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _mareHub!.SendAsync(Api.SendUserPairedClientPauseChange, uid, paused);
|
||||
}
|
||||
|
||||
public async Task SendPairedClientRemoval(string uid)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _mareHub!.SendAsync(Api.SendUserPairedClientRemoval, uid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,372 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI.Utils;
|
||||
using Microsoft.AspNetCore.Http.Connections;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.WebAPI
|
||||
{
|
||||
public delegate void SimpleStringDelegate(string str);
|
||||
public enum ServerState
|
||||
{
|
||||
Offline,
|
||||
Disconnected,
|
||||
Connected,
|
||||
Unauthorized,
|
||||
VersionMisMatch,
|
||||
RateLimited
|
||||
}
|
||||
|
||||
public partial class ApiController : IDisposable
|
||||
{
|
||||
public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)";
|
||||
public const string MainServiceUri = "wss://maresynchronos.com";
|
||||
|
||||
public readonly int[] SupportedServerVersions = { Api.Version };
|
||||
|
||||
private readonly Configuration _pluginConfiguration;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly FileCacheManager _fileDbManager;
|
||||
private CancellationTokenSource _connectionCancellationTokenSource;
|
||||
|
||||
private HubConnection? _mareHub;
|
||||
|
||||
private CancellationTokenSource? _uploadCancellationTokenSource = new();
|
||||
|
||||
private ConnectionDto? _connectionDto;
|
||||
public SystemInfoDto SystemInfoDto { get; private set; } = new();
|
||||
public bool IsModerator => (_connectionDto?.IsAdmin ?? false) || (_connectionDto?.IsModerator ?? false);
|
||||
|
||||
public bool IsAdmin => _connectionDto?.IsAdmin ?? false;
|
||||
|
||||
public ApiController(Configuration pluginConfiguration, DalamudUtil dalamudUtil, FileCacheManager fileDbManager)
|
||||
{
|
||||
Logger.Verbose("Creating " + nameof(ApiController));
|
||||
|
||||
_pluginConfiguration = pluginConfiguration;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_fileDbManager = fileDbManager;
|
||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||
_dalamudUtil.LogIn += DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut += DalamudUtilOnLogOut;
|
||||
ServerState = ServerState.Offline;
|
||||
_verifiedUploadedHashes = new();
|
||||
|
||||
if (_dalamudUtil.IsLoggedIn)
|
||||
{
|
||||
DalamudUtilOnLogIn();
|
||||
}
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogOut()
|
||||
{
|
||||
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token));
|
||||
ServerState = ServerState.Offline;
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogIn()
|
||||
{
|
||||
Task.Run(CreateConnections);
|
||||
}
|
||||
|
||||
|
||||
public event EventHandler<CharacterReceivedEventArgs>? CharacterReceived;
|
||||
|
||||
public event VoidDelegate? Connected;
|
||||
|
||||
public event VoidDelegate? Disconnected;
|
||||
|
||||
public event SimpleStringDelegate? PairedClientOffline;
|
||||
|
||||
public event SimpleStringDelegate? PairedClientOnline;
|
||||
|
||||
public event SimpleStringDelegate? PairedWithOther;
|
||||
|
||||
public event SimpleStringDelegate? UnpairedFromOther;
|
||||
public event VoidDelegate? DownloadStarted;
|
||||
public event VoidDelegate? DownloadFinished;
|
||||
|
||||
public ConcurrentDictionary<int, List<DownloadFileTransfer>> CurrentDownloads { get; } = new();
|
||||
|
||||
public List<FileTransfer> CurrentUploads { get; } = new();
|
||||
|
||||
public List<FileTransfer> ForbiddenTransfers { get; } = new();
|
||||
|
||||
public List<BannedUserDto> AdminBannedUsers { get; private set; } = new();
|
||||
|
||||
public List<ForbiddenFileDto> AdminForbiddenFiles { get; private set; } = new();
|
||||
|
||||
public bool IsConnected => ServerState == ServerState.Connected;
|
||||
|
||||
public bool IsDownloading => CurrentDownloads.Count > 0;
|
||||
|
||||
public bool IsUploading => CurrentUploads.Count > 0;
|
||||
|
||||
public List<ClientPairDto> PairedClients { get; set; } = new();
|
||||
|
||||
public string SecretKey => _pluginConfiguration.ClientSecret.ContainsKey(ApiUri)
|
||||
? _pluginConfiguration.ClientSecret[ApiUri] : string.Empty;
|
||||
|
||||
public bool ServerAlive => ServerState is ServerState.Connected or ServerState.RateLimited or ServerState.Unauthorized or ServerState.Disconnected;
|
||||
|
||||
public Dictionary<string, string> ServerDictionary => new Dictionary<string, string>()
|
||||
{ { MainServiceUri, MainServer } }
|
||||
.Concat(_pluginConfiguration.CustomServerList)
|
||||
.ToDictionary(k => k.Key, k => k.Value);
|
||||
|
||||
public string UID => _connectionDto?.UID ?? string.Empty;
|
||||
private string ApiUri => _pluginConfiguration.ApiUri;
|
||||
public int OnlineUsers => SystemInfoDto.OnlineUsers;
|
||||
|
||||
private ServerState _serverState;
|
||||
public ServerState ServerState
|
||||
{
|
||||
get => _serverState;
|
||||
private set
|
||||
{
|
||||
Logger.Debug($"New ServerState: {value}, prev ServerState: {_serverState}");
|
||||
_serverState = value;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CreateConnections()
|
||||
{
|
||||
Logger.Debug("CreateConnections called");
|
||||
|
||||
if (_pluginConfiguration.FullPause)
|
||||
{
|
||||
Logger.Info("Not recreating Connection, paused");
|
||||
ServerState = ServerState.Disconnected;
|
||||
_connectionDto = null;
|
||||
await StopConnection(_connectionCancellationTokenSource.Token);
|
||||
return;
|
||||
}
|
||||
|
||||
await StopConnection(_connectionCancellationTokenSource.Token);
|
||||
|
||||
Logger.Info("Recreating Connection");
|
||||
|
||||
_connectionCancellationTokenSource.Cancel();
|
||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||
var token = _connectionCancellationTokenSource.Token;
|
||||
_verifiedUploadedHashes.Clear();
|
||||
while (ServerState is not ServerState.Connected && !token.IsCancellationRequested)
|
||||
{
|
||||
if (string.IsNullOrEmpty(SecretKey))
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(2));
|
||||
continue;
|
||||
}
|
||||
|
||||
await StopConnection(token);
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Debug("Building connection");
|
||||
|
||||
while (!_dalamudUtil.IsPlayerPresent && !token.IsCancellationRequested)
|
||||
{
|
||||
Logger.Debug("Player not loaded in yet, waiting");
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), token);
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested) break;
|
||||
|
||||
_mareHub = BuildHubConnection(Api.Path);
|
||||
|
||||
await _mareHub.StartAsync(token);
|
||||
|
||||
_mareHub.On<SystemInfoDto>(Api.OnUpdateSystemInfo, (dto) => SystemInfoDto = dto);
|
||||
|
||||
_connectionDto =
|
||||
await _mareHub.InvokeAsync<ConnectionDto>(Api.InvokeHeartbeat, _dalamudUtil.PlayerNameHashed, token);
|
||||
|
||||
ServerState = ServerState.Connected;
|
||||
|
||||
if (_connectionDto.ServerVersion != Api.Version)
|
||||
{
|
||||
ServerState = ServerState.VersionMisMatch;
|
||||
await StopConnection(token);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ServerState is ServerState.Connected) // user is authorized && server is legit
|
||||
{
|
||||
await InitializeData(token);
|
||||
|
||||
_mareHub.Closed += MareHubOnClosed;
|
||||
_mareHub.Reconnecting += MareHubOnReconnecting;
|
||||
_mareHub.Reconnected += MareHubOnReconnected;
|
||||
}
|
||||
}
|
||||
catch (HubException ex)
|
||||
{
|
||||
Logger.Warn(ex.GetType().ToString());
|
||||
Logger.Warn(ex.Message);
|
||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||
|
||||
ServerState = ServerState.RateLimited;
|
||||
await StopConnection(token);
|
||||
return;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
Logger.Warn(ex.GetType().ToString());
|
||||
Logger.Warn(ex.Message);
|
||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||
|
||||
if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||
{
|
||||
ServerState = ServerState.Unauthorized;
|
||||
await StopConnection(token);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerState = ServerState.Offline;
|
||||
Logger.Info("Failed to establish connection, retrying");
|
||||
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn(ex.GetType().ToString());
|
||||
Logger.Warn(ex.Message);
|
||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||
Logger.Info("Failed to establish connection, retrying");
|
||||
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task MareHubOnReconnected(string? arg)
|
||||
{
|
||||
_ = Task.Run(CreateConnections);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task InitializeData(CancellationToken token)
|
||||
{
|
||||
if (_mareHub == null) return;
|
||||
|
||||
Logger.Debug("Initializing data");
|
||||
_mareHub.On<ClientPairDto, string>(Api.OnUserUpdateClientPairs,
|
||||
UpdateLocalClientPairsCallback);
|
||||
_mareHub.On<CharacterCacheDto, string>(Api.OnUserReceiveCharacterData,
|
||||
ReceiveCharacterDataCallback);
|
||||
_mareHub.On<string>(Api.OnUserRemoveOnlinePairedPlayer,
|
||||
(s) => PairedClientOffline?.Invoke(s));
|
||||
_mareHub.On<string>(Api.OnUserAddOnlinePairedPlayer,
|
||||
(s) => PairedClientOnline?.Invoke(s));
|
||||
_mareHub.On(Api.OnAdminForcedReconnect, UserForcedReconnectCallback);
|
||||
|
||||
PairedClients =
|
||||
await _mareHub!.InvokeAsync<List<ClientPairDto>>(Api.InvokeUserGetPairedClients, token);
|
||||
|
||||
if (IsModerator)
|
||||
{
|
||||
AdminForbiddenFiles =
|
||||
await _mareHub.InvokeAsync<List<ForbiddenFileDto>>(Api.InvokeAdminGetForbiddenFiles,
|
||||
token);
|
||||
AdminBannedUsers =
|
||||
await _mareHub.InvokeAsync<List<BannedUserDto>>(Api.InvokeAdminGetBannedUsers,
|
||||
token);
|
||||
_mareHub.On<BannedUserDto>(Api.OnAdminUpdateOrAddBannedUser,
|
||||
UpdateOrAddBannedUserCallback);
|
||||
_mareHub.On<BannedUserDto>(Api.OnAdminDeleteBannedUser, DeleteBannedUserCallback);
|
||||
_mareHub.On<ForbiddenFileDto>(Api.OnAdminUpdateOrAddForbiddenFile,
|
||||
UpdateOrAddForbiddenFileCallback);
|
||||
_mareHub.On<ForbiddenFileDto>(Api.OnAdminDeleteForbiddenFile,
|
||||
DeleteForbiddenFileCallback);
|
||||
}
|
||||
|
||||
Connected?.Invoke();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Logger.Verbose("Disposing " + nameof(ApiController));
|
||||
|
||||
_dalamudUtil.LogIn -= DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut -= DalamudUtilOnLogOut;
|
||||
|
||||
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token));
|
||||
_connectionCancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
private HubConnection BuildHubConnection(string hubName)
|
||||
{
|
||||
return new HubConnectionBuilder()
|
||||
.WithUrl(ApiUri + hubName, options =>
|
||||
{
|
||||
options.Headers.Add("Authorization", SecretKey);
|
||||
options.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling;
|
||||
})
|
||||
.WithAutomaticReconnect(new ForeverRetryPolicy())
|
||||
.ConfigureLogging(a => {
|
||||
a.ClearProviders().AddProvider(new DalamudLoggingProvider());
|
||||
a.SetMinimumLevel(LogLevel.Warning);
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
private Task MareHubOnClosed(Exception? arg)
|
||||
{
|
||||
CurrentUploads.Clear();
|
||||
CurrentDownloads.Clear();
|
||||
_uploadCancellationTokenSource?.Cancel();
|
||||
Disconnected?.Invoke();
|
||||
ServerState = ServerState.Offline;
|
||||
Logger.Info("Connection closed");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task MareHubOnReconnecting(Exception? arg)
|
||||
{
|
||||
ServerState = ServerState.Disconnected;
|
||||
Logger.Warn("Connection closed... Reconnecting");
|
||||
Logger.Warn(arg?.Message ?? string.Empty);
|
||||
Logger.Warn(arg?.StackTrace ?? string.Empty);
|
||||
Disconnected?.Invoke();
|
||||
ServerState = ServerState.Offline;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task StopConnection(CancellationToken token)
|
||||
{
|
||||
if (_mareHub is not null)
|
||||
{
|
||||
_uploadCancellationTokenSource?.Cancel();
|
||||
Logger.Info("Stopping existing connection");
|
||||
_mareHub.Closed -= MareHubOnClosed;
|
||||
_mareHub.Reconnecting -= MareHubOnReconnecting;
|
||||
_mareHub.Reconnected -= MareHubOnReconnected;
|
||||
await _mareHub.StopAsync(token);
|
||||
await _mareHub.DisposeAsync();
|
||||
CurrentUploads.Clear();
|
||||
CurrentDownloads.Clear();
|
||||
_uploadCancellationTokenSource?.Cancel();
|
||||
Disconnected?.Invoke();
|
||||
_mareHub = null;
|
||||
}
|
||||
|
||||
if (ServerState != ServerState.Disconnected)
|
||||
{
|
||||
while (ServerState != ServerState.Offline)
|
||||
{
|
||||
await Task.Delay(16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,45 +3,44 @@ using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
|
||||
namespace MareSynchronos.WebAPI
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public partial class ApiController
|
||||
{
|
||||
public partial class ApiController
|
||||
public async Task AddOrUpdateForbiddenFileEntry(ForbiddenFileDto forbiddenFile)
|
||||
{
|
||||
public async Task AddOrUpdateForbiddenFileEntry(ForbiddenFileDto forbiddenFile)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendAdminUpdateOrAddForbiddenFile, forbiddenFile);
|
||||
}
|
||||
await _mareHub!.SendAsync(Api.SendAdminUpdateOrAddForbiddenFile, forbiddenFile);
|
||||
}
|
||||
|
||||
public async Task DeleteForbiddenFileEntry(ForbiddenFileDto forbiddenFile)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendAdminDeleteForbiddenFile, forbiddenFile);
|
||||
}
|
||||
public async Task DeleteForbiddenFileEntry(ForbiddenFileDto forbiddenFile)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendAdminDeleteForbiddenFile, forbiddenFile);
|
||||
}
|
||||
|
||||
public async Task AddOrUpdateBannedUserEntry(BannedUserDto bannedUser)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendAdminUpdateOrAddBannedUser, bannedUser);
|
||||
}
|
||||
public async Task AddOrUpdateBannedUserEntry(BannedUserDto bannedUser)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendAdminUpdateOrAddBannedUser, bannedUser);
|
||||
}
|
||||
|
||||
public async Task DeleteBannedUserEntry(BannedUserDto bannedUser)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendAdminDeleteBannedUser, bannedUser);
|
||||
}
|
||||
public async Task DeleteBannedUserEntry(BannedUserDto bannedUser)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendAdminDeleteBannedUser, bannedUser);
|
||||
}
|
||||
|
||||
public async Task RefreshOnlineUsers()
|
||||
{
|
||||
AdminOnlineUsers = await _mareHub!.InvokeAsync<List<OnlineUserDto>>(Api.InvokeAdminGetOnlineUsers);
|
||||
}
|
||||
public async Task RefreshOnlineUsers()
|
||||
{
|
||||
AdminOnlineUsers = await _mareHub!.InvokeAsync<List<OnlineUserDto>>(Api.InvokeAdminGetOnlineUsers);
|
||||
}
|
||||
|
||||
public List<OnlineUserDto> AdminOnlineUsers { get; set; } = new List<OnlineUserDto>();
|
||||
public List<OnlineUserDto> AdminOnlineUsers { get; set; } = new List<OnlineUserDto>();
|
||||
|
||||
public void PromoteToModerator(string onlineUserUID)
|
||||
{
|
||||
_mareHub!.SendAsync(Api.SendAdminChangeModeratorStatus, onlineUserUID, true);
|
||||
}
|
||||
public void PromoteToModerator(string onlineUserUID)
|
||||
{
|
||||
_mareHub!.SendAsync(Api.SendAdminChangeModeratorStatus, onlineUserUID, true);
|
||||
}
|
||||
|
||||
public void DemoteFromModerator(string onlineUserUID)
|
||||
{
|
||||
_mareHub!.SendAsync(Api.SendAdminChangeModeratorStatus, onlineUserUID, false);
|
||||
}
|
||||
public void DemoteFromModerator(string onlineUserUID)
|
||||
{
|
||||
_mareHub!.SendAsync(Api.SendAdminChangeModeratorStatus, onlineUserUID, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,75 +4,84 @@ using MareSynchronos.API;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI.Utils;
|
||||
|
||||
namespace MareSynchronos.WebAPI
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public partial class ApiController
|
||||
{
|
||||
public partial class ApiController
|
||||
private void UserForcedReconnectCallback()
|
||||
{
|
||||
private void UserForcedReconnectCallback()
|
||||
_ = CreateConnections();
|
||||
}
|
||||
|
||||
private void UpdateLocalClientPairsCallback(ClientPairDto dto)
|
||||
{
|
||||
var entry = PairedClients.SingleOrDefault(e => e.OtherUID == dto.OtherUID);
|
||||
if (dto.IsRemoved)
|
||||
{
|
||||
_ = CreateConnections();
|
||||
PairedClients.RemoveAll(p => p.OtherUID == dto.OtherUID);
|
||||
return;
|
||||
}
|
||||
if (entry == null)
|
||||
{
|
||||
PairedClients.Add(dto);
|
||||
return;
|
||||
}
|
||||
|
||||
private void UpdateLocalClientPairsCallback(ClientPairDto dto)
|
||||
{
|
||||
var entry = PairedClients.SingleOrDefault(e => e.OtherUID == dto.OtherUID);
|
||||
if (dto.IsRemoved)
|
||||
{
|
||||
PairedClients.RemoveAll(p => p.OtherUID == dto.OtherUID);
|
||||
return;
|
||||
}
|
||||
if (entry == null)
|
||||
{
|
||||
PairedClients.Add(dto);
|
||||
return;
|
||||
}
|
||||
entry.IsPaused = dto.IsPaused;
|
||||
entry.IsPausedFromOthers = dto.IsPausedFromOthers;
|
||||
entry.IsSynced = dto.IsSynced;
|
||||
}
|
||||
|
||||
entry.IsPaused = dto.IsPaused;
|
||||
entry.IsPausedFromOthers = dto.IsPausedFromOthers;
|
||||
entry.IsSynced = dto.IsSynced;
|
||||
private Task ReceiveCharacterDataCallback(CharacterCacheDto character, string characterHash)
|
||||
{
|
||||
Logger.Verbose("Received DTO for " + characterHash);
|
||||
CharacterReceived?.Invoke(null, new CharacterReceivedEventArgs(characterHash, character));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void UpdateOrAddBannedUserCallback(BannedUserDto obj)
|
||||
{
|
||||
var user = AdminBannedUsers.SingleOrDefault(b => b.CharacterHash == obj.CharacterHash);
|
||||
if (user == null)
|
||||
{
|
||||
AdminBannedUsers.Add(obj);
|
||||
}
|
||||
|
||||
private Task ReceiveCharacterDataCallback(CharacterCacheDto character, string characterHash)
|
||||
else
|
||||
{
|
||||
Logger.Verbose("Received DTO for " + characterHash);
|
||||
CharacterReceived?.Invoke(null, new CharacterReceivedEventArgs(characterHash, character));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void UpdateOrAddBannedUserCallback(BannedUserDto obj)
|
||||
{
|
||||
var user = AdminBannedUsers.SingleOrDefault(b => b.CharacterHash == obj.CharacterHash);
|
||||
if (user == null)
|
||||
{
|
||||
AdminBannedUsers.Add(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.Reason = obj.Reason;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteBannedUserCallback(BannedUserDto obj)
|
||||
{
|
||||
AdminBannedUsers.RemoveAll(a => a.CharacterHash == obj.CharacterHash);
|
||||
}
|
||||
|
||||
private void UpdateOrAddForbiddenFileCallback(ForbiddenFileDto obj)
|
||||
{
|
||||
var user = AdminForbiddenFiles.SingleOrDefault(b => b.Hash == obj.Hash);
|
||||
if (user == null)
|
||||
{
|
||||
AdminForbiddenFiles.Add(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.ForbiddenBy = obj.ForbiddenBy;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteForbiddenFileCallback(ForbiddenFileDto obj)
|
||||
{
|
||||
AdminForbiddenFiles.RemoveAll(f => f.Hash == obj.Hash);
|
||||
user.Reason = obj.Reason;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteBannedUserCallback(BannedUserDto obj)
|
||||
{
|
||||
AdminBannedUsers.RemoveAll(a => a.CharacterHash == obj.CharacterHash);
|
||||
}
|
||||
|
||||
private void UpdateOrAddForbiddenFileCallback(ForbiddenFileDto obj)
|
||||
{
|
||||
var user = AdminForbiddenFiles.SingleOrDefault(b => b.Hash == obj.Hash);
|
||||
if (user == null)
|
||||
{
|
||||
AdminForbiddenFiles.Add(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.ForbiddenBy = obj.ForbiddenBy;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteForbiddenFileCallback(ForbiddenFileDto obj)
|
||||
{
|
||||
AdminForbiddenFiles.RemoveAll(f => f.Hash == obj.Hash);
|
||||
}
|
||||
|
||||
private void GroupPairChangedCallback(GroupPairDto dto)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void GroupChangedCallback(GroupDto dto)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
64
MareSynchronos/WebAPI/ApiController.Functions.Groups.cs
Normal file
64
MareSynchronos/WebAPI/ApiController.Functions.Groups.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using MareSynchronos.API;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MareSynchronos.WebAPI;
|
||||
public partial class ApiController
|
||||
{
|
||||
public async Task<GroupCreatedDto> CreateGroup()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<GroupCreatedDto>(Api.InvokeGroupCreate);
|
||||
}
|
||||
|
||||
public async Task<bool> ChangeGroupPassword(string gid, string newpassword)
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<bool>(Api.InvokeGroupChangePassword, gid, newpassword);
|
||||
}
|
||||
|
||||
public async Task<List<GroupDto>> GetGroups()
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<GroupDto>>(Api.InvokeGroupGetGroups);
|
||||
}
|
||||
|
||||
public async Task<List<GroupPairDto>> GetUsersInGroup(string gid)
|
||||
{
|
||||
return await _mareHub!.InvokeAsync<List<GroupPairDto>>(Api.InvokeGroupGetUsersInGroup, gid);
|
||||
}
|
||||
|
||||
public async Task SendGroupJoin(string gid, string password)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _mareHub!.SendAsync(Api.SendGroupJoin, gid, password);
|
||||
}
|
||||
|
||||
public async Task SendGroupChangeInviteState(string gid, bool opened)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendGroupChangeInviteState, gid, opened);
|
||||
}
|
||||
|
||||
public async Task SendDeleteGroup(string gid)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendGroupDelete, gid);
|
||||
}
|
||||
|
||||
public async Task SendLeaveGroup(string gid)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendGroupLeave, gid);
|
||||
}
|
||||
|
||||
public async Task SendPauseGroup(string gid, bool isPaused)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendGroupPause, gid, isPaused);
|
||||
}
|
||||
|
||||
public async Task SendRemoveUserFromGroup(string gid, string uid)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendGroupRemoveUser, gid, uid);
|
||||
}
|
||||
|
||||
public async Task ChangeOwnerOfGroup(string gid, string uid)
|
||||
{
|
||||
await _mareHub!.SendAsync(Api.SendGroupChangeOwner, gid, uid);
|
||||
}
|
||||
}
|
||||
372
MareSynchronos/WebAPI/ApiController.cs
Normal file
372
MareSynchronos/WebAPI/ApiController.cs
Normal file
@@ -0,0 +1,372 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.FileCache;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI.Utils;
|
||||
using Microsoft.AspNetCore.Http.Connections;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public delegate void SimpleStringDelegate(string str);
|
||||
|
||||
public partial class ApiController : IDisposable
|
||||
{
|
||||
public const string MainServer = "Lunae Crescere Incipientis (Central Server EU)";
|
||||
public const string MainServiceUri = "wss://maresynchronos.com";
|
||||
|
||||
public readonly int[] SupportedServerVersions = { Api.Version };
|
||||
|
||||
private readonly Configuration _pluginConfiguration;
|
||||
private readonly DalamudUtil _dalamudUtil;
|
||||
private readonly FileCacheManager _fileDbManager;
|
||||
private CancellationTokenSource _connectionCancellationTokenSource;
|
||||
|
||||
private HubConnection? _mareHub;
|
||||
|
||||
private CancellationTokenSource? _uploadCancellationTokenSource = new();
|
||||
|
||||
private ConnectionDto? _connectionDto;
|
||||
public SystemInfoDto SystemInfoDto { get; private set; } = new();
|
||||
public bool IsModerator => (_connectionDto?.IsAdmin ?? false) || (_connectionDto?.IsModerator ?? false);
|
||||
|
||||
public bool IsAdmin => _connectionDto?.IsAdmin ?? false;
|
||||
|
||||
public ApiController(Configuration pluginConfiguration, DalamudUtil dalamudUtil, FileCacheManager fileDbManager)
|
||||
{
|
||||
Logger.Verbose("Creating " + nameof(ApiController));
|
||||
|
||||
_pluginConfiguration = pluginConfiguration;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_fileDbManager = fileDbManager;
|
||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||
_dalamudUtil.LogIn += DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut += DalamudUtilOnLogOut;
|
||||
ServerState = ServerState.Offline;
|
||||
_verifiedUploadedHashes = new();
|
||||
|
||||
if (_dalamudUtil.IsLoggedIn)
|
||||
{
|
||||
DalamudUtilOnLogIn();
|
||||
}
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogOut()
|
||||
{
|
||||
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token));
|
||||
ServerState = ServerState.Offline;
|
||||
}
|
||||
|
||||
private void DalamudUtilOnLogIn()
|
||||
{
|
||||
Task.Run(CreateConnections);
|
||||
}
|
||||
|
||||
|
||||
public event EventHandler<CharacterReceivedEventArgs>? CharacterReceived;
|
||||
|
||||
public event VoidDelegate? Connected;
|
||||
|
||||
public event VoidDelegate? Disconnected;
|
||||
|
||||
public event SimpleStringDelegate? PairedClientOffline;
|
||||
|
||||
public event SimpleStringDelegate? PairedClientOnline;
|
||||
|
||||
public event SimpleStringDelegate? PairedWithOther;
|
||||
|
||||
public event SimpleStringDelegate? UnpairedFromOther;
|
||||
public event VoidDelegate? DownloadStarted;
|
||||
public event VoidDelegate? DownloadFinished;
|
||||
|
||||
public ConcurrentDictionary<int, List<DownloadFileTransfer>> CurrentDownloads { get; } = new();
|
||||
|
||||
public List<FileTransfer> CurrentUploads { get; } = new();
|
||||
|
||||
public List<FileTransfer> ForbiddenTransfers { get; } = new();
|
||||
|
||||
public List<BannedUserDto> AdminBannedUsers { get; private set; } = new();
|
||||
|
||||
public List<ForbiddenFileDto> AdminForbiddenFiles { get; private set; } = new();
|
||||
|
||||
public bool IsConnected => ServerState == ServerState.Connected;
|
||||
|
||||
public bool IsDownloading => CurrentDownloads.Count > 0;
|
||||
|
||||
public bool IsUploading => CurrentUploads.Count > 0;
|
||||
|
||||
public List<ClientPairDto> PairedClients { get; set; } = new();
|
||||
public List<GroupPairDto> GroupPairedClients { get; set; } = new();
|
||||
public List<GroupDto> Groups { get; set; } = new();
|
||||
|
||||
public string SecretKey => _pluginConfiguration.ClientSecret.ContainsKey(ApiUri)
|
||||
? _pluginConfiguration.ClientSecret[ApiUri] : string.Empty;
|
||||
|
||||
public bool ServerAlive => ServerState is ServerState.Connected or ServerState.RateLimited or ServerState.Unauthorized or ServerState.Disconnected;
|
||||
|
||||
public Dictionary<string, string> ServerDictionary => new Dictionary<string, string>()
|
||||
{ { MainServiceUri, MainServer } }
|
||||
.Concat(_pluginConfiguration.CustomServerList)
|
||||
.ToDictionary(k => k.Key, k => k.Value);
|
||||
|
||||
public string UID => _connectionDto?.UID ?? string.Empty;
|
||||
private string ApiUri => _pluginConfiguration.ApiUri;
|
||||
public int OnlineUsers => SystemInfoDto.OnlineUsers;
|
||||
|
||||
private ServerState _serverState;
|
||||
public ServerState ServerState
|
||||
{
|
||||
get => _serverState;
|
||||
private set
|
||||
{
|
||||
Logger.Debug($"New ServerState: {value}, prev ServerState: {_serverState}");
|
||||
_serverState = value;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CreateConnections()
|
||||
{
|
||||
Logger.Debug("CreateConnections called");
|
||||
|
||||
if (_pluginConfiguration.FullPause)
|
||||
{
|
||||
Logger.Info("Not recreating Connection, paused");
|
||||
ServerState = ServerState.Disconnected;
|
||||
_connectionDto = null;
|
||||
await StopConnection(_connectionCancellationTokenSource.Token);
|
||||
return;
|
||||
}
|
||||
|
||||
await StopConnection(_connectionCancellationTokenSource.Token);
|
||||
|
||||
Logger.Info("Recreating Connection");
|
||||
|
||||
_connectionCancellationTokenSource.Cancel();
|
||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||
var token = _connectionCancellationTokenSource.Token;
|
||||
_verifiedUploadedHashes.Clear();
|
||||
while (ServerState is not ServerState.Connected && !token.IsCancellationRequested)
|
||||
{
|
||||
if (string.IsNullOrEmpty(SecretKey))
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(2));
|
||||
continue;
|
||||
}
|
||||
|
||||
await StopConnection(token);
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Debug("Building connection");
|
||||
|
||||
while (!_dalamudUtil.IsPlayerPresent && !token.IsCancellationRequested)
|
||||
{
|
||||
Logger.Debug("Player not loaded in yet, waiting");
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), token);
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested) break;
|
||||
|
||||
_mareHub = BuildHubConnection(Api.Path);
|
||||
|
||||
await _mareHub.StartAsync(token);
|
||||
|
||||
_mareHub.On<SystemInfoDto>(Api.OnUpdateSystemInfo, (dto) => SystemInfoDto = dto);
|
||||
|
||||
_connectionDto =
|
||||
await _mareHub.InvokeAsync<ConnectionDto>(Api.InvokeHeartbeat, _dalamudUtil.PlayerNameHashed, token);
|
||||
|
||||
ServerState = ServerState.Connected;
|
||||
|
||||
if (_connectionDto.ServerVersion != Api.Version)
|
||||
{
|
||||
ServerState = ServerState.VersionMisMatch;
|
||||
await StopConnection(token);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ServerState is ServerState.Connected) // user is authorized && server is legit
|
||||
{
|
||||
await InitializeData(token);
|
||||
|
||||
_mareHub.Closed += MareHubOnClosed;
|
||||
_mareHub.Reconnecting += MareHubOnReconnecting;
|
||||
_mareHub.Reconnected += MareHubOnReconnected;
|
||||
}
|
||||
}
|
||||
catch (HubException ex)
|
||||
{
|
||||
Logger.Warn(ex.GetType().ToString());
|
||||
Logger.Warn(ex.Message);
|
||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||
|
||||
ServerState = ServerState.RateLimited;
|
||||
await StopConnection(token);
|
||||
return;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
Logger.Warn(ex.GetType().ToString());
|
||||
Logger.Warn(ex.Message);
|
||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||
|
||||
if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||
{
|
||||
ServerState = ServerState.Unauthorized;
|
||||
await StopConnection(token);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerState = ServerState.Offline;
|
||||
Logger.Info("Failed to establish connection, retrying");
|
||||
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn(ex.GetType().ToString());
|
||||
Logger.Warn(ex.Message);
|
||||
Logger.Warn(ex.StackTrace ?? string.Empty);
|
||||
Logger.Info("Failed to establish connection, retrying");
|
||||
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task MareHubOnReconnected(string? arg)
|
||||
{
|
||||
_ = Task.Run(CreateConnections);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task InitializeData(CancellationToken token)
|
||||
{
|
||||
if (_mareHub == null) return;
|
||||
|
||||
Logger.Debug("Initializing data");
|
||||
_mareHub.On<ClientPairDto>(Api.OnUserUpdateClientPairs,
|
||||
UpdateLocalClientPairsCallback);
|
||||
_mareHub.On<CharacterCacheDto, string>(Api.OnUserReceiveCharacterData,
|
||||
ReceiveCharacterDataCallback);
|
||||
_mareHub.On<string>(Api.OnUserRemoveOnlinePairedPlayer,
|
||||
(s) => PairedClientOffline?.Invoke(s));
|
||||
_mareHub.On<string>(Api.OnUserAddOnlinePairedPlayer,
|
||||
(s) => PairedClientOnline?.Invoke(s));
|
||||
_mareHub.On(Api.OnAdminForcedReconnect, UserForcedReconnectCallback);
|
||||
_mareHub.On<GroupDto>(Api.OnGroupChange, GroupChangedCallback);
|
||||
_mareHub.On<GroupPairDto>(Api.OnGroupUserChange, GroupPairChangedCallback);
|
||||
|
||||
PairedClients =
|
||||
await _mareHub!.InvokeAsync<List<ClientPairDto>>(Api.InvokeUserGetPairedClients, token);
|
||||
Groups = await GetGroups();
|
||||
foreach (var group in Groups)
|
||||
{
|
||||
GroupPairedClients.AddRange(await GetUsersInGroup(group.GID));
|
||||
}
|
||||
|
||||
if (IsModerator)
|
||||
{
|
||||
AdminForbiddenFiles =
|
||||
await _mareHub.InvokeAsync<List<ForbiddenFileDto>>(Api.InvokeAdminGetForbiddenFiles,
|
||||
token);
|
||||
AdminBannedUsers =
|
||||
await _mareHub.InvokeAsync<List<BannedUserDto>>(Api.InvokeAdminGetBannedUsers,
|
||||
token);
|
||||
_mareHub.On<BannedUserDto>(Api.OnAdminUpdateOrAddBannedUser,
|
||||
UpdateOrAddBannedUserCallback);
|
||||
_mareHub.On<BannedUserDto>(Api.OnAdminDeleteBannedUser, DeleteBannedUserCallback);
|
||||
_mareHub.On<ForbiddenFileDto>(Api.OnAdminUpdateOrAddForbiddenFile,
|
||||
UpdateOrAddForbiddenFileCallback);
|
||||
_mareHub.On<ForbiddenFileDto>(Api.OnAdminDeleteForbiddenFile,
|
||||
DeleteForbiddenFileCallback);
|
||||
}
|
||||
|
||||
Connected?.Invoke();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Logger.Verbose("Disposing " + nameof(ApiController));
|
||||
|
||||
_dalamudUtil.LogIn -= DalamudUtilOnLogIn;
|
||||
_dalamudUtil.LogOut -= DalamudUtilOnLogOut;
|
||||
|
||||
Task.Run(async () => await StopConnection(_connectionCancellationTokenSource.Token));
|
||||
_connectionCancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
private HubConnection BuildHubConnection(string hubName)
|
||||
{
|
||||
return new HubConnectionBuilder()
|
||||
.WithUrl(ApiUri + hubName, options =>
|
||||
{
|
||||
options.Headers.Add("Authorization", SecretKey);
|
||||
options.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling;
|
||||
})
|
||||
.WithAutomaticReconnect(new ForeverRetryPolicy())
|
||||
.ConfigureLogging(a =>
|
||||
{
|
||||
a.ClearProviders().AddProvider(new DalamudLoggingProvider());
|
||||
a.SetMinimumLevel(LogLevel.Warning);
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
private Task MareHubOnClosed(Exception? arg)
|
||||
{
|
||||
CurrentUploads.Clear();
|
||||
CurrentDownloads.Clear();
|
||||
_uploadCancellationTokenSource?.Cancel();
|
||||
Disconnected?.Invoke();
|
||||
ServerState = ServerState.Offline;
|
||||
Logger.Info("Connection closed");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task MareHubOnReconnecting(Exception? arg)
|
||||
{
|
||||
ServerState = ServerState.Disconnected;
|
||||
Logger.Warn("Connection closed... Reconnecting");
|
||||
Logger.Warn(arg?.Message ?? string.Empty);
|
||||
Logger.Warn(arg?.StackTrace ?? string.Empty);
|
||||
Disconnected?.Invoke();
|
||||
ServerState = ServerState.Offline;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task StopConnection(CancellationToken token)
|
||||
{
|
||||
if (_mareHub is not null)
|
||||
{
|
||||
_uploadCancellationTokenSource?.Cancel();
|
||||
Logger.Info("Stopping existing connection");
|
||||
_mareHub.Closed -= MareHubOnClosed;
|
||||
_mareHub.Reconnecting -= MareHubOnReconnecting;
|
||||
_mareHub.Reconnected -= MareHubOnReconnected;
|
||||
await _mareHub.StopAsync(token);
|
||||
await _mareHub.DisposeAsync();
|
||||
CurrentUploads.Clear();
|
||||
CurrentDownloads.Clear();
|
||||
_uploadCancellationTokenSource?.Cancel();
|
||||
Disconnected?.Invoke();
|
||||
_mareHub = null;
|
||||
}
|
||||
|
||||
if (ServerState != ServerState.Disconnected)
|
||||
{
|
||||
while (ServerState != ServerState.Offline)
|
||||
{
|
||||
await Task.Delay(16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
MareSynchronos/WebAPI/ServerState.cs
Normal file
11
MareSynchronos/WebAPI/ServerState.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace MareSynchronos.WebAPI;
|
||||
|
||||
public enum ServerState
|
||||
{
|
||||
Offline,
|
||||
Disconnected,
|
||||
Connected,
|
||||
Unauthorized,
|
||||
VersionMisMatch,
|
||||
RateLimited
|
||||
}
|
||||
Reference in New Issue
Block a user