case 4670 multiple changes by Claude to break out message sending from the individual forms and support the trial server request

This commit is contained in:
2026-10-09 14:47:47 -07:00
parent 38113310aa
commit 7feb501bc2
11 changed files with 426 additions and 541 deletions

View File

@@ -1,12 +1,19 @@
@page @page
@model ContactModel @model ContactModel
@{ @{
ViewData["Title"] = "Contact"; ViewData["Title"] = "Contact";
} }
<div class="container"> <div class="container">
@if (Model.SendFailed)
{
<div class="alert alert-danger">
Sorry, your message could not be sent. Please try again in a few minutes,
or email support@ayanova.com directly.
</div>
}
<div class="jumbotron text-center" <div class="jumbotron text-center"
style="background-image: url(images/jumbotronbackgroundABC.png); background-size: 100% no-repeat center;"> style="background-image: url(images/jumbotronbackgroundABC.png); background-size: 100% no-repeat center;">
<font color="white"> <font color="white">
@@ -63,9 +70,9 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<div class="form-group"> <div class="form-group">
<label asp-for="Contact.DbId" class="col-md-3 right">DbId:</label> <label asp-for="Contact.DbId" class="col-md-3 right">Database ID:</label>
<div class="col-md-9"> <div class="col-md-9">
<input asp-for="Contact.DbId" placeholder="AyaNova Database Id (optional)" class="form-control" required /> <input asp-for="Contact.DbId" placeholder="AyaNova Database Id (optional)" class="form-control" />
<span asp-validation-for="Contact.DbId"></span> <span asp-validation-for="Contact.DbId"></span>
</div> </div>
</div> </div>
@@ -97,29 +104,12 @@
<!-- Button -->
<!-- <div class="form-group">
<label class="col-md-4 control-label" for="Send"></label>
<div class="col-md-4">
<div class="g-recaptcha" data-callback="imNotARobot" data-sitekey="6LcH7GUUAAAAAJIDf_JDZolSv__xN6oqr9Dx79zs">
</div>
<button id="btnsubmit" type="submit" class="btn btn-info">Send us your question</button>
</div>
</div> -->
<div class="form-group"> <div class="form-group">
<label class="col-md-4 control-label" for="Send"></label> <label class="col-md-4 control-label" for="Send"></label>
<div class="col-md-4"> <div class="col-md-4">
<button type="submit" class="btn btn-info">Send us your question</button> <button type="submit" class="btn btn-info">Send us your question</button>
</div> </div>
</div> </div>
<!-- <div class="row">
<div class="col-md-12">
<button type="submit">Send us your question</button>
</div>
</div> -->
</form> </form>
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-12"> <div class="col-sm-12 col-md-12">
@@ -130,7 +120,7 @@
<strong> <strong>
<p>Why do we ask for your name, company name and email address?</p> <p>Why do we ask for your name, company name and email address?</p>
</strong> </strong>
<p>We use the information you send us to respond to you, to provide sales support and technicial support to <p>We use the information you send us to respond to you, to provide sales support and technical support to
you and your company, and to make AyaNova recommendations and suggestions to you and your company. Also you and your company, and to make AyaNova recommendations and suggestions to you and your company. Also
refer to our <a href="https://www.ayanova.com/privacypolicy.htm" target="_blank">Privacy Policy</a> for more refer to our <a href="https://www.ayanova.com/privacypolicy.htm" target="_blank">Privacy Policy</a> for more
clarity.</p> clarity.</p>
@@ -164,8 +154,7 @@
<p>In order to be able to provide an affordable product for small business with fast technical support <p>In order to be able to provide an affordable product for small business with fast technical support
globally, we do not provide phone support.</p> globally, we do not provide phone support.</p>
<p>Please contact us via the online form above, or send us a direct email to support@ayanova.com and our <p>Please contact us via the online form above, or send us a direct email to support@ayanova.com and our
North American support staff will respond promptly between 9:00AM and 5:00PM Pacific Standard Time (-8GMT/ North American support staff will respond promptly between 9:00AM and 5:00PM Pacific Time Monday to Friday.</p>
UTC) Monday to Friday.</p>
</div> </div>
</div> </div>
@@ -185,4 +174,6 @@
<span><a href="https://ayanova.com/privacypolicy.htm">Privacy Policy</a></span></small> <span><a href="https://ayanova.com/privacypolicy.htm">Privacy Policy</a></span></small>
</div> </div>
</div> </div>
<!--end bottom container--> <!--end bottom container-->
</div>

