using Microsoft.Extensions.Options; using System.Text.Json; using System.Text.Json.Serialization; namespace Nuuru.Server.Services { public interface IIntegrityService { bool IsEnabled { get; } Task VerifyTokenAsync(string token, string ipAddress, string action, string? userId = null); Task ReportErrorAsync(string error, string? ipAddress = null, string? userId = null); } public sealed class IntegrityCheckResult { public bool Valid { get; init; } public Guid? AssessmentId { get; init; } public string? BrowserHash { get; init; } } public class IntegrityService : IIntegrityService { private readonly HttpClient _httpClient; private readonly ILogger _logger; private readonly string? _apiUrl; private readonly string? _siteId; private readonly bool _isEnabled; public IntegrityService( HttpClient httpClient, IConfiguration configuration, ILogger logger) { _httpClient = httpClient; _logger = logger; _apiUrl = configuration["Integrity:ApiUrl"]; _siteId = configuration["Integrity:SiteId"]; _isEnabled = !string.IsNullOrEmpty(_apiUrl); } public bool IsEnabled => _isEnabled; public async Task VerifyTokenAsync(string token, string ipAddress, string action, string? userId = null) { if (!_isEnabled || string.IsNullOrEmpty(token)) return null; try { var requestBody = new Dictionary { { "action", action }, { "payload", token }, { "site_id", _siteId ?? string.Empty }, { "user_ip", ipAddress } }; if (!string.IsNullOrEmpty(userId)) { requestBody["user_id"] = userId; } var response = await _httpClient.PostAsJsonAsync(_apiUrl + "/api/assessments", requestBody); if (!response.IsSuccessStatusCode) { _logger.LogWarning("Integrity API returned non-success parsing token. Status: {status}", response.StatusCode); return null; } var content = await response.Content.ReadFromJsonAsync(); return new IntegrityCheckResult { Valid = content?.Valid ?? false, AssessmentId = content?.Id, BrowserHash = content?.BrowserHash }; } catch (Exception ex) { _logger.LogError(ex, "Exception while verifying integrity token with external API"); return null; } } private class IntegrityApiResponse { [JsonPropertyName("valid")] public bool Valid { get; set; } [JsonPropertyName("id")] public Guid? Id { get; set; } [JsonPropertyName("browser_hash")] public string? BrowserHash { get; set; } } public async Task ReportErrorAsync(string error, string? ipAddress = null, string? userId = null) { if (!_isEnabled || string.IsNullOrEmpty(error)) return false; try { var requestBody = new Dictionary { { "error", error }, { "site_id", _siteId ?? string.Empty } }; if (!string.IsNullOrEmpty(ipAddress)) { requestBody["user_ip"] = ipAddress; } if (!string.IsNullOrEmpty(userId)) { requestBody["user_id"] = userId; } var response = await _httpClient.PostAsJsonAsync(_apiUrl + "/api/error-reports", requestBody); return response.IsSuccessStatusCode; } catch (Exception ex) { _logger.LogError(ex, "Exception while reporting integrity error to external API"); return false; } } } }