using System.Text.Json; using System.Text.Json.Serialization; using Nuuru.Server.Models; namespace Nuuru.Server.Services { public class HCaptchaService : ICaptchaService { private readonly ILogger _logger; private readonly ISiteSettingsService _siteSettings; private readonly HttpClient _httpClient; public HCaptchaService(ILogger logger, ISiteSettingsService siteSettings, IHttpClientFactory httpClientFactory) { _logger = logger; _siteSettings = siteSettings; _httpClient = httpClientFactory.CreateClient(); } public async Task ValidateAsync(string token) { var secretKey = await _siteSettings.GetCaptchaSettingAsync( SiteSettingKeys.CaptchaHCaptchaSecretKey, ""); if (string.IsNullOrEmpty(secretKey)) { _logger.LogError("HCaptcha secret key is not configured"); return false; } if (string.IsNullOrEmpty(token)) { _logger.LogWarning("CAPTCHA token is empty"); return false; } try { var verifyUrl = await _siteSettings.GetCaptchaSettingAsync( SiteSettingKeys.CaptchaHCaptchaVerifyUrl, SiteSettingKeys.DefaultCaptchaHCaptchaVerifyUrl); var formData = new Dictionary { { "secret", secretKey }, { "response", token } }; var response = await _httpClient.PostAsync(verifyUrl, new FormUrlEncodedContent(formData)); var responseContent = await response.Content.ReadAsStringAsync(); var result = JsonSerializer.Deserialize(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); if (result?.Success == true) { _logger.LogInformation("CAPTCHA validation successful"); return true; } _logger.LogWarning("CAPTCHA validation failed: {ErrorCodes}", result?.ErrorCodes != null ? string.Join(", ", result.ErrorCodes) : "Unknown error"); return false; } catch (Exception ex) { _logger.LogError(ex, "Error validating CAPTCHA"); return false; } } private class HCaptchaResponse { [JsonPropertyName("success")] public bool Success { get; set; } [JsonPropertyName("challenge_ts")] public string? ChallengeTs { get; set; } [JsonPropertyName("hostname")] public string? Hostname { get; set; } [JsonPropertyName("error-codes")] public string[]? ErrorCodes { get; set; } } } }