using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; using contact.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace contact.Pages { [BindProperties(SupportsGet = true)] public class RequestFormModel { public RequestFormModel() { DataCenter = "SF"; } [Required] public string Name { get; set; } [Required] public string Company { get; set; } [Required, EmailAddress] public string Email { get; set; } [Required] public string DataCenter { get; set; } } public class RequestModel : PageModel { private const string EvaluateUrl = "https://ayanova.com/docs/ay-evaluate/"; [BindProperty(SupportsGet = true)] public RequestFormModel SubRequest { get; set; } //Set when neither Sockeye nor the email got the request, so the page can say so public bool SendFailed { get; set; } public List DataCenters { get; } = new List { new SelectListItem { Value = "SF", Text = "San Francisco" }, new SelectListItem { Value = "ATL", Text = "Atlanta" }, new SelectListItem { Value = "NY", Text = "New York" }, new SelectListItem { Value = "TOR", Text = "Toronto" }, new SelectListItem { Value = "AMS", Text = "Amsterdam" }, new SelectListItem { Value = "LON", Text = "London" }, new SelectListItem { Value = "FFT", Text = "Frankfurt" }, new SelectListItem { Value = "SNG", Text = "Singapore" }, new SelectListItem { Value = "BNG", Text = "Bangalore" }, new SelectListItem { Value = "SYD", Text = "Sydney Australia" } }; private readonly IConfiguration _configuration; private readonly IHttpClientFactory _httpClientFactory; private readonly MailSender _mail; private readonly ILogger _logger; public RequestModel(IConfiguration configuration, IHttpClientFactory httpClientFactory, MailSender mail, ILogger logger) { _configuration = configuration; _httpClientFactory = httpClientFactory; _mail = mail; _logger = logger; } public void OnGet() { } public async Task OnPostAsync() { //The select can be bypassed by a hand-crafted POST; only accept codes from the list if (!DataCenters.Any(d => d.Value == SubRequest.DataCenter)) { ModelState.AddModelError("SubRequest.DataCenter", "Please choose a data center from the list."); } if (!ModelState.IsValid) { return Page(); } if (ContactModel.IsBlocked(SubRequest.Company)) { return Redirect(EvaluateUrl); } //1. Record the request in Sockeye. A failure here is logged and reported in the email, // so the trial can be set up by hand instead of the request disappearing. string sockeyeResult; bool sockeyeOk; try { await PostRequestToSockeyeAsync(); sockeyeOk = true; sockeyeResult = "Recorded in Sockeye."; } catch (Exception ex) { sockeyeOk = false; sockeyeResult = $"NOT RECORDED IN SOCKEYE ({ex.Message}). Set this trial up by hand."; _logger.LogError(ex, "Trial request post to Sockeye failed. {Body}", Describe()); } //2. Notify support. Email is in the body; From is the form account, so it was never there before either var body = $"{Describe()}\r\n\r\nSockeye:\r\n{sockeyeResult}"; var subject = sockeyeOk ? $"Trial request from {SubRequest.Company}" : $"Trial request NOT RECORDED from {SubRequest.Company}"; bool mailOk; try { await _mail.SendToSupportAsync(subject, body, SubRequest.Name, SubRequest.Email); mailOk = true; } catch (Exception ex) { mailOk = false; _logger.LogError(ex, "Trial request email failed. Submission:\r\n{Body}", body); } //Either path reaching John means the request is not lost; only both failing is a failure for the visitor if (!sockeyeOk && !mailOk) { SendFailed = true; return Page(); } return Redirect(EvaluateUrl); } private string Describe() => $"Name:\r\n{SubRequest.Name}\r\n\r\nCompany:\r\n{SubRequest.Company}\r\n\r\nEmail:\r\n{SubRequest.Email}\r\n\r\nDataCenter:\r\n{SubRequest.DataCenter}"; private async Task PostRequestToSockeyeAsync() { //Sockeye:TrialRequestUrl. On the server this is http://127.0.0.1:7575/subscription-server-trial-request, //straight to Kestrel on the same host, so it depends on neither DNS nor the droplet reaching its own reserved IP. var url = _configuration["Sockeye:TrialRequestUrl"]; if (string.IsNullOrWhiteSpace(url)) { throw new InvalidOperationException("Sockeye:TrialRequestUrl is not configured"); } var r = new { dataCenter = SubRequest.DataCenter, email = SubRequest.Email, company = SubRequest.Company, name = SubRequest.Name, rValue = "totallylegit" }; var client = _httpClientFactory.CreateClient("sockeye"); var response = await client.PostAsJsonAsync(url, r); //Previously the response was never checked, so a rejected request failed silently response.EnsureSuccessStatusCode(); } } }