Modularize project structure by splitting responsibilities into distinct files and namespaces. Add helper methods for authentication and JSON operations.

This commit is contained in:
Andre Beging
2025-12-11 16:41:22 +01:00
parent 63bcdf003d
commit 67260ae450
12 changed files with 333 additions and 211 deletions

80
Helper/AuthHelper.cs Normal file
View File

@@ -0,0 +1,80 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Nodes;
using FsTool.Tasks;
namespace FsTool.Helpers;
public static class AuthHelper
{
private static async Task<string?> LoadCsrfTokenAsync()
{
if (!File.Exists("csrf_token.json")) return null;
var json = await File.ReadAllTextAsync("csrf_token.json");
var tokenInfo = JsonSerializer.Deserialize<JsonObject>(json);
if (tokenInfo == null) return null;
var token = tokenInfo["token"]?.GetValue<string>();
var expiresAtString = tokenInfo["expiresAt"]?.GetValue<string>();
if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(expiresAtString)) return null;
if (DateTime.TryParse(expiresAtString, out var expiresAt))
{
if (DateTime.UtcNow < expiresAt.ToUniversalTime())
return token;
}
return null;
}
internal static async Task StoreCsrfTokenAsync(string? csrfToken, DateTime expiration)
{
if (string.IsNullOrWhiteSpace(csrfToken)) return;
var tokenInfo = new
{
token = csrfToken,
expiresAt = expiration
};
var json = JsonSerializer.Serialize(tokenInfo, new JsonSerializerOptions
{
WriteIndented = true
});
await File.WriteAllTextAsync("csrf_token.json", json);
}
public static async Task EnsureAuthenticationAsync(HttpClient httpClient)
{
// Check if header already contains a CSRF token
if (httpClient.DefaultRequestHeaders.TryGetValues("X-CSRF-Token", out var existingTokens))
{
var existingToken = existingTokens.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(existingToken))
return;
}
// Try to load CSRF token from file
var csrfToken = await LoadCsrfTokenAsync();
// If no valid token found, call login endpoint to get a new one
if (string.IsNullOrWhiteSpace(csrfToken))
csrfToken = await UserTasks.CallLoginEndpointAsync(httpClient);
// Set CSRF token in HTTP client headers
if (!string.IsNullOrWhiteSpace(csrfToken))
{
csrfToken = csrfToken.ReplaceLineEndings(string.Empty);
httpClient.DefaultRequestHeaders.Remove("X-CSRF-Token");
httpClient.DefaultRequestHeaders.Add("X-CSRF-Token", csrfToken);
}
}
}

41
Helper/Extensions.cs Normal file
View File

@@ -0,0 +1,41 @@
using System.Text;
using System.Text.Json.Nodes;
namespace FsTool.Helpers
{
public static class Extensions
{
#region Public Method FsPostAsync
/// <summary>
/// Sends a POST request to the specified URI with the provided JSON content.
/// </summary>
/// <param name="httpClient">The instance of <see cref="HttpClient" /> used to send the request.</param>
/// <param name="requestUri">The URI to which the request is sent.</param>
/// <param name="jsonObject">The JSON object to include in the request body.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the HTTP response.</returns>
public static async Task<HttpResponseMessage> FsPostAsync(this HttpClient httpClient, string requestUri, JsonObject jsonObject)
{
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
content.Headers.ContentType = new("application/json");
return await httpClient.PostAsync(requestUri, content);
}
#endregion
#region Public Method ToList
/// <summary>
/// Converts the specified JSON node into a list of non-null <see cref="JsonNode" /> elements.
/// </summary>
/// <param name="node">The <see cref="JsonNode" /> to be converted. If null, an empty list is returned.</param>
/// <returns>A list of <see cref="JsonNode" /> instances containing non-null elements.</returns>
public static List<JsonNode> ToList(this JsonNode? node)
{
var array = node?.AsArray() ?? [];
return array.Where(x => x != null).Select(x => x!).ToList();
}
#endregion
}
}