Compare commits

...

3 Commits

Author SHA1 Message Date
Francesco Lorenzo D'Amico df781eaf84 Move contact endpoint from Program.cs into ContactController
Deploy Staging / deploy (push) Successful in 1m11s
2026-08-20 11:41:05 +02:00
Francesco Lorenzo D'Amico 10737b8272 Validate phone number before submit, point form /api/v1/contact 2026-08-20 11:40:03 +02:00
Francesco Lorenzo D'Amico a8f664a6ba Log warning when telegram config is missing 2026-08-20 11:36:28 +02:00
5 changed files with 69 additions and 35 deletions
@@ -0,0 +1,3 @@
namespace RazorPagesMovie.Api.V1.Contact;
public record ContactDto(string? Name, string? Email, string? Phone, string? Subject, string? Message);
@@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using RazorPagesMovie.Api.V1.Contact;
using RazorPagesMovie.Data;
using RazorPagesMovie.Models;
namespace RazorPagesMovie.Controllers.Api.V1;
[ApiController]
[Route("api/v1/[controller]")]
public class ContactController(RazorPagesMovieContext context) : ControllerBase
{
[HttpPost]
public async Task<IActionResult> Post(ContactDto request)
{
var contactMessage = new ContactMessage
{
Name = request.Name ?? string.Empty,
Email = request.Email ?? string.Empty,
Phone = request.Phone,
Subject = request.Subject,
Message = request.Message ?? string.Empty
};
var validationResults = new List<ValidationResult>();
if (!Validator.TryValidateObject(contactMessage, new ValidationContext(contactMessage), validationResults, validateAllProperties: true))
{
return BadRequest(new { error = "Bitte alle Pflichtfelder korrekt ausfüllen." });
}
context.ContactMessage.Add(contactMessage);
await context.SaveChangesAsync();
return Ok(new { success = true });
}
}
+19 -8
View File
@@ -958,7 +958,7 @@
<input
type="text"
name="name"
placeholder="Dein Name*"
placeholder="Name*"
required>
</div>
</div>
@@ -972,7 +972,7 @@
<input
type="email"
name="email"
placeholder="Deine E-Mail*"
placeholder="E-Mail*"
required>
</div>
</div>
@@ -1013,7 +1013,7 @@
name="message"
cols="30"
rows="10"
placeholder="Deine Nachricht*"
placeholder="Nachricht*"
required></textarea>
</div>
</div>
@@ -1082,27 +1082,38 @@
messageBox.style.display = "block";
}
function isValidPhone(value) {
if (!value) return true;
var digitCount = (value.match(/\d/g) || []).length;
return /^[0-9+\-\s()]+$/.test(value) && digitCount >= 7 && digitCount <= 15;
}
form.addEventListener("submit", function (event) {
event.preventDefault();
submitButton.disabled = true;
var payload = {
name: form.querySelector("[name=name]").value,
email: form.querySelector("[name=email]").value,
phone: form.querySelector("[name=phone]").value,
phone: form.querySelector("[name=phone]").value.trim(),
subject: form.querySelector("[name=subject]").value,
message: form.querySelector("[name=message]").value
};
fetch("/api/contact", {
if (!isValidPhone(payload.phone)) {
showMessage("Bitte gib eine gültige Telefonnummer ein.", false);
return;
}
submitButton.disabled = true;
fetch("/api/v1/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
})
.then(function (response) {
if (response.ok) {
showMessage("Danke für deine Nachricht! Ich melde mich so schnell wie möglich.", true);
showMessage("Danke für die Nachricht! Ich melde mich so schnell wie möglich.", true);
form.reset();
} else {
showMessage("Beim Senden ist ein Fehler aufgetreten. Bitte versuche es erneut.", false);
+10 -27
View File
@@ -1,4 +1,3 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Data;
@@ -6,7 +5,12 @@ using RazorPagesMovie.Models;
using RazorPagesMovie.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("appsettings.Local.json", optional: true, reloadOnChange: true);
if (builder.Environment.IsDevelopment())
{
builder.Configuration.AddJsonFile("appsettings.Local.json", optional: true, reloadOnChange: true);
}
builder.Services.AddDbContext<RazorPagesMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("RazorPagesMovieContext") ?? throw new InvalidOperationException("Connection string 'RazorPagesMovieContext' not found.")));
@@ -22,6 +26,7 @@ builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/Admin");
});
builder.Services.AddControllers();
builder.Services.AddHttpClient();
builder.Services.AddHostedService<ContactMessageNotificationService>();
@@ -32,7 +37,9 @@ using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<RazorPagesMovieContext>();
context.Database.Migrate();
SeedData.Initialize(services);
}
@@ -48,30 +55,6 @@ app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapPost("/api/contact", async (ContactMessageRequest request, RazorPagesMovieContext context) =>
{
var contactMessage = new ContactMessage
{
Name = request.Name ?? string.Empty,
Email = request.Email ?? string.Empty,
Phone = request.Phone,
Subject = request.Subject,
Message = request.Message ?? string.Empty
};
var validationResults = new List<ValidationResult>();
if (!Validator.TryValidateObject(contactMessage, new ValidationContext(contactMessage), validationResults, validateAllProperties: true))
{
return Results.BadRequest(new { error = "Bitte alle Pflichtfelder korrekt ausfüllen." });
}
context.ContactMessage.Add(contactMessage);
await context.SaveChangesAsync();
return Results.Ok(new { success = true });
});
app.MapControllers();
app.Run();
record ContactMessageRequest(string? Name, string? Email, string? Phone, string? Subject, string? Message);
@@ -41,6 +41,7 @@ public class ContactMessageNotificationService : BackgroundService
if (string.IsNullOrWhiteSpace(botToken) || string.IsNullOrWhiteSpace(chatId))
{
_logger.LogWarning("Telegram:BotToken oder Telegram:ChatId ist nicht konfiguriert Benachrichtigung wird übersprungen.");
return;
}