Minimum Viable Mailsender

This commit is contained in:
femsci 2023-10-21 18:03:33 +02:00
parent f6f6c17555
commit b77acdb322
Signed by: femsci
GPG key ID: 08F7911F0E650C67
6 changed files with 147 additions and 11 deletions

View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Nyanlabs.SFood.Api.Controllers;
[ApiController]
[Route("/api")]
public class ApiController : ControllerBase
{
[HttpGet("version")]
public string Version() => "1.0.0E";
}

View file

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net.Mail;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Nyanlabs.SFood.Api.Models;
using Nyanlabs.SFood.Api.Services;
namespace Nyanlabs.SFood.Api.Controllers;
[ApiController]
[Route("api/incident")]
public class IncidentController : ControllerBase
{
public IncidentController(EmailClient mail)
{
_mail = mail;
}
private readonly EmailClient _mail;
[HttpPost("submit")]
public async Task<IActionResult> SubmitIncident([FromForm] IncidentSubmission incident)
{
string body = $@"<p>Dziękujemy za zgłoszenie incydentu.<p>
<p>Dane zgłoszenia:<br/>
<b>Identyfikator zgłoszenia:</b> {Random.Shared.NextInt64()}<br/>
<b>Produkt:</b> {incident.ProductName}<br/>
<b>Producent:</b> {incident.Producer}<br/>
<b>Opis incydentu:</b> {incident.Description}<br/>
<b>Miejsce zdarzenia:</b> {incident.Location}<br/>
<b>Data zgłoszenia:</b> {incident.Submitted.ToLongDateString()}
</p>";
return (await _mail.SendRawAsync(incident.SubmitterAddress, body)) ? NoContent() : BadRequest();
}
}

View file

@ -10,13 +10,11 @@ namespace Nyanlabs.SFood.Api.Models;
public class IncidentSubmission public class IncidentSubmission
{ {
//TODO: Validation //TODO: Validation
[Key]
public long Id { get; set; }
public required string Name { get; set; } public required string Name { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime Submitted { get; set; } public DateTime Submitted { get; set; } = DateTime.Now;
[EmailAddress] [EmailAddress]
public string? SubmitterAddress { get; set; } public required string SubmitterAddress { get; set; }
public required string ProductName { get; set; } public required string ProductName { get; set; }
public required string Producer { get; set; } public required string Producer { get; set; }
public required string Description { get; set; } public required string Description { get; set; }

View file

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MailKit" Version="4.2.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.12" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.12" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.12" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.12" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.12"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.12">

View file

@ -1,19 +1,23 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Nyanlabs.SFood.Api; using Nyanlabs.SFood.Api;
using Nyanlabs.SFood.Api.Services;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<EmailClientOptions>(o => builder.Configuration.GetSection("EmailClient").Bind(o));
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();
builder.Services.AddScoped<EmailClient>();
builder.Services.AddDbContext<DataContext>(o => // builder.Services.AddDbContext<DataContext>(o =>
{ // {
if (builder.Environment.IsDevelopment()) // if (builder.Environment.IsDevelopment())
{ // {
o.UseSqlite("Data Source=/data/store.db;"); // o.UseSqlite("Data Source=/data/store.db;");
} // }
}); // });
var app = builder.Build(); var app = builder.Build();

View file

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using Microsoft.Extensions.Options;
using MimeKit;
namespace Nyanlabs.SFood.Api.Services;
public class EmailClient : IDisposable
{
public EmailClient(IOptions<EmailClientOptions> opts)
{
_opts = opts.Value;
_cli = new();
_cli.Connect(_opts.Host, _opts.Port, true);
if (!_cli.IsConnected)
{
throw new DataException($"Cannot connect to {_opts.Host}:{_opts.Port}...");
}
var credentials = new NetworkCredential(_opts.User, _opts.Password);
_cli.Authenticate(credentials);
if (!_cli.IsAuthenticated)
{
throw new DataException($"{_opts.Host}:{_opts.Port}: Cannot authenticate user {_opts.User}.");
}
}
private readonly EmailClientOptions _opts;
private readonly SmtpClient _cli;
public async Task<bool> SendAsync([EmailAddress] string addr)
{
return false;
}
internal async Task<bool> SendRawAsync([EmailAddress] string addr, string html)
{
MimeMessage msg = new();
msg.From.Add(new MailboxAddress(_opts.FromName, _opts.FromAddr));
var recipient = new MailboxAddress(addr, addr);
if (recipient is null)
{
return false;
}
msg.To.Add(recipient);
msg.Subject = "Zgłoszenie przyjęte";
msg.Body = new TextPart("html", html);
string result = await _cli.SendAsync(msg);
return result.StartsWith("2.") && result.Contains("ok", StringComparison.InvariantCultureIgnoreCase);
}
public void Dispose()
{
_cli.Disconnect(true);
_cli.Dispose();
}
}
public record EmailClientOptions
{
public required string Host { get; set; }
public required string User { get; set; }
public required string Password { get; set; }
public required string FromName { get; set; }
public required string FromAddr { get; set; }
public int Port { get; set; } = 465;
};