using Flawless.Communication.Request; using Flawless.Communication.Response; using Flawless.Communication.Shared; using Flawless.Server.Models; using Flawless.Server.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace Flawless.Server.Controllers; [ApiController, Authorize, Route("api/repo_manage")] public class RepositoryManageController(AppDbContext dbContext, UserManager userManager) : ControllerBase { [HttpGet("list")] public async Task ListAllAvailableRepositoriesAsync(QueryPagesRequest r) { var u = (await userManager.GetUserAsync(HttpContext.User))!; var query = await dbContext.Repositories .Include(repository => repository.Owner) .Include(repository => repository.Commits) .Include(repository => repository.Members) .ThenInclude(repositoryMember => repositoryMember.User) .Where(rp => rp.Members.Any(m => m.User == u)) .Skip(r.Offset) .Take(r.Length) .ToArrayAsync(); return Ok(new PagedResponse { Length = r.Length, Offset = r.Offset, Data = query.Select(rp => new RepositoryInfoResponse { RepositoryName = rp.Name, OwnerUsername = rp.Owner.UserName!, LatestCommitId = rp.Commits.OrderByDescending(cm => cm.CommittedOn).FirstOrDefault()?.Id ?? Guid.Empty, Description = rp.Description, IsArchived = rp.IsArchived, Role = rp.Members.First(m => m.User == u).Role }), }); } [HttpPost("create/{repositoryName}")] public async Task CreateRepositoryAsync(string repositoryName) { if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3) return BadRequest(new FailedResponse("Repository name is empty or too short!")); var u = (await userManager.GetUserAsync(HttpContext.User))!; if (await dbContext.Repositories.AnyAsync(rp => rp.Name == repositoryName && u == rp.Owner)) return BadRequest(new FailedResponse("Repository name has already created!")); await dbContext.Repositories.AddAsync(new Repository() { Name = repositoryName, Owner = u, }); await dbContext.SaveChangesAsync(); return Ok(); } [HttpPost("delete/{repositoryName}")] public async Task DeleteRepositoryAsync(string repositoryName) { if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3) return BadRequest(new FailedResponse("Repository name is empty or too short!")); var u = (await userManager.GetUserAsync(HttpContext.User))!; var rp = await dbContext.Repositories.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner); if (rp == null) return BadRequest(new FailedResponse("Repository not found!")); dbContext.Repositories.Remove(rp); await dbContext.SaveChangesAsync(); return Ok(); } [HttpGet("info/{repositoryName}")] public async Task IsRepositoryArchiveAsync(string repositoryName) { if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3) return BadRequest(new FailedResponse("Repository name is empty or too short!")); var u = (await userManager.GetUserAsync(HttpContext.User))!; var rp = await dbContext.Repositories .Include(repository => repository.Owner) .Include(repository => repository.Commits) .Include(repository => repository.Members) .ThenInclude(repositoryMember => repositoryMember.User) .FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner); if (rp == null) return BadRequest(new FailedResponse("Repository not found!")); return Ok(new RepositoryInfoResponse { RepositoryName = rp.Name, OwnerUsername = rp.Owner.UserName!, LatestCommitId = rp.Commits.Max(cm => cm.Id), Description = rp.Description, IsArchived = rp.IsArchived, Role = rp.Members.First(m => m.User == u).Role }); } [HttpPost("archive/{repositoryName}")] public async Task ArchiveRepositoryAsync(string repositoryName) { if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3) return BadRequest(new FailedResponse("Repository name is empty or too short!")); var u = (await userManager.GetUserAsync(HttpContext.User))!; var rp = await dbContext.Repositories.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner); if (rp == null) return BadRequest(new FailedResponse("Repository not found!")); if (rp.IsArchived) return BadRequest(new FailedResponse("Repository is archived!")); rp.IsArchived = true; await dbContext.SaveChangesAsync(); return Ok(); } [HttpPost("unarchive/{repositoryName}")] public async Task UnarchiveRepositoryAsync(string repositoryName) { if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3) return BadRequest(new FailedResponse("Repository name is empty or too short!")); var u = (await userManager.GetUserAsync(HttpContext.User))!; var rp = await dbContext.Repositories.FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner); if (rp == null) return BadRequest(new FailedResponse("Repository not found!")); if (!rp.IsArchived) return BadRequest(new FailedResponse("Repository is not archived!")); rp.IsArchived = false; await dbContext.SaveChangesAsync(); return Ok(); } [HttpGet("get_users/{repositoryName}")] public async Task GetUsersAsync(string repositoryName, QueryPagesRequest r) { if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3) return BadRequest(new FailedResponse("Repository name is empty or too short!")); var u = (await userManager.GetUserAsync(HttpContext.User))!; var rp = await dbContext.Repositories .Include(repository => repository.Owner) .Include(repository => repository.Members) .ThenInclude(repositoryMember => repositoryMember.User) .FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner); if (rp == null) return BadRequest(new FailedResponse("Repository not found!")); return Ok(new PagedResponse { Length = r.Length, Offset = r.Offset, Total = rp.Members.Count, Data = rp.Members.Select(pm => new RepoUserRole { Username = pm.User.UserName!, Role = pm.Role }) }); } [HttpPost("update_user/{repositoryName}")] public async Task UpdateUserAsync(string repositoryName, [FromBody] RepoUserRole r) { if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3) return BadRequest(new FailedResponse("Repository name is empty or too short!")); var u = (await userManager.GetUserAsync(HttpContext.User))!; var tu = await userManager.FindByNameAsync(r.Username); if (tu == null) return BadRequest(new FailedResponse("User not found!")); if (u == tu) return BadRequest(new FailedResponse("Not able to update the role on self-own repository!")); var rp = await dbContext.Repositories .Include(repository => repository.Members) .ThenInclude(repositoryMember => repositoryMember.User) .FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner); if (rp == null) return BadRequest(new FailedResponse("Repository not found!")); var m = rp.Members.FirstOrDefault(m => m.User == tu); if (m == null) { m = new RepositoryMember { User = tu, Role = r.Role ?? RepositoryRole.Guest }; rp.Members.Add(m); } else { m.Role = r.Role ?? RepositoryRole.Guest; } await dbContext.SaveChangesAsync(); return Ok(); } [HttpPost("delete_user/{repositoryName}")] public async Task DeleteUserAsync(string repositoryName, [FromBody] RepoUserRole r) { if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3) return BadRequest(new FailedResponse("Repository name is empty or too short!")); var u = (await userManager.GetUserAsync(HttpContext.User))!; var tu = await userManager.FindByNameAsync(r.Username); if (tu == null) return BadRequest(new FailedResponse("User not found!")); if (u == tu) return BadRequest(new FailedResponse("Not able to update the role on self-own repository!")); var rp = await dbContext.Repositories .Include(repository => repository.Members) .ThenInclude(repositoryMember => repositoryMember.User) .FirstOrDefaultAsync(rp => rp.Name == repositoryName && u == rp.Owner); if (rp == null) return BadRequest(new FailedResponse("Repository not found!")); var m = rp.Members.FirstOrDefault(m => m.User == tu); if (m == null) return BadRequest(new FailedResponse("User is not being granted to this repository!")); rp.Members.Remove(m); await dbContext.SaveChangesAsync(); return Ok(); } }