using Socialize.Infrastructure.Security; using Socialize.Modules.Workspaces.Data; namespace Socialize.Modules.Workspaces.Handlers; public record CreateWorkspaceRequest( string Name, string Slug, string TimeZone); public class CreateWorkspaceRequestValidator : Validator { public CreateWorkspaceRequestValidator() { RuleFor(x => x.Name).NotEmpty().MaximumLength(256); RuleFor(x => x.Slug) .NotEmpty() .MaximumLength(128) .Matches("^[a-z0-9]+(?:-[a-z0-9]+)*$"); RuleFor(x => x.TimeZone).NotEmpty().MaximumLength(128); } } public class CreateWorkspaceHandler( AppDbContext dbContext, AccessScopeService accessScopeService) : Endpoint { public override void Configure() { Post("/api/workspaces"); Options(o => o.WithTags("Workspaces")); } public override async Task HandleAsync(CreateWorkspaceRequest request, CancellationToken ct) { if (!accessScopeService.IsManager(User)) { await SendForbiddenAsync(ct); return; } string normalizedName = request.Name.Trim(); string normalizedSlug = request.Slug.Trim().ToLowerInvariant(); string normalizedTimeZone = request.TimeZone.Trim(); bool duplicateWorkspace = await dbContext.Workspaces .AnyAsync(workspace => workspace.Slug == normalizedSlug, ct); if (duplicateWorkspace) { AddError(request => request.Slug, "A workspace with this slug already exists."); await SendErrorsAsync(StatusCodes.Status409Conflict, ct); return; } Workspace workspace = new() { Id = Guid.NewGuid(), Name = normalizedName, Slug = normalizedSlug, OwnerUserId = User.GetUserId(), TimeZone = normalizedTimeZone, CreatedAt = DateTimeOffset.UtcNow, }; dbContext.Workspaces.Add(workspace); await dbContext.SaveChangesAsync(ct); WorkspaceDto dto = new( workspace.Id, workspace.Name, workspace.Slug, workspace.TimeZone, workspace.CreatedAt); await SendAsync(dto, StatusCodes.Status201Created, ct); } }