ProjectResponse now carries OwnerId and a server-resolved MyRole so Editors/Reviewers see read-only fields and no delete/grant-management affordances instead of only finding out via a 403 after the fact. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PuBH9QSv66DPXSSBERmPs6
58 lines
2.4 KiB
C#
58 lines
2.4 KiB
C#
using Novelly.Api.Common;
|
|
using Novelly.Api.Common.Validation;
|
|
using Novelly.Api.Users;
|
|
|
|
namespace Novelly.Api.Projects;
|
|
|
|
public static class ProjectEndpoints
|
|
{
|
|
public static IEndpointRouteBuilder MapProjectEndpoints(this IEndpointRouteBuilder app)
|
|
{
|
|
var group = app.MapGroup("/api/projects").WithTags("Projects")
|
|
.AddEndpointFilter<RequestLoggingEndpointFilter>()
|
|
.AddEndpointFilter<ValidationEndpointFilter>();
|
|
|
|
group.MapGet("/", async (ProjectService service, CancellationToken ct) =>
|
|
Results.Ok(await service.ListAsync(ct)))
|
|
.WithSummary("List all novel projects.");
|
|
|
|
group.MapGet("/{id:guid}", async (Guid id, ProjectService service, ProjectAccessService access, CancellationToken ct) =>
|
|
{
|
|
var project = await service.GetAsync(id, ct);
|
|
if (project is null)
|
|
return Results.NotFound();
|
|
|
|
var myRole = await access.GetMyRoleAsync(project, ct);
|
|
return Results.Ok(project.ToResponse(myRole));
|
|
})
|
|
.WithSummary("Read a project's brief.");
|
|
|
|
group.MapPost("/", async (CreateProjectRequest request, ProjectService service, ProjectAccessService access, CancellationToken ct) =>
|
|
{
|
|
var project = await service.CreateAsync(request, ct);
|
|
var myRole = await access.GetMyRoleAsync(project, ct);
|
|
var created = project.ToResponse(myRole);
|
|
return Results.Created($"/api/projects/{created.Id}", created);
|
|
})
|
|
.WithSummary("Create a novel project.");
|
|
|
|
group.MapPatch("/{id:guid}", async (
|
|
Guid id, UpdateProjectRequest request, ProjectService service, ProjectAccessService access, CancellationToken ct) =>
|
|
{
|
|
var project = await service.UpdateAsync(id, request, ct);
|
|
if (project is null)
|
|
return Results.NotFound();
|
|
|
|
var myRole = await access.GetMyRoleAsync(project, ct);
|
|
return Results.Ok(project.ToResponse(myRole));
|
|
})
|
|
.WithSummary("Update a project's brief.");
|
|
|
|
group.MapDelete("/{id:guid}", async (Guid id, ProjectService service, CancellationToken ct) =>
|
|
await service.DeleteAsync(id, ct) ? Results.NoContent() : Results.NotFound())
|
|
.WithSummary("Delete a project and everything in it.");
|
|
|
|
return app;
|
|
}
|
|
}
|