using System.Net; using System.Net.Http; using XFEToolBox.Client.Profiles.CrossVersionProfiles; using XFEToolBox.Core.Models.Users; using XFEExtension.NetCore.ServerInteractive.Models.RequesterModels; using XFEExtension.NetCore.ServerInteractive.Utilities.Extensions; using XFEExtension.NetCore.ServerInteractive.Utilities.Requester; namespace XFEToolBox.Client.Utilities.Server; public static class ClientSession { private const string DefaultApiAddress = "https://toolbox.api.xfe.studio/api"; public static string ApiAddress { get; } = ResolveApiAddress(); private static ClientRequester _requester = CreateRequester(); public static event EventHandler? SessionChanged; public static ClientRequester Requester => _requester; public static ToolBoxUserFaceInfo? CurrentUser { get; private set; } public static bool IsLoggedIn => CurrentUser is not null; public static bool IsAdministrator => CurrentUser?.IsAdministrator == true; public static async Task<(bool Success, string Message)> LoginAsync(string account, string password) { try { var response = await _requester.Request>( "login", account.Trim(), password); if (response.StatusCode != HttpStatusCode.OK || response.Result?.UserInfo is null) return (false, string.IsNullOrWhiteSpace(response.Message) ? "账号或密码错误。" : response.Message); _requester.Session = response.Result.Session; CurrentUser = response.Result.UserInfo; SystemProfile.LastLoginAccount = account.Trim(); SystemProfile.LoginSession = response.Result.Session; SystemProfile.SaveProfile(); SessionChanged?.Invoke(null, EventArgs.Empty); return (true, "登录成功"); } catch (HttpRequestException exception) { return (false, $"无法连接工具服务器:{exception.Message}"); } catch (TaskCanceledException) { return (false, "连接服务器超时。"); } catch (Exception exception) { return (false, $"登录失败:{exception.Message}"); } } public static async Task TryRestoreAsync() { if (string.IsNullOrWhiteSpace(SystemProfile.LoginSession)) return false; try { _requester.Session = SystemProfile.LoginSession; var response = await _requester.Request("relogin"); if (response.StatusCode != HttpStatusCode.OK || response.Result is null) { ClearLocalSession(); return false; } CurrentUser = response.Result; SessionChanged?.Invoke(null, EventArgs.Empty); return true; } catch { return false; } } public static async Task<(bool Success, string Message)> RegisterAsync(string account, string password, string nickName) { try { var response = await _requester.Request( "register", account.Trim(), password, nickName.Trim()); if ((int)response.StatusCode is < 200 or >= 300 || response.Result is null) return (false, string.IsNullOrWhiteSpace(response.Message) ? "注册失败。" : response.Message); SystemProfile.LastLoginAccount = account.Trim(); SystemProfile.SaveProfile(); return await LoginAsync(account, password); } catch (Exception exception) { return (false, $"注册失败:{exception.Message}"); } } public static async Task<(bool Success, string Message)> UpdateProfileAsync(string nickName, string bio) { try { var response = await _requester.Request("updateProfile", nickName.Trim(), bio.Trim()); if (response.StatusCode != HttpStatusCode.OK || response.Result is null) return (false, string.IsNullOrWhiteSpace(response.Message) ? "保存失败。" : response.Message); CurrentUser = response.Result; SessionChanged?.Invoke(null, EventArgs.Empty); return (true, "个人资料已保存。"); } catch (Exception exception) { return (false, $"保存失败:{exception.Message}"); } } public static async Task<(bool Success, string Message)> ChangePasswordAsync(string currentPassword, string newPassword) { try { var response = await _requester.Request( "changePassword", currentPassword, newPassword); if (response.StatusCode != HttpStatusCode.OK) return (false, string.IsNullOrWhiteSpace(response.Message) ? "修改密码失败。" : response.Message); await LogoutAsync(); return (true, "密码已修改,请使用新密码重新登录。"); } catch (Exception exception) { return (false, $"修改密码失败:{exception.Message}"); } } public static async Task LogoutAsync() { var session = _requester.Session; try { if (!string.IsNullOrWhiteSpace(session)) await _requester.Request("logout") .WaitAsync(TimeSpan.FromSeconds(5)); } catch { // 本地退出必须始终完成;网络恢复后服务端会按会话有效期清理无法撤销的令牌。 } finally { ClearLocalSession(); } } private static void ClearLocalSession() { _requester.Session = string.Empty; CurrentUser = null; SystemProfile.LoginSession = string.Empty; SystemProfile.SaveProfile(); SessionChanged?.Invoke(null, EventArgs.Empty); } private static ClientRequester CreateRequester() => ClientRequesterBuilder.CreateBuilder() .UseXFEStandardRequest() .AddRequest() .Build(options => options.RequestAddress = ApiAddress); private static string ResolveApiAddress() { var configured = Environment.GetEnvironmentVariable("XFETOOLBOX_API_ADDRESS")?.Trim().TrimEnd('/'); if (Uri.TryCreate(configured, UriKind.Absolute, out var uri) && (string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) || (string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && uri.IsLoopback))) return uri.AbsoluteUri.TrimEnd('/'); return DefaultApiAddress; } }