View File

@@ -1,124 +1,98 @@
using System; using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MailKit.Net.Smtp; using contact.Services;
using MailKit.Security; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; namespace contact.Pages
using MimeKit; {
[BindProperties(SupportsGet = true)]
namespace contact.Pages public class ContactFormModel
{ {
[BindProperties(SupportsGet = true)] public ContactFormModel()
public class ContactFormModel {
{ DbId = "n/a v7";
public ContactFormModel() }
{ [Required]
DbId = "n/a v7"; public string Name { get; set; }
} [Required]
[Required] public string Company { get; set; }
public string Name { get; set; } //Optional; AyaNova clients deep-link to this page with their database ID.
[Required] //Binding attributes left exactly as they were so that link keeps working.
public string Company { get; set; } [BindProperty(SupportsGet = true)]
[BindProperty(SupportsGet = true)] public string DbId { get; set; }
public string DbId { get; set; } [Required, EmailAddress]
[Required, EmailAddress] public string Email { get; set; }
public string Email { get; set; } [Required]
[Required] public string Message { get; set; }
public string Message { get; set; } }
}
public class ContactModel : PageModel
public class ContactModel : PageModel {
{ private const string ConfirmedUrl = "https://ayanova.com/confirmed.htm";
private const string ConfirmedUrl = "https://ayanova.com/confirmed.htm";
//2022-05-06 Company names containing any of these are dropped silently but still get the
//2022-05-06 Company names containing any of these are dropped silently but still get the //success redirect, so the spammer gets no signal and has to pay someone to enter them by hand
//success redirect, so the spammer gets no signal and has to pay someone to enter them by hand public static readonly string[] SpamBlocks = { "google", "jumboleadmagnet", "boostleadgeneration" };
private static readonly string[] SpamBlocks = { "google", "jumboleadmagnet", "boostleadgeneration" };
[BindProperty(SupportsGet = true)]
public string Message { get; set; } public ContactFormModel Contact { get; set; }
[BindProperty(SupportsGet = true)] //Set when the send fails, so the page can tell the visitor instead of claiming success
public ContactFormModel Contact { get; set; } public bool SendFailed { get; set; }
//Set when the send fails, so Contact.cshtml can tell the visitor instead of claiming success private readonly MailSender _mail;
public bool SendFailed { get; set; } private readonly ILogger<ContactModel> _logger;
private readonly IConfiguration _configuration; public ContactModel(MailSender mail, ILogger<ContactModel> logger)
private readonly ILogger<ContactModel> _logger; {
_mail = mail;
public ContactModel(IConfiguration configuration, ILogger<ContactModel> logger) _logger = logger;
{ }
_configuration = configuration;
_logger = logger; public void OnGet()
} {
}
public void OnGet()
{ public async Task<IActionResult> OnPostAsync()
Message = "Your contact page."; {
} if (!ModelState.IsValid)
{
public async Task<IActionResult> OnPostAsync() return Page();
{ }
if (!ModelState.IsValid)
{ //2025-11-01 reCAPTCHA removed (Google service change). Replacement deferred, see runbook Phase 6.
return Page();
} if (IsBlocked(Contact.Company))
{
//2025-11-01 reCAPTCHA removed (Google service change). Replacement deferred, see runbook Phase 6. return Redirect(ConfirmedUrl);
}
var company = Contact.Company.ToLowerInvariant();
if (SpamBlocks.Any(s => company.Contains(s))) //Email is in the body as well as Reply-To, so it survives even if Reply-To can't be set
{ var body = $"Name:\r\n{Contact.Name}\r\n\r\nCompany:\r\n{Contact.Company}\r\n\r\nEmail:\r\n{Contact.Email}\r\n\r\nDatabase Id:\r\n{Contact.DbId}\r\n\r\nMessage:\r\n{Contact.Message}";
return Redirect(ConfirmedUrl);
} try
{
try await _mail.SendToSupportAsync($"Question about AyaNova from {Contact.Company}", body, Contact.Name, Contact.Email);
{ }
await SendMailAsync(); catch (Exception ex)
} {
catch (Exception ex) //Log the whole submission so it can be recovered from the journal, not lost
{ _logger.LogError(ex, "Contact form send failed. Submission:\r\n{Body}", body);
//Log the whole submission so it can be recovered from the journal, not lost SendFailed = true;
_logger.LogError(ex, return Page();
"Contact form send failed. Name: {Name} | Company: {Company} | Email: {Email} | DbId: {DbId} | Message: {Message}", }
Contact.Name, Contact.Company, Contact.Email, Contact.DbId, Contact.Message);
SendFailed = true; return Redirect(ConfirmedUrl);
return Page(); }
}
public static bool IsBlocked(string company)
return Redirect(ConfirmedUrl); {
} var c = company.ToLowerInvariant();
return SpamBlocks.Any(s => c.Contains(s));
private async Task SendMailAsync() }
{ }
var smtp = _configuration.GetSection("Smtp"); }
//Email is in the body as well as Reply-To, so it survives even if Reply-To can't be set
var body = $"Name:\r\n{Contact.Name}\r\n\r\nCompany:\r\n{Contact.Company}\r\n\r\nEmail:\r\n{Contact.Email}\r\n\r\nDatabase Id:\r\n{Contact.DbId}\r\n\r\nMessage:\r\n{Contact.Message}";
var message = new MimeMessage();
//From is always the authenticated account; the visitor goes in Reply-To (no sender spoofing)
message.From.Add(new MailboxAddress("AyaNova contact form", smtp["User"]));
message.To.Add(MailboxAddress.Parse("support@ayanova.com"));
if (MailboxAddress.TryParse(Contact.Email, out var replyTo))
{
replyTo.Name = Contact.Name;
message.ReplyTo.Add(replyTo);
}
message.Subject = $"Question about AyaNova from {OneLine(Contact.Company)}";
message.Body = new TextPart("plain") { Text = body };
using var client = new SmtpClient();
await client.ConnectAsync(smtp["Host"], smtp.GetValue<int>("Port"), SecureSocketOptions.StartTls);
await client.AuthenticateAsync(smtp["User"], smtp["Password"]);
await client.SendAsync(message);
await client.DisconnectAsync(true);
}
private static string OneLine(string s) => s.Replace("\r", " ").Replace("\n", " ");
}
}

