116 lines
3.6 KiB
C#
116 lines
3.6 KiB
C#
using FastEndpoints;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Socialize.Api.Data;
|
|
using Socialize.Api.Infrastructure.Security;
|
|
using Socialize.Api.Modules.Channels.Data;
|
|
|
|
namespace Socialize.Api.Modules.Channels.Handlers;
|
|
|
|
public record CreateChannelRequest(
|
|
Guid WorkspaceId,
|
|
string Name,
|
|
string Network,
|
|
string? Handle,
|
|
string? ExternalUrl);
|
|
|
|
public class CreateChannelRequestValidator
|
|
: Validator<CreateChannelRequest>
|
|
{
|
|
private static readonly string[] AllowedNetworks =
|
|
[
|
|
"Instagram",
|
|
"TikTok",
|
|
"Facebook",
|
|
"LinkedIn",
|
|
"YouTube",
|
|
"X",
|
|
"Reddit",
|
|
"Website",
|
|
];
|
|
|
|
public CreateChannelRequestValidator()
|
|
{
|
|
RuleFor(x => x.WorkspaceId).NotEmpty();
|
|
RuleFor(x => x.Name).NotEmpty().MaximumLength(256);
|
|
RuleFor(x => x.Network).NotEmpty().Must(network => AllowedNetworks.Contains(network))
|
|
.WithMessage("Selected network is invalid.");
|
|
RuleFor(x => x.Handle).MaximumLength(256);
|
|
RuleFor(x => x.ExternalUrl).MaximumLength(2048);
|
|
}
|
|
}
|
|
|
|
public class CreateChannelHandler(
|
|
AppDbContext dbContext,
|
|
AccessScopeService accessScopeService)
|
|
: Endpoint<CreateChannelRequest, ChannelDto>
|
|
{
|
|
public override void Configure()
|
|
{
|
|
Post("/api/channels");
|
|
Options(o => o.WithTags("Channels"));
|
|
}
|
|
|
|
public override async Task HandleAsync(CreateChannelRequest request, CancellationToken ct)
|
|
{
|
|
if (!await accessScopeService.CanManageWorkspaceAsync(User, request.WorkspaceId, ct))
|
|
{
|
|
await SendForbiddenAsync(ct);
|
|
return;
|
|
}
|
|
|
|
bool workspaceExists = await dbContext.Workspaces
|
|
.AnyAsync(workspace => workspace.Id == request.WorkspaceId, ct);
|
|
|
|
if (!workspaceExists)
|
|
{
|
|
AddError(request => request.WorkspaceId, "The selected workspace does not exist.");
|
|
await SendErrorsAsync(StatusCodes.Status400BadRequest, ct);
|
|
return;
|
|
}
|
|
|
|
string normalizedName = request.Name.Trim();
|
|
string normalizedNetwork = request.Network.Trim();
|
|
string? normalizedHandle = request.Handle?.Trim();
|
|
string? normalizedExternalUrl = request.ExternalUrl?.Trim();
|
|
|
|
bool duplicateChannel = await dbContext.Channels
|
|
.AnyAsync(
|
|
channel => channel.WorkspaceId == request.WorkspaceId
|
|
&& channel.Network == normalizedNetwork
|
|
&& channel.Name == normalizedName,
|
|
ct);
|
|
|
|
if (duplicateChannel)
|
|
{
|
|
AddError(request => request.Name, "A channel with this name already exists for the selected network.");
|
|
await SendErrorsAsync(StatusCodes.Status409Conflict, ct);
|
|
return;
|
|
}
|
|
|
|
Channel channel = new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
WorkspaceId = request.WorkspaceId,
|
|
Name = normalizedName,
|
|
Network = normalizedNetwork,
|
|
Handle = string.IsNullOrWhiteSpace(normalizedHandle) ? null : normalizedHandle,
|
|
ExternalUrl = string.IsNullOrWhiteSpace(normalizedExternalUrl) ? null : normalizedExternalUrl,
|
|
CreatedAt = DateTimeOffset.UtcNow,
|
|
};
|
|
|
|
dbContext.Channels.Add(channel);
|
|
await dbContext.SaveChangesAsync(ct);
|
|
|
|
ChannelDto dto = new(
|
|
channel.Id,
|
|
channel.WorkspaceId,
|
|
channel.Name,
|
|
channel.Network,
|
|
channel.Handle,
|
|
channel.ExternalUrl,
|
|
channel.CreatedAt);
|
|
|
|
await SendAsync(dto, StatusCodes.Status201Created, ct);
|
|
}
|
|
}
|