From 32fd63c5f3b9456cee090dae37a6eb95641925c6 Mon Sep 17 00:00:00 2001 From: Cinka Date: Sat, 1 Feb 2025 18:19:18 +0300 Subject: [PATCH] - add: TFA think --- .idea/.idea.Nebula/.idea/avalonia.xml | 1 + Nebula.Launcher/LauncherConVar.cs | 20 ++++ .../ViewModels/Pages/AccountInfoViewModel.cs | 102 ++++++++++++------ .../Pages/ServerListViewModel.Favorite.cs | 10 +- .../ViewModels/Popup/TfaViewModel.cs | 29 +++++ .../ViewModels/ServerEntryModelView.cs | 8 +- .../Views/Pages/AccountInfoView.axaml | 2 +- Nebula.Launcher/Views/Popup/TfaView.axaml | 39 +++++++ Nebula.Launcher/Views/Popup/TfaView.axaml.cs | 100 +++++++++++++++++ Nebula.Launcher/Views/ServerEntryView.axaml | 2 +- Nebula.Shared/CurrentConVar.cs | 13 --- Nebula.Shared/Models/Auth/LoginToken.cs | 12 +-- Nebula.Shared/Services/AuthService.cs | 70 ++++++++---- Nebula.Shared/Services/ContentService.cs | 7 +- Nebula.Shared/Services/EngineService.cs | 16 ++- Nebula.Shared/Services/RestService.cs | 68 ++++-------- 16 files changed, 351 insertions(+), 148 deletions(-) create mode 100644 Nebula.Launcher/LauncherConVar.cs create mode 100644 Nebula.Launcher/ViewModels/Popup/TfaViewModel.cs create mode 100644 Nebula.Launcher/Views/Popup/TfaView.axaml create mode 100644 Nebula.Launcher/Views/Popup/TfaView.axaml.cs diff --git a/.idea/.idea.Nebula/.idea/avalonia.xml b/.idea/.idea.Nebula/.idea/avalonia.xml index 6c38c66..42497a3 100644 --- a/.idea/.idea.Nebula/.idea/avalonia.xml +++ b/.idea/.idea.Nebula/.idea/avalonia.xml @@ -25,6 +25,7 @@ + diff --git a/Nebula.Launcher/LauncherConVar.cs b/Nebula.Launcher/LauncherConVar.cs new file mode 100644 index 0000000..cd13180 --- /dev/null +++ b/Nebula.Launcher/LauncherConVar.cs @@ -0,0 +1,20 @@ +using Nebula.Launcher.ViewModels.Pages; +using Nebula.Shared.Services; + +namespace Nebula.Launcher; + +public static class LauncherConVar +{ + public static readonly ConVar AuthProfiles = + ConVarBuilder.Build("auth.profiles.v2", []); + + public static readonly ConVar AuthCurrent = + ConVarBuilder.Build("auth.current.v2"); + + public static readonly ConVar Favorites = + ConVarBuilder.Build("server.favorites", []); + + public static readonly ConVar AuthServers = ConVarBuilder.Build("launcher.authServers", [ + "https://auth.spacestation14.com/" + ]); +} \ No newline at end of file diff --git a/Nebula.Launcher/ViewModels/Pages/AccountInfoViewModel.cs b/Nebula.Launcher/ViewModels/Pages/AccountInfoViewModel.cs index 87ca87b..74e2279 100644 --- a/Nebula.Launcher/ViewModels/Pages/AccountInfoViewModel.cs +++ b/Nebula.Launcher/ViewModels/Pages/AccountInfoViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.ObjectModel; using System.Linq; +using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Windows.Input; using CommunityToolkit.Mvvm.ComponentModel; @@ -38,7 +39,7 @@ public partial class AccountInfoViewModel : ViewModelBase, IViewModelPage [GenerateProperty] private AuthService AuthService { get; } = default!; [GenerateProperty, DesignConstruct] private ViewHelperService ViewHelperService { get; } = default!; - public ObservableCollection Accounts { get; } = new(); + public ObservableCollection Accounts { get; } = new(); public ObservableCollection AuthUrls { get; } = new(); private AuthLoginPassword CurrentAlp @@ -52,6 +53,9 @@ public partial class AccountInfoViewModel : ViewModelBase, IViewModelPage } } + + private CurrentAuthInfo? _currAuthTemp; + public string AuthItemSelect { set => CurrentAuthServer = value; @@ -71,37 +75,61 @@ public partial class AccountInfoViewModel : ViewModelBase, IViewModelPage ReadAuthConfig(); } - public void AuthByAlp(AuthLoginPassword authLoginPassword) + public void AuthByProfile(ProfileAuthCredentials credentials) { - CurrentAlp = authLoginPassword; + CurrentAlp = new AuthLoginPassword(credentials.Login, credentials.Password, credentials.AuthServer); DoAuth(); } - public void DoAuth() + public void DoAuth(string? code = null) { var message = ViewHelperService.GetViewModel(); message.InfoText = "Auth think, please wait..."; message.IsInfoClosable = false; - Console.WriteLine("AUTH SHIT"); PopupMessageService.Popup(message); Task.Run(async () => { - if (await AuthService.Auth(CurrentAlp)) + try { + await AuthService.Auth(CurrentAlp, code); message.Dispose(); IsLogged = true; - ConfigurationService.SetConfigValue(CurrentConVar.AuthCurrent, CurrentAlp); + ConfigurationService.SetConfigValue(LauncherConVar.AuthCurrent, AuthService.SelectedAuth); } - else + catch (AuthException e) + { + message.Dispose(); + + switch (e.Error.Code) + { + case AuthenticateDenyCode.TfaRequired: + case AuthenticateDenyCode.TfaInvalid: + var p = ViewHelperService.GetViewModel(); + p.OnTfaEntered += OnTfaEntered; + PopupMessageService.Popup(p); + break; + case AuthenticateDenyCode.InvalidCredentials: + PopupMessageService.Popup("Invalid Credentials!"); + break; + default: + throw; + } + } + catch (Exception e) { message.Dispose(); Logout(); - PopupMessageService.Popup("Well, shit is happened: " + AuthService.Reason); + PopupMessageService.Popup(e); } }); } + private void OnTfaEntered(string code) + { + DoAuth(code); + } + public void Logout() { IsLogged = false; @@ -118,10 +146,10 @@ public partial class AccountInfoViewModel : ViewModelBase, IViewModelPage private void AddAccount(AuthLoginPassword authLoginPassword) { - var onDelete = new DelegateCommand(OnDeleteProfile); - var onSelect = new DelegateCommand(AuthByAlp); + var onDelete = new DelegateCommand(OnDeleteProfile); + var onSelect = new DelegateCommand(AuthByProfile); - var alpm = new AuthLoginPasswordModel( + var alpm = new ProfileAuthCredentials( authLoginPassword.Login, authLoginPassword.Password, authLoginPassword.AuthServer, @@ -134,25 +162,39 @@ public partial class AccountInfoViewModel : ViewModelBase, IViewModelPage Accounts.Add(alpm); } - private void ReadAuthConfig() + private async void ReadAuthConfig() { + var message = ViewHelperService.GetViewModel(); + message.InfoText = "Read configuration file, please wait..."; + message.IsInfoClosable = false; + PopupMessageService.Popup(message); foreach (var profile in - ConfigurationService.GetConfigValue(CurrentConVar.AuthProfiles)!) - AddAccount(profile); + ConfigurationService.GetConfigValue(LauncherConVar.AuthProfiles)!) + AddAccount(new AuthLoginPassword(profile.Login, profile.Password, profile.AuthServer)); if (Accounts.Count == 0) UpdateAuthMenu(); - var currProfile = ConfigurationService.GetConfigValue(CurrentConVar.AuthCurrent); + AuthUrls.Clear(); + var authUrls = ConfigurationService.GetConfigValue(LauncherConVar.AuthServers)!; + foreach (var url in authUrls) AuthUrls.Add(url); + + var currProfile = ConfigurationService.GetConfigValue(LauncherConVar.AuthCurrent); if (currProfile != null) { - CurrentAlp = currProfile; - DoAuth(); + try + { + CurrentAlp = new AuthLoginPassword(currProfile.Login, string.Empty, currProfile.AuthServer); + IsLogged = await AuthService.SetAuth(currProfile); + } + catch (Exception e) + { + message.Dispose(); + PopupMessageService.Popup(e); + } } - - AuthUrls.Clear(); - var authUrls = ConfigurationService.GetConfigValue(CurrentConVar.AuthServers)!; - foreach (var url in authUrls) AuthUrls.Add(url); + + message.Dispose(); } [RelayCommand] @@ -164,7 +206,7 @@ public partial class AccountInfoViewModel : ViewModelBase, IViewModelPage DirtyProfile(); } - private void OnDeleteProfile(AuthLoginPasswordModel account) + private void OnDeleteProfile(ProfileAuthCredentials account) { Accounts.Remove(account); _isProfilesEmpty = Accounts.Count == 0; @@ -187,20 +229,18 @@ public partial class AccountInfoViewModel : ViewModelBase, IViewModelPage private void DirtyProfile() { - ConfigurationService.SetConfigValue(CurrentConVar.AuthProfiles, - Accounts.Select(a => (AuthLoginPassword)a).ToArray()); + ConfigurationService.SetConfigValue(LauncherConVar.AuthProfiles, + Accounts.ToArray()); } public void OnPageOpen(object? args) { - } } - -public record AuthLoginPasswordModel( +public sealed record ProfileAuthCredentials( string Login, string Password, string AuthServer, - ICommand OnSelect = default!, - ICommand OnDelete = default!) - : AuthLoginPassword(Login, Password, AuthServer); \ No newline at end of file + [property: JsonIgnore] ICommand OnSelect = default!, + [property: JsonIgnore] ICommand OnDelete = default! + ); \ No newline at end of file diff --git a/Nebula.Launcher/ViewModels/Pages/ServerListViewModel.Favorite.cs b/Nebula.Launcher/ViewModels/Pages/ServerListViewModel.Favorite.cs index 6bf0221..64b9152 100644 --- a/Nebula.Launcher/ViewModels/Pages/ServerListViewModel.Favorite.cs +++ b/Nebula.Launcher/ViewModels/Pages/ServerListViewModel.Favorite.cs @@ -22,7 +22,7 @@ public partial class ServerListViewModel { FavoriteServers.Clear(); - var servers = ConfigurationService.GetConfigValue(CurrentConVar.Favorites); + var servers = ConfigurationService.GetConfigValue(LauncherConVar.Favorites); if (servers is null || servers.Length == 0) { return; @@ -44,17 +44,17 @@ public partial class ServerListViewModel public void AddFavorite(RobustUrl robustUrl) { - var servers = (ConfigurationService.GetConfigValue(CurrentConVar.Favorites) ?? []).ToList(); + var servers = (ConfigurationService.GetConfigValue(LauncherConVar.Favorites) ?? []).ToList(); servers.Add(robustUrl.ToString()); - ConfigurationService.SetConfigValue(CurrentConVar.Favorites, servers.ToArray()); + ConfigurationService.SetConfigValue(LauncherConVar.Favorites, servers.ToArray()); UpdateFavoriteEntries(); } public void RemoveFavorite(ServerEntryModelView entryModelView) { - var servers = (ConfigurationService.GetConfigValue(CurrentConVar.Favorites) ?? []).ToList(); + var servers = (ConfigurationService.GetConfigValue(LauncherConVar.Favorites) ?? []).ToList(); servers.Remove(entryModelView.Address.ToString()); - ConfigurationService.SetConfigValue(CurrentConVar.Favorites, servers.ToArray()); + ConfigurationService.SetConfigValue(LauncherConVar.Favorites, servers.ToArray()); entryModelView.IsFavorite = false; UpdateFavoriteEntries(); } diff --git a/Nebula.Launcher/ViewModels/Popup/TfaViewModel.cs b/Nebula.Launcher/ViewModels/Popup/TfaViewModel.cs new file mode 100644 index 0000000..62511ae --- /dev/null +++ b/Nebula.Launcher/ViewModels/Popup/TfaViewModel.cs @@ -0,0 +1,29 @@ +using System; +using Nebula.Launcher.Views.Popup; +using Nebula.Shared.Services; + +namespace Nebula.Launcher.ViewModels.Popup; + +[ConstructGenerator, ViewModelRegister(typeof(TfaView))] +public partial class TfaViewModel : PopupViewModelBase +{ + public Action? OnTfaEntered; + + protected override void InitialiseInDesignMode() + { + } + + protected override void Initialise() + { + } + + public void OnTfaEnter(string code) + { + OnTfaEntered?.Invoke(code); + Dispose(); + } + + [GenerateProperty] public override PopupMessageService PopupMessageService { get; } + public override string Title => "2fa"; + public override bool IsClosable => true; +} \ No newline at end of file diff --git a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs index e7e7827..a257c01 100644 --- a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs +++ b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs @@ -185,10 +185,10 @@ public partial class ServerEntryModelView : ViewModelBase { { "ROBUST_AUTH_USERID", authProv?.UserId.ToString() }, { "ROBUST_AUTH_TOKEN", authProv?.Token.Token }, - { "ROBUST_AUTH_SERVER", authProv?.AuthLoginPassword.AuthServer }, + { "ROBUST_AUTH_SERVER", authProv?.AuthServer }, { "ROBUST_AUTH_PUBKEY", buildInfo.BuildInfo.Auth.PublicKey }, { "GAME_URL", Address.ToString() }, - { "AUTH_LOGIN", authProv?.AuthLoginPassword.Login } + { "AUTH_LOGIN", authProv?.Login } }, CreateNoWindow = true, UseShellExecute = false, @@ -253,8 +253,7 @@ public partial class ServerEntryModelView : ViewModelBase CurrLog.Append(e.Data); } } - - + public void ReadLog() { PopupMessageService.Popup(CurrLog); @@ -286,7 +285,6 @@ public partial class ServerEntryModelView : ViewModelBase { Links.Add(link); } - } private static string FindDotnetPath() diff --git a/Nebula.Launcher/Views/Pages/AccountInfoView.axaml b/Nebula.Launcher/Views/Pages/AccountInfoView.axaml index dd3933b..608b9e3 100644 --- a/Nebula.Launcher/Views/Pages/AccountInfoView.axaml +++ b/Nebula.Launcher/Views/Pages/AccountInfoView.axaml @@ -31,7 +31,7 @@ ItemsSource="{Binding Accounts}" Padding="0"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Nebula.Launcher/Views/Popup/TfaView.axaml.cs b/Nebula.Launcher/Views/Popup/TfaView.axaml.cs new file mode 100644 index 0000000..daddcbb --- /dev/null +++ b/Nebula.Launcher/Views/Popup/TfaView.axaml.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Nebula.Launcher.ViewModels.Popup; + +namespace Nebula.Launcher.Views.Popup; + +public partial class TfaView : UserControl +{ + public List Boxes = new(); + + public TfaView() + { + InitializeComponent(); + + foreach (var textBox in TContainer.Children.Select(UnzipBox)) + { + var currIndex = Boxes.Count; + Boxes.Add(textBox); + textBox.TextChanged += (_,_) => OnTextChanged(currIndex); + textBox.PastingFromClipboard += OnPasteFromClipboard; + textBox.KeyUp += (sender, args) => + { + if (args.Key == Key.Back && string.IsNullOrEmpty(textBox.Text)) OnTextChanged(currIndex); + }; + textBox.KeyDown += (sender, args) => + { + textBox.Text = args.KeySymbol; + textBox.SelectionStart = 1; + //OnTextChanged(currIndex); + }; + } + } + + private void OnPasteFromClipboard(object? sender, RoutedEventArgs e) + { + // TODO: CLIPBOARD THINK + } + + private void OnTextChanged(int index) + { + var box = Boxes[index]; + + if (string.IsNullOrEmpty(box.Text)) + { + if(index == 0) return; + index--; + } + else + { + if(!int.TryParse(box.Text, out var _)) + { + box.Text = ""; + return; + } + + if (index == 5) + { + CheckupCode(); + return; + } + index++; + } + + Boxes[index].Focus(); + } + + private void CheckupCode() + { + var str = ""; + foreach (var vtTextBox in Boxes) + { + if(string.IsNullOrEmpty(vtTextBox.Text)) return; + str += vtTextBox.Text; + } + + ((TfaViewModel)DataContext!).OnTfaEnter(str); + } + + private TextBox UnzipBox(Control control) + { + var box = (Border)control; + return (TextBox)box.Child!; + } + + public TfaView(TfaViewModel tfaViewModel) : this() + { + DataContext = tfaViewModel; + } + + private void Button_OnClick(object? sender, RoutedEventArgs e) + { + CheckupCode(); + } +} \ No newline at end of file diff --git a/Nebula.Launcher/Views/ServerEntryView.axaml b/Nebula.Launcher/Views/ServerEntryView.axaml index d2c4d7f..3f42ce4 100644 --- a/Nebula.Launcher/Views/ServerEntryView.axaml +++ b/Nebula.Launcher/Views/ServerEntryView.axaml @@ -228,7 +228,7 @@ AuthServers = ConVarBuilder.Build("launcher.authServers", [ - "https://auth.spacestation14.com/" - ]); - - public static readonly ConVar AuthProfiles = - ConVarBuilder.Build("auth.profiles", []); - - public static readonly ConVar AuthCurrent = - ConVarBuilder.Build("auth.current"); - - public static readonly ConVar Favorites = - ConVarBuilder.Build("server.favorites", []); - public static readonly ConVar> EngineManifestBackup = ConVarBuilder.Build>("engine.manifest.backup"); public static readonly ConVar ModuleManifestBackup = diff --git a/Nebula.Shared/Models/Auth/LoginToken.cs b/Nebula.Shared/Models/Auth/LoginToken.cs index 09abe04..e7b69fd 100644 --- a/Nebula.Shared/Models/Auth/LoginToken.cs +++ b/Nebula.Shared/Models/Auth/LoginToken.cs @@ -1,13 +1,3 @@ namespace Nebula.Shared.Models.Auth; -public readonly struct LoginToken -{ - public readonly string Token; - public readonly DateTimeOffset ExpireTime; - - public LoginToken(string token, DateTimeOffset expireTime) - { - Token = token; - ExpireTime = expireTime; - } -} \ No newline at end of file +public sealed record LoginToken(string Token, DateTimeOffset ExpireTime); \ No newline at end of file diff --git a/Nebula.Shared/Services/AuthService.cs b/Nebula.Shared/Services/AuthService.cs index 9b9f6b0..d6e2b7b 100644 --- a/Nebula.Shared/Services/AuthService.cs +++ b/Nebula.Shared/Services/AuthService.cs @@ -1,5 +1,9 @@ -using System.Net.Http.Headers; +using System.Net; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Text.Json.Serialization; using Nebula.Shared.Models.Auth; +using Nebula.Shared.Utils; namespace Nebula.Shared.Services; @@ -10,11 +14,9 @@ public class AuthService( CancellationService cancellationService) { private readonly HttpClient _httpClient = new(); + public CurrentAuthInfo? SelectedAuth { get; private set; } - public string Reason = ""; - public CurrentAuthInfo? SelectedAuth { get; internal set; } - - public async Task Auth(AuthLoginPassword authLoginPassword) + public async Task Auth(AuthLoginPassword authLoginPassword, string? code = null) { var authServer = authLoginPassword.AuthServer; var login = authLoginPassword.Login; @@ -24,20 +26,24 @@ public class AuthService( var authUrl = new Uri($"{authServer}api/auth/authenticate"); - var result = - await restService.PostAsync( - new AuthenticateRequest(login, password), authUrl, cancellationService.Token); - - if (result.Value is null) + try { - Reason = result.Message; - return false; + var result = + await restService.PostAsync( + new AuthenticateRequest(login, null, password, code), authUrl, cancellationService.Token); + + SelectedAuth = new CurrentAuthInfo(result.UserId, + new LoginToken(result.Token, result.ExpireTime), authLoginPassword.Login, authLoginPassword.AuthServer); + } + catch (RestRequestException e) + { + Console.WriteLine(e.Content); + if (e.StatusCode != HttpStatusCode.Unauthorized) throw; + var err = await e.Content.AsJson(); + + if (err is null) throw; + throw new AuthException(err); } - - SelectedAuth = new CurrentAuthInfo(result.Value.UserId, - new LoginToken(result.Value.Token, result.Value.ExpireTime), authLoginPassword); - - return true; } public void ClearAuth() @@ -45,17 +51,17 @@ public class AuthService( SelectedAuth = null; } - public void SetAuth(Guid guid, string token, string login, string authServer) + public async Task SetAuth(CurrentAuthInfo info) { - SelectedAuth = new CurrentAuthInfo(guid, new LoginToken(token, DateTimeOffset.Now), - new AuthLoginPassword(login, "", authServer)); + SelectedAuth = info; + return await EnsureToken(); } public async Task EnsureToken() { if (SelectedAuth is null) return false; - var authUrl = new Uri($"{SelectedAuth.AuthLoginPassword.AuthServer}api/auth/ping"); + var authUrl = new Uri($"{SelectedAuth.AuthServer}api/auth/ping"); using var requestMessage = new HttpRequestMessage(HttpMethod.Get, authUrl); requestMessage.Headers.Authorization = new AuthenticationHeaderValue("SS14Auth", SelectedAuth.Token.Token); @@ -67,6 +73,24 @@ public class AuthService( } } -public sealed record CurrentAuthInfo(Guid UserId, LoginToken Token, AuthLoginPassword AuthLoginPassword); +public sealed record CurrentAuthInfo(Guid UserId, LoginToken Token, string Login, string AuthServer); -public record AuthLoginPassword(string Login, string Password, string AuthServer); \ No newline at end of file +public record AuthLoginPassword(string Login, string Password, string AuthServer); + +public sealed record AuthDenyError(string[] Errors, AuthenticateDenyCode Code); + +public sealed class AuthException(AuthDenyError error) : Exception +{ + public AuthDenyError Error { get; } = error; +} + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum AuthenticateDenyCode +{ + None = 0, + InvalidCredentials = 1, + AccountUnconfirmed = 2, + TfaRequired = 3, + TfaInvalid = 4, + AccountLocked = 5, +} diff --git a/Nebula.Shared/Services/ContentService.cs b/Nebula.Shared/Services/ContentService.cs index c29cfe3..b5223af 100644 --- a/Nebula.Shared/Services/ContentService.cs +++ b/Nebula.Shared/Services/ContentService.cs @@ -17,13 +17,12 @@ public partial class ContentService( var info = new RobustBuildInfo(); info.Url = url; var bi = await restService.GetAsync(url.InfoUri, cancellationToken); - if (bi.Value is null) throw new NoNullAllowedException(); - info.BuildInfo = bi.Value; + info.BuildInfo = bi; info.RobustManifestInfo = info.BuildInfo.Build.Acz ? new RobustManifestInfo(new RobustPath(info.Url, "manifest.txt"), new RobustPath(info.Url, "download"), - bi.Value.Build.ManifestHash) + bi.Build.ManifestHash) : new RobustManifestInfo(new Uri(info.BuildInfo.Build.ManifestUrl), - new Uri(info.BuildInfo.Build.ManifestDownloadUrl), bi.Value.Build.ManifestHash); + new Uri(info.BuildInfo.Build.ManifestDownloadUrl), bi.Build.ManifestHash); return info; } diff --git a/Nebula.Shared/Services/EngineService.cs b/Nebula.Shared/Services/EngineService.cs index b80f6fd..447f72d 100644 --- a/Nebula.Shared/Services/EngineService.cs +++ b/Nebula.Shared/Services/EngineService.cs @@ -40,12 +40,10 @@ public sealed class EngineService _debugService.Log("Fetching engine manifest from: " + CurrentConVar.EngineManifestUrl); var info = await _restService.GetAsync>( new Uri(_varService.GetConfigValue(CurrentConVar.EngineManifestUrl)!), cancellationToken); - if (info.Value is null) - throw new Exception("Engine version info is null"); - - VersionInfos = info.Value; - _varService.SetConfigValue(CurrentConVar.EngineManifestBackup, info.Value); + VersionInfos = info; + + _varService.SetConfigValue(CurrentConVar.EngineManifestBackup, info); } catch (Exception e) { @@ -64,11 +62,11 @@ public sealed class EngineService var moduleInfo = await _restService.GetAsync( new Uri(_varService.GetConfigValue(CurrentConVar.EngineModuleManifestUrl)!), cancellationToken); - if (moduleInfo.Value is null) + if (moduleInfo is null) throw new Exception("Module version info is null"); - ModuleInfos = moduleInfo.Value.Modules; - _varService.SetConfigValue(CurrentConVar.ModuleManifestBackup, moduleInfo.Value); + ModuleInfos = moduleInfo.Modules; + _varService.SetConfigValue(CurrentConVar.ModuleManifestBackup, moduleInfo); } catch (Exception e) { @@ -177,7 +175,7 @@ public sealed class EngineService var engineVersionObj = Version.Parse(engineVersion); var module = ModuleInfos[moduleName]; - var selectedVersion = module.Versions.Select(kv => new { Version = Version.Parse(kv.Key), kv.Key, kv.Value }) + var selectedVersion = module.Versions.Select(kv => new { Version = Version.Parse(kv.Key), kv.Key, kv }) .Where(kv => engineVersionObj >= kv.Version) .MaxBy(kv => kv.Version); diff --git a/Nebula.Shared/Services/RestService.cs b/Nebula.Shared/Services/RestService.cs index 5273960..65bcc3b 100644 --- a/Nebula.Shared/Services/RestService.cs +++ b/Nebula.Shared/Services/RestService.cs @@ -23,19 +23,26 @@ public class RestService _debug = debug; } - public async Task> GetAsync(Uri uri, CancellationToken cancellationToken) where T : notnull + public async Task GetAsync(Uri uri, CancellationToken cancellationToken) where T : notnull { var response = await _client.GetAsync(uri, cancellationToken); return await ReadResult(response, cancellationToken); } - public async Task GetAsyncDefault(Uri uri, T defaultValue, CancellationToken cancellationToken) + public async Task GetAsyncDefault(Uri uri, T defaultValue, CancellationToken cancellationToken) where T : notnull { - var result = await GetAsync(uri, cancellationToken); - return result.Value ?? defaultValue; + try + { + return await GetAsync(uri, cancellationToken); + } + catch (Exception e) + { + _debug.Error(e); + return defaultValue; + } } - public async Task> PostAsync(T information, Uri uri, CancellationToken cancellationToken) where K : notnull + public async Task PostAsync(T information, Uri uri, CancellationToken cancellationToken) where K : notnull { var json = JsonSerializer.Serialize(information, _serializerOptions); var content = new StringContent(json, Encoding.UTF8, "application/json"); @@ -43,7 +50,7 @@ public class RestService return await ReadResult(response, cancellationToken); } - public async Task> PostAsync(Stream stream, Uri uri, CancellationToken cancellationToken) where T : notnull + public async Task PostAsync(Stream stream, Uri uri, CancellationToken cancellationToken) where T : notnull { using var multipartFormContent = new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture)); @@ -52,61 +59,32 @@ public class RestService return await ReadResult(response, cancellationToken); } - public async Task> DeleteAsync(Uri uri, CancellationToken cancellationToken) where T : notnull + public async Task DeleteAsync(Uri uri, CancellationToken cancellationToken) where T : notnull { var response = await _client.DeleteAsync(uri, cancellationToken); return await ReadResult(response, cancellationToken); } - private async Task> ReadResult(HttpResponseMessage response, CancellationToken cancellationToken) where T : notnull + private async Task ReadResult(HttpResponseMessage response, CancellationToken cancellationToken) where T : notnull { var content = await response.Content.ReadAsStringAsync(cancellationToken); - if (typeof(T) == typeof(RawResult)) - return (new RestResult(new RawResult(content), null, response.StatusCode) as RestResult)!; - + if (typeof(T) == typeof(string) && content is T t) + return t; + if (response.IsSuccessStatusCode) { _debug.Debug($"SUCCESSFUL GET CONTENT {typeof(T)}"); - return new RestResult(await response.Content.AsJson(), null, - response.StatusCode); + return await response.Content.AsJson(); } - throw new HttpRequestException(); + throw new RestRequestException(response.Content, response.StatusCode); } } -public class RestResult +public sealed class RestRequestException(HttpContent content, HttpStatusCode statusCode) : Exception { - public string Message = "Ok"; - public HttpStatusCode StatusCode; - public T Value; - - public RestResult(T value, string? message, HttpStatusCode statusCode) - { - Value = value; - if (message != null) Message = message; - StatusCode = statusCode; - } - - public static implicit operator T(RestResult result) - { - return result.Value; - } -} - -public class RawResult -{ - public string Result; - - public RawResult(string result) - { - Result = result; - } - - public static implicit operator string(RawResult result) - { - return result.Result; - } + public HttpStatusCode StatusCode { get; } = statusCode; + public HttpContent Content { get; } = content; } \ No newline at end of file