View File

@@ -1,4 +1,4 @@
@page @page
@model RequestModel @model RequestModel
@{ @{
ViewData["Title"] = "Request"; ViewData["Title"] = "Request";
@@ -7,6 +7,14 @@
<div class="container"> <div class="container">
@if (Model.SendFailed)
{
<div class="alert alert-danger">
Sorry, your request could not be sent. Please try again in a few minutes,
or email support@ayanova.com directly.
</div>
}
<div class="jumbotron text-center"> <div class="jumbotron text-center">
<h2>Request AyaNova subscription 7 day trial account</h2> <h2>Request AyaNova subscription 7 day trial account</h2>
</div> </div>
@@ -58,7 +66,7 @@
<label asp-for="SubRequest.DataCenter" class="col-md-3 right">Data center (closest to <label asp-for="SubRequest.DataCenter" class="col-md-3 right">Data center (closest to
you):</label> you):</label>
<div class="col-md-9"> <div class="col-md-9">
<select asp-for="SubRequest.DataCenter" asp-items="Model.DataCenters"></select> <select asp-for="SubRequest.DataCenter" asp-items="Model.DataCenters" class="form-control"></select>
<span asp-validation-for="SubRequest.DataCenter"></span> <span asp-validation-for="SubRequest.DataCenter"></span>
</div> </div>
</div> </div>
@@ -67,18 +75,6 @@
<!-- Button -->
<!-- <div class="form-group">
<label class="col-md-4 control-label" for="Send"></label>
<div class="col-md-4">
<div class="g-recaptcha" data-callback="imNotARobot"
data-sitekey="6LcH7GUUAAAAAJIDf_JDZolSv__xN6oqr9Dx79zs">
</div>
<button id="btnsubmit" type="submit" class="btn btn-info">Send us your request</button>
</div>
</div> -->
<div class="form-group"> <div class="form-group">
<label class="col-md-4 control-label" for="Send"></label> <label class="col-md-4 control-label" for="Send"></label>
@@ -102,4 +98,6 @@
<span><a href="https://ayanova.com/r/privacy.htm">Privacy Policy</a></span></small> <span><a href="https://ayanova.com/r/privacy.htm">Privacy Policy</a></span></small>
</div> </div>
</div> </div>
<!--end bottom container--> <!--end bottom container-->
</div>

View File

@@ -1,228 +1,165 @@
using System.Collections.Generic; using System;
using Microsoft.AspNetCore.Mvc.RazorPages; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc; using System.Linq;
using System.Net.Mail; using System.Net.Http;
using System.Net; using System.Net.Http.Json;
//for captcha: using System.Threading.Tasks;
using System.Net.Http; using contact.Services;
using Newtonsoft.Json.Linq; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Configuration;
namespace contact.Pages using Microsoft.Extensions.Logging;
{
[BindProperties(SupportsGet = true)] namespace contact.Pages
public class RequestFormModel {
{ [BindProperties(SupportsGet = true)]
public RequestFormModel() public class RequestFormModel
{ {
DataCenter = "SF"; public RequestFormModel()
} {
[Required] DataCenter = "SF";
public string Name { get; set; } }
[Required] [Required]
public string Company { get; set; } public string Name { get; set; }
[Required]
[Required, EmailAddress] public string Company { get; set; }
public string Email { get; set; } [Required, EmailAddress]
[Required] public string Email { get; set; }
public string DataCenter { get; set; } [Required]
public string DataCenter { get; set; }
}
public class RequestModel : PageModel
} {
private const string EvaluateUrl = "https://ayanova.com/docs/ay-evaluate/";
public class RequestModel : PageModel
{ [BindProperty(SupportsGet = true)]
// public string DataCenter { get; set; } public RequestFormModel SubRequest { get; set; }
[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<SelectListItem> DataCenters { get; } = new List<SelectListItem>
{ public List<SelectListItem> DataCenters { get; } = new List<SelectListItem>
new SelectListItem { Value = "SF", Text = "San Francisco" }, {
new SelectListItem { Value = "NY", Text = "New York" }, new SelectListItem { Value = "SF", Text = "San Francisco" },
new SelectListItem { Value = "TOR", Text = "Toronto" }, new SelectListItem { Value = "ATL", Text = "Atlanta" },
new SelectListItem { Value = "AMS", Text = "Amsterdam" }, new SelectListItem { Value = "NY", Text = "New York" },
new SelectListItem { Value = "LON", Text = "London" }, new SelectListItem { Value = "TOR", Text = "Toronto" },
new SelectListItem { Value = "FFT", Text = "Frankfurt" }, new SelectListItem { Value = "AMS", Text = "Amsterdam" },
new SelectListItem { Value = "SNG", Text = "Singapore" }, new SelectListItem { Value = "LON", Text = "London" },
new SelectListItem { Value = "BNG", Text = "Bangalore" }, new SelectListItem { Value = "FFT", Text = "Frankfurt" },
new SelectListItem { Value = "SYD", Text = "Sydney Australia" } new SelectListItem { Value = "SNG", Text = "Singapore" },
new SelectListItem { Value = "BNG", Text = "Bangalore" },
}; new SelectListItem { Value = "SYD", Text = "Sydney Australia" }
};
private readonly IConfiguration _configuration;
public RequestModel(IConfiguration configuration) private readonly IConfiguration _configuration;
{ private readonly IHttpClientFactory _httpClientFactory;
_configuration = configuration; private readonly MailSender _mail;
} private readonly ILogger<RequestModel> _logger;
public ActionResult OnPost() public RequestModel(IConfiguration configuration, IHttpClientFactory httpClientFactory, MailSender mail, ILogger<RequestModel> logger)
{ {
if (!ModelState.IsValid) _configuration = configuration;
{ _httpClientFactory = httpClientFactory;
return Page(); _mail = mail;
} _logger = logger;
}
//2025-11-01 Google dropping support for captcha, just nuking it for now, see how much spam I get
// if (!ReCaptchaPassed( public void OnGet()
// Request.Form["g-recaptcha-response"], // that's how you get it from the Request object {
// _configuration.GetSection("GoogleReCaptcha:secret").Value }
// ))
// { public async Task<IActionResult> OnPostAsync()
// //Return a fail code that will hopefully take us off the spammers list {
// return StatusCode(500); //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.");
}
//fuck those spammers
//2022-05-06 changes to get rid of jumbo lead magnet spammer but still force him to pay someone to manually enter the stuff if (!ModelState.IsValid)
//ideally it should read from a text file here but this is a quickie fix while Joyce is on important viha phone call {
var blocks = new List<string>(); return Page();
blocks.Add("google"); }
blocks.Add("jumboleadmagnet");//<--fuck this guy in particular, keeps spamming
blocks.Add("boostleadgeneration"); if (ContactModel.IsBlocked(SubRequest.Company))
//ericjonesmyemail@gmail.com {
var companyname = SubRequest.Company.ToLowerInvariant(); return Redirect(EvaluateUrl);
foreach (string s in blocks) }
{
if (companyname.Contains(s)) //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.
goto skipspam; string sockeyeResult;
bool sockeyeOk;
} try
} {
await PostRequestToSockeyeAsync();
PostRequestToSockeye(); sockeyeOk = true;
SendMail(); sockeyeResult = "Recorded in Sockeye.";
}
skipspam: catch (Exception ex)
return Redirect("https://ayanova.com/docs/ay-evaluate/"); {
} sockeyeOk = false;
sockeyeResult = $"NOT RECORDED IN SOCKEYE ({ex.Message}). Set this trial up by hand.";
private void PostRequestToSockeye() _logger.LogError(ex, "Trial request post to Sockeye failed. {Body}", Describe());
{ }
using var client = new HttpClient(); //2. Notify support. Email is in the body; From is the form account, so it was never there before either
//client.BaseAddress = new System.Uri("https://sockeye.ayanova.com");///api/v8.0/subscription-server/ var body = $"{Describe()}\r\n\r\nSockeye:\r\n{sockeyeResult}";
var subject = sockeyeOk
var r = new ? $"Trial request from {SubRequest.Company}"
{ : $"Trial request NOT RECORDED from {SubRequest.Company}";
dataCenter = SubRequest.DataCenter, bool mailOk;
email = SubRequest.Email, try
company = SubRequest.Company, {
name = SubRequest.Name, await _mail.SendToSupportAsync(subject, body, SubRequest.Name, SubRequest.Email);
rValue = "totallylegit" mailOk = true;
}; }
catch (Exception ex)
{
HttpContent body = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(r), mailOk = false;
System.Text.Encoding.UTF8, _logger.LogError(ex, "Trial request email failed. Submission:\r\n{Body}", body);
"application/json"); }
var response = client.PostAsync("https://sockeye.ayanova.com/subscription-server-trial-request", body).Result;
//var response = client.PostAsync("http://localhost:7676/subscription-server-trial-request", body).Result; //Either path reaching John means the request is not lost; only both failing is a failure for the visitor
if (!sockeyeOk && !mailOk)
// if (response.IsSuccessStatusCode) {
// { SendFailed = true;
// var content = await response.Content.ReadAsStringAsync(); return Page();
// if (!string.IsNullOrEmpty(content)) }
// {
// var objDeserializeObject = JsonConvert.DeserializeObject<SongsModel>(content); return Redirect(EvaluateUrl);
}
// Console.WriteLine("Data Saved Successfully.");
private string Describe() =>
// if (objDeserializeObject != null) $"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}";
// {
// Console.WriteLine(objDeserializeObject.Name); 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");
private void SendMail() }
{
var MessageBody = $"Name:\r\n{SubRequest.Name}\r\n\r\nCompany:\r\n{SubRequest.Company}\r\n\r\nDataCenter:\r\n{SubRequest.DataCenter}"; var r = new
{
//SEND TO US dataCenter = SubRequest.DataCenter,
using (var message = new MailMessage()) email = SubRequest.Email,
{ company = SubRequest.Company,
message.To.Add(new MailAddress("support@ayanova.com")); name = SubRequest.Name,
message.From = new MailAddress(SubRequest.Email); rValue = "totallylegit"
message.Subject = $"Trial request from {SubRequest.Company}"; };
message.Body = MessageBody;
using (var smtpClient = new SmtpClient("smtp.fastmail.com")) var client = _httpClientFactory.CreateClient("sockeye");
{ var response = await client.PostAsJsonAsync(url, r);
//Previously the response was never checked, so a rejected request failed silently
smtpClient.Host = "smtp.fastmail.com"; response.EnsureSuccessStatusCode();
smtpClient.Port = 587; }
smtpClient.UseDefaultCredentials = false; }
//testing }
smtpClient.EnableSsl = true;
// smtpClient.Port=465;
// smtpClient.DeliveryMethod= SmtpDeliveryMethod.Network;
//NOTE: Do not use the noreply email address to send mail, it crashes the server somehow
//******************************************************************************************************************
//******************************************************************************************************************
//TEST CRASH
// smtpClient.Credentials = new System.Net.NetworkCredential("noreply@ayanova.com", "91768700489f8edd28aa71e3e0f4073eba54ce83c4c1a6a910700fa94094ddfd");
//FASTMAIL CHANGES
//Fastmail server names and ports help page: https://www.fastmail.help/hc/en-us/articles/1500000278342
//APP Password created in fastmail for smtp use only labelled 'contact-form': cnea8agl6nlx5j7l
//try using from as support@onayanova.com
//try domain version if this still won't work
smtpClient.Credentials = new System.Net.NetworkCredential("support@onayanova.com", "cnea8agl6nlx5j7l");
//******************************************************************************************************************
//******************************************************************************************************************
smtpClient.Send(message);
}
}
}
public void OnGet()
{
}
//2025-11-01 Google dropping support for captcha, just nuking it for now, see how much spam I get
// public static bool ReCaptchaPassed(string gRecaptchaResponse, string secret)
// {
// HttpClient httpClient = new HttpClient();
// var res = httpClient.GetAsync($"https://www.google.com/recaptcha/api/siteverify?secret={secret}&response={gRecaptchaResponse}").Result;
// if (res.StatusCode != HttpStatusCode.OK)
// {
// // logger.LogError("Error while sending request to ReCaptcha");
// return false;
// }
// string JSONres = res.Content.ReadAsStringAsync().Result;
// dynamic JSONdata = JObject.Parse(JSONres);
// if (JSONdata.success != "true")
// {
// return false;
// }
// return true;
// }
}
}

