353 lines
11 KiB
C#
353 lines
11 KiB
C#
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using Model;
|
|
|
|
namespace Web.Services;
|
|
|
|
public class UserDecksService
|
|
{
|
|
private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true };
|
|
private readonly HttpClient _http;
|
|
private readonly AuthService _authService;
|
|
private readonly NetworkStatusService _networkStatus;
|
|
private readonly string _graphqlEndpoint;
|
|
private readonly string _restEndpoint;
|
|
|
|
public UserDecksService(HttpClient http, AuthService authService, NetworkStatusService networkStatus)
|
|
{
|
|
_http = http;
|
|
_authService = authService;
|
|
_networkStatus = networkStatus;
|
|
_graphqlEndpoint = http.BaseAddress != null && http.BaseAddress.Port == 7266
|
|
? "/graphql"
|
|
: "https://localhost:7266/graphql";
|
|
_restEndpoint = http.BaseAddress != null && http.BaseAddress.Port == 7266
|
|
? "/api/decks"
|
|
: "https://localhost:7266/api/decks";
|
|
}
|
|
|
|
public async Task<List<UserDeck>> GetDecksAsync()
|
|
{
|
|
if (!_networkStatus.IsOnline)
|
|
return [];
|
|
|
|
try
|
|
{
|
|
var request = new GraphQLRequest
|
|
{
|
|
Query = @"query GetDecks {
|
|
decks {
|
|
id
|
|
name
|
|
cards
|
|
divers
|
|
notes
|
|
season
|
|
updatedAt
|
|
}
|
|
}"
|
|
};
|
|
|
|
var response = await SendGraphQLAsync<GetDecksResponseData>(request);
|
|
if (response?.Data?.Decks != null)
|
|
return response.Data.Decks;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[WARNING] GraphQL GetDecks failed: {ex.Message}. Trying REST.");
|
|
}
|
|
|
|
// Fallback to REST
|
|
try
|
|
{
|
|
var req = new HttpRequestMessage(HttpMethod.Get, _restEndpoint);
|
|
AddAuthHeader(req);
|
|
var res = await _http.SendAsync(req);
|
|
if (res.IsSuccessStatusCode)
|
|
{
|
|
var decks = await res.Content.ReadFromJsonAsync<List<UserDeck>>(JsonOptions);
|
|
return decks ?? [];
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[ERROR] REST GetDecks failed: {ex.Message}");
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
public async Task<UserDeck?> GetDeckAsync(Guid id)
|
|
{
|
|
if (!_networkStatus.IsOnline)
|
|
return null;
|
|
|
|
try
|
|
{
|
|
var request = new GraphQLRequest
|
|
{
|
|
Query = @"query GetDeck($id: UUID!) {
|
|
deck(id: $id) {
|
|
id
|
|
name
|
|
cards
|
|
divers
|
|
notes
|
|
season
|
|
updatedAt
|
|
}
|
|
}",
|
|
Variables = new Dictionary<string, object> { ["id"] = id }
|
|
};
|
|
|
|
var response = await SendGraphQLAsync<GetDeckResponseData>(request);
|
|
if (response?.Data?.Deck != null)
|
|
return response.Data.Deck;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[WARNING] GraphQL GetDeck failed: {ex.Message}. Trying REST.");
|
|
}
|
|
|
|
// Fallback to REST
|
|
try
|
|
{
|
|
var req = new HttpRequestMessage(HttpMethod.Get, $"{_restEndpoint}/{id}");
|
|
AddAuthHeader(req);
|
|
var res = await _http.SendAsync(req);
|
|
if (res.IsSuccessStatusCode)
|
|
{
|
|
return await res.Content.ReadFromJsonAsync<UserDeck>(JsonOptions);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[ERROR] REST GetDeck failed: {ex.Message}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public async Task<UserDeck?> SaveDeckAsync(UserDeck deck)
|
|
{
|
|
if (!_networkStatus.IsOnline)
|
|
return null;
|
|
|
|
try
|
|
{
|
|
var request = new GraphQLRequest
|
|
{
|
|
Query = @"mutation SaveDeck($input: UserDeckInput!) {
|
|
saveDeck(input: $input) {
|
|
id
|
|
name
|
|
cards
|
|
divers
|
|
notes
|
|
season
|
|
updatedAt
|
|
}
|
|
}",
|
|
Variables = new Dictionary<string, object>
|
|
{
|
|
["input"] = new Dictionary<string, object?>
|
|
{
|
|
["id"] = deck.Id,
|
|
["name"] = deck.Name,
|
|
["cards"] = deck.Cards,
|
|
["divers"] = deck.Divers,
|
|
["notes"] = deck.Notes,
|
|
["season"] = deck.Season
|
|
}
|
|
}
|
|
};
|
|
|
|
var response = await SendGraphQLAsync<SaveDeckResponseData>(request);
|
|
if (response?.Data?.SaveDeck != null)
|
|
return response.Data.SaveDeck;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[WARNING] GraphQL SaveDeck failed: {ex.Message}. Trying REST.");
|
|
}
|
|
|
|
// Fallback to REST
|
|
try
|
|
{
|
|
var req = new HttpRequestMessage(HttpMethod.Post, _restEndpoint)
|
|
{
|
|
Content = JsonContent.Create(deck)
|
|
};
|
|
AddAuthHeader(req);
|
|
var res = await _http.SendAsync(req);
|
|
if (res.IsSuccessStatusCode)
|
|
{
|
|
return await res.Content.ReadFromJsonAsync<UserDeck>(JsonOptions);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[ERROR] REST SaveDeck failed: {ex.Message}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public async Task<bool> DeleteDeckAsync(Guid id)
|
|
{
|
|
if (!_networkStatus.IsOnline)
|
|
return false;
|
|
|
|
try
|
|
{
|
|
var request = new GraphQLRequest
|
|
{
|
|
Query = @"mutation DeleteDeck($id: UUID!) {
|
|
deleteDeck(id: $id)
|
|
}",
|
|
Variables = new Dictionary<string, object> { ["id"] = id }
|
|
};
|
|
|
|
var response = await SendGraphQLAsync<DeleteDeckResponseData>(request);
|
|
if (response?.Data?.DeleteDeck == true)
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[WARNING] GraphQL DeleteDeck failed: {ex.Message}. Trying REST.");
|
|
}
|
|
|
|
// Fallback to REST
|
|
try
|
|
{
|
|
var req = new HttpRequestMessage(HttpMethod.Delete, $"{_restEndpoint}/{id}");
|
|
AddAuthHeader(req);
|
|
var res = await _http.SendAsync(req);
|
|
return res.IsSuccessStatusCode;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[ERROR] REST DeleteDeck failed: {ex.Message}");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void AddAuthHeader(HttpRequestMessage request)
|
|
{
|
|
if (!string.IsNullOrEmpty(_authService.AuthToken))
|
|
{
|
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _authService.AuthToken);
|
|
}
|
|
}
|
|
|
|
private async Task<GraphQLResponse<T>?> SendGraphQLAsync<T>(GraphQLRequest request)
|
|
{
|
|
var httpRequest = new HttpRequestMessage(HttpMethod.Post, _graphqlEndpoint)
|
|
{
|
|
Content = JsonContent.Create(request)
|
|
};
|
|
AddAuthHeader(httpRequest);
|
|
|
|
try
|
|
{
|
|
var httpResponse = await _http.SendAsync(httpRequest);
|
|
if (httpResponse.IsSuccessStatusCode)
|
|
return await httpResponse.Content.ReadFromJsonAsync<GraphQLResponse<T>>(JsonOptions);
|
|
|
|
if (_graphqlEndpoint == "/graphql" || _graphqlEndpoint.StartsWith("https://"))
|
|
{
|
|
var fallbackUrl = _graphqlEndpoint == "/graphql"
|
|
? "https://localhost:7266/graphql"
|
|
: "http://localhost:7266/graphql";
|
|
var fallbackReq = new HttpRequestMessage(HttpMethod.Post, fallbackUrl)
|
|
{
|
|
Content = JsonContent.Create(request)
|
|
};
|
|
AddAuthHeader(fallbackReq);
|
|
var fallbackRes = await _http.SendAsync(fallbackReq);
|
|
if (fallbackRes.IsSuccessStatusCode)
|
|
return await fallbackRes.Content.ReadFromJsonAsync<GraphQLResponse<T>>(JsonOptions);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
try
|
|
{
|
|
var fallbackUrl = _graphqlEndpoint.StartsWith("http://")
|
|
? "https://localhost:7266/graphql"
|
|
: "http://localhost:7266/graphql";
|
|
var fallbackReq = new HttpRequestMessage(HttpMethod.Post, fallbackUrl)
|
|
{
|
|
Content = JsonContent.Create(request)
|
|
};
|
|
AddAuthHeader(fallbackReq);
|
|
var fallbackRes = await _http.SendAsync(fallbackReq);
|
|
if (fallbackRes.IsSuccessStatusCode)
|
|
return await fallbackRes.Content.ReadFromJsonAsync<GraphQLResponse<T>>(JsonOptions);
|
|
}
|
|
catch
|
|
{
|
|
if (_graphqlEndpoint == "/graphql")
|
|
{
|
|
try
|
|
{
|
|
var fallbackReq = new HttpRequestMessage(HttpMethod.Post, "http://localhost:7266/graphql")
|
|
{
|
|
Content = JsonContent.Create(request)
|
|
};
|
|
AddAuthHeader(fallbackReq);
|
|
var fallbackRes = await _http.SendAsync(fallbackReq);
|
|
if (fallbackRes.IsSuccessStatusCode)
|
|
return await fallbackRes.Content.ReadFromJsonAsync<GraphQLResponse<T>>(JsonOptions);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private class GraphQLRequest
|
|
{
|
|
[JsonPropertyName("query")] public string Query { get; set; } = string.Empty;
|
|
[JsonPropertyName("variables")] public Dictionary<string, object>? Variables { get; set; }
|
|
}
|
|
|
|
private class GraphQLResponse<T>
|
|
{
|
|
[JsonPropertyName("data")] public T? Data { get; set; }
|
|
[JsonPropertyName("errors")] public List<GraphQLError>? Errors { get; set; }
|
|
}
|
|
|
|
private class GraphQLError
|
|
{
|
|
[JsonPropertyName("message")] public string Message { get; set; } = string.Empty;
|
|
}
|
|
|
|
private class GetDecksResponseData
|
|
{
|
|
[JsonPropertyName("decks")] public List<UserDeck>? Decks { get; set; }
|
|
}
|
|
|
|
private class GetDeckResponseData
|
|
{
|
|
[JsonPropertyName("deck")] public UserDeck? Deck { get; set; }
|
|
}
|
|
|
|
private class SaveDeckResponseData
|
|
{
|
|
[JsonPropertyName("saveDeck")] public UserDeck? SaveDeck { get; set; }
|
|
}
|
|
|
|
private class DeleteDeckResponseData
|
|
{
|
|
[JsonPropertyName("deleteDeck")] public bool DeleteDeck { get; set; }
|
|
}
|
|
}
|