105 lines
3.1 KiB
C#
105 lines
3.1 KiB
C#
using System.Net;
|
|
using FluentValidation.Results;
|
|
using Hutopy.Web.Common.Security;
|
|
using Hutopy.Web.Features.Contents.Data;
|
|
using Microsoft.Extensions.Options;
|
|
using Npgsql;
|
|
|
|
namespace Hutopy.Web.Features.Contents.Handlers;
|
|
|
|
[PublicAPI]
|
|
public record ReserveSlugRequest
|
|
{
|
|
public required Guid ReservationId { get; set; }
|
|
public string Slug { get; set; } = null!;
|
|
}
|
|
|
|
[PublicAPI]
|
|
public sealed class ReserveSlugRequestValidator : Validator<ReserveSlugRequest>
|
|
{
|
|
public ReserveSlugRequestValidator()
|
|
{
|
|
RuleFor(r => r.Slug)
|
|
.NotEmpty()
|
|
.NotNull()
|
|
.WithMessage("You should specify a valid Slug");
|
|
}
|
|
}
|
|
|
|
[PublicAPI]
|
|
public sealed class ReserveSlug(
|
|
ContentDbContext context,
|
|
IOptions<ContentOptions> opts)
|
|
: Endpoint<ReserveSlugRequest>
|
|
{
|
|
public override void Configure()
|
|
{
|
|
Post("/api/creators/@{Slug}/reserve");
|
|
Options(o => o.WithTags("Contents"));
|
|
}
|
|
|
|
public override async Task HandleAsync(
|
|
ReserveSlugRequest req,
|
|
CancellationToken ct)
|
|
{
|
|
await using var transaction = await context.Database.BeginTransactionAsync(ct);
|
|
|
|
try
|
|
{
|
|
var reservation = await context.Slugs.FirstOrDefaultAsync(
|
|
s => s.Id == req.ReservationId && s.CreatedBy == User.GetUserId(),
|
|
cancellationToken: ct);
|
|
|
|
if (reservation == null)
|
|
{
|
|
reservation = new Slugs
|
|
{
|
|
Id = req.ReservationId,
|
|
CreatedBy = User.GetUserId(),
|
|
CreatedAt = DateTimeOffset.UtcNow,
|
|
};
|
|
|
|
context.Slugs.Attach(reservation);
|
|
context.Entry(reservation).State = EntityState.Added;
|
|
}
|
|
|
|
reservation.Name = req.Slug;
|
|
reservation.ReservedUntil = DateTimeOffset.UtcNow + opts.Value.SlugReservationDuration;
|
|
|
|
await context.SaveChangesAsync(ct);
|
|
|
|
await transaction.CommitAsync(ct);
|
|
|
|
await SendOkAsync(new { Message = "Slug reserved." }, ct);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
await transaction.RollbackAsync(ct);
|
|
|
|
Logger.LogError("Transaction failed: {Message}", e.Message);
|
|
|
|
if (e.InnerException is PostgresException innerException)
|
|
{
|
|
if (innerException.ConstraintName == "IX_Slugs_NormalizedName")
|
|
{
|
|
await SendResultAsync(new ProblemDetails(
|
|
[
|
|
new ValidationFailure(nameof(Slugs.Name),
|
|
"The name is already taken.")
|
|
],
|
|
(int)HttpStatusCode.Conflict));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
await SendResultAsync(new ProblemDetails(
|
|
[
|
|
new ValidationFailure(nameof(Slugs.Name),
|
|
e.Message)
|
|
],
|
|
(int)HttpStatusCode.Conflict));
|
|
}
|
|
}
|
|
}
|
|
}
|