View File

@@ -16,40 +16,16 @@
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment> </environment>
<script src='https://www.google.com/recaptcha/api.js'></script>
</head> </head>
<body> <body>
<!-- <nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a asp-page="/Index" class="navbar-brand">contact</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a asp-page="/Index">Home</a></li>
<li><a asp-page="/About">About</a></li>
<li><a asp-page="/Contact">Contact</a></li>
</ul>
</div>
</div>
</nav> -->
<!--
<partial name="_CookieConsentPartial" /> -->
<div class="container body-content"> <div class="container body-content">
@RenderBody() @RenderBody()
<!-- <hr />
<footer>
<p>&copy; 2018 - contact</p>
</footer> -->
</div> </div>
<environment include="Development"> <environment include="Development">
@@ -72,13 +48,13 @@
</script> </script>
<script src="~/js/site.min.js" asp-append-version="true"></script> <script src="~/js/site.min.js" asp-append-version="true"></script>
</environment> </environment>
<script type="text/javascript"> @* <script type="text/javascript">
$("#btnsubmit").hide(); $("#btnsubmit").hide();
var imNotARobot = function() { var imNotARobot = function() {
//console.info("Button was clicked"); //console.info("Button was clicked");
$("#btnsubmit").show(); $("#btnsubmit").show();
}; };
</script> </script> *@
@RenderSection("Scripts", required: false) @RenderSection("Scripts", required: false)
</body> </body>
</html> </html>

