240 lines
9.1 KiB
C#
240 lines
9.1 KiB
C#
using Flawless.Communication.Request;
|
|
using Flawless.Communication.Response;
|
|
using Flawless.Communication.Shared;
|
|
using Flawless.Server.Models;
|
|
using Flawless.Server.Services;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Flawless.Server.Controllers;
|
|
[ApiController, Microsoft.AspNetCore.Authorization.Authorize, Route("api/issue/{userName}/{repositoryName}")]
|
|
public class IssueController(
|
|
UserManager<AppUser> userManager,
|
|
AppDbContext dbContext) : ControllerBase
|
|
{
|
|
[HttpPost("create")]
|
|
public async Task<ActionResult<IssueInfo>> CreateIssueAsync(
|
|
string userName,
|
|
string repositoryName,
|
|
[FromBody] CreateIssueRequest request)
|
|
{
|
|
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
|
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Reporter);
|
|
if (grantIssue is not Repository repository) return (ActionResult)grantIssue;
|
|
|
|
var issue = new RepositoryIssue
|
|
{
|
|
Repository = repository,
|
|
Author = user,
|
|
Title = request.Title,
|
|
CreatedAt = DateTime.UtcNow,
|
|
Descripion = request.Description,
|
|
Closed = false,
|
|
Tag = request.Tag
|
|
};
|
|
|
|
dbContext.RepositoryIssues.Add(issue);
|
|
await dbContext.SaveChangesAsync();
|
|
|
|
return Ok(new IssueInfo(
|
|
issue.Id,
|
|
issue.Author.UserName!,
|
|
issue.Title,
|
|
issue.CreatedAt,
|
|
issue.CreatedAt,
|
|
issue.Closed,
|
|
issue.Tag));
|
|
}
|
|
|
|
[HttpPost("{issueId}/comment")]
|
|
public async Task<IActionResult> AddCommentAsync(
|
|
string userName,
|
|
string repositoryName,
|
|
int issueId,
|
|
[FromBody] AddCommentRequest request)
|
|
{
|
|
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
|
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Reporter);
|
|
if (grantIssue is not Repository _) return (ActionResult) grantIssue;
|
|
|
|
var issue = await dbContext.RepositoryIssues
|
|
.Include(i => i.Repository)
|
|
.Include(repositoryIssue => repositoryIssue.Contents)
|
|
.FirstOrDefaultAsync(i => i.Id == issueId);
|
|
|
|
if (issue == null) return NotFound(new FailedResponse("Issue not found"));
|
|
if (issue.Closed) return BadRequest(new FailedResponse("Issue is closed"));
|
|
|
|
var comment = new RepositoryIssueContent
|
|
{
|
|
Issue = issue,
|
|
Author = user,
|
|
Content = request.Content,
|
|
CreatedAt = DateTime.UtcNow,
|
|
ReplyTo = request.ReplyTo.HasValue
|
|
? issue.Contents.Find(v => v.Id == request.ReplyTo.Value) : null
|
|
};
|
|
|
|
issue.Contents.Add(comment);
|
|
await dbContext.SaveChangesAsync();
|
|
|
|
return Ok();
|
|
}
|
|
|
|
[HttpPost("{issueId}/close")]
|
|
public async Task<IActionResult> CloseIssueAsync(
|
|
string userName,
|
|
string repositoryName,
|
|
ulong issueId)
|
|
{
|
|
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
|
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Developer);
|
|
if (grantIssue is not Repository _) return (ActionResult) grantIssue;
|
|
|
|
var issue = await dbContext.RepositoryIssues.FindAsync(issueId);
|
|
if (issue == null) return NotFound(new FailedResponse("Issue not found"));
|
|
|
|
issue.Closed = true;
|
|
await dbContext.SaveChangesAsync();
|
|
|
|
return Ok();
|
|
}
|
|
|
|
[HttpGet("list")]
|
|
public async Task<ActionResult<ListingResponse<IssueInfo>>> GetIssuesAsync(
|
|
string userName,
|
|
string repositoryName)
|
|
{
|
|
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
|
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Reporter);
|
|
if (grantIssue is not Repository repository) return (ActionResult)grantIssue;
|
|
|
|
var issues = await dbContext.RepositoryIssues
|
|
.Where(i => i.Repository == repository)
|
|
.Include(i => i.Author)
|
|
.ToArrayAsync();
|
|
|
|
return Ok(new ListingResponse<RepositoryIssue>(issues));
|
|
}
|
|
|
|
[HttpGet("{issueId}")]
|
|
public async Task<ActionResult<IssueDetailInfo>> GetIssueDetailsAsync(
|
|
string userName,
|
|
string repositoryName,
|
|
int issueId)
|
|
{
|
|
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
|
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Reporter);
|
|
if (grantIssue is not Repository repo) return (ActionResult) grantIssue;
|
|
|
|
var issue = await dbContext.RepositoryIssues
|
|
.Include(i => i.Author)
|
|
.Include(i => i.Repository)
|
|
.Include(i => i.Contents)
|
|
.ThenInclude(c => c.Author)
|
|
.FirstOrDefaultAsync(x => x.Id == issueId && x.Repository == repo);
|
|
|
|
return issue == null
|
|
? NotFound(new FailedResponse("Issue not found"))
|
|
: Ok(new IssueDetailInfo(
|
|
issue.Id,
|
|
issue.Author.UserName!,
|
|
issue.Title,
|
|
issue.Descripion ?? String.Empty,
|
|
issue.CreatedAt,
|
|
issue.Contents.MaxBy(x => x.CreatedAt)?.CreatedAt ?? issue.CreatedAt,
|
|
issue.Closed,
|
|
issue.Tag));
|
|
}
|
|
|
|
[HttpPatch("{issueId}/edit")]
|
|
public async Task<IActionResult> UpdateIssueAsync(
|
|
string userName,
|
|
string repositoryName,
|
|
int issueId,
|
|
[FromBody] UpdateIssueRequest request)
|
|
{
|
|
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
|
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Reporter);
|
|
if (grantIssue is not Repository repo) return (ActionResult) grantIssue;
|
|
|
|
var issue = await dbContext.RepositoryIssues
|
|
.Include(x => x.Author)
|
|
.FirstOrDefaultAsync(x => x.Id == issueId && x.Repository == repo);
|
|
|
|
if (issue == null) return NotFound(new FailedResponse("Issue not found"));
|
|
if (issue.Author != user && repo.Owner != user)
|
|
return BadRequest(new FailedResponse("You are not allowed to do this operation"));
|
|
|
|
if (!string.IsNullOrEmpty(request.Title)) issue.Title = request.Title;
|
|
if (!string.IsNullOrEmpty(request.Description)) issue.Descripion = request.Description;
|
|
if (request.Tag != null) issue.Tag = request.Tag;
|
|
|
|
await dbContext.SaveChangesAsync();
|
|
return Ok();
|
|
}
|
|
|
|
[HttpPost("{issueId}/reopen")]
|
|
public async Task<IActionResult> ReopenIssueAsync(
|
|
string userName,
|
|
string repositoryName,
|
|
ulong issueId)
|
|
{
|
|
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
|
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Developer);
|
|
if (grantIssue is not Repository _) return (ActionResult) grantIssue;
|
|
|
|
var issue = await dbContext.RepositoryIssues.FindAsync(issueId);
|
|
if (issue == null) return NotFound(new FailedResponse("Issue not found"));
|
|
|
|
issue.Closed = false;
|
|
await dbContext.SaveChangesAsync();
|
|
|
|
return Ok();
|
|
}
|
|
|
|
[HttpGet("{issueId}/comments")]
|
|
public async Task<ActionResult<ListingResponse<CommentResponse>>> GetIssueCommentsAsync(
|
|
string userName,
|
|
string repositoryName,
|
|
int issueId)
|
|
{
|
|
var user = (await userManager.GetUserAsync(HttpContext.User))!;
|
|
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Reporter);
|
|
if (grantIssue is not Repository repo) return (ActionResult) grantIssue;
|
|
|
|
var comments = await dbContext.RepositoryIssues
|
|
.Where(x => x.Id == issueId && x.Repository == repo)
|
|
.SelectMany(x => x.Contents)
|
|
.Include(c => c.Author)
|
|
.OrderBy(c => c.CreatedAt)
|
|
.Select(c => new CommentResponse(
|
|
c.Id,
|
|
c.Author.UserName!,
|
|
c.Content,
|
|
c.CreatedAt,
|
|
c.ReplyTo != null ? c.ReplyTo.Id : null))
|
|
.ToArrayAsync();
|
|
|
|
return Ok(new ListingResponse<CommentResponse>(comments));
|
|
}
|
|
|
|
|
|
private async ValueTask<object> ValidateRepositoryAsync(
|
|
string userName, string repositoryName, AppUser user, RepositoryRole role)
|
|
{
|
|
// 复用RepositoryInnieController中的验证逻辑
|
|
var rp = await dbContext.Repositories
|
|
.Include(r => r.Members)
|
|
.ThenInclude(m => m.User)
|
|
.Include(r => r.Owner)
|
|
.FirstOrDefaultAsync(r => r.Owner.UserName == userName && r.Name == repositoryName);
|
|
|
|
if (rp == null) return NotFound(new FailedResponse($"Could not find repository {userName}:{repositoryName}"));
|
|
if (rp.Owner != user && !rp.Members.Any(m => m.User == user && m.Role >= role))
|
|
return Unauthorized(new FailedResponse("You are not allowed to do this operation"));
|
|
|
|
return rp;
|
|
}
|
|
} |