View File

@@ -1,14 +1,17 @@
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
namespace contact namespace contact
{ {
public class Program public class Program
{ {
public static void Main(string[] args) => //No UseUrls here. The listening address comes from ASPNETCORE_URLS in the systemd unit
Host.CreateDefaultBuilder(args) //(http://127.0.0.1:3002) and from launchSettings.json for local runs.
.ConfigureWebHostDefaults(web => web.UseStartup<Startup>()) //The old "only *:3002 works with nginx" comment was F1: nginx proxied to $server_addr, the public IP.
.Build() public static void Main(string[] args) =>
.Run(); Host.CreateDefaultBuilder(args)
} .ConfigureWebHostDefaults(web => web.UseStartup<Startup>())
} .Build()
.Run();
}
}

View File

@@ -1,27 +1,12 @@
{ {
"iisSettings": { "profiles": {
"windowsAuthentication": false, "contact": {
"anonymousAuthentication": true, "commandName": "Project",
"iisExpress": { "launchBrowser": false,
"applicationUrl": "http://localhost:30176", "applicationUrl": "http://localhost:3002",
"sslPort": 44340 "environmentVariables": {
} "ASPNETCORE_ENVIRONMENT": "Development"
}, }
"profiles": { }
"IIS Express": { }
"commandName": "IISExpress", }
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"contact": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -1,52 +1,65 @@
using Microsoft.AspNetCore.Builder; using System;
using Microsoft.AspNetCore.Hosting; using System.IO;
using Microsoft.Extensions.Configuration; using contact.Services;
using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
namespace contact using Microsoft.Extensions.Configuration;
{ using Microsoft.Extensions.DependencyInjection;
public class Startup using Microsoft.Extensions.Hosting;
{
public Startup(IConfiguration configuration) namespace contact
{ {
Configuration = configuration; public class Startup
} {
public Startup(IConfiguration configuration)
public IConfiguration Configuration { get; } {
Configuration = configuration;
// This method gets called by the runtime. Use this method to add services to the container. }
public void ConfigureServices(IServiceCollection services)
{ public IConfiguration Configuration { get; }
services.AddControllers().AddNewtonsoftJson();
services.AddRazorPages(); public void ConfigureServices(IServiceCollection services)
} {
services.AddControllers();
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. services.AddRazorPages();
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) services.AddSingleton<MailSender>();
{
if (env.IsDevelopment()) services.AddHttpClient("sockeye", client =>
{ {
app.UseDeveloperExceptionPage(); client.Timeout = TimeSpan.FromSeconds(15);
} });
else
{ //Antiforgery tokens on the forms are encrypted with Data Protection keys. The service account has no
app.UseExceptionHandler("/Error"); //home directory, so without this the keys live only in memory and every restart breaks forms that
app.UseHsts(); //visitors already have open. Set DataProtection:KeyPath on the server (systemd StateDirectory=contact).
} var keyPath = Configuration["DataProtection:KeyPath"];
if (!string.IsNullOrWhiteSpace(keyPath))
// app.UseHttpsRedirection(); {
app.UseStaticFiles(); services.AddDataProtection()
// app.UseCookiePolicy(); .SetApplicationName("contact")
app.UseRouting(); .PersistKeysToFileSystem(new DirectoryInfo(keyPath));
app.UseEndpoints(endpoints => }
{ }
endpoints.MapControllers();
endpoints.MapRazorPages(); public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
}); {
// app.UseMvc(); app.UseDeveloperExceptionPage();
} }
} else
} {
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
}
}

View File

@@ -1,10 +1,6 @@
{ {
"Logging": { "AllowedHosts": "localhost",
"LogLevel": { "Sockeye": {
"Default": "Debug", "TrialRequestUrl": "http://localhost:7575/subscription-server-trial-request"
"System": "Information", }
"Microsoft": "Information" }
}
},
"AllowedHosts": "localhost"
}

View File

@@ -1,8 +1,17 @@
{ {
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Warning" "Default": "Information",
} "Microsoft.AspNetCore": "Warning"
}, }
"AllowedHosts": "*" },
} "AllowedHosts": "contact.ayanova.com",
"Smtp": {
"Host": "smtp.fastmail.com",
"Port": 587,
"User": "support@onayanova.com"
},
"Sockeye": {
"TrialRequestUrl": "http://127.0.0.1:7575/subscription-server-trial-request"
}
}

View File

@@ -1,9 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework> <PropertyGroup>
<UserSecretsId>efc95c93-c628-4ad5-a697-cd1613181a44</UserSecretsId> <TargetFramework>net10.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="MailKit" Version="4.18.0" /> <ItemGroup>
</ItemGroup> <!-- Match whatever 4.x Sockeye is on -->
</Project> <PackageReference Include="MailKit" Version="4.18.1" />
</ItemGroup>
